Compare commits

...

35 Commits

Author SHA1 Message Date
5e73327b2f revisat intro.cpp, path_sprite i writer.cpp 2025-09-23 11:13:15 +02:00
720d286dcf revisat logo.cpp 2025-09-23 09:11:29 +02:00
dd13a2bd7c revisat background, game_logo, smart sprite, tiled_bg 2025-09-23 09:00:00 +02:00
1a6ef79466 revisat moving i animated sprite 2025-09-23 08:29:29 +02:00
8f83a1d13e magic numbers: moving_sprite.cpp i game_logo.cpp 2025-09-22 13:57:58 +02:00
9edfe6877f magic numbers: item.cpp 2025-09-22 13:14:23 +02:00
91b26631c6 magic numbers: bullet.cpp 2025-09-22 12:14:09 +02:00
331a690b78 fix: balloon_manager.cpp conflicto al crear los globos y detener el tiempo 2025-09-22 10:13:12 +02:00
5e3946e28b fix: no inicialitzava be animated_sprite 2025-09-22 10:03:38 +02:00
d4a0189dc8 arreglant balloon.cpp per a deltaTime pur 2025-09-19 14:15:44 +02:00
568b941990 eliminats metodes frame-based obsolets 2025-09-19 09:56:25 +02:00
49a3989ecf magic numbers: game.cpp 2025-09-19 09:11:10 +02:00
af7cb01ead magic numbers: game.cpp 2025-09-19 07:33:27 +02:00
5c82916650 magic numbers: game.cpp i player.cpp 2025-09-18 14:17:54 +02:00
0c0518adac magic numbers: game.cpp i player.cpp (en progres) 2025-09-18 13:15:43 +02:00
cb7b290818 magic numbers: game.cpp 2025-09-17 14:20:13 +02:00
ae30c9b34f magic numbers: title.cpp 2025-09-17 13:53:31 +02:00
9acd9aa631 magic numbers: intro.cpp 2025-09-17 13:25:16 +02:00
577510ff8c magic numbers: logo.cpp 2025-09-17 13:01:43 +02:00
66566913f6 delta-time: game.cpp (funciona pero va un poc massa ràpid) 2025-09-16 22:56:00 +02:00
3e6cc9dfab delta-time: explosions.cpp 2025-09-16 22:43:16 +02:00
a15e29344f delta-time: balloon.cpp
delta-time: balloon_manager.cpp
delta-time: credits.cpp
2025-09-16 22:38:48 +02:00
a96a17e11b delta-time: tabe.cpp 2025-09-16 20:29:35 +02:00
e0f6a424a9 delta-time: bullet.cpp 2025-09-16 20:26:22 +02:00
49e30f947a delta-time: title.cpp 2025-09-16 20:23:10 +02:00
470a07d28c delta-time: player.cpp 2025-09-16 19:21:44 +02:00
65716fce20 delta-time: tiled_bg.cpp 2025-09-16 17:35:03 +02:00
dfa66b0e95 delta-time: game_logo.cpp
delta-time: smart_sprite.cpp
2025-09-16 17:19:05 +02:00
3d9ffe356e Elimina .claude 2025-09-16 17:18:39 +02:00
19768cb72b delta-time: animated_sprite.cpp 2025-09-16 16:51:31 +02:00
26e0fd7247 delta-time: moving_sprite.cpp 2025-09-16 16:37:39 +02:00
e2fd470ad3 migració a delta time 2025-09-16 12:23:02 +02:00
a72ae0a5fc migració a delta time 2025-09-16 12:06:49 +02:00
7579594c22 migració a delta time 2025-09-16 10:48:22 +02:00
6c702e7e23 Logo: convertit per a usar delta time 2025-09-16 08:40:41 +02:00
57 changed files with 2191 additions and 1289 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
.vscode .vscode
.claude
build/ build/
data/config/config.txt data/config/config.txt
*.DS_Store *.DS_Store

View File

@@ -0,0 +1,81 @@
# Plan de Migración DeltaTime - Eliminación de frameFactor
## Problema Identificado
Se están usando `frameFactor` conversions en 7 archivos, lo que indica una migración incompleta a deltaTime.
El patrón `float frameFactor = deltaTime / (1000.0f / 60.0f)` simula frames de 60fps en lugar de usar tiempo real.
## Archivos Afectados y Estado
1. **balloon.cpp** - 9 ocurrencias en métodos: moveX(), moveY(), updateState(), updateCreation()
2. **balloon_manager.cpp** - 2 ocurrencias en updateBalloonDeployment()
3. **bullet.cpp** - 3 ocurrencias en move()
4. **item.cpp** - 6 ocurrencias en move()
5. **moving_sprite.cpp** - 5 ocurrencias en move()
6. **tabe.cpp** - 5 ocurrencias en update() y updateHitEffect()
7. **credits.cpp** - 3 ocurrencias en update() y handleFadeOut()
## Estrategia de Migración
### Opción A: Velocidades ya en pixels/segundo
Si las velocidades están definidas en pixels/segundo:
```cpp
// ANTES (incorrecto)
float frameFactor = deltaTime / (1000.0f / 60.0f);
pos_x_ += vel_x_ * frameFactor;
// DESPUÉS (correcto)
pos_x_ += vel_x_ * (deltaTime / 1000.0f); // deltaTime en ms -> segundos
```
### Opción B: Velocidades en pixels/frame (legacy)
Si las velocidades están en pixels/frame (sistema legacy):
```cpp
// ANTES (incorrecto con deltaTime)
float frameFactor = deltaTime / (1000.0f / 60.0f);
pos_x_ += vel_x_ * frameFactor;
// OPCIÓN 1: Convertir velocidades a pixels/segundo
static constexpr float VEL_X_PER_SECOND = VEL_X_PER_FRAME * 60.0f;
pos_x_ += VEL_X_PER_SECOND * (deltaTime / 1000.0f);
// OPCIÓN 2: Mantener frame-factor pero mejorar claridad
pos_x_ += vel_x_ * (deltaTime / FRAME_TIME_MS); // donde FRAME_TIME_MS = 16.67f
```
## Plan de Ejecución
### Fase 1: Análisis de Velocidades
- [ ] Revisar definiciones de velocidades en cada clase
- [ ] Determinar si están en pixels/frame o pixels/segundo
- [ ] Identificar constantes que necesitan conversión
### Fase 2: Migración por Archivo
- [x] **balloon.cpp**: Migrar velocidades x/y y contadores ✅
- [x] **balloon_manager.cpp**: Migrar balloon_deploy_counter_ ✅
- [x] **bullet.cpp**: Migrar velocidades de bala ✅ (VEL_Y: -3.0F→-0.18F, VEL_X: ±2.0F→±0.12F)
- [x] **item.cpp**: Migrar física de ítems ✅ (vel_x: ±1.0F→±0.06F, vel_y: -4.0F→-0.24F, accel_y: 0.2F→0.012F)
- [ ] **moving_sprite.cpp**: Migrar sistema base de movimiento
- [ ] **tabe.cpp**: Migrar movimiento y efectos
- [ ] **credits.cpp**: Migrar contadores de timing
### Fase 3: Verificación
- [ ] Compilar y probar cada archivo migrado
- [ ] Verificar que el comportamiento se mantiene consistente
- [ ] Eliminar todas las referencias a frameFactor
- [ ] Actualizar comentarios para reflejar unidades correctas
## Criterios de Éxito
1. ✅ Cero ocurrencias de "frameFactor" en el código
2. ✅ Todas las velocidades claramente documentadas (pixels/segundo vs pixels/frame)
3. ✅ Comportamiento del juego idéntico al anterior
4. ✅ Código más limpio y mantenible
## Notas Importantes
- El frameFactor actual simula 60fps: `deltaTime / 16.67ms`
- Esto significa que las velocidades actuales están en "pixels per 16.67ms"
- Para verdadero deltaTime, necesitamos convertir a "pixels per second" o usar factor de frame explícito
- Mantener constantes claras para evitar números mágicos
## Estado: En Progreso
- Análisis iniciado
- Plan documentado
- Próximo paso: Análisis de velocidades en cada archivo

View File

@@ -18,148 +18,148 @@ X3_75, 0, DEFAULT_POS_Y, LEFT, BALLOON, EXTRALARGE, 0
formation: 2 formation: 2
# Cuatro enemigos BALLOON1 uno detrás del otro. A la izquierda y hacia el centro # Cuatro enemigos BALLOON1 uno detrás del otro. A la izquierda y hacia el centro
X1_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 30 X1_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 500
X1_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 20 X1_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 334
X1_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 10 X1_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 167
X1_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 0 X1_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 0
formation: 3 formation: 3
# Cuatro enemigos BALLOON1 uno detrás del otro. A la derecha y hacia el centro # Cuatro enemigos BALLOON1 uno detrás del otro. A la derecha y hacia el centro
X1_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 30 X1_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 500
X1_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 20 X1_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 334
X1_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 10 X1_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 167
X1_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 0 X1_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 0
formation: 4 formation: 4
# Tres enemigos BALLOON2. 0, 25, 50. Hacia la derecha # Tres enemigos BALLOON2. 0, 417, 834. Hacia la derecha
X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 20 X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 334
X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 10 X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 167
X2_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0 X2_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0
formation: 5 formation: 5
# Tres enemigos BALLOON2. 50, 75, 100. Hacia la izquierda # Tres enemigos BALLOON2. 50, 75, 1667. Hacia la izquierda
X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 20 X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 334
X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 10 X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 167
X2_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0 X2_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0
formation: 6 formation: 6
# Tres enemigos BALLOON2. 0, 0, 0. Hacia la derecha # Tres enemigos BALLOON2. 0, 0, 0. Hacia la derecha
X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 20 X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 334
X2_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 10 X2_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 167
X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0 X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0
formation: 7 formation: 7
# Tres enemigos BALLOON2. 100, 100, 100. Hacia la izquierda # Tres enemigos BALLOON2. 100, 1667, 1667. Hacia la izquierda
X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 20 X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 334
X2_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 10 X2_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 167
X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0 X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0
formation: 8 formation: 8
# Seis enemigos BALLOON0. 0, 0, 0, 0, 0, 0. Hacia la derecha # Seis enemigos BALLOON0. 0, 0, 0, 0, 0, 0. Hacia la derecha
X0_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 50 X0_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 834
X0_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 40 X0_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 667
X0_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 30 X0_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 500
X0_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 20 X0_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 334
X0_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 10 X0_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 167
X0_0, 5, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 0 X0_0, 5, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 0
formation: 9 formation: 9
# Seis enemigos BALLOON0. 100, 100, 100, 100, 100, 100. Hacia la izquierda # Seis enemigos BALLOON0. 100, 1667, 1667, 1667, 1667, 1667. Hacia la izquierda
X0_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 50 X0_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 834
X0_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 40 X0_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 667
X0_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 30 X0_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 500
X0_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 20 X0_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 334
X0_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 10 X0_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 167
X0_100, -5, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 0 X0_100, -5, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 0
formation: 10 formation: 10
# Tres enemigos BALLOON3 seguidos desde la izquierda. Hacia la derecha # Tres enemigos BALLOON3 seguidos desde la izquierda. Hacia la derecha
X3_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, EXTRALARGE, 30 X3_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, EXTRALARGE, 500
X3_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, EXTRALARGE, 15 X3_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, EXTRALARGE, 250
X3_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, EXTRALARGE, 0 X3_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, EXTRALARGE, 0
formation: 11 formation: 11
# Tres enemigos BALLOON3 seguidos desde la derecha. Hacia la izquierda # Tres enemigos BALLOON3 seguidos desde la derecha. Hacia la izquierda
X3_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, EXTRALARGE, 30 X3_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, EXTRALARGE, 500
X3_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, EXTRALARGE, 15 X3_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, EXTRALARGE, 250
X3_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, EXTRALARGE, 0 X3_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, EXTRALARGE, 0
formation: 12 formation: 12
# Seis enemigos BALLOON1 uno detrás del otro. A la izquierda y hacia el centro # Seis enemigos BALLOON1 uno detrás del otro. A la izquierda y hacia el centro
X1_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 50 X1_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 834
X1_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 40 X1_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 667
X1_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 30 X1_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 500
X1_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 20 X1_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 334
X1_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 10 X1_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 167
X1_0, 5, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 0 X1_0, 5, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 0
formation: 13 formation: 13
# Seis enemigos BALLOON1 uno detrás del otro. A la derecha y hacia el centro # Seis enemigos BALLOON1 uno detrás del otro. A la derecha y hacia el centro
X1_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 50 X1_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 834
X1_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 40 X1_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 667
X1_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 30 X1_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 500
X1_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 20 X1_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 334
X1_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 10 X1_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 167
X1_100, -5, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 0 X1_100, -5, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 0
formation: 14 formation: 14
# Cinco enemigos BALLOON2. Hacia la derecha. Separados # Cinco enemigos BALLOON2. Hacia la derecha. Separados
X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 40 X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 667
X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 30 X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 500
X2_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 20 X2_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 334
X2_0, 6, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 10 X2_0, 6, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 167
X2_0, 8, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0 X2_0, 8, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0
formation: 15 formation: 15
# Cinco enemigos BALLOON2. Hacia la izquierda. Separados # Cinco enemigos BALLOON2. Hacia la izquierda. Separados
X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 40 X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 667
X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 30 X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 500
X2_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 20 X2_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 334
X2_100, -6, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 10 X2_100, -6, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 167
X2_100, -8, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0 X2_100, -8, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0
formation: 16 formation: 16
# Cinco enemigos BALLOON2. Hacia la derecha. Juntos # Cinco enemigos BALLOON2. Hacia la derecha. Juntos
X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 40 X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 667
X2_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 30 X2_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 500
X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 20 X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 334
X2_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 10 X2_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 167
X2_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0 X2_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0
formation: 17 formation: 17
# Cinco enemigos BALLOON2. Hacia la izquierda. Juntos # Cinco enemigos BALLOON2. Hacia la izquierda. Juntos
X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 40 X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 667
X2_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 30 X2_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 500
X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 20 X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 334
X2_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 10 X2_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 167
X2_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0 X2_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0
formation: 18 formation: 18
# Doce enemigos BALLOON0. Hacia la derecha. Juntos # Doce enemigos BALLOON0. Hacia la derecha. Juntos
X0_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 110 X0_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 1834
X0_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 100 X0_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 1667
X0_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 90 X0_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 25000
X0_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 80 X0_0, 50, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 1334
X0_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 70 X0_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 70
X0_0, 5, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 60 X0_0, 83, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 1000
X0_0, 6, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 50 X0_0, 100, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 830
X0_0, 7, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 40 X0_0, 7, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 40
X0_0, 8, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 30 X0_0, 8, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 500
X0_0, 9, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 20 X0_0, 150, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 20
X0_0, 10, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 10 X0_0, 167, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 10
X0_0, 11, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 0 X0_0, 11, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 0
formation: 19 formation: 19
# Doce enemigos BALLOON0. Hacia la izquierda. Juntos # Doce enemigos BALLOON0. Hacia la izquierda. Juntos
X0_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 110 X0_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 1834
X0_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 100 X0_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 1667
X0_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 90 X0_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 25000
X0_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 80 X0_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 1334
X0_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 70 X0_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 70
X0_100, -5, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 60 X0_100, -5, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 1000
X0_100, -6, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 50 X0_100, -6, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 830
X0_100, -7, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 40 X0_100, -7, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 40
X0_100, -8, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 30 X0_100, -8, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 500
X0_100, -9, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 20 X0_100, -9, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 20
X0_100, -10, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 10 X0_100, -10, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 10
X0_100, -11, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 0 X0_100, -11, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 0
@@ -173,105 +173,105 @@ X3_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, EXTRALARGE, 0
formation: 21 formation: 21
# Diez enemigos BALLOON1 uno detrás del otro. Izquierda/derecha. Simétricos # Diez enemigos BALLOON1 uno detrás del otro. Izquierda/derecha. Simétricos
X1_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 12 X1_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 200
X1_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 9 X1_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 150
X1_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 6 X1_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 100
X1_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 3 X1_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 50
X1_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 0 X1_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, MEDIUM, 0
X1_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 12 X1_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 200
X1_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 9 X1_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 150
X1_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 6 X1_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 100
X1_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 3 X1_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 50
X1_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 0 X1_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, MEDIUM, 0
formation: 22 formation: 22
# Diez enemigos BALLOON2. Hacia la derecha/izquierda. Separados. Simétricos # Diez enemigos BALLOON2. Hacia la derecha/izquierda. Separados. Simétricos
X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 40 X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 667
X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 30 X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 500
X2_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 20 X2_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 334
X2_0, 6, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 10 X2_0, 6, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 167
X2_0, 8, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0 X2_0, 8, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0
X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 40 X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 667
X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 30 X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 500
X2_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 20 X2_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 334
X2_100, -6, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 10 X2_100, -6, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 167
X2_100, -8, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0 X2_100, -8, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0
formation: 23 formation: 23
# Diez enemigos BALLOON2. Hacia la derecha. Juntos. Simétricos # Diez enemigos BALLOON2. Hacia la derecha. Juntos. Simétricos
X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 40 X2_0, 0, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 667
X2_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 30 X2_0, 1, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 500
X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 20 X2_0, 2, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 334
X2_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 10 X2_0, 3, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 167
X2_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0 X2_0, 4, DEFAULT_POS_Y, RIGHT, BALLOON, LARGE, 0
X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 40 X2_100, 0, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 667
X2_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 30 X2_100, -1, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 500
X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 20 X2_100, -2, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 334
X2_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 10 X2_100, -3, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 167
X2_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0 X2_100, -4, DEFAULT_POS_Y, LEFT, BALLOON, LARGE, 0
formation: 24 formation: 24
# Treinta enemigos BALLOON0. Del centro hacia los extremos. Juntos. Simétricos # Treinta enemigos BALLOON0. Del centro hacia los extremos. Juntos. Simétricos
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 0 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 0
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 5 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 83
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 10 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 167
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 15 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 250
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 20 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 334
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 25 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 417
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 30 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 500
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 35 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 584
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 40 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 667
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 45 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 750
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 50 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 834
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 55 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 917
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 60 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 16700
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 65 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 16784
X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 70 X0_50, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 1167
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 0 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 0
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 5 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 83
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 10 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 167
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 15 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 250
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 20 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 334
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 25 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 417
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 30 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 500
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 35 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 584
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 40 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 667
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 45 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 750
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 50 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 834
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 55 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 917
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 60 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 16700
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 65 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 16784
X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 70 X0_50, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 1167
formation: 25 formation: 25
# Treinta enemigos BALLOON0. Del centro hacia adentro. Juntos. Simétricos # Treinta enemigos BALLOON0. Del centro hacia adentro. Juntos. Simétricos
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 70 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 1167
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 65 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 16784
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 60 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 16700
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 55 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 917
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 50 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 834
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 45 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 750
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 40 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 667
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 35 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 584
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 30 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 500
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 25 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 417
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 20 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 334
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 15 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 250
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 10 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 167
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 5 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 83
X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 0 X0_50 + 20, 0, DEFAULT_POS_Y, LEFT, BALLOON, SMALL, 0
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 70 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 1167
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 65 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 16784
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 60 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 16700
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 55 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 917
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 50 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 834
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 45 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 750
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 40 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 667
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 35 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 584
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 30 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 500
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 25 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 417
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 20 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 334
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 15 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 250
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 10 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 167
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 5 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 83
X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 0 X0_50 - 20, 0, DEFAULT_POS_Y, RIGHT, BALLOON, SMALL, 0

View File

@@ -10,8 +10,8 @@ game.play_area.rect.x 0 # Posición X de la zona jugable
game.play_area.rect.y 0 # Posición Y de la zona jugable game.play_area.rect.y 0 # Posición Y de la zona jugable
game.play_area.rect.w 320 # Ancho de la zona jugable game.play_area.rect.w 320 # Ancho de la zona jugable
game.play_area.rect.h 200 # Alto de la zona jugable game.play_area.rect.h 200 # Alto de la zona jugable
game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada game.name_entry_idle_time 10000 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida game.name_entry_total_time 60000 # Segundos totales para introducir el nombre al finalizar la partida
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop
@@ -39,24 +39,24 @@ scoreboard.text_color2 FFFFFF # Color secundario del texto del marca
scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás (segundos) scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás (segundos)
# --- TITLE --- # --- TITLE ---
title.press_start_position 180 # Posición Y del texto "Press Start" title.press_start_position 180 # Posición Y del texto "Press Start"
title.title_duration 800 # Duración de la pantalla de título (frames) title.title_duration 14000 # Duración de la pantalla de título (milisegundos)
title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition" title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition"
title.title_c_c_position 80 # Posición Y del título principal title.title_c_c_position 80 # Posición Y del título principal
title.bg_color 41526F # Color de fondo en la sección titulo title.bg_color 41526F # Color de fondo en la sección titulo
# --- BACKGROUND --- # --- BACKGROUND ---
background.attenuate_color FFFFFF00 # Color de atenuación del fondo (RGBA hexadecimal) background.attenuate_color FFFFFF00 # Color de atenuación del fondo (RGBA hexadecimal)
# --- BALLOONS --- # --- BALLOONS --- (deltaTime puro: vel en pixels/ms, grav en pixels/ms²)
balloon.settings[0].vel 2.75f # Velocidad inicial del globo 1 balloon.settings[0].vel 0.165f # Velocidad inicial del globo 1 (pixels/ms)
balloon.settings[0].grav 0.09f # Gravedad aplicada al globo 1 balloon.settings[0].grav 0.00032f # Gravedad aplicada al globo 1 (pixels/ms²)
balloon.settings[1].vel 3.70f # Velocidad inicial del globo 2 balloon.settings[1].vel 0.222f # Velocidad inicial del globo 2 (pixels/ms)
balloon.settings[1].grav 0.10f # Gravedad aplicada al globo 2 balloon.settings[1].grav 0.00036f # Gravedad aplicada al globo 2 (pixels/ms²)
balloon.settings[2].vel 4.70f # Velocidad inicial del globo 3 balloon.settings[2].vel 0.282f # Velocidad inicial del globo 3 (pixels/ms)
balloon.settings[2].grav 0.10f # Gravedad aplicada al globo 3 balloon.settings[2].grav 0.00036f # Gravedad aplicada al globo 3 (pixels/ms²)
balloon.settings[3].vel 5.45f # Velocidad inicial del globo 4 balloon.settings[3].vel 0.327f # Velocidad inicial del globo 4 (pixels/ms)
balloon.settings[3].grav 0.10f # Gravedad aplicada al globo 4 balloon.settings[3].grav 0.00036f # Gravedad aplicada al globo 4 (pixels/ms²)
balloon.color[0] blue # Color de creación del globo normal balloon.color[0] blue # Color de creación del globo normal
balloon.color[1] orange # Color del globo normal balloon.color[1] orange # Color del globo normal

View File

@@ -10,8 +10,8 @@ game.play_area.rect.x 0 # Posición X de la zona jugable
game.play_area.rect.y 0 # Posición Y de la zona jugable game.play_area.rect.y 0 # Posición Y de la zona jugable
game.play_area.rect.w 320 # Ancho de la zona jugable game.play_area.rect.w 320 # Ancho de la zona jugable
game.play_area.rect.h 216 # Alto de la zona jugable game.play_area.rect.h 216 # Alto de la zona jugable
game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada game.name_entry_idle_time 10000 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida game.name_entry_total_time 60000 # Segundos totales para introducir el nombre al finalizar la partida
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop
@@ -39,24 +39,24 @@ scoreboard.text_color2 FFFFFF # Color secundario del texto del marca
scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás (segundos) scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás (segundos)
# --- TITLE --- # --- TITLE ---
title.press_start_position 180 # Posición Y del texto "Press Start" title.press_start_position 180 # Posición Y del texto "Press Start"
title.title_duration 800 # Duración de la pantalla de título (frames) title.title_duration 14000 # Duración de la pantalla de título (milisegundos)
title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition" title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition"
title.title_c_c_position 80 # Posición Y del título principal title.title_c_c_position 80 # Posición Y del título principal
title.bg_color 41526F # Color de fondo en la sección titulo title.bg_color 41526F # Color de fondo en la sección titulo
# --- BACKGROUND --- # --- BACKGROUND ---
background.attenuate_color FFFFFF00 # Color de atenuación del fondo (RGBA hexadecimal) background.attenuate_color FFFFFF00 # Color de atenuación del fondo (RGBA hexadecimal)
# --- BALLOONS --- # --- BALLOONS --- (deltaTime puro: vel en pixels/ms, grav en pixels/ms²)
balloon.settings[0].vel 2.75f # Velocidad inicial del globo 1 balloon.settings[0].vel 0.165f # Velocidad inicial del globo 1 (pixels/ms)
balloon.settings[0].grav 0.09f # Gravedad aplicada al globo 1 balloon.settings[0].grav 0.00032f # Gravedad aplicada al globo 1 (pixels/ms²)
balloon.settings[1].vel 3.70f # Velocidad inicial del globo 2 balloon.settings[1].vel 0.222f # Velocidad inicial del globo 2 (pixels/ms)
balloon.settings[1].grav 0.10f # Gravedad aplicada al globo 2 balloon.settings[1].grav 0.00036f # Gravedad aplicada al globo 2 (pixels/ms²)
balloon.settings[2].vel 4.70f # Velocidad inicial del globo 3 balloon.settings[2].vel 0.282f # Velocidad inicial del globo 3 (pixels/ms)
balloon.settings[2].grav 0.10f # Gravedad aplicada al globo 3 balloon.settings[2].grav 0.00036f # Gravedad aplicada al globo 3 (pixels/ms²)
balloon.settings[3].vel 5.45f # Velocidad inicial del globo 4 balloon.settings[3].vel 0.327f # Velocidad inicial del globo 4 (pixels/ms)
balloon.settings[3].grav 0.10f # Gravedad aplicada al globo 4 balloon.settings[3].grav 0.00036f # Gravedad aplicada al globo 4 (pixels/ms²)
balloon.color[0] blue # Color de creación del globo normal balloon.color[0] blue # Color de creación del globo normal
balloon.color[1] orange # Color del globo normal balloon.color[1] orange # Color del globo normal

79
deltatime_cleanup_plan.md Normal file
View File

@@ -0,0 +1,79 @@
# Plan de Limpieza Post-Migración DeltaTime
## Estado Actual
✅ Migración básica completada: bullet.cpp, item.cpp, moving_sprite.cpp, game_logo.cpp
✅ Magic numbers convertidos a constantes en game_logo.cpp
## Tareas Pendientes
### 1. Eliminar Contadores Frame-Based
- [ ] Buscar todos los contadores que usen lógica frame-based
- [ ] Convertir a timers basados en deltaTime
- [ ] Eliminar variables como `counter_`, `frame_counter_`, etc.
- [ ] Patrón: `if (counter-- <= 0)``if (timer >= DURATION_MS)`
- [ ] **IMPORTANTE**: Todos los contadores han de ser crecientes, de cero hasta llegar a la constante que define su tope
### 2. Revisar Inicializaciones de Aceleraciones MovingSprite
- [ ] Buscar todas las llamadas a `setAccelX()`, `setAccelY()`
- [ ] Buscar asignaciones directas a `ax_`, `ay_`
- [ ] Convertir de `pixels/frame²` a `pixels/ms²`
- [ ] Factor de conversión: `valor_original / (16.67)²`
### 3. Problema Detectado: Demo - Creación Incorrecta de Globos
- [ ] Investigar cómo se crean los globos en modo demo
- [ ] Verificar si usan timing frame-based obsoleto
- [ ] Corregir la lógica de creación para deltaTime
### 4. Búsqueda de Patrones Problemáticos
- [ ] Buscar `frameFactor` residual
- [ ] Buscar `1000.0f / 60.0f` hardcodeado
- [ ] Buscar `16.67f` hardcodeado
- [ ] Buscar comentarios con "frame" o "60fps"
- [ ] Localizar magic numbers y convertirlos a constantes con nombres descriptivos
- [ ] **IMPORTANTE**: Modificar speed en ficheros .ani - está en frames, hay que pasarlo a milisegundos (multiplicar speed por 1000/60 = 16.67)
- [ ] **SmartSprite**: Revisar inicialización de SmartSprites en el código - cambiar setFinishedCounter() a setFinishedDelay() y convertir valores de frames a milisegundos
### 5. Cambio de Unidades de Tiempo en sections/*
- [ ] Cambiar el cálculo de deltatime en source/sections/* para que devuelva segundos (float) en lugar de milisegundos
- [ ] Cambiar velocidades de pixeles/ms a pixeles/segundos para evitar valores absurdamente pequeños
- [ ] Cambiar aceleraciones de pixeles/ms² a pixeles/segundos²
- [ ] Actualizar todas las constantes de tiempo en archivos de sections
### 6. Archivos Prioritarios a Revisar
- [ ] **player.cpp** - puede tener aceleraciones
- [ ] **balloon.cpp** - contadores de estado
- [ ] **stage.cpp** - timers de nivel
- [ ] **credits.cpp** - efectos de texto
- [ ] **tabe.cpp** - movimiento del protagonista
- [ ] **sections/*.cpp** - transiciones y efectos
### 7. Validación Final
- [ ] Compilar sin warnings
- [ ] Probar gameplay normal
- [ ] Probar modo demo
- [ ] Verificar que no hay saltos de velocidad
- [ ] Confirmar que el timing es consistente en diferentes framerates
## Comandos Útiles de Búsqueda
```bash
# Buscar contadores frame-based
rg "counter.*--|\+\+.*counter|counter.*\+\+|--.*counter"
# Buscar inicializaciones de aceleración
rg "setAccel|\.ax.*=|\.ay.*="
# Buscar hardcoded framerates
rg "60\.0|16\.67|1000\.0.*60"
```
## DECISIÓN IMPORTANTE: TODO EL CÓDIGO USA SEGUNDOS
- **CAMBIO DE PLAN**: Todo el código del juego debe usar deltaTime en segundos (float)
- **NO** debe haber soporte para frames, milisegundos y segundos simultáneamente
- **SOLO SEGUNDOS** en todo el codebase
- Velocidades en `pixels/segundos`, aceleraciones en `pixels/segundos²`
- Todos los contadores deben ser crecientes (0 → constante_tope)
- Eliminar todos los métodos duales (updateS, setSpeedS, etc.) - solo una versión
- Convertir completamente: path_sprite.cpp, writer.cpp, tiled_bg.cpp, etc.
- Documentar las conversiones en comentarios
- Crear constantes para valores repetidos
- Evitar números mágicos

View File

@@ -43,6 +43,10 @@ auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffe
std::vector<std::string> buffer; std::vector<std::string> buffer;
std::string line; std::string line;
while (std::getline(input_stream, line)) { while (std::getline(input_stream, line)) {
// Eliminar caracteres de retorno de carro (\r) al final de la línea
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
if (!line.empty()) { if (!line.empty()) {
buffer.push_back(line); buffer.push_back(line);
} }
@@ -82,33 +86,33 @@ auto AnimatedSprite::getAnimationIndex(const std::string& name) -> int {
return -1; return -1;
} }
// Calcula el frame correspondiente a la animación // Calcula el frame correspondiente a la animación (time-based)
void AnimatedSprite::animate() { void AnimatedSprite::animate(float deltaTime) {
if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) { if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) {
return; return;
} }
// Calcula el frame actual a partir del contador // Acumular tiempo transcurrido
animations_[current_animation_].current_frame = animations_[current_animation_].counter / animations_[current_animation_].speed; animations_[current_animation_].time_accumulator += deltaTime;
// Si alcanza el final de la animación, reinicia el contador de la animación // Verificar si es momento de cambiar frame
// en función de la variable loop y coloca el nuevo frame if (animations_[current_animation_].time_accumulator >= animations_[current_animation_].speed) {
if (animations_[current_animation_].current_frame >= animations_[current_animation_].frames.size()) { animations_[current_animation_].time_accumulator -= animations_[current_animation_].speed;
if (animations_[current_animation_].loop == -1) { // Si no hay loop, deja el último frame animations_[current_animation_].current_frame++;
animations_[current_animation_].current_frame = animations_[current_animation_].frames.size();
animations_[current_animation_].completed = true; // Si alcanza el final de la animación
} else { // Si hay loop, vuelve al frame indicado if (animations_[current_animation_].current_frame >= animations_[current_animation_].frames.size()) {
animations_[current_animation_].counter = 0; if (animations_[current_animation_].loop == -1) { // Si no hay loop, deja el último frame
animations_[current_animation_].current_frame = animations_[current_animation_].loop; animations_[current_animation_].current_frame = animations_[current_animation_].frames.size() - 1;
animations_[current_animation_].completed = true;
} else { // Si hay loop, vuelve al frame indicado
animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].current_frame = animations_[current_animation_].loop;
}
} }
}
// En caso contrario // Actualizar el sprite clip
else {
// Escoge el frame correspondiente de la animación
updateSpriteClip(); updateSpriteClip();
// Incrementa el contador de la animacion
animations_[current_animation_].counter++;
} }
} }
@@ -125,11 +129,11 @@ void AnimatedSprite::setCurrentAnimation(const std::string& name, bool reset) {
current_animation_ = NEW_ANIMATION; current_animation_ = NEW_ANIMATION;
if (reset) { if (reset) {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0; animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
} else { } else {
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size() - 1); animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size() - 1);
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; animations_[current_animation_].time_accumulator = animations_[OLD_ANIMATION].time_accumulator;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
} }
updateSpriteClip(); updateSpriteClip();
@@ -144,27 +148,27 @@ void AnimatedSprite::setCurrentAnimation(int index, bool reset) {
current_animation_ = NEW_ANIMATION; current_animation_ = NEW_ANIMATION;
if (reset) { if (reset) {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0; animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
} else { } else {
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size()); animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size());
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; animations_[current_animation_].time_accumulator = animations_[OLD_ANIMATION].time_accumulator;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
} }
updateSpriteClip(); updateSpriteClip();
} }
} }
// Actualiza las variables del objeto // Actualiza las variables del objeto (time-based)
void AnimatedSprite::update() { void AnimatedSprite::update(float deltaTime) {
animate(); animate(deltaTime);
MovingSprite::update(); MovingSprite::update(deltaTime);
} }
// Reinicia la animación // Reinicia la animación
void AnimatedSprite::resetAnimation() { void AnimatedSprite::resetAnimation() {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0; animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
animations_[current_animation_].paused = false; animations_[current_animation_].paused = false;
updateSpriteClip(); updateSpriteClip();
@@ -190,6 +194,12 @@ void AnimatedSprite::loadFromAnimationsFileBuffer(const AnimationsFileBuffer& so
// Pone un valor por defecto // Pone un valor por defecto
setWidth(config.frame_width); setWidth(config.frame_width);
setHeight(config.frame_height); setHeight(config.frame_height);
// Establece el primer frame inmediatamente si hay animaciones
if (!animations_.empty()) {
current_animation_ = 0;
updateSpriteClip();
}
} }
// Procesa una línea de configuración // Procesa una línea de configuración

View File

@@ -17,15 +17,15 @@ class Texture;
// --- Estructuras --- // --- Estructuras ---
struct Animation { struct Animation {
static constexpr int DEFAULT_SPEED = 5; static constexpr float DEFAULT_SPEED = 80.0F;
std::string name; // Nombre de la animación std::string name; // Nombre de la animación
std::vector<SDL_FRect> frames; // Frames que componen la animación std::vector<SDL_FRect> frames; // Frames que componen la animación
int speed{DEFAULT_SPEED}; // Velocidad de reproducción float speed{DEFAULT_SPEED}; // Velocidad de reproducción (ms entre frames)
int loop{0}; // Frame de vuelta al terminar (-1 para no repetir) int loop{0}; // Frame de vuelta al terminar (-1 para no repetir)
bool completed{false}; // Indica si la animación ha finalizado bool completed{false}; // Indica si la animación ha finalizado
size_t current_frame{0}; // Frame actual en reproducción size_t current_frame{0}; // Frame actual en reproducción
int counter{0}; // Contador para la animación float time_accumulator{0.0f}; // Acumulador de tiempo para animaciones time-based
bool paused{false}; // La animación no avanza bool paused{false}; // La animación no avanza
Animation() = default; Animation() = default;
@@ -55,7 +55,7 @@ class AnimatedSprite : public MovingSprite {
~AnimatedSprite() override = default; ~AnimatedSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
void update() override; // Actualiza la animación void update(float deltaTime); // Actualiza la animación (time-based)
// --- Control de animaciones --- // --- Control de animaciones ---
void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre
@@ -78,7 +78,7 @@ class AnimatedSprite : public MovingSprite {
int current_animation_ = 0; // Índice de la animación activa int current_animation_ = 0; // Índice de la animación activa
// --- Métodos internos --- // --- Métodos internos ---
void animate(); // Calcula el frame correspondiente a la animación void animate(float deltaTime); // Calcula el frame correspondiente a la animación (time-based)
void loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source); // Carga la animación desde un vector de cadenas void loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source); // Carga la animación desde un vector de cadenas
void processConfigLine(const std::string& line, AnimationConfig& config); // Procesa una línea de configuración void processConfigLine(const std::string& line, AnimationConfig& config); // Procesa una línea de configuración
void updateFrameCalculations(AnimationConfig& config); // Actualiza los cálculos basados en las dimensiones del frame void updateFrameCalculations(AnimationConfig& config); // Actualiza los cálculos basados en las dimensiones del frame

View File

@@ -126,7 +126,7 @@ void Background::initializeTextures() {
} }
// Actualiza la lógica del objeto // Actualiza la lógica del objeto
void Background::update() { void Background::update(float delta_time) {
// Actualiza la progresión y calcula transiciones // Actualiza la progresión y calcula transiciones
if (!manual_mode_) { if (!manual_mode_) {
updateProgression(); updateProgression();
@@ -136,10 +136,14 @@ void Background::update() {
updateAlphaColorTexture(); updateAlphaColorTexture();
// Actualiza las nubes // Actualiza las nubes
updateClouds(); updateClouds(delta_time);
// Calcula el frame de la hierba // Actualiza timer de hierba
grass_sprite_->setSpriteClip(0, (10 * (counter_ / 20 % 2)), 320, 10); grass_timer_ += delta_time;
// Calcula el frame de la hierba (alterna cada GRASS_FRAME_DURATION ms)
int grass_frame = static_cast<int>(grass_timer_ / GRASS_FRAME_DURATION) % 2;
grass_sprite_->setSpriteClip(0, (10 * grass_frame), 320, 10);
// Calcula el valor de alpha // Calcula el valor de alpha
alpha_ = std::max((255 - (int)(255 * transition_)), 0); alpha_ = std::max((255 - (int)(255 * transition_)), 0);
@@ -148,9 +152,6 @@ void Background::update() {
sun_sprite_->setPosition(sun_path_.at(sun_index_)); sun_sprite_->setPosition(sun_path_.at(sun_index_));
moon_sprite_->setPosition(moon_path_.at(moon_index_)); moon_sprite_->setPosition(moon_path_.at(moon_index_));
// Incrementa el contador
++counter_;
// Compone todos los elementos del fondo en la textura // Compone todos los elementos del fondo en la textura
fillCanvas(); fillCanvas();
} }
@@ -314,12 +315,12 @@ void Background::updateCloudsSpeed() {
} }
// Actualiza las nubes // Actualiza las nubes
void Background::updateClouds() { void Background::updateClouds(float deltaTime) {
// Mueve las nubes // Mueve las nubes
top_clouds_sprite_a_->update(); top_clouds_sprite_a_->update(deltaTime);
top_clouds_sprite_b_->update(); top_clouds_sprite_b_->update(deltaTime);
bottom_clouds_sprite_a_->update(); bottom_clouds_sprite_a_->update(deltaTime);
bottom_clouds_sprite_b_->update(); bottom_clouds_sprite_b_->update(deltaTime);
// Calcula el offset de las nubes // Calcula el offset de las nubes
if (top_clouds_sprite_a_->getPosX() < -top_clouds_sprite_a_->getWidth()) { if (top_clouds_sprite_a_->getPosX() < -top_clouds_sprite_a_->getWidth()) {

View File

@@ -31,7 +31,7 @@ class Background {
~Background(); // Destructor ~Background(); // Destructor
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la lógica del objeto void update(float delta_time); // Actualiza la lógica del objeto
void render(); // Dibuja el objeto void render(); // Dibuja el objeto
void reset(); // Reinicia la progresión void reset(); // Reinicia la progresión
@@ -107,7 +107,8 @@ class Background {
float clouds_speed_ = 0; // Velocidad de las nubes float clouds_speed_ = 0; // Velocidad de las nubes
float transition_ = 0; // Porcentaje de transición float transition_ = 0; // Porcentaje de transición
size_t gradient_number_ = 0; // Índice de fondo degradado size_t gradient_number_ = 0; // Índice de fondo degradado
size_t counter_ = 0; // Contador interno float grass_timer_ = 0.0f; // Timer para animación de hierba (ms)
static constexpr float GRASS_FRAME_DURATION = 333.34f; // Duración por frame de hierba (20 frames * 16.67ms)
size_t alpha_color_texture_ = 0; // Transparencia de atenuación size_t alpha_color_texture_ = 0; // Transparencia de atenuación
size_t previous_alpha_color_texture_ = 0; // Transparencia anterior size_t previous_alpha_color_texture_ = 0; // Transparencia anterior
size_t sun_index_ = 0; // Índice del recorrido del sol size_t sun_index_ = 0; // Índice del recorrido del sol
@@ -129,7 +130,7 @@ class Background {
void renderBottomClouds(); // Dibuja las nubes inferiores void renderBottomClouds(); // Dibuja las nubes inferiores
void fillCanvas(); // Compone todos los elementos en la textura void fillCanvas(); // Compone todos los elementos en la textura
void updateAlphaColorTexture(); // Actualiza el alpha de la textura de atenuación void updateAlphaColorTexture(); // Actualiza el alpha de la textura de atenuación
void updateClouds(); // Actualiza el movimiento de las nubes void updateClouds(float deltaTime); // Actualiza el movimiento de las nubes (time-based)
void createSunPath(); // Precalcula el recorrido del sol void createSunPath(); // Precalcula el recorrido del sol
void createMoonPath(); // Precalcula el recorrido de la luna void createMoonPath(); // Precalcula el recorrido de la luna
}; };

View File

@@ -23,7 +23,7 @@ Balloon::Balloon(const Config& config)
creation_counter_ini_(config.creation_counter), creation_counter_ini_(config.creation_counter),
type_(config.type), type_(config.type),
size_(config.size), size_(config.size),
speed_(config.speed), game_tempo_(config.game_tempo),
play_area_(config.play_area), play_area_(config.play_area),
sound_(config.sound) { sound_(config.sound) {
switch (type_) { switch (type_) {
@@ -89,6 +89,12 @@ Balloon::Balloon(const Config& config)
// Establece la animación a usar // Establece la animación a usar
setAnimation(); setAnimation();
// Si no se está creando (creation_counter = 0), asegurar estado activo
if (!being_created_) {
start();
setInvulnerable(false);
}
} }
// Centra el globo en la posición X // Centra el globo en la posición X
@@ -135,19 +141,20 @@ void Balloon::render() {
} }
} }
// Actualiza la posición y estados del globo // Actualiza la posición y estados del globo (time-based)
void Balloon::move() { void Balloon::move(float deltaTime) {
if (isStopped()) { if (isStopped()) {
return; return;
} }
handleHorizontalMovement(); handleHorizontalMovement(deltaTime);
handleVerticalMovement(); handleVerticalMovement(deltaTime);
applyGravity(); applyGravity(deltaTime);
} }
void Balloon::handleHorizontalMovement() { void Balloon::handleHorizontalMovement(float deltaTime) {
x_ += vx_ * speed_; // DeltaTime puro: velocidad (pixels/ms) * tempo * tiempo (ms)
x_ += vx_ * game_tempo_ * deltaTime;
const int CLIP = 2; const int CLIP = 2;
const float MIN_X = play_area_.x - CLIP; const float MIN_X = play_area_.x - CLIP;
@@ -158,8 +165,9 @@ void Balloon::handleHorizontalMovement() {
} }
} }
void Balloon::handleVerticalMovement() { void Balloon::handleVerticalMovement(float deltaTime) {
y_ += vy_ * speed_; // DeltaTime puro: velocidad (pixels/ms) * tempo * tiempo (ms)
y_ += vy_ * game_tempo_ * deltaTime;
if (shouldCheckTopCollision()) { if (shouldCheckTopCollision()) {
handleTopCollision(); handleTopCollision();
@@ -214,20 +222,9 @@ void Balloon::handleBottomCollision() {
} }
} }
void Balloon::applyGravity() { void Balloon::applyGravity(float deltaTime) {
/* // DeltaTime puro: aceleración (pixels/ms²) * tempo * tiempo (ms)
Para aplicar la gravedad, el diseño original la aplicaba en cada iteración del bucle vy_ += gravity_ * game_tempo_ * deltaTime;
Al añadir el modificador de velocidad se reduce la distancia que recorre el objeto y por
tanto recibe mas gravedad. Para solucionarlo se va a aplicar la gravedad cuando se haya
recorrido una distancia igual a la velocidad en Y, que era el cálculo inicial
*/
travel_y_ += speed_;
if (travel_y_ >= 1.0F) {
travel_y_ -= 1.0F;
vy_ += gravity_;
}
} }
void Balloon::playBouncingSound() { void Balloon::playBouncingSound() {
@@ -242,19 +239,20 @@ void Balloon::playPoppingSound() {
} }
} }
// Actualiza al globo a su posicion, animación y controla los contadores // Actualiza al globo a su posicion, animación y controla los contadores (time-based)
void Balloon::update() { void Balloon::update(float deltaTime) {
move(); move(deltaTime);
updateState(); updateState(deltaTime);
updateBounceEffect(); updateBounceEffect();
shiftSprite(); shiftSprite();
shiftColliders(); shiftColliders();
sprite_->update(); sprite_->update(deltaTime);
++counter_; // Contador interno con deltaTime puro
counter_ += deltaTime;
} }
// Actualiza los estados del globo // Actualiza los estados del globo (time-based)
void Balloon::updateState() { void Balloon::updateState(float deltaTime) {
// Si se está creando // Si se está creando
if (isBeingCreated()) { if (isBeingCreated()) {
// Actualiza el valor de las variables // Actualiza el valor de las variables
@@ -263,9 +261,13 @@ void Balloon::updateState() {
if (creation_counter_ > 0) { if (creation_counter_ > 0) {
// Desplaza lentamente el globo hacia abajo y hacia un lado // Desplaza lentamente el globo hacia abajo y hacia un lado
if (creation_counter_ % 10 == 0) { // Cada 166ms (equivalente a 10 frames a 60fps)
movement_accumulator_ += deltaTime;
if (movement_accumulator_ >= 166.0f) {
movement_accumulator_ -= 166.0f;
y_++; y_++;
x_ += vx_; x_ += vx_ * 10.0f; // Movimiento equivalente a 10 frames de velocidad horizontal
// Comprueba no se salga por los laterales // Comprueba no se salga por los laterales
const int MIN_X = play_area_.x; const int MIN_X = play_area_.x;
@@ -273,11 +275,12 @@ void Balloon::updateState() {
if (x_ < MIN_X || x_ > MAX_X) { if (x_ < MIN_X || x_ > MAX_X) {
// Corrige y cambia el sentido de la velocidad // Corrige y cambia el sentido de la velocidad
x_ -= vx_; x_ -= vx_ * 10.0f;
vx_ = -vx_; vx_ = -vx_;
} }
} }
--creation_counter_; creation_counter_ -= deltaTime;
if (creation_counter_ < 0) creation_counter_ = 0;
} }
else { else {
@@ -311,11 +314,14 @@ void Balloon::setAnimation() {
} }
// Establece el frame de animación // Establece el frame de animación
std::string chosen_animation;
if (use_reversed_colors_) { if (use_reversed_colors_) {
sprite_->setCurrentAnimation(creating_animation); chosen_animation = creating_animation;
} else { } else {
sprite_->setCurrentAnimation(isBeingCreated() ? creating_animation : normal_animation); chosen_animation = isBeingCreated() ? creating_animation : normal_animation;
} }
sprite_->setCurrentAnimation(chosen_animation);
} }
// Detiene el globo // Detiene el globo

View File

@@ -36,10 +36,12 @@ class Balloon {
"balloon_pop2.wav", "balloon_pop2.wav",
"balloon_pop3.wav"}; "balloon_pop3.wav"};
static constexpr float VELX_POSITIVE = 0.7F; // Velocidades horizontales en pixels/ms (convertidas desde 0.7 pixels/frame a 60fps)
static constexpr float VELX_NEGATIVE = -0.7F; static constexpr float VELX_POSITIVE = 0.7F / (1000.0F / 60.0F); // ~0.042 pixels/ms
static constexpr float VELX_NEGATIVE = -0.7F / (1000.0F / 60.0F); // ~-0.042 pixels/ms
static constexpr std::array<float, 5> SPEED = {0.60F, 0.70F, 0.80F, 0.90F, 1.00F}; // Multiplicadores de tempo del juego (sin cambios, son puros multiplicadores)
static constexpr std::array<float, 5> GAME_TEMPO = {0.60F, 0.70F, 0.80F, 0.90F, 1.00F};
static constexpr int POWERBALL_SCREENPOWER_MINIMUM = 10; static constexpr int POWERBALL_SCREENPOWER_MINIMUM = 10;
static constexpr int POWERBALL_COUNTER = 8; static constexpr int POWERBALL_COUNTER = 8;
@@ -74,7 +76,7 @@ class Balloon {
Type type = Type::BALLOON; Type type = Type::BALLOON;
Size size = Size::EXTRALARGE; Size size = Size::EXTRALARGE;
float vel_x = VELX_POSITIVE; float vel_x = VELX_POSITIVE;
float speed = SPEED.at(0); float game_tempo = GAME_TEMPO.at(0);
Uint16 creation_counter = 0; Uint16 creation_counter = 0;
SDL_FRect play_area = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; SDL_FRect play_area = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
std::shared_ptr<Texture> texture = nullptr; std::shared_ptr<Texture> texture = nullptr;
@@ -87,11 +89,11 @@ class Balloon {
~Balloon() = default; ~Balloon() = default;
// --- Métodos principales --- // --- Métodos principales ---
void alignTo(int x); // Centra el globo en la posición X void alignTo(int x); // Centra el globo en la posición X
void render(); // Pinta el globo en la pantalla void render(); // Pinta el globo en la pantalla
void move(); // Actualiza la posición y estados del globo void move(float deltaTime); // Actualiza la posición y estados del globo (time-based)
void update(); // Actualiza el globo (posición, animación, contadores) void update(float deltaTime); // Actualiza el globo (posición, animación, contadores) (time-based)
void stop(); // Detiene el globo void stop(); // Detiene el globo
void start(); // Pone el globo en movimiento void start(); // Pone el globo en movimiento
void pop(bool should_sound = false); // Explota el globo void pop(bool should_sound = false); // Explota el globo
@@ -120,7 +122,7 @@ class Balloon {
// --- Setters --- // --- Setters ---
void setVelY(float vel_y) { vy_ = vel_y; } void setVelY(float vel_y) { vy_ = vel_y; }
void setSpeed(float speed) { speed_ = speed; } void setGameTempo(float tempo) { game_tempo_ = tempo; }
void setInvulnerable(bool value) { invulnerable_ = value; } void setInvulnerable(bool value) { invulnerable_ = value; }
void setBouncingSound(bool value) { sound_.bouncing_enabled = value; } void setBouncingSound(bool value) { sound_.bouncing_enabled = value; }
void setPoppingSound(bool value) { sound_.poping_enabled = value; } void setPoppingSound(bool value) { sound_.poping_enabled = value; }
@@ -256,15 +258,15 @@ class Balloon {
bool stopped_; // Si el globo está parado bool stopped_; // Si el globo está parado
bool use_reversed_colors_ = false; // Si se usa el color alternativo bool use_reversed_colors_ = false; // Si se usa el color alternativo
Circle collider_; // Círculo de colisión Circle collider_; // Círculo de colisión
Uint16 creation_counter_; // Temporizador de creación float creation_counter_; // Temporizador de creación
Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación float creation_counter_ini_; // Valor inicial del temporizador de creación
Uint16 score_; // Puntos al destruir el globo Uint16 score_; // Puntos al destruir el globo
Type type_; // Tipo de globo Type type_; // Tipo de globo
Size size_; // Tamaño de globo Size size_; // Tamaño de globo
Uint8 menace_; // Amenaza que genera el globo Uint8 menace_; // Amenaza que genera el globo
Uint32 counter_ = 0; // Contador interno Uint32 counter_ = 0; // Contador interno
float travel_y_ = 1.0F; // Distancia a recorrer en Y antes de aplicar gravedad float game_tempo_; // Multiplicador de tempo del juego
float speed_; // Velocidad del globo float movement_accumulator_ = 0.0f; // Acumulador para movimiento durante creación (deltaTime)
Uint8 power_; // Poder que alberga el globo Uint8 power_; // Poder que alberga el globo
SDL_FRect play_area_; // Zona de movimiento del globo SDL_FRect play_area_; // Zona de movimiento del globo
Sound sound_; // Configuración de sonido del globo Sound sound_; // Configuración de sonido del globo
@@ -280,9 +282,9 @@ class Balloon {
void playPoppingSound(); // Reproduce el sonido de reventar void playPoppingSound(); // Reproduce el sonido de reventar
// --- Movimiento y física --- // --- Movimiento y física ---
void handleHorizontalMovement(); // Maneja el movimiento horizontal void handleHorizontalMovement(float deltaTime); // Maneja el movimiento horizontal (time-based)
void handleVerticalMovement(); // Maneja el movimiento vertical void handleVerticalMovement(float deltaTime); // Maneja el movimiento vertical (time-based)
void applyGravity(); // Aplica la gravedad al objeto void applyGravity(float deltaTime); // Aplica la gravedad al objeto (time-based)
// --- Rebote --- // --- Rebote ---
void enableBounceEffect(); // Activa el efecto de rebote void enableBounceEffect(); // Activa el efecto de rebote
@@ -297,5 +299,5 @@ class Balloon {
void handleBottomCollision(); // Maneja la colisión inferior void handleBottomCollision(); // Maneja la colisión inferior
// --- Lógica de estado --- // --- Lógica de estado ---
void updateState(); // Actualiza los estados del globo void updateState(float deltaTime); // Actualiza los estados del globo (time-based)
}; };

View File

@@ -82,7 +82,7 @@ class BalloonFormations {
private: private:
// --- Constantes --- // --- Constantes ---
static constexpr int BALLOON_SPAWN_HEIGHT = 208; // Altura desde el suelo en la que aparecen los globos static constexpr int BALLOON_SPAWN_HEIGHT = 208; // Altura desde el suelo en la que aparecen los globos
static constexpr int DEFAULT_CREATION_TIME = 200; // Tiempo base de creación de los globos para las formaciones static constexpr int DEFAULT_CREATION_TIME = 3334; // Tiempo base de creación de los globos en ms (200 frames × 16.67ms)
// --- Variables --- // --- Variables ---
std::vector<Formation> formations_; // Vector con todas las formaciones disponibles std::vector<Formation> formations_; // Vector con todas las formaciones disponibles

View File

@@ -62,13 +62,13 @@ void BalloonManager::init() {
explosions_->addTexture(3, explosions_textures_.at(3), explosions_animations_.at(3)); explosions_->addTexture(3, explosions_textures_.at(3), explosions_animations_.at(3));
} }
// Actualiza // Actualiza (time-based)
void BalloonManager::update() { void BalloonManager::update(float deltaTime) {
for (const auto &balloon : balloons_) { for (const auto &balloon : balloons_) {
balloon->update(); balloon->update(deltaTime);
} }
updateBalloonDeployCounter(); updateBalloonDeployCounter(deltaTime);
explosions_->update(); explosions_->update(deltaTime);
} }
// Renderiza los objetos // Renderiza los objetos
@@ -82,11 +82,11 @@ void BalloonManager::render() {
// Crea una formación de globos // Crea una formación de globos
void BalloonManager::deployRandomFormation(int stage) { void BalloonManager::deployRandomFormation(int stage) {
// Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última // Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última
if (balloon_deploy_counter_ == 0) { if (balloon_deploy_counter_ >= DEFAULT_BALLOON_DEPLOY_COUNTER) {
// En este punto se decide entre crear una powerball o una formación enemiga // En este punto se decide entre crear una powerball o una formación enemiga
if ((rand() % 100 < 15) && (canPowerBallBeCreated())) { if ((rand() % 100 < 15) && (canPowerBallBeCreated())) {
createPowerBall(); // Crea una powerball createPowerBall(); // Crea una powerball
balloon_deploy_counter_ = 10; // Da un poco de margen para que se creen mas globos balloon_deploy_counter_ = -167; // Resetea con pequeño retraso (10 frames = 167ms negativos)
} else { } else {
// Decrementa el contador de despliegues de globos necesarios para la siguiente PowerBall // Decrementa el contador de despliegues de globos necesarios para la siguiente PowerBall
if (power_ball_counter_ > 0) { if (power_ball_counter_ > 0) {
@@ -113,13 +113,13 @@ void BalloonManager::deployRandomFormation(int stage) {
.type = balloon.type, .type = balloon.type,
.size = balloon.size, .size = balloon.size,
.vel_x = balloon.vel_x, .vel_x = balloon.vel_x,
.speed = balloon_speed_, .game_tempo = balloon_speed_,
.creation_counter = static_cast<Uint16>(creation_time_enabled_ ? balloon.creation_counter : 0)}; .creation_counter = static_cast<Uint16>(creation_time_enabled_ ? balloon.creation_counter : 0)};
createBalloon(config); createBalloon(config);
} }
// Reinicia el contador para el próximo despliegue // Reinicia el contador para el próximo despliegue
balloon_deploy_counter_ = DEFAULT_BALLOON_DEPLOY_COUNTER; balloon_deploy_counter_ = 0;
} }
} }
} }
@@ -134,7 +134,7 @@ void BalloonManager::deployFormation(int formation_id) {
.type = balloon.type, .type = balloon.type,
.size = balloon.size, .size = balloon.size,
.vel_x = balloon.vel_x, .vel_x = balloon.vel_x,
.speed = balloon_speed_, .game_tempo = balloon_speed_,
.creation_counter = balloon.creation_counter}; .creation_counter = balloon.creation_counter};
createBalloon(config); createBalloon(config);
} }
@@ -150,7 +150,7 @@ void BalloonManager::deployFormation(int formation_id, float y) {
.type = balloon.type, .type = balloon.type,
.size = balloon.size, .size = balloon.size,
.vel_x = balloon.vel_x, .vel_x = balloon.vel_x,
.speed = balloon_speed_, .game_tempo = balloon_speed_,
.creation_counter = balloon.creation_counter}; .creation_counter = balloon.creation_counter};
createBalloon(config); createBalloon(config);
} }
@@ -162,11 +162,10 @@ void BalloonManager::freeBalloons() {
balloons_.erase(result.begin(), balloons_.end()); balloons_.erase(result.begin(), balloons_.end());
} }
// Actualiza la variable enemyDeployCounter // Actualiza la variable enemyDeployCounter (time-based)
void BalloonManager::updateBalloonDeployCounter() { void BalloonManager::updateBalloonDeployCounter(float deltaTime) {
if (balloon_deploy_counter_ > 0) { // DeltaTime puro - contador incrementa hasta llegar al umbral
--balloon_deploy_counter_; balloon_deploy_counter_ += deltaTime;
}
} }
// Indica si se puede crear una powerball // Indica si se puede crear una powerball
@@ -211,14 +210,15 @@ void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon,
.y = balloon->getPosY() + ((PARENT_HEIGHT - CHILD_HEIGHT) / 2), .y = balloon->getPosY() + ((PARENT_HEIGHT - CHILD_HEIGHT) / 2),
.size = static_cast<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1), .size = static_cast<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1),
.vel_x = direction == "LEFT" ? Balloon::VELX_NEGATIVE : Balloon::VELX_POSITIVE, .vel_x = direction == "LEFT" ? Balloon::VELX_NEGATIVE : Balloon::VELX_POSITIVE,
.speed = balloon_speed_, .game_tempo = balloon_speed_,
.creation_counter = 0}; .creation_counter = 0};
// Crea el globo // Crea el globo
auto b = createBalloon(config); auto b = createBalloon(config);
// Establece parametros // Establece parametros (deltaTime puro - valores ya en pixels/ms)
b->setVelY(b->getType() == Balloon::Type::BALLOON ? -2.50F : Balloon::VELX_NEGATIVE * 2.0F); constexpr float VEL_Y_BALLOON_PER_MS = -0.15F; // -2.50F convertido a pixels/ms
b->setVelY(b->getType() == Balloon::Type::BALLOON ? VEL_Y_BALLOON_PER_MS : Balloon::VELX_NEGATIVE * 2.0F);
// Herencia de estados // Herencia de estados
if (balloon->isStopped()) { b->stop(); } if (balloon->isStopped()) { b->stop(); }
@@ -245,7 +245,7 @@ void BalloonManager::createPowerBall() {
.type = Balloon::Type::POWERBALL, .type = Balloon::Type::POWERBALL,
.size = Balloon::Size::EXTRALARGE, .size = Balloon::Size::EXTRALARGE,
.vel_x = VEL_X.at(LUCK), .vel_x = VEL_X.at(LUCK),
.speed = balloon_speed_, .game_tempo = balloon_speed_,
.creation_counter = 0, .creation_counter = 0,
.play_area = play_area_, .play_area = play_area_,
.texture = balloon_textures_.at(4), .texture = balloon_textures_.at(4),
@@ -267,7 +267,7 @@ void BalloonManager::createPowerBall() {
void BalloonManager::setBalloonSpeed(float speed) { void BalloonManager::setBalloonSpeed(float speed) {
balloon_speed_ = speed; balloon_speed_ = speed;
for (auto &balloon : balloons_) { for (auto &balloon : balloons_) {
balloon->setSpeed(speed); balloon->setGameTempo(speed);
} }
} }
@@ -280,7 +280,7 @@ auto BalloonManager::popBalloon(const std::shared_ptr<Balloon> &balloon) -> int
balloon->pop(true); balloon->pop(true);
score = destroyAllBalloons(); score = destroyAllBalloons();
power_ball_enabled_ = false; power_ball_enabled_ = false;
balloon_deploy_counter_ = 20; balloon_deploy_counter_ = -334; // Resetea con retraso (20 frames = 334ms negativos)
} else { } else {
score = balloon->getScore(); score = balloon->getScore();
if (balloon->getSize() != Balloon::Size::SMALL) { if (balloon->getSize() != Balloon::Size::SMALL) {
@@ -336,7 +336,7 @@ auto BalloonManager::destroyAllBalloons() -> int {
score += destroyBalloon(balloon); score += destroyBalloon(balloon);
} }
balloon_deploy_counter_ = 300; balloon_deploy_counter_ = -5000; // Resetea con retraso grande (300 frames = 5000ms negativos)
Screen::get()->flash(Colors::FLASH, 3); Screen::get()->flash(Colors::FLASH, 3);
Screen::get()->shake(); Screen::get()->shake();
@@ -346,7 +346,9 @@ auto BalloonManager::destroyAllBalloons() -> int {
// Detiene todos los globos // Detiene todos los globos
void BalloonManager::stopAllBalloons() { void BalloonManager::stopAllBalloons() {
for (auto &balloon : balloons_) { for (auto &balloon : balloons_) {
balloon->stop(); if (!balloon->isBeingCreated()) {
balloon->stop();
}
} }
} }

View File

@@ -28,8 +28,8 @@ class BalloonManager {
~BalloonManager() = default; ~BalloonManager() = default;
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza el estado de los globos void update(float deltaTime); // Actualiza el estado de los globos (time-based)
void render(); // Renderiza los globos en pantalla void render(); // Renderiza los globos en pantalla
// --- Gestión de globos --- // --- Gestión de globos ---
void freeBalloons(); // Libera globos que ya no sirven void freeBalloons(); // Libera globos que ya no sirven
@@ -49,7 +49,7 @@ class BalloonManager {
void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos
void setDefaultBalloonSpeed(float speed) { default_balloon_speed_ = speed; }; // Establece la velocidad base void setDefaultBalloonSpeed(float speed) { default_balloon_speed_ = speed; }; // Establece la velocidad base
void resetBalloonSpeed() { setBalloonSpeed(default_balloon_speed_); }; // Restablece la velocidad de los globos void resetBalloonSpeed() { setBalloonSpeed(default_balloon_speed_); }; // Restablece la velocidad de los globos
void updateBalloonDeployCounter(); // Actualiza el contador de despliegue void updateBalloonDeployCounter(float deltaTime); // Actualiza el contador de despliegue (time-based)
auto canPowerBallBeCreated() -> bool; // Indica si se puede crear una PowerBall auto canPowerBallBeCreated() -> bool; // Indica si se puede crear una PowerBall
auto calculateScreenPower() -> int; // Calcula el poder de los globos en pantalla auto calculateScreenPower() -> int; // Calcula el poder de los globos en pantalla
@@ -82,7 +82,7 @@ class BalloonManager {
private: private:
// --- Constantes --- // --- Constantes ---
static const int DEFAULT_BALLOON_DEPLOY_COUNTER = 300; static const int DEFAULT_BALLOON_DEPLOY_COUNTER = 5000; // 300 frames × 16.67ms = 5000ms
// --- Objetos y punteros --- // --- Objetos y punteros ---
Balloons balloons_; // Vector con los globos activos Balloons balloons_; // Vector con los globos activos
@@ -96,9 +96,9 @@ class BalloonManager {
// --- Variables de estado --- // --- Variables de estado ---
SDL_FRect play_area_ = param.game.play_area.rect; SDL_FRect play_area_ = param.game.play_area.rect;
float balloon_speed_ = Balloon::SPEED.at(0); float balloon_speed_ = Balloon::GAME_TEMPO.at(0);
float default_balloon_speed_ = Balloon::SPEED.at(0); float default_balloon_speed_ = Balloon::GAME_TEMPO.at(0);
int balloon_deploy_counter_ = 0; float balloon_deploy_counter_ = 0;
int power_ball_counter_ = 0; int power_ball_counter_ = 0;
int last_balloon_deploy_ = 0; int last_balloon_deploy_ = 0;
bool power_ball_enabled_ = false; bool power_ball_enabled_ = false;

View File

@@ -60,21 +60,22 @@ void Bullet::render() {
} }
} }
// Actualiza el estado del objeto // Actualiza el estado del objeto (time-based)
auto Bullet::update() -> BulletMoveStatus { auto Bullet::update(float deltaTime) -> BulletMoveStatus {
sprite_->update(); sprite_->update(deltaTime);
return move(); return move(deltaTime);
} }
// Implementación del movimiento usando BulletMoveStatus // Implementación del movimiento usando BulletMoveStatus (time-based)
auto Bullet::move() -> BulletMoveStatus { auto Bullet::move(float deltaTime) -> BulletMoveStatus {
pos_x_ += vel_x_; // DeltaTime puro: velocidad (pixels/ms) * tiempo (ms)
pos_x_ += vel_x_ * deltaTime;
if (pos_x_ < param.game.play_area.rect.x - WIDTH || pos_x_ > param.game.play_area.rect.w) { if (pos_x_ < param.game.play_area.rect.x - WIDTH || pos_x_ > param.game.play_area.rect.w) {
disable(); disable();
return BulletMoveStatus::OUT; return BulletMoveStatus::OUT;
} }
pos_y_ += VEL_Y; pos_y_ += VEL_Y * deltaTime;
if (pos_y_ < param.game.play_area.rect.y - HEIGHT) { if (pos_y_ < param.game.play_area.rect.y - HEIGHT) {
disable(); disable();
return BulletMoveStatus::OUT; return BulletMoveStatus::OUT;

View File

@@ -34,9 +34,9 @@ class Bullet {
~Bullet() = default; // Destructor ~Bullet() = default; // Destructor
// --- Métodos principales --- // --- Métodos principales ---
void render(); // Dibuja la bala en pantalla void render(); // Dibuja la bala en pantalla
auto update() -> BulletMoveStatus; // Actualiza el estado del objeto auto update(float deltaTime) -> BulletMoveStatus; // Actualiza el estado del objeto (time-based)
void disable(); // Desactiva la bala void disable(); // Desactiva la bala
// --- Getters --- // --- Getters ---
[[nodiscard]] auto isEnabled() const -> bool; // Comprueba si está activa [[nodiscard]] auto isEnabled() const -> bool; // Comprueba si está activa
@@ -45,9 +45,9 @@ class Bullet {
private: private:
// --- Constantes --- // --- Constantes ---
static constexpr float VEL_Y = -3.0F; // Velocidad vertical static constexpr float VEL_Y = -0.18F; // Velocidad vertical (pixels/ms)
static constexpr float VEL_X_LEFT = -2.0F; // Velocidad izquierda static constexpr float VEL_X_LEFT = -0.12F; // Velocidad izquierda (pixels/ms)
static constexpr float VEL_X_RIGHT = 2.0F; // Velocidad derecha static constexpr float VEL_X_RIGHT = 0.12F; // Velocidad derecha (pixels/ms)
static constexpr float VEL_X_CENTER = 0.0F; // Velocidad central static constexpr float VEL_X_CENTER = 0.0F; // Velocidad central
// --- Objetos y punteros --- // --- Objetos y punteros ---
@@ -64,7 +64,7 @@ class Bullet {
// --- Métodos internos --- // --- Métodos internos ---
void shiftColliders(); // Ajusta el círculo de colisión void shiftColliders(); // Ajusta el círculo de colisión
void shiftSprite(); // Ajusta el sprite void shiftSprite(); // Ajusta el sprite
auto move() -> BulletMoveStatus; // Mueve la bala y devuelve su estado auto move(float deltaTime) -> BulletMoveStatus; // Mueve la bala y devuelve su estado (time-based)
static auto calculateVelocity(BulletType bullet_type) -> float; // Calcula la velocidad horizontal de la bala static auto calculateVelocity(BulletType bullet_type) -> float; // Calcula la velocidad horizontal de la bala
static auto buildAnimationString(BulletType bullet_type, bool powered) -> std::string; // Construye el string de animación static auto buildAnimationString(BulletType bullet_type, bool powered) -> std::string; // Construye el string de animación
}; };

View File

@@ -15,8 +15,8 @@ namespace Game {
constexpr float WIDTH = 320.0F; constexpr float WIDTH = 320.0F;
constexpr float HEIGHT = 256.0F; constexpr float HEIGHT = 256.0F;
constexpr float ITEM_SIZE = 20.0F; constexpr float ITEM_SIZE = 20.0F;
constexpr int NAME_ENTRY_IDLE_TIME = 10; constexpr int NAME_ENTRY_IDLE_TIME = 10000; // 10 segundos en milisegundos
constexpr int NAME_ENTRY_TOTAL_TIME = 60; constexpr int NAME_ENTRY_TOTAL_TIME = 60000; // 60 segundos en milisegundos
constexpr bool HIT_STOP = false; constexpr bool HIT_STOP = false;
constexpr int HIT_STOP_MS = 500; constexpr int HIT_STOP_MS = 500;
constexpr const char* ITEM_TEXT_OUTLINE_COLOR = "FFFFFF00"; // 255, 255, 255, 0 constexpr const char* ITEM_TEXT_OUTLINE_COLOR = "FFFFFF00"; // 255, 255, 255, 0
@@ -58,7 +58,7 @@ constexpr int SKIP_COUNTDOWN_VALUE = 8;
// --- TITLE --- // --- TITLE ---
namespace Title { namespace Title {
constexpr int PRESS_START_POSITION = 180; constexpr int PRESS_START_POSITION = 180;
constexpr int DURATION = 800; constexpr float DURATION = 14000;
constexpr int ARCADE_EDITION_POSITION = 123; constexpr int ARCADE_EDITION_POSITION = 123;
constexpr int TITLE_C_C_POSITION = 80; constexpr int TITLE_C_C_POSITION = 80;
constexpr const char* BG_COLOR = "41526F"; constexpr const char* BG_COLOR = "41526F";
@@ -80,11 +80,12 @@ struct BalloonSettings {
grav(g) {} grav(g) {}
}; };
// Valores para deltaTime puro: vel en pixels/ms, grav en pixels/ms² (aceleración)
constexpr std::array<BalloonSettings, 4> SETTINGS = {{ constexpr std::array<BalloonSettings, 4> SETTINGS = {{
BalloonSettings(2.75F, 0.09F), // Globo 0 BalloonSettings(2.75F / 16.67F, 0.09F / (16.67F * 16.67F)), // Globo 0: vel=0.165 pixels/ms, grav=0.00032 pixels/ms²
BalloonSettings(3.70F, 0.10F), // Globo 1 BalloonSettings(3.70F / 16.67F, 0.10F / (16.67F * 16.67F)), // Globo 1: vel=0.222 pixels/ms, grav=0.00036 pixels/ms²
BalloonSettings(4.70F, 0.10F), // Globo 2 BalloonSettings(4.70F / 16.67F, 0.10F / (16.67F * 16.67F)), // Globo 2: vel=0.282 pixels/ms, grav=0.00036 pixels/ms²
BalloonSettings(5.45F, 0.10F) // Globo 3 BalloonSettings(5.45F / 16.67F, 0.10F / (16.67F * 16.67F)) // Globo 3: vel=0.327 pixels/ms, grav=0.00036 pixels/ms²
}}; }};
constexpr std::array<const char*, 4> COLORS = { constexpr std::array<const char*, 4> COLORS = {

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::HI_SCORE_TABLE; Section::name = Section::Name::INTRO;
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

@@ -6,10 +6,10 @@
class Texture; // lines 4-4 class Texture; // lines 4-4
// Actualiza la lógica de la clase // Actualiza la lógica de la clase (time-based)
void Explosions::update() { void Explosions::update(float deltaTime) {
for (auto &explosion : explosions_) { for (auto &explosion : explosions_) {
explosion->update(); explosion->update(deltaTime);
} }
// Vacia el vector de elementos finalizados // Vacia el vector de elementos finalizados

View File

@@ -29,8 +29,8 @@ class Explosions {
~Explosions() = default; // Destructor por defecto ~Explosions() = default; // Destructor por defecto
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la lógica de la clase void update(float deltaTime); // Actualiza la lógica de la clase (time-based)
void render(); // Dibuja el objeto en pantalla void render(); // Dibuja el objeto en pantalla
// --- Configuración --- // --- Configuración ---
void addTexture(int size, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation); // Añade texturas al objeto void addTexture(int size, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation); // Añade texturas al objeto

View File

@@ -82,6 +82,11 @@ void Fade::update() {
} }
} }
// Compatibilidad delta-time (ignora el parámetro ya que usa SDL_GetTicks)
void Fade::update(float delta_time) {
update(); // Llama al método original
}
void Fade::updatePreState() { void Fade::updatePreState() {
// Sistema basado en tiempo únicamente // Sistema basado en tiempo únicamente
Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_; Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_;

View File

@@ -37,10 +37,11 @@ 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 void update(); // Actualiza el estado interno (ya usa tiempo real)
void activate(); // Activa el fade void update(float delta_time); // Compatibilidad delta-time (ignora el parámetro)
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

View File

@@ -45,6 +45,7 @@ void GameLogo::init() {
arcade_edition_status_ = Status::DISABLED; arcade_edition_status_ = Status::DISABLED;
shake_.init(1, 2, 8, XP); shake_.init(1, 2, 8, XP);
zoom_ = 3.0F * ZOOM_FACTOR; zoom_ = 3.0F * ZOOM_FACTOR;
post_finished_timer_ = 0.0f;
// Inicializa el bitmap de 'Coffee' // Inicializa el bitmap de 'Coffee'
coffee_sprite_->setPosX(XP); coffee_sprite_->setPosX(XP);
@@ -52,44 +53,44 @@ void GameLogo::init() {
coffee_sprite_->setWidth(coffee_texture_->getWidth()); coffee_sprite_->setWidth(coffee_texture_->getWidth());
coffee_sprite_->setHeight(coffee_texture_->getHeight()); coffee_sprite_->setHeight(coffee_texture_->getHeight());
coffee_sprite_->setVelX(0.0F); coffee_sprite_->setVelX(0.0F);
coffee_sprite_->setVelY(2.5F); coffee_sprite_->setVelY(COFFEE_VEL_Y);
coffee_sprite_->setAccelX(0.0F); coffee_sprite_->setAccelX(0.0F);
coffee_sprite_->setAccelY(0.1F); coffee_sprite_->setAccelY(COFFEE_ACCEL_Y);
coffee_sprite_->setSpriteClip(0, 0, coffee_texture_->getWidth(), coffee_texture_->getHeight()); coffee_sprite_->setSpriteClip(0, 0, coffee_texture_->getWidth(), coffee_texture_->getHeight());
coffee_sprite_->setEnabled(true); coffee_sprite_->setEnabled(true);
coffee_sprite_->setFinishedCounter(0); coffee_sprite_->setFinishedDelay(0.0f);
coffee_sprite_->setDestX(XP); coffee_sprite_->setDestX(XP);
coffee_sprite_->setDestY(y_ - coffee_texture_->getHeight()); coffee_sprite_->setDestY(y_ - coffee_texture_->getHeight());
// Inicializa el bitmap de 'Crisis' // Inicializa el bitmap de 'Crisis'
crisis_sprite_->setPosX(XP + 15); crisis_sprite_->setPosX(XP + CRISIS_OFFSET_X);
crisis_sprite_->setPosY(y_ + DESP); crisis_sprite_->setPosY(y_ + DESP);
crisis_sprite_->setWidth(crisis_texture_->getWidth()); crisis_sprite_->setWidth(crisis_texture_->getWidth());
crisis_sprite_->setHeight(crisis_texture_->getHeight()); crisis_sprite_->setHeight(crisis_texture_->getHeight());
crisis_sprite_->setVelX(0.0F); crisis_sprite_->setVelX(0.0F);
crisis_sprite_->setVelY(-2.5F); crisis_sprite_->setVelY(CRISIS_VEL_Y);
crisis_sprite_->setAccelX(0.0F); crisis_sprite_->setAccelX(0.0F);
crisis_sprite_->setAccelY(-0.1F); crisis_sprite_->setAccelY(CRISIS_ACCEL_Y);
crisis_sprite_->setSpriteClip(0, 0, crisis_texture_->getWidth(), crisis_texture_->getHeight()); crisis_sprite_->setSpriteClip(0, 0, crisis_texture_->getWidth(), crisis_texture_->getHeight());
crisis_sprite_->setEnabled(true); crisis_sprite_->setEnabled(true);
crisis_sprite_->setFinishedCounter(0); crisis_sprite_->setFinishedDelay(0.0f);
crisis_sprite_->setDestX(XP + 15); crisis_sprite_->setDestX(XP + CRISIS_OFFSET_X);
crisis_sprite_->setDestY(y_); crisis_sprite_->setDestY(y_);
// Inicializa el bitmap de 'DustRight' // Inicializa el bitmap de 'DustRight'
dust_right_sprite_->resetAnimation(); dust_right_sprite_->resetAnimation();
dust_right_sprite_->setPosX(coffee_sprite_->getPosX() + coffee_sprite_->getWidth()); dust_right_sprite_->setPosX(coffee_sprite_->getPosX() + coffee_sprite_->getWidth());
dust_right_sprite_->setPosY(y_); dust_right_sprite_->setPosY(y_);
dust_right_sprite_->setWidth(16); dust_right_sprite_->setWidth(DUST_SIZE);
dust_right_sprite_->setHeight(16); dust_right_sprite_->setHeight(DUST_SIZE);
dust_right_sprite_->setFlip(SDL_FLIP_HORIZONTAL); dust_right_sprite_->setFlip(SDL_FLIP_HORIZONTAL);
// Inicializa el bitmap de 'DustLeft' // Inicializa el bitmap de 'DustLeft'
dust_left_sprite_->resetAnimation(); dust_left_sprite_->resetAnimation();
dust_left_sprite_->setPosX(coffee_sprite_->getPosX() - 16); dust_left_sprite_->setPosX(coffee_sprite_->getPosX() - DUST_SIZE);
dust_left_sprite_->setPosY(y_); dust_left_sprite_->setPosY(y_);
dust_left_sprite_->setWidth(16); dust_left_sprite_->setWidth(DUST_SIZE);
dust_left_sprite_->setHeight(16); dust_left_sprite_->setHeight(DUST_SIZE);
// Inicializa el bitmap de 'Arcade Edition' // Inicializa el bitmap de 'Arcade Edition'
arcade_edition_sprite_->setZoom(zoom_); arcade_edition_sprite_->setZoom(zoom_);
@@ -112,45 +113,45 @@ void GameLogo::render() {
} }
} }
// Actualiza la lógica de la clase // Actualiza la lógica de la clase (time-based)
void GameLogo::update() { void GameLogo::update(float deltaTime) {
updateCoffeeCrisis(); updateCoffeeCrisis(deltaTime);
updateArcadeEdition(); updateArcadeEdition(deltaTime);
updatePostFinishedCounter(); updatePostFinishedCounter(deltaTime);
} }
void GameLogo::updateCoffeeCrisis() { void GameLogo::updateCoffeeCrisis(float deltaTime) {
switch (coffee_crisis_status_) { switch (coffee_crisis_status_) {
case Status::MOVING: case Status::MOVING:
handleCoffeeCrisisMoving(); handleCoffeeCrisisMoving(deltaTime);
break; break;
case Status::SHAKING: case Status::SHAKING:
handleCoffeeCrisisShaking(); handleCoffeeCrisisShaking(deltaTime);
break; break;
case Status::FINISHED: case Status::FINISHED:
handleCoffeeCrisisFinished(); handleCoffeeCrisisFinished(deltaTime);
break; break;
default: default:
break; break;
} }
} }
void GameLogo::updateArcadeEdition() { void GameLogo::updateArcadeEdition(float deltaTime) {
switch (arcade_edition_status_) { switch (arcade_edition_status_) {
case Status::MOVING: case Status::MOVING:
handleArcadeEditionMoving(); handleArcadeEditionMoving(deltaTime);
break; break;
case Status::SHAKING: case Status::SHAKING:
handleArcadeEditionShaking(); handleArcadeEditionShaking(deltaTime);
break; break;
default: default:
break; break;
} }
} }
void GameLogo::handleCoffeeCrisisMoving() { void GameLogo::handleCoffeeCrisisMoving(float deltaTime) {
coffee_sprite_->update(); coffee_sprite_->update(deltaTime);
crisis_sprite_->update(); crisis_sprite_->update(deltaTime);
if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished()) { if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished()) {
coffee_crisis_status_ = Status::SHAKING; coffee_crisis_status_ = Status::SHAKING;
@@ -158,22 +159,23 @@ void GameLogo::handleCoffeeCrisisMoving() {
} }
} }
void GameLogo::handleCoffeeCrisisShaking() { void GameLogo::handleCoffeeCrisisShaking(float deltaTime) {
if (shake_.remaining > 0) { if (shake_.remaining > 0) {
processShakeEffect(coffee_sprite_.get(), crisis_sprite_.get()); processShakeEffect(coffee_sprite_.get(), crisis_sprite_.get(), deltaTime);
} else { } else {
finishCoffeeCrisisShaking(); finishCoffeeCrisisShaking();
} }
updateDustSprites(); updateDustSprites(deltaTime);
} }
void GameLogo::handleCoffeeCrisisFinished() { void GameLogo::handleCoffeeCrisisFinished(float deltaTime) {
updateDustSprites(); updateDustSprites(deltaTime);
} }
void GameLogo::handleArcadeEditionMoving() { void GameLogo::handleArcadeEditionMoving(float deltaTime) {
zoom_ -= 0.1F * ZOOM_FACTOR; // DeltaTime puro: decremento por milisegundo
zoom_ -= (ZOOM_DECREMENT_PER_MS * ZOOM_FACTOR) * deltaTime;
arcade_edition_sprite_->setZoom(zoom_); arcade_edition_sprite_->setZoom(zoom_);
if (zoom_ <= 1.0F) { if (zoom_ <= 1.0F) {
@@ -181,34 +183,38 @@ void GameLogo::handleArcadeEditionMoving() {
} }
} }
void GameLogo::handleArcadeEditionShaking() { void GameLogo::handleArcadeEditionShaking(float deltaTime) {
if (shake_.remaining > 0) { if (shake_.remaining > 0) {
processArcadeEditionShake(); processArcadeEditionShake(deltaTime);
} else { } else {
arcade_edition_sprite_->setX(shake_.origin); arcade_edition_sprite_->setX(shake_.origin);
arcade_edition_status_ = Status::FINISHED; arcade_edition_status_ = Status::FINISHED;
} }
} }
void GameLogo::processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite) {
if (shake_.counter > 0) { void GameLogo::processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite, float deltaTime) {
shake_.counter--; shake_.time_accumulator += deltaTime;
} else {
shake_.counter = shake_.delay; if (shake_.time_accumulator >= SHAKE_DELAY_MS) {
shake_.time_accumulator -= SHAKE_DELAY_MS;
const auto DISPLACEMENT = calculateShakeDisplacement(); const auto DISPLACEMENT = calculateShakeDisplacement();
primary_sprite->setPosX(shake_.origin + DISPLACEMENT); primary_sprite->setPosX(shake_.origin + DISPLACEMENT);
if (secondary_sprite != nullptr) { if (secondary_sprite != nullptr) {
secondary_sprite->setPosX(shake_.origin + DISPLACEMENT + 15); secondary_sprite->setPosX(shake_.origin + DISPLACEMENT + CRISIS_OFFSET_X);
} }
shake_.remaining--; shake_.remaining--;
} }
} }
void GameLogo::processArcadeEditionShake() { void GameLogo::processArcadeEditionShake(float deltaTime) {
if (shake_.counter > 0) { // Delay fijo en milisegundos (shake_.delay era frames, ahora usamos constante)
shake_.counter--; float delayTime = SHAKE_DELAY_MS;
} else {
shake_.counter = shake_.delay; shake_.time_accumulator += deltaTime;
if (shake_.time_accumulator >= delayTime) {
shake_.time_accumulator -= delayTime;
const auto DISPLACEMENT = calculateShakeDisplacement(); const auto DISPLACEMENT = calculateShakeDisplacement();
arcade_edition_sprite_->setX(shake_.origin + DISPLACEMENT); arcade_edition_sprite_->setX(shake_.origin + DISPLACEMENT);
shake_.remaining--; shake_.remaining--;
@@ -221,7 +227,7 @@ auto GameLogo::calculateShakeDisplacement() const -> int {
void GameLogo::finishCoffeeCrisisShaking() { void GameLogo::finishCoffeeCrisisShaking() {
coffee_sprite_->setPosX(shake_.origin); coffee_sprite_->setPosX(shake_.origin);
crisis_sprite_->setPosX(shake_.origin + 15); crisis_sprite_->setPosX(shake_.origin + CRISIS_OFFSET_X);
coffee_crisis_status_ = Status::FINISHED; coffee_crisis_status_ = Status::FINISHED;
arcade_edition_status_ = Status::MOVING; arcade_edition_status_ = Status::MOVING;
} }
@@ -240,16 +246,16 @@ void GameLogo::playTitleEffects() {
Screen::get()->shake(); Screen::get()->shake();
} }
void GameLogo::updateDustSprites() { void GameLogo::updateDustSprites(float deltaTime) {
dust_right_sprite_->update(); dust_right_sprite_->update(deltaTime);
dust_left_sprite_->update(); dust_left_sprite_->update(deltaTime);
} }
void GameLogo::updatePostFinishedCounter() { void GameLogo::updatePostFinishedCounter(float deltaTime) {
if (coffee_crisis_status_ == Status::FINISHED && if (coffee_crisis_status_ == Status::FINISHED &&
arcade_edition_status_ == Status::FINISHED && arcade_edition_status_ == Status::FINISHED) {
post_finished_counter_ > 0) {
--post_finished_counter_; post_finished_timer_ += deltaTime;
} }
} }
@@ -261,7 +267,7 @@ void GameLogo::enable() {
// Indica si ha terminado la animación // Indica si ha terminado la animación
auto GameLogo::hasFinished() const -> bool { auto GameLogo::hasFinished() const -> bool {
return post_finished_counter_ == 0; return post_finished_timer_ >= post_finished_delay_ms_;
} }
// Calcula el desplazamiento vertical inicial // Calcula el desplazamiento vertical inicial

View File

@@ -11,14 +11,25 @@ class Texture;
// --- Clase GameLogo: gestor del logo del juego --- // --- Clase GameLogo: gestor del logo del juego ---
class GameLogo { class GameLogo {
public: public:
// --- Constantes ---
static constexpr float COFFEE_VEL_Y = 0.15F; // Velocidad Y de coffee sprite (pixels/ms) - 2.5F/16.67
static constexpr float COFFEE_ACCEL_Y = 0.00036F; // Aceleración Y de coffee sprite (pixels/ms²) - 0.1F/(16.67)²
static constexpr float CRISIS_VEL_Y = -0.15F; // Velocidad Y de crisis sprite (pixels/ms) - -2.5F/16.67
static constexpr float CRISIS_ACCEL_Y = -0.00036F; // Aceleración Y de crisis sprite (pixels/ms²) - -0.1F/(16.67)²
static constexpr int CRISIS_OFFSET_X = 15; // Desplazamiento X de crisis sprite
static constexpr int DUST_SIZE = 16; // Tamaño de dust sprites
static constexpr float ZOOM_DECREMENT_PER_MS = 0.006F; // Decremento de zoom por milisegundo (0.1F/16.67ms)
static constexpr float SHAKE_DELAY_MS = 33.34F; // Delay de shake en milisegundos (2 frames * 16.67ms)
static constexpr float POST_FINISHED_FRAME_TIME = 16.67F; // Tiempo entre decrementos del counter (1 frame)
// --- Constructores y destructor --- // --- Constructores y destructor ---
GameLogo(int x, int y); GameLogo(int x, int y);
~GameLogo() = default; ~GameLogo() = default;
// --- Métodos principales --- // --- Métodos principales ---
void render(); // Pinta la clase en pantalla void render(); // Pinta la clase en pantalla
void update(); // Actualiza la lógica de la clase void update(float deltaTime); // Actualiza la lógica de la clase (time-based)
void enable(); // Activa la clase void enable(); // Activa la clase
// --- Getters --- // --- Getters ---
[[nodiscard]] auto hasFinished() const -> bool; // Indica si ha terminado la animación [[nodiscard]] auto hasFinished() const -> bool; // Indica si ha terminado la animación
@@ -34,12 +45,13 @@ class GameLogo {
// --- Estructuras privadas --- // --- Estructuras privadas ---
struct Shake { struct Shake {
int desp = 1; // Pixels de desplazamiento para agitar la pantalla en el eje x int desp = 1; // Pixels de desplazamiento para agitar la pantalla en el eje x
int delay = 2; // Retraso entre cada desplazamiento de la pantalla al agitarse int delay = 2; // Retraso entre cada desplazamiento de la pantalla al agitarse (frame-based)
int length = 8; // Cantidad de desplazamientos a realizar int length = 8; // Cantidad de desplazamientos a realizar
int remaining = length; // Cantidad de desplazamientos pendientes a realizar int remaining = length; // Cantidad de desplazamientos pendientes a realizar
int counter = delay; // Contador para el retraso int counter = delay; // Contador para el retraso (frame-based)
int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento float time_accumulator = 0.0f; // Acumulador de tiempo para deltaTime
int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento
Shake() = default; Shake() = default;
Shake(int d, int de, int l, int o) Shake(int d, int de, int l, int o)
@@ -56,6 +68,7 @@ class GameLogo {
length = l; length = l;
remaining = l; remaining = l;
counter = de; counter = de;
time_accumulator = 0.0f;
origin = o; origin = o;
} }
}; };
@@ -79,32 +92,34 @@ class GameLogo {
float x_; // Posición X del logo float x_; // Posición X del logo
float y_; // Posición Y del logo float y_; // Posición Y del logo
float zoom_ = 1.0F; // Zoom aplicado al texto "ARCADE EDITION" float zoom_ = 1.0F; // Zoom aplicado al texto "ARCADE EDITION"
int post_finished_counter_ = 1; // Contador final tras animaciones float post_finished_delay_ms_ = POST_FINISHED_FRAME_TIME; // Retraso final tras animaciones (ms)
float post_finished_timer_ = 0.0f; // Timer acumulado para retraso final (ms)
// --- Inicialización --- // --- Inicialización ---
void init(); // Inicializa las variables void init(); // Inicializa las variables
[[nodiscard]] auto getInitialVerticalDesp() const -> int; // Calcula el desplazamiento vertical inicial [[nodiscard]] auto getInitialVerticalDesp() const -> int; // Calcula el desplazamiento vertical inicial
// --- Actualización de estados específicos --- // --- Actualización de estados específicos ---
void updateCoffeeCrisis(); // Actualiza el estado de "Coffee Crisis" void updateCoffeeCrisis(float deltaTime); // Actualiza el estado de "Coffee Crisis" (time-based)
void updateArcadeEdition(); // Actualiza el estado de "Arcade Edition" void updateArcadeEdition(float deltaTime); // Actualiza el estado de "Arcade Edition" (time-based)
void updatePostFinishedCounter(); // Actualiza el contador tras finalizar una animación void updatePostFinishedCounter(float deltaTime); // Actualiza el contador tras finalizar una animación (time-based)
// --- Efectos visuales: movimiento y sacudidas --- // --- Efectos visuales: movimiento y sacudidas ---
void handleCoffeeCrisisMoving(); // Maneja el movimiento de "Coffee Crisis" void handleCoffeeCrisisMoving(float deltaTime); // Maneja el movimiento de "Coffee Crisis" (time-based)
void handleCoffeeCrisisShaking(); // Maneja la sacudida de "Coffee Crisis" void handleCoffeeCrisisShaking(float deltaTime); // Maneja la sacudida de "Coffee Crisis" (time-based)
void handleArcadeEditionMoving(); // Maneja el movimiento de "Arcade Edition" void handleArcadeEditionMoving(float deltaTime); // Maneja el movimiento de "Arcade Edition" (time-based)
void handleArcadeEditionShaking(); // Maneja la sacudida de "Arcade Edition" void handleArcadeEditionShaking(float deltaTime); // Maneja la sacudida de "Arcade Edition" (time-based)
void processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite = nullptr); // Procesa el efecto de sacudida en sprites void processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite = nullptr); // Procesa el efecto de sacudida en sprites (frame-based)
void processArcadeEditionShake(); // Procesa la sacudida específica de "Arcade Edition" void processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite, float deltaTime); // Procesa el efecto de sacudida en sprites (time-based)
[[nodiscard]] auto calculateShakeDisplacement() const -> int; // Calcula el desplazamiento de la sacudida void processArcadeEditionShake(float deltaTime); // Procesa la sacudida específica de "Arcade Edition" (time-based)
[[nodiscard]] auto calculateShakeDisplacement() const -> int; // Calcula el desplazamiento de la sacudida
// --- Gestión de finalización de efectos --- // --- Gestión de finalización de efectos ---
void handleCoffeeCrisisFinished(); // Maneja el final de la animación "Coffee Crisis" void handleCoffeeCrisisFinished(float deltaTime); // Maneja el final de la animación "Coffee Crisis" (time-based)
void finishCoffeeCrisisShaking(); // Finaliza la sacudida de "Coffee Crisis" void finishCoffeeCrisisShaking(); // Finaliza la sacudida de "Coffee Crisis"
void finishArcadeEditionMoving(); // Finaliza el movimiento de "Arcade Edition" void finishArcadeEditionMoving(); // Finaliza el movimiento de "Arcade Edition"
// --- Utilidades --- // --- Utilidades ---
static void playTitleEffects(); // Reproduce efectos visuales/sonoros del título static void playTitleEffects(); // Reproduce efectos visuales/sonoros del título
void updateDustSprites(); // Actualiza los sprites de polvo void updateDustSprites(float deltaTime); // Actualiza los sprites de polvo (time-based)
}; };

View File

@@ -1,6 +1,7 @@
#include "item.h" #include "item.h"
#include <algorithm> // Para clamp #include <algorithm> // Para clamp
#include <cmath> // Para fmod
#include <cstdlib> // Para rand #include <cstdlib> // Para rand
#include "animated_sprite.h" // Para AnimatedSprite #include "animated_sprite.h" // Para AnimatedSprite
@@ -18,9 +19,9 @@ Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::sha
height_ = COFFEE_MACHINE_HEIGHT; height_ = COFFEE_MACHINE_HEIGHT;
pos_x_ = getCoffeeMachineSpawn(x, width_, play_area_.w); pos_x_ = getCoffeeMachineSpawn(x, width_, play_area_.w);
pos_y_ = y; pos_y_ = y;
vel_x_ = ((rand() % 3) - 1) * 0.5F; vel_x_ = ((rand() % 3) - 1) * COFFEE_MACHINE_VEL_X_FACTOR;
vel_y_ = -0.1F; vel_y_ = COFFEE_MACHINE_VEL_Y;
accel_y_ = 0.1F; accel_y_ = COFFEE_MACHINE_ACCEL_Y;
collider_.r = 10; collider_.r = 10;
break; break;
} }
@@ -33,13 +34,13 @@ Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::sha
const int direction = rand() % 6; const int direction = rand() % 6;
if (direction < 3) { if (direction < 3) {
// Velocidades negativas: -1.0, -0.66, -0.33 // Velocidades negativas: -1.0, -0.66, -0.33
vel_x_ = -1.0F + (direction * 0.33F); vel_x_ = -ITEM_VEL_X_BASE + (direction * ITEM_VEL_X_STEP);
} else { } else {
// Velocidades positivas: 0.33, 0.66, 1.0 // Velocidades positivas: 0.33, 0.66, 1.0
vel_x_ = 0.33F + ((direction - 3) * 0.33F); vel_x_ = ITEM_VEL_X_STEP + ((direction - 3) * ITEM_VEL_X_STEP);
} }
vel_y_ = -4.0F; vel_y_ = ITEM_VEL_Y;
accel_y_ = 0.2F; accel_y_ = ITEM_ACCEL_Y;
collider_.r = width_ / 2; collider_.r = width_ / 2;
break; break;
} }
@@ -66,24 +67,34 @@ void Item::alignTo(int x) {
void Item::render() { void Item::render() {
if (enabled_) { if (enabled_) {
if (time_to_live_ > 200) { // Muestra normalmente hasta los últimos ~3.3 segundos (200 frames)
sprite_->render(); constexpr float BLINK_START_MS = LIFETIME_DURATION_MS - (200.0f * (1000.0f / 60.0f));
} else if (time_to_live_ % 20 > 10) {
if (lifetime_timer_ < BLINK_START_MS) {
sprite_->render(); sprite_->render();
} else {
// Efecto de parpadeo en los últimos segundos (cada ~333ms o 20 frames)
constexpr float BLINK_INTERVAL_MS = 20.0f * (1000.0f / 60.0f);
const float phase = fmod(lifetime_timer_, BLINK_INTERVAL_MS);
const float half_interval = BLINK_INTERVAL_MS / 2.0f;
if (phase < half_interval) {
sprite_->render();
}
} }
} }
} }
void Item::move() { void Item::move(float deltaTime) {
floor_collision_ = false; floor_collision_ = false;
// Calcula la nueva posición // Calcula la nueva posición usando deltaTime puro (velocidad en pixels/ms)
pos_x_ += vel_x_; pos_x_ += vel_x_ * deltaTime;
pos_y_ += vel_y_; pos_y_ += vel_y_ * deltaTime;
// Aplica las aceleraciones a la velocidad // Aplica las aceleraciones a la velocidad usando deltaTime puro (aceleración en pixels/ms²)
vel_x_ += accel_x_; vel_x_ += accel_x_ * deltaTime;
vel_y_ += accel_y_; vel_y_ += accel_y_ * deltaTime;
// Comprueba los laterales de la zona de juego // Comprueba los laterales de la zona de juego
const float MIN_X = param.game.play_area.rect.x; const float MIN_X = param.game.play_area.rect.x;
@@ -113,24 +124,24 @@ void Item::move() {
case ItemType::COFFEE_MACHINE: case ItemType::COFFEE_MACHINE:
// La máquina de café es mas pesada y tiene una fisica diferente, ademas hace ruido // La máquina de café es mas pesada y tiene una fisica diferente, ademas hace ruido
floor_collision_ = true; floor_collision_ = true;
if (vel_y_ < 1.0F) { if (std::abs(vel_y_) < BOUNCE_VEL_THRESHOLD) {
// Si la velocidad vertical es baja, detiene el objeto // Si la velocidad vertical es baja, detiene el objeto
vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0; vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0;
} else { } else {
// Si la velocidad vertical es alta, el objeto rebota y pierde velocidad // Si la velocidad vertical es alta, el objeto rebota y pierde velocidad
vel_y_ *= -0.20F; vel_y_ *= COFFEE_BOUNCE_DAMPING;
vel_x_ *= 0.75F; vel_x_ *= HORIZONTAL_DAMPING;
} }
break; break;
default: default:
// Si no es una máquina de café // Si no es una máquina de café
if (vel_y_ < 1.0F) { if (std::abs(vel_y_) < BOUNCE_VEL_THRESHOLD) {
// Si la velocidad vertical es baja, detiene el objeto // Si la velocidad vertical es baja, detiene el objeto
vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0; vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0;
} else { } else {
// Si la velocidad vertical es alta, el objeto rebota y pierde velocidad // Si la velocidad vertical es alta, el objeto rebota y pierde velocidad
vel_y_ *= -0.5F; vel_y_ *= ITEM_BOUNCE_DAMPING;
vel_x_ *= 0.75F; vel_x_ *= HORIZONTAL_DAMPING;
} }
break; break;
} }
@@ -143,16 +154,15 @@ void Item::move() {
void Item::disable() { enabled_ = false; } void Item::disable() { enabled_ = false; }
void Item::update() { void Item::update(float deltaTime) {
move(); move(deltaTime);
sprite_->update(); sprite_->update(deltaTime);
updateTimeToLive(); updateTimeToLive(deltaTime);
} }
void Item::updateTimeToLive() { void Item::updateTimeToLive(float deltaTime) {
if (time_to_live_ > 0) { lifetime_timer_ += deltaTime;
time_to_live_--; if (lifetime_timer_ >= LIFETIME_DURATION_MS) {
} else {
disable(); disable();
} }
} }

View File

@@ -29,16 +29,34 @@ class Item {
// --- Constantes --- // --- Constantes ---
static constexpr int COFFEE_MACHINE_WIDTH = 30; // Anchura de la máquina de café static constexpr int COFFEE_MACHINE_WIDTH = 30; // Anchura de la máquina de café
static constexpr int COFFEE_MACHINE_HEIGHT = 39; // Altura de la máquina de café static constexpr int COFFEE_MACHINE_HEIGHT = 39; // Altura de la máquina de café
static constexpr float LIFETIME_DURATION_MS = 10000.0f; // Duración de vida del ítem (600 frames a 60fps)
// Velocidades base (pixels/ms) - Coffee Machine
static constexpr float COFFEE_MACHINE_VEL_X_FACTOR = 0.03F; // Factor para velocidad X de máquina de café
static constexpr float COFFEE_MACHINE_VEL_Y = -0.006F; // Velocidad Y inicial de máquina de café
static constexpr float COFFEE_MACHINE_ACCEL_Y = 0.00036F; // Aceleración Y de máquina de café (pixels/ms²)
// Velocidades base (pixels/ms) - Items normales (del plan: vel_x: ±1.0F→±0.06F, vel_y: -4.0F→-0.24F, accel_y: 0.2F→0.012F)
static constexpr float ITEM_VEL_X_BASE = 0.06F; // Velocidad X base para items (1.0F/16.67)
static constexpr float ITEM_VEL_X_STEP = 0.02F; // Incremento de velocidad X (0.33F/16.67)
static constexpr float ITEM_VEL_Y = -0.24F; // Velocidad Y inicial de items (-4.0F/16.67)
static constexpr float ITEM_ACCEL_Y = 0.00072F; // Aceleración Y de items (pixels/ms²) - 0.2F * (60/1000)²
// Constantes de física de rebote
static constexpr float BOUNCE_VEL_THRESHOLD = 0.06F; // Umbral de velocidad para parar (1.0F/16.67)
static constexpr float COFFEE_BOUNCE_DAMPING = -0.20F; // Factor de rebote Y para máquina de café
static constexpr float ITEM_BOUNCE_DAMPING = -0.5F; // Factor de rebote Y para items normales
static constexpr float HORIZONTAL_DAMPING = 0.75F; // Factor de amortiguación horizontal
// --- Constructor y destructor --- // --- Constructor y destructor ---
Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation); // Constructor principal Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation); // Constructor principal
~Item() = default; // Destructor ~Item() = default; // Destructor
// --- Métodos principales --- // --- Métodos principales ---
void alignTo(int x); // Centra el objeto en la posición X indicada void alignTo(int x); // Centra el objeto en la posición X indicada
void render(); // Renderiza el objeto en pantalla void render(); // Renderiza el objeto en pantalla
void disable(); // Desactiva el objeto void disable(); // Desactiva el objeto
void update(); // Actualiza la posición, animación y contadores void update(float deltaTime); // Actualiza la posición, animación y contadores (time-based)
// --- Getters --- // --- Getters ---
[[nodiscard]] auto getPosX() const -> float { return pos_x_; } // Obtiene la posición X [[nodiscard]] auto getPosX() const -> float { return pos_x_; } // Obtiene la posición X
@@ -66,14 +84,14 @@ class Item {
float accel_y_; // Aceleración en el eje Y float accel_y_; // Aceleración en el eje Y
int width_; // Ancho del objeto int width_; // Ancho del objeto
int height_; // Alto del objeto int height_; // Alto del objeto
Uint16 time_to_live_ = 600; // Tiempo que el objeto está presente float lifetime_timer_ = 0.0f; // Acumulador de tiempo de vida del ítem (milisegundos)
bool floor_collision_ = false; // Indica si el objeto colisiona con el suelo bool floor_collision_ = false; // Indica si el objeto colisiona con el suelo
bool enabled_ = true; // Indica si el objeto está habilitado bool enabled_ = true; // Indica si el objeto está habilitado
// --- Métodos internos --- // --- Métodos internos ---
void shiftColliders(); // Alinea el círculo de colisión con la posición del objeto void shiftColliders(); // Alinea el círculo de colisión con la posición del objeto
void shiftSprite(); // Coloca el sprite en la posición del objeto void shiftSprite(); // Coloca el sprite en la posición del objeto
void move(); // Actualiza la posición y estados del objeto void move(float deltaTime); // Actualiza la posición y estados del objeto (time-based)
void updateTimeToLive(); // Actualiza el contador de tiempo de vida void updateTimeToLive(float deltaTime); // Actualiza el contador de tiempo de vida (time-based)
static auto getCoffeeMachineSpawn(int player_x, int item_width, int area_width, int margin = 2) -> int; // Calcula la zona de aparición de la máquina de café static auto getCoffeeMachineSpawn(int player_x, int item_width, int area_width, int margin = 2) -> int; // Calcula la zona de aparición de la máquina de café
}; };

View File

@@ -53,42 +53,41 @@ void MovingSprite::stop() {
flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite
} }
// Mueve el sprite // Mueve el sprite (time-based)
void MovingSprite::move() { void MovingSprite::move(float deltaTime) {
x_ += vx_; // DeltaTime puro: velocidad (pixels/ms) * tiempo (ms)
y_ += vy_; x_ += vx_ * deltaTime;
y_ += vy_ * deltaTime;
vx_ += ax_; // Aceleración (pixels/ms²) * tiempo (ms)
vy_ += ay_; vx_ += ax_ * deltaTime;
vy_ += ay_ * deltaTime;
pos_.x = static_cast<int>(x_); pos_.x = static_cast<int>(x_);
pos_.y = static_cast<int>(y_); pos_.y = static_cast<int>(y_);
} }
// Actualiza las variables internas del objeto // Actualiza las variables internas del objeto (time-based)
void MovingSprite::update() { void MovingSprite::update(float deltaTime) {
move(); move(deltaTime);
rotate(); rotate(deltaTime);
} }
// Muestra el sprite por pantalla // Muestra el sprite por pantalla
void MovingSprite::render() { getTexture()->render(pos_.x, pos_.y, &sprite_clip_, horizontal_zoom_, vertical_zoom_, rotate_.angle, &rotate_.center, flip_); } void MovingSprite::render() {
getTexture()->render(pos_.x, pos_.y, &sprite_clip_, horizontal_zoom_, vertical_zoom_, rotate_.angle, &rotate_.center, flip_);
}
// Establece la rotacion // Establece la rotacion (time-based)
void MovingSprite::rotate() { void MovingSprite::rotate(float deltaTime) {
if (rotate_.enabled) { if (rotate_.enabled) {
++rotate_.counter; rotate_.angle += rotate_.amount * deltaTime;
if (rotate_.counter % rotate_.speed == 0) {
updateAngle();
rotate_.counter = 0;
}
} }
} }
// Activa o desactiva el efecto de rotación // Activa o desactiva el efecto de rotación
void MovingSprite::setRotate(bool enable) { void MovingSprite::setRotate(bool enable) {
rotate_.enabled = enable; rotate_.enabled = enable;
rotate_.counter = 0;
} }
// Establece la posición y_ el tamaño del objeto // Establece la posición y_ el tamaño del objeto

View File

@@ -15,7 +15,6 @@ class MovingSprite : public Sprite {
// --- Estructuras --- // --- Estructuras ---
struct Rotate { struct Rotate {
bool enabled{false}; // Indica si ha de rotar bool enabled{false}; // Indica si ha de rotar
int counter{0}; // Contador
int speed{1}; // Velocidad de giro int speed{1}; // Velocidad de giro
double angle{0.0}; // Ángulo para dibujarlo double angle{0.0}; // Ángulo para dibujarlo
float amount{0.0F}; // Cantidad de grados a girar en cada iteración float amount{0.0F}; // Cantidad de grados a girar en cada iteración
@@ -29,10 +28,10 @@ class MovingSprite : public Sprite {
~MovingSprite() override = default; ~MovingSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
virtual void update(); // Actualiza las variables internas del objeto virtual void update(float deltaTime); // Actualiza las variables internas del objeto (time-based)
void clear() override; // Reinicia todas las variables a cero void clear() override; // Reinicia todas las variables a cero
void stop(); // Elimina el movimiento del sprite void stop(); // Elimina el movimiento del sprite
void render() override; // Muestra el sprite por pantalla void render() override; // Muestra el sprite por pantalla
// --- Configuración --- // --- Configuración ---
void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto
@@ -79,6 +78,6 @@ class MovingSprite : public Sprite {
// --- Métodos internos --- // --- Métodos internos ---
void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo
void move(); // Mueve el sprite según velocidad y aceleración void move(float deltaTime); // Mueve el sprite según velocidad y aceleración (time-based)
void rotate(); // Rota el sprite según los parámetros de rotación void rotate(float deltaTime); // Rota el sprite según los parámetros de rotación (time-based)
}; };

View File

@@ -106,7 +106,6 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
{"scoreboard.rect.h", [](const std::string& v) { param.scoreboard.rect.h = std::stoi(v); }}, {"scoreboard.rect.h", [](const std::string& v) { param.scoreboard.rect.h = std::stoi(v); }},
{"scoreboard.skip_countdown_value", [](const std::string& v) { param.scoreboard.skip_countdown_value = std::stoi(v); }}, {"scoreboard.skip_countdown_value", [](const std::string& v) { param.scoreboard.skip_countdown_value = std::stoi(v); }},
{"title.press_start_position", [](const std::string& v) { param.title.press_start_position = std::stoi(v); }}, {"title.press_start_position", [](const std::string& v) { param.title.press_start_position = std::stoi(v); }},
{"title.title_duration", [](const std::string& v) { param.title.title_duration = std::stoi(v); }},
{"title.arcade_edition_position", [](const std::string& v) { param.title.arcade_edition_position = std::stoi(v); }}, {"title.arcade_edition_position", [](const std::string& v) { param.title.arcade_edition_position = std::stoi(v); }},
{"title.title_c_c_position", [](const std::string& v) { param.title.title_c_c_position = std::stoi(v); }}, {"title.title_c_c_position", [](const std::string& v) { param.title.title_c_c_position = std::stoi(v); }},
{"intro.text_distance_from_bottom", [](const std::string& v) { param.intro.text_distance_from_bottom = std::stoi(v); }}}; {"intro.text_distance_from_bottom", [](const std::string& v) { param.intro.text_distance_from_bottom = std::stoi(v); }}};
@@ -182,6 +181,7 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
{"balloon.settings[3].grav", [](const std::string& v) { param.balloon.settings.at(3).grav = std::stof(v); }}, {"balloon.settings[3].grav", [](const std::string& v) { param.balloon.settings.at(3).grav = std::stof(v); }},
{"tabe.min_spawn_time", [](const std::string& v) { param.tabe.min_spawn_time = std::stof(v); }}, {"tabe.min_spawn_time", [](const std::string& v) { param.tabe.min_spawn_time = std::stof(v); }},
{"tabe.max_spawn_time", [](const std::string& v) { param.tabe.max_spawn_time = std::stof(v); }}, {"tabe.max_spawn_time", [](const std::string& v) { param.tabe.max_spawn_time = std::stof(v); }},
{"title.title_duration", [](const std::string& v) { param.title.title_duration = std::stof(v); }},
{"service_menu.window_message.padding", [](const std::string& v) { param.service_menu.window_message.padding = std::stof(v); }}, {"service_menu.window_message.padding", [](const std::string& v) { param.service_menu.window_message.padding = std::stof(v); }},
{"service_menu.window_message.line_spacing", [](const std::string& v) { param.service_menu.window_message.line_spacing = std::stof(v); }}, {"service_menu.window_message.line_spacing", [](const std::string& v) { param.service_menu.window_message.line_spacing = std::stof(v); }},
{"service_menu.window_message.title_separator_spacing", [](const std::string& v) { param.service_menu.window_message.title_separator_spacing = std::stof(v); }}, {"service_menu.window_message.title_separator_spacing", [](const std::string& v) { param.service_menu.window_message.title_separator_spacing = std::stof(v); }},

View File

@@ -38,7 +38,7 @@ struct ParamFade {
// --- Parámetros de la pantalla de título --- // --- Parámetros de la pantalla de título ---
struct ParamTitle { struct ParamTitle {
int press_start_position = GameDefaults::Title::PRESS_START_POSITION; int press_start_position = GameDefaults::Title::PRESS_START_POSITION;
int title_duration = GameDefaults::Title::DURATION; float title_duration = GameDefaults::Title::DURATION;
int arcade_edition_position = GameDefaults::Title::ARCADE_EDITION_POSITION; int arcade_edition_position = GameDefaults::Title::ARCADE_EDITION_POSITION;
int title_c_c_position = GameDefaults::Title::TITLE_C_C_POSITION; int title_c_c_position = GameDefaults::Title::TITLE_C_C_POSITION;
Color bg_color = Color::fromHex(GameDefaults::Title::BG_COLOR); Color bg_color = Color::fromHex(GameDefaults::Title::BG_COLOR);

View File

@@ -4,6 +4,12 @@
#include <functional> // Para function #include <functional> // Para function
#include <utility> // Para move #include <utility> // Para move
// Constructor para paths por puntos (convertido a segundos)
Path::Path(const std::vector<SDL_FPoint> &spots_init, float waiting_time_s_init)
: spots(spots_init), is_point_path(true) {
waiting_time_s = waiting_time_s_init;
}
// Devuelve un vector con los puntos que conforman la ruta // Devuelve un vector con los puntos que conforman la ruta
auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easing_function) -> std::vector<SDL_FPoint> { auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easing_function) -> std::vector<SDL_FPoint> {
std::vector<SDL_FPoint> v; std::vector<SDL_FPoint> v;
@@ -33,9 +39,9 @@ auto createPath(float start, float end, PathType type, float fixed_pos, int step
} }
// Actualiza la posición y comprueba si ha llegado a su destino // Actualiza la posición y comprueba si ha llegado a su destino
void PathSprite::update() { void PathSprite::update(float delta_time) {
if (enabled_ && !has_finished_) { if (enabled_ && !has_finished_) {
moveThroughCurrentPath(); moveThroughCurrentPath(delta_time);
goToNextPathOrDie(); goToNextPathOrDie();
} }
} }
@@ -77,14 +83,14 @@ void PathSprite::addPath(Path path, bool centered) {
} }
} }
// Añade un recorrido // Añade un recorrido generado (en segundos)
void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easing_function, int waiting_counter) { void PathSprite::addPath(float start, float end, PathType type, float fixed_pos, float duration_s, const std::function<double(double)> &easing_function, float waiting_time_s) {
paths_.emplace_back(createPath(start, end, type, fixed_pos, steps, easing_function), waiting_counter); paths_.emplace_back(start, end, type, fixed_pos, duration_s, waiting_time_s, easing_function);
} }
// Añade un recorrido // Añade un recorrido por puntos (en segundos)
void PathSprite::addPath(const std::vector<SDL_FPoint> &spots, int waiting_counter) { void PathSprite::addPath(const std::vector<SDL_FPoint> &spots, float waiting_time_s) {
paths_.emplace_back(spots, waiting_counter); paths_.emplace_back(spots, waiting_time_s);
} }
// Habilita el objeto // Habilita el objeto
@@ -95,35 +101,78 @@ void PathSprite::enable() {
enabled_ = true; enabled_ = true;
// Establece la posición // Establece la posición inicial
auto &path = paths_.at(current_path_); auto &path = paths_.at(current_path_);
const auto &p = path.spots.at(path.counter); if (path.is_point_path) {
setPosition(p); const auto &p = path.spots.at(path.counter);
setPosition(p);
} else {
// Para paths generados, establecer posición inicial
SDL_FPoint initial_pos;
if (path.type == PathType::HORIZONTAL) {
initial_pos = {path.start_pos, path.fixed_pos};
} else {
initial_pos = {path.fixed_pos, path.start_pos};
}
setPosition(initial_pos);
}
} }
// Coloca el sprite en los diferentes puntos del recorrido // Coloca el sprite en los diferentes puntos del recorrido
void PathSprite::moveThroughCurrentPath() { void PathSprite::moveThroughCurrentPath(float delta_time) {
auto &path = paths_.at(current_path_); auto &path = paths_.at(current_path_);
// Establece la posición if (path.is_point_path) {
const auto &p = path.spots.at(path.counter); // Lógica para paths por puntos (compatibilidad)
setPosition(p); const auto &p = path.spots.at(path.counter);
setPosition(p);
// Comprobar si ha terminado el recorrido if (!path.on_destination) {
if (!path.on_destination) { ++path.counter;
++path.counter; if (path.counter >= static_cast<int>(path.spots.size())) {
if (path.counter >= static_cast<int>(path.spots.size())) { path.on_destination = true;
path.on_destination = true; path.counter = static_cast<int>(path.spots.size()) - 1;
path.counter = static_cast<int>(path.spots.size()) - 1; }
} }
}
// Comprobar si ha terminado la espera if (path.on_destination) {
if (path.on_destination) { path.waiting_elapsed += delta_time;
if (path.waiting_counter == 0) { if (path.waiting_elapsed >= path.waiting_time_s) {
path.finished = true; path.finished = true;
}
}
} else {
// Lógica para paths generados en tiempo real
if (!path.on_destination) {
path.elapsed_time += delta_time;
// Calcular progreso (0.0 a 1.0)
float progress = path.elapsed_time / path.duration_s;
if (progress >= 1.0f) {
progress = 1.0f;
path.on_destination = true;
}
// Aplicar función de easing
double eased_progress = path.easing_function(progress);
// Calcular posición actual
float current_pos = path.start_pos + (path.end_pos - path.start_pos) * static_cast<float>(eased_progress);
// Establecer posición según el tipo
SDL_FPoint position;
if (path.type == PathType::HORIZONTAL) {
position = {current_pos, path.fixed_pos};
} else {
position = {path.fixed_pos, current_pos};
}
setPosition(position);
} else { } else {
--path.waiting_counter; // Esperar en destino
path.waiting_elapsed += delta_time;
if (path.waiting_elapsed >= path.waiting_time_s) {
path.finished = true;
}
} }
} }
} }

View File

@@ -24,17 +24,31 @@ enum class PathCentered { // Centrado del recorrido
}; };
// --- Estructuras --- // --- Estructuras ---
struct Path { // Define un recorrido para el sprite struct Path { // Define un recorrido para el sprite
std::vector<SDL_FPoint> spots; // Puntos por los que se desplazará el sprite float start_pos; // Posición inicial
int waiting_counter; // Tiempo de espera una vez en el destino float end_pos; // Posición final
bool on_destination = false; // Indica si ha llegado al destino PathType type; // Tipo de movimiento (horizontal/vertical)
bool finished = false; // Indica si ha terminado de esperarse float fixed_pos; // Posición fija en el eje contrario
int counter = 0; // Contador interno float duration_s; // Duración de la animación en segundos
float waiting_time_s; // Tiempo de espera una vez en el destino
std::function<double(double)> easing_function; // Función de easing
float elapsed_time = 0.0f; // Tiempo transcurrido
float waiting_elapsed = 0.0f; // Tiempo de espera transcurrido
bool on_destination = false; // Indica si ha llegado al destino
bool finished = false; // Indica si ha terminado de esperarse
// Constructor // Constructor para paths generados
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init) Path(float start, float end, PathType path_type, float fixed, float duration, float waiting, std::function<double(double)> easing)
: spots(spots_init), : start_pos(start), end_pos(end), type(path_type), fixed_pos(fixed),
waiting_counter(waiting_counter_init) {} duration_s(duration), waiting_time_s(waiting), easing_function(std::move(easing)) {}
// Constructor para paths por puntos (convertido a segundos)
Path(const std::vector<SDL_FPoint> &spots_init, float waiting_time_s_init);
// Variables para paths por puntos
std::vector<SDL_FPoint> spots; // Solo para paths por puntos
int counter = 0; // Solo para paths por puntos
bool is_point_path = false; // Indica si es un path por puntos
}; };
// --- Funciones --- // --- Funciones ---
@@ -49,13 +63,13 @@ class PathSprite : public Sprite {
~PathSprite() override = default; ~PathSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la posición del sprite según el recorrido void update(float delta_time); // Actualiza la posición del sprite según el recorrido (delta_time en segundos)
void render() override; // Muestra el sprite por pantalla void render() override; // Muestra el sprite por pantalla
// --- Gestión de recorridos --- // --- Gestión de recorridos ---
void addPath(Path path, bool centered = false); // Añade un recorrido (Path) void addPath(Path path, bool centered = false); // Añade un recorrido (Path)
void addPath(const std::vector<SDL_FPoint> &spots, int waiting_counter = 0); // Añade un recorrido a partir de puntos void addPath(const std::vector<SDL_FPoint> &spots, float waiting_time_s = 0.0f); // Añade un recorrido a partir de puntos
void addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easing_function, int waiting_counter = 0); // Añade un recorrido generado void addPath(float start, float end, PathType type, float fixed_pos, float duration_s, const std::function<double(double)> &easing_function, float waiting_time_s = 0.0f); // Añade un recorrido generado
// --- Estado y control --- // --- Estado y control ---
void enable(); // Habilita el objeto void enable(); // Habilita el objeto
@@ -72,6 +86,6 @@ class PathSprite : public Sprite {
std::vector<Path> paths_; // Caminos a recorrer por el sprite std::vector<Path> paths_; // Caminos a recorrer por el sprite
// --- Métodos internos --- // --- Métodos internos ---
void moveThroughCurrentPath(); // Coloca el sprite en los diferentes puntos del recorrido void moveThroughCurrentPath(float delta_time); // Coloca el sprite en los diferentes puntos del recorrido
void goToNextPathOrDie(); // Cambia de recorrido o finaliza void goToNextPathOrDie(); // Cambia de recorrido o finaliza
}; };

View File

@@ -3,6 +3,7 @@
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_FlipMode #include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_FlipMode
#include <algorithm> // Para clamp, max, min #include <algorithm> // Para clamp, max, min
#include <cmath> // Para fmod
#include <cstdlib> // Para rand #include <cstdlib> // Para rand
#include "animated_sprite.h" // Para AnimatedSprite #include "animated_sprite.h" // Para AnimatedSprite
@@ -59,17 +60,14 @@ void Player::init() {
power_up_counter_ = POWERUP_COUNTER; power_up_counter_ = POWERUP_COUNTER;
extra_hit_ = false; extra_hit_ = false;
coffees_ = 0; coffees_ = 0;
continue_ticks_ = 0;
continue_counter_ = 10; continue_counter_ = 10;
name_entry_ticks_ = 0; name_entry_idle_time_accumulator_ = 0.0f;
name_entry_idle_counter_ = 0; name_entry_total_time_accumulator_ = 0.0f;
name_entry_total_counter_ = 0;
shiftColliders(); shiftColliders();
vel_x_ = 0; vel_x_ = 0;
vel_y_ = 0; vel_y_ = 0;
score_ = 0; score_ = 0;
score_multiplier_ = 1.0F; score_multiplier_ = 1.0F;
cant_fire_counter_ = 10;
enter_name_->init(last_enter_name_); enter_name_->init(last_enter_name_);
// Establece la posición del sprite // Establece la posición del sprite
@@ -148,38 +146,38 @@ void Player::setInputEnteringName(Input::Action action) {
default: default:
break; break;
} }
name_entry_idle_counter_ = 0; name_entry_idle_time_accumulator_ = 0.0f;
} }
// Mueve el jugador a la posición y animación que le corresponde // Fase 1: Sistema de movimiento time-based
void Player::move() { void Player::move(float deltaTime) {
switch (playing_state_) { switch (playing_state_) {
case State::PLAYING: case State::PLAYING:
handlePlayingMovement(); handlePlayingMovement(deltaTime);
break; break;
case State::ROLLING: case State::ROLLING:
handleRollingMovement(); handleRollingMovement();
break; break;
case State::TITLE_ANIMATION: case State::TITLE_ANIMATION:
handleTitleAnimation(); handleTitleAnimation();
break; break;
case State::CONTINUE_TIME_OUT: case State::CONTINUE_TIME_OUT:
handleContinueTimeOut(); handleContinueTimeOut();
break; break;
case State::LEAVING_SCREEN: case State::LEAVING_SCREEN:
handleLeavingScreen(); handleLeavingScreen();
break; break;
case State::ENTERING_SCREEN: case State::ENTERING_SCREEN:
handleEnteringScreen(); handleEnteringScreen();
break; break;
case State::CREDITS: case State::CREDITS:
handleCreditsMovement(); handleCreditsMovement(deltaTime);
break; break;
case State::WAITING: case State::WAITING:
handleWaitingMovement(); handleWaitingMovement(deltaTime);
break; break;
case State::RECOVER: case State::RECOVER:
handleRecoverMovement(); handleRecoverMovement();
break; break;
default: default:
break; break;
@@ -198,6 +196,19 @@ void Player::handlePlayingMovement() {
shiftSprite(); shiftSprite();
} }
// Fase 1: Movimiento time-based durante el juego
void Player::handlePlayingMovement(float deltaTime) {
// Mueve el jugador a derecha o izquierda (time-based)
pos_x_ += vel_x_ * deltaTime / (1000.0f / 60.0f);
// Si el jugador abandona el area de juego por los laterales, restaura su posición
const float MIN_X = play_area_.x - 5;
const float MAX_X = play_area_.w + 5 - WIDTH;
pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);
shiftSprite();
}
void Player::handleRecoverMovement() { void Player::handleRecoverMovement() {
if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_recover.wav"); } if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_recover.wav"); }
if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); } if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); }
@@ -337,6 +348,20 @@ void Player::handleCreditsMovement() {
shiftSprite(); shiftSprite();
} }
// Fase 4: Movimiento general en la pantalla de créditos (time-based)
void Player::handleCreditsMovement(float deltaTime) {
pos_x_ += (vel_x_ / 2.0F) * deltaTime / (1000.0f / 60.0f); // Convert frame-based movement to time-based
if (vel_x_ > 0) {
handleCreditsRightMovement();
} else {
handleCreditsLeftMovement();
}
updateWalkingStateForCredits();
shiftSprite();
}
void Player::handleCreditsRightMovement() { void Player::handleCreditsRightMovement() {
if (pos_x_ > param.game.game_area.rect.w - WIDTH) { if (pos_x_ > param.game.game_area.rect.w - WIDTH) {
pos_x_ = param.game.game_area.rect.w - WIDTH; pos_x_ = param.game.game_area.rect.w - WIDTH;
@@ -359,6 +384,16 @@ void Player::handleWaitingMovement() {
} }
} }
// Fase 4: Controla la animación del jugador saludando (time-based)
void Player::handleWaitingMovement(float deltaTime) {
waiting_time_accumulator_ += deltaTime;
float waiting_duration = static_cast<float>(WAITING_COUNTER) * (1000.0f / 60.0f); // Convert frames to milliseconds
if (waiting_time_accumulator_ >= waiting_duration) {
waiting_time_accumulator_ = 0.0f;
player_sprite_->resetAnimation();
}
}
void Player::updateWalkingStateForCredits() { void Player::updateWalkingStateForCredits() {
if (pos_x_ > param.game.game_area.center_x - WIDTH / 2) { if (pos_x_ > param.game.game_area.center_x - WIDTH / 2) {
setWalkingState(State::WALKING_LEFT); setWalkingState(State::WALKING_LEFT);
@@ -387,11 +422,35 @@ void Player::updateStepCounter() {
} }
} }
// Fase 4: Incrementa o ajusta el contador de pasos (time-based)
void Player::updateStepCounter(float deltaTime) {
step_time_accumulator_ += deltaTime;
float step_interval = 10.0f / 60.0f; // 10 frames converted to seconds
if (step_time_accumulator_ >= step_interval) {
step_time_accumulator_ = 0.0f;
playSound("walk.wav");
}
}
// Pinta el jugador en pantalla // Pinta el jugador en pantalla
void Player::render() { void Player::render() {
if (power_up_ && isPlaying()) { if (power_up_ && isPlaying()) {
if (power_up_counter_ > (POWERUP_COUNTER / 4) || power_up_counter_ % 20 > 4) { // Convertir lógica de parpadeo a deltaTime
float total_powerup_time_ms = static_cast<float>(POWERUP_COUNTER) / 60.0f * 1000.0f; // Total time in ms
float quarter_time_ms = total_powerup_time_ms / 4.0f; // 25% del tiempo total
if (power_up_time_accumulator_ > quarter_time_ms) {
// En los primeros 75% del tiempo, siempre visible
power_sprite_->render(); power_sprite_->render();
} else {
// En el último 25%, parpadea cada 20 frames (333ms aprox)
constexpr float blink_period_ms = 20.0f / 60.0f * 1000.0f; // 20 frames in ms
constexpr float visible_proportion = 4.0f / 20.0f; // 4 frames visible de 20 total
float cycle_position = fmod(power_up_time_accumulator_, blink_period_ms) / blink_period_ms;
if (cycle_position >= visible_proportion) {
power_sprite_->render();
}
} }
} }
@@ -454,8 +513,8 @@ auto Player::computeAnimation() const -> std::pair<std::string, SDL_FlipMode> {
return {anim_name, flip_mode}; return {anim_name, flip_mode};
} }
// Establece la animación correspondiente al estado // Fase 1: Establece la animación correspondiente al estado (time-based)
void Player::setAnimation() { 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_NAME_GAME_COMPLETED:
@@ -491,108 +550,23 @@ void Player::setAnimation() {
break; break;
} }
player_sprite_->update(); // La diferencia clave: usa deltaTime para las animaciones
power_sprite_->update(); player_sprite_->update(deltaTime);
power_sprite_->update(deltaTime);
} }
// Actualiza el valor de la variable // Actualiza al jugador con deltaTime (time-based)
void Player::updateCooldown() { void Player::update(float deltaTime) {
if (playing_state_ != State::PLAYING) { move(deltaTime); // Sistema de movimiento time-based
return; setAnimation(deltaTime); // Animaciones time-based
} shiftColliders(); // Sin cambios (posicional)
updateFireSystem(deltaTime); // Sistema de disparo de dos líneas
if (cant_fire_counter_ > 0) { updatePowerUp(deltaTime); // Sistema de power-up time-based
handleFiringCooldown(); updateInvulnerable(deltaTime); // Sistema de invulnerabilidad time-based
} else { updateScoreboard(); // Sin cambios (no temporal)
handleRecoilAndCooling(); updateContinueCounter(deltaTime); // Sistema de continue time-based
} updateEnterNameCounter(deltaTime); // Sistema de name entry time-based
} updateShowingName(deltaTime); // Sistema de showing name time-based
void Player::handleFiringCooldown() {
cooling_state_counter_ = COOLING_DURATION;
// Transition to recoiling state at halfway point
if (cant_fire_counter_ == recoiling_state_duration_ / 2) {
transitionToRecoiling();
}
--cant_fire_counter_;
if (cant_fire_counter_ == 0) {
recoiling_state_counter_ = recoiling_state_duration_;
}
}
void Player::handleRecoilAndCooling() {
if (recoiling_state_counter_ > 0) {
--recoiling_state_counter_;
return;
}
handleCoolingState();
}
void Player::handleCoolingState() {
if (cooling_state_counter_ > COOLING_COMPLETE) {
if (cooling_state_counter_ == COOLING_DURATION) {
transitionToCooling();
}
--cooling_state_counter_;
}
if (cooling_state_counter_ == COOLING_COMPLETE) {
completeCooling();
}
}
void Player::transitionToRecoiling() {
switch (firing_state_) {
case State::FIRING_LEFT:
setFiringState(State::RECOILING_LEFT);
break;
case State::FIRING_RIGHT:
setFiringState(State::RECOILING_RIGHT);
break;
case State::FIRING_UP:
setFiringState(State::RECOILING_UP);
break;
default:
break;
}
}
void Player::transitionToCooling() {
switch (firing_state_) {
case State::RECOILING_LEFT:
setFiringState(State::COOLING_LEFT);
break;
case State::RECOILING_RIGHT:
setFiringState(State::COOLING_RIGHT);
break;
case State::RECOILING_UP:
setFiringState(State::COOLING_UP);
break;
default:
break;
}
}
void Player::completeCooling() {
setFiringState(State::FIRING_NONE);
cooling_state_counter_ = -1;
}
// Actualiza al jugador a su posicion, animación y controla los contadores
void Player::update() {
move();
setAnimation();
shiftColliders();
updateCooldown();
updatePowerUp();
updateInvulnerable();
updateScoreboard();
updateContinueCounter();
updateEnterNameCounter();
updateShowingName();
} }
void Player::passShowingName() { void Player::passShowingName() {
@@ -662,8 +636,8 @@ void Player::setPlayingState(State state) {
} }
case State::CONTINUE: { case State::CONTINUE: {
// Inicializa el contador de continuar // Inicializa el contador de continuar
continue_ticks_ = SDL_GetTicks();
continue_counter_ = 9; continue_counter_ = 9;
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;
@@ -682,6 +656,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
shiftSprite(); shiftSprite();
player_sprite_->setCurrentAnimation("hello"); player_sprite_->setCurrentAnimation("hello");
player_sprite_->animtionPause(); player_sprite_->animtionPause();
@@ -693,7 +668,7 @@ void Player::setPlayingState(State state) {
break; break;
} }
case State::SHOWING_NAME: { case State::SHOWING_NAME: {
showing_name_ticks_ = SDL_GetTicks(); showing_name_time_accumulator_ = 0.0f; // Inicializar acumulador time-based
setScoreboardMode(Scoreboard::Mode::SHOW_NAME); setScoreboardMode(Scoreboard::Mode::SHOW_NAME);
Scoreboard::get()->setRecordName(scoreboard_panel_, last_enter_name_); Scoreboard::get()->setRecordName(scoreboard_panel_, last_enter_name_);
addScoreToScoreBoard(); addScoreToScoreBoard();
@@ -749,12 +724,14 @@ void Player::setPlayingState(State state) {
} }
case State::LEAVING_SCREEN: { case State::LEAVING_SCREEN: {
step_counter_ = 0; step_counter_ = 0;
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
setScoreboardMode(Scoreboard::Mode::SCORE); setScoreboardMode(Scoreboard::Mode::SCORE);
switch (id_) { switch (id_) {
case Id::PLAYER1: case Id::PLAYER1:
@@ -795,24 +772,26 @@ 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 * 1000.0f : 0.0f; // Convert frames to milliseconds
} }
// Monitoriza el estado // Monitoriza el estado (time-based)
void Player::updateInvulnerable() { void Player::updateInvulnerable(float deltaTime) {
if (playing_state_ == State::PLAYING && invulnerable_) { if (playing_state_ == State::PLAYING && invulnerable_) {
if (invulnerable_counter_ > 0) { if (invulnerable_time_accumulator_ > 0) {
--invulnerable_counter_; invulnerable_time_accumulator_ -= deltaTime;
// Frecuencia fija de parpadeo (como el original) // Frecuencia fija de parpadeo adaptada a deltaTime (en milisegundos)
constexpr int blink_speed = 8; constexpr float blink_period_ms = 8.0f / 60.0f * 1000.0f; // 8 frames convertidos a ms
// Calcula proporción decreciente: menos textura blanca hacia el final // Calcula proporción decreciente basada en tiempo restante
// Al inicio: 50-50, hacia el final: 70-30 (menos blanco) float total_invulnerable_time_ms = static_cast<float>(INVULNERABLE_COUNTER) / 60.0f * 1000.0f;
float progress = 1.0f - (static_cast<float>(invulnerable_counter_) / INVULNERABLE_COUNTER); float progress = 1.0f - (invulnerable_time_accumulator_ / total_invulnerable_time_ms);
int white_frames = static_cast<int>((0.5f - progress * 0.2f) * blink_speed); float white_proportion = 0.5f - progress * 0.2f; // Menos blanco hacia el final
// Alterna entre texturas con proporción variable // Calcula si debe mostrar textura de invulnerabilidad basado en el ciclo temporal
bool should_show_invulnerable = (invulnerable_counter_ % blink_speed) < white_frames; float cycle_position = fmod(invulnerable_time_accumulator_, blink_period_ms) / blink_period_ms;
bool should_show_invulnerable = cycle_position < white_proportion;
size_t target_texture = should_show_invulnerable ? INVULNERABLE_TEXTURE : coffees_; size_t target_texture = should_show_invulnerable ? INVULNERABLE_TEXTURE : coffees_;
// Solo cambia textura si es diferente (optimización) // Solo cambia textura si es diferente (optimización)
@@ -821,6 +800,7 @@ void Player::updateInvulnerable() {
} }
} else { } else {
// Fin de invulnerabilidad // Fin de invulnerabilidad
invulnerable_time_accumulator_ = 0;
setInvulnerable(false); setInvulnerable(false);
player_sprite_->setActiveTexture(coffees_); player_sprite_->setActiveTexture(coffees_);
} }
@@ -831,14 +811,18 @@ void Player::updateInvulnerable() {
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 * 1000.0f; // Convert frames to milliseconds
} }
// Actualiza el valor de la variable // Actualiza el valor de la variable (time-based)
void Player::updatePowerUp() { void Player::updatePowerUp(float deltaTime) {
if (playing_state_ == State::PLAYING) { if (playing_state_ == State::PLAYING) {
if (power_up_) { if (power_up_) {
--power_up_counter_; power_up_time_accumulator_ -= deltaTime;
power_up_ = power_up_counter_ > 0; power_up_ = power_up_time_accumulator_ > 0;
if (!power_up_) {
power_up_time_accumulator_ = 0;
}
} }
} }
} }
@@ -875,31 +859,36 @@ void Player::setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &text
power_sprite_->setTexture(texture[1]); power_sprite_->setTexture(texture[1]);
} }
// Actualiza el contador de continue // Actualiza el contador de continue (time-based)
void Player::updateContinueCounter() { void Player::updateContinueCounter(float deltaTime) {
if (playing_state_ == State::CONTINUE) { if (playing_state_ == State::CONTINUE) {
constexpr int TICKS_SPEED = 1000; continue_time_accumulator_ += deltaTime;
if (SDL_GetTicks() - continue_ticks_ > TICKS_SPEED) { constexpr float CONTINUE_INTERVAL = 1000.0f; // 1 segundo en milisegundos
if (continue_time_accumulator_ >= CONTINUE_INTERVAL) {
continue_time_accumulator_ -= CONTINUE_INTERVAL;
decContinueCounter(); decContinueCounter();
} }
} }
} }
// Actualiza el contador de entrar nombre // Actualiza el contador de entrar nombre (time-based)
void Player::updateEnterNameCounter() { 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) {
constexpr int TICKS_SPEED = 1000; name_entry_time_accumulator_ += deltaTime;
if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED) { constexpr float NAME_ENTRY_INTERVAL = 1000.0f; // 1 segundo en milisegundos
if (name_entry_time_accumulator_ >= NAME_ENTRY_INTERVAL) {
name_entry_time_accumulator_ -= NAME_ENTRY_INTERVAL;
decNameEntryCounter(); decNameEntryCounter();
} }
} }
} }
// Actualiza el estado de SHOWING_NAME // Actualiza el estado de SHOWING_NAME (time-based)
void Player::updateShowingName() { void Player::updateShowingName(float deltaTime) {
if (playing_state_ == State::SHOWING_NAME) { if (playing_state_ == State::SHOWING_NAME) {
constexpr int TICKS_SPEED = 5000; showing_name_time_accumulator_ += deltaTime;
if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED) { constexpr float SHOWING_NAME_DURATION = 5000.0f; // 5 segundos en milisegundos
if (showing_name_time_accumulator_ >= SHOWING_NAME_DURATION) {
game_completed_ ? setPlayingState(State::LEAVING_SCREEN) : setPlayingState(State::CONTINUE); game_completed_ ? setPlayingState(State::LEAVING_SCREEN) : setPlayingState(State::CONTINUE);
} }
} }
@@ -907,7 +896,7 @@ void Player::updateShowingName() {
// Decrementa el contador de continuar // Decrementa el contador de continuar
void Player::decContinueCounter() { void Player::decContinueCounter() {
continue_ticks_ = SDL_GetTicks(); 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);
@@ -918,17 +907,17 @@ void Player::decContinueCounter() {
// Decrementa el contador de entrar nombre // Decrementa el contador de entrar nombre
void Player::decNameEntryCounter() { void Player::decNameEntryCounter() {
name_entry_ticks_ = SDL_GetTicks(); name_entry_time_accumulator_ = 0.0f; // Reset time accumulator
// Actualiza contadores // Incrementa acumuladores de tiempo (1 segundo = 1000ms)
++name_entry_idle_counter_; name_entry_idle_time_accumulator_ += 1000.0f;
++name_entry_total_counter_; name_entry_total_time_accumulator_ += 1000.0f;
// Comprueba los contadores // Comprueba los acumuladores directamente contra los límites en milisegundos
if ((name_entry_total_counter_ >= param.game.name_entry_total_time) || if ((name_entry_total_time_accumulator_ >= param.game.name_entry_total_time) ||
(name_entry_idle_counter_ >= param.game.name_entry_idle_time)) { (name_entry_idle_time_accumulator_ >= param.game.name_entry_idle_time)) {
name_entry_total_counter_ = 0; name_entry_total_time_accumulator_ = 0.0f;
name_entry_idle_counter_ = 0; name_entry_idle_time_accumulator_ = 0.0f;
if (playing_state_ == State::ENTERING_NAME) { if (playing_state_ == State::ENTERING_NAME) {
last_enter_name_ = getRecordName(); last_enter_name_ = getRecordName();
setPlayingState(State::SHOWING_NAME); setPlayingState(State::SHOWING_NAME);
@@ -988,4 +977,145 @@ void Player::addScoreToScoreBoard() const {
void Player::addCredit() { void Player::addCredit() {
++credits_used_; ++credits_used_;
playSound("credit.wav"); playSound("credit.wav");
}
// ========================================
// SISTEMA DE DISPARO DE DOS LÍNEAS
// ========================================
// Método principal del sistema de disparo
void Player::updateFireSystem(float deltaTime) {
updateFunctionalLine(deltaTime); // Línea 1: CanFire
updateVisualLine(deltaTime); // Línea 2: Animaciones
}
// LÍNEA 1: Sistema Funcional (CanFire)
void Player::updateFunctionalLine(float deltaTime) {
if (fire_cooldown_timer_ > 0) {
fire_cooldown_timer_ -= deltaTime;
can_fire_new_system_ = false;
} else {
fire_cooldown_timer_ = 0; // Evitar valores negativos
can_fire_new_system_ = true;
}
}
// LÍNEA 2: Sistema Visual (Animaciones)
void Player::updateVisualLine(float deltaTime) {
if (visual_fire_state_ == VisualFireState::NORMAL) {
return; // No hay temporizador activo en estado NORMAL
}
visual_state_timer_ -= deltaTime;
switch (visual_fire_state_) {
case VisualFireState::AIMING:
if (visual_state_timer_ <= 0) {
transitionToRecoilingNew();
}
break;
case VisualFireState::RECOILING:
if (visual_state_timer_ <= 0) {
transitionToThreatPose();
}
break;
case VisualFireState::THREAT_POSE:
if (visual_state_timer_ <= 0) {
transitionToNormalNew();
}
break;
case VisualFireState::NORMAL:
// Ya manejado arriba
break;
}
}
// Inicia un disparo en ambas líneas
void Player::startFiringSystem(int cooldown_frames) {
// LÍNEA 1: Inicia cooldown funcional
fire_cooldown_timer_ = static_cast<float>(cooldown_frames) / 60.0f * 1000.0f; // Convertir frames a ms
can_fire_new_system_ = false;
// LÍNEA 2: Resetea completamente el estado visual
aiming_duration_ = fire_cooldown_timer_ * AIMING_DURATION_FACTOR; // 50% del cooldown
recoiling_duration_ = aiming_duration_ * RECOILING_DURATION_MULTIPLIER; // 4 veces la duración de aiming
visual_fire_state_ = VisualFireState::AIMING;
visual_state_timer_ = aiming_duration_;
updateFiringStateFromVisual(); // Sincroniza firing_state_ para animaciones
}
// Sincroniza firing_state_ con visual_fire_state_
void Player::updateFiringStateFromVisual() {
// Mantener la dirección actual del disparo
State base_state = State::FIRING_NONE;
if (firing_state_ == State::FIRING_LEFT || firing_state_ == State::RECOILING_LEFT || firing_state_ == State::COOLING_LEFT) {
base_state = State::FIRING_LEFT;
} else if (firing_state_ == State::FIRING_RIGHT || firing_state_ == State::RECOILING_RIGHT || firing_state_ == State::COOLING_RIGHT) {
base_state = State::FIRING_RIGHT;
} else if (firing_state_ == State::FIRING_UP || firing_state_ == State::RECOILING_UP || firing_state_ == State::COOLING_UP) {
base_state = State::FIRING_UP;
}
switch (visual_fire_state_) {
case VisualFireState::NORMAL:
firing_state_ = State::FIRING_NONE;
break;
case VisualFireState::AIMING:
firing_state_ = base_state; // FIRING_LEFT/RIGHT/UP
break;
case VisualFireState::RECOILING:
switch (base_state) {
case State::FIRING_LEFT: firing_state_ = State::RECOILING_LEFT; 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;
case VisualFireState::THREAT_POSE:
switch (base_state) {
case State::FIRING_LEFT: firing_state_ = State::COOLING_LEFT; 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;
}
}
// Transiciones del sistema visual
void Player::transitionToRecoilingNew() {
visual_fire_state_ = VisualFireState::RECOILING;
visual_state_timer_ = recoiling_duration_;
updateFiringStateFromVisual();
}
void Player::transitionToThreatPose() {
visual_fire_state_ = VisualFireState::THREAT_POSE;
// Calcular threat_pose_duration ajustada:
// Duración original (833ms) menos el tiempo extra que ahora dura recoiling
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 extra_recoiling_time = new_recoiling_duration - original_recoiling_duration;
float adjusted_threat_duration = THREAT_POSE_DURATION - extra_recoiling_time;
// Asegurar que no sea negativo
visual_state_timer_ = std::max(adjusted_threat_duration, MIN_THREAT_POSE_DURATION);
updateFiringStateFromVisual();
}
void Player::transitionToNormalNew() {
visual_fire_state_ = VisualFireState::NORMAL;
visual_state_timer_ = 0;
updateFiringStateFromVisual();
} }

View File

@@ -17,7 +17,21 @@
class Texture; class Texture;
// --- Clase Player --- // --- Clase Player: jugador principal del juego ---
//
// Esta clase gestiona todos los aspectos de un jugador durante el juego,
// incluyendo movimiento, disparos, animaciones y estados especiales.
//
// Funcionalidades principales:
// • Sistema de disparo de dos líneas: funcional (cooldown) + visual (animaciones)
// • Estados de animación: normal → aiming → recoiling → threat_pose → normal
// • Movimiento time-based: compatibilidad con deltaTime para fluidez variable
// • Power-ups e invulnerabilidad: coffee machine, extra hits, parpadeos
// • Sistema de puntuación: multipliers, high scores, entrada de nombres
// • Estados de juego: playing, rolling, continue, entering_name, etc.
//
// El sistema de disparo utiliza duraciones configurables mediante constantes
// para facilitar el ajuste del gameplay y la sensación de disparo.
class Player { class Player {
public: public:
// --- Constantes --- // --- Constantes ---
@@ -94,9 +108,9 @@ class Player {
~Player() = default; ~Player() = default;
// --- Inicialización y ciclo de vida --- // --- Inicialización y ciclo de vida ---
void init(); // Inicializa el jugador void init(); // Inicializa el jugador
void update(); // Actualiza estado, animación y contadores void update(float deltaTime); // Actualiza estado, animación y contadores (time-based)
void render(); // Dibuja el jugador en pantalla void render(); // Dibuja el jugador en pantalla
// --- Entrada y control --- // --- Entrada y control ---
void setInput(Input::Action action); // Procesa entrada general void setInput(Input::Action action); // Procesa entrada general
@@ -104,14 +118,13 @@ class Player {
void setInputEnteringName(Input::Action action); // Procesa entrada al introducir nombre void setInputEnteringName(Input::Action action); // Procesa entrada al introducir nombre
// --- Movimiento y animación --- // --- Movimiento y animación ---
void move(); // Mueve el jugador void move(float deltaTime); // Mueve el jugador (time-based)
void setAnimation(); // Establece la animación según el estado void setAnimation(float deltaTime); // Establece la animación según el estado (time-based)
// --- Texturas y animaciones --- // --- Texturas y animaciones ---
void setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture); // Cambia las texturas del jugador void setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture); // Cambia las texturas del jugador
// --- Estados y contadores --- // --- Estados y contadores ---
void updateCooldown(); // Actualiza el cooldown de disparo
// --- Puntuación y marcador --- // --- Puntuación y marcador ---
void addScore(int score, int lowest_hi_score_entry); // Añade puntos void addScore(int score, int lowest_hi_score_entry); // Añade puntos
@@ -122,7 +135,7 @@ class Player {
void setPlayingState(State state); // Cambia el estado de juego void setPlayingState(State state); // Cambia el estado de juego
void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad
void setPowerUp(); // Activa el modo PowerUp void setPowerUp(); // Activa el modo PowerUp
void updatePowerUp(); // Actualiza el valor de PowerUp void updatePowerUp(float deltaTime); // Actualiza el valor de PowerUp (time-based)
void giveExtraHit(); // Concede un toque extra al jugador void giveExtraHit(); // Concede un toque extra al jugador
void removeExtraHit(); // Quita el toque extra al jugador void removeExtraHit(); // Quita el toque extra al jugador
void decContinueCounter(); // Decrementa el contador de continuar void decContinueCounter(); // Decrementa el contador de continuar
@@ -145,7 +158,7 @@ class Player {
[[nodiscard]] auto isTitleHidden() const -> bool { return playing_state_ == State::TITLE_HIDDEN; } [[nodiscard]] auto isTitleHidden() const -> bool { return playing_state_ == State::TITLE_HIDDEN; }
// Getters // Getters
[[nodiscard]] auto canFire() const -> bool { return cant_fire_counter_ <= 0; } [[nodiscard]] auto canFire() const -> bool { return can_fire_new_system_; } // Usa nuevo sistema
[[nodiscard]] auto hasExtraHit() const -> bool { return extra_hit_; } [[nodiscard]] auto hasExtraHit() const -> bool { return extra_hit_; }
[[nodiscard]] auto isCooling() const -> bool { return firing_state_ == State::COOLING_LEFT || firing_state_ == State::COOLING_UP || firing_state_ == State::COOLING_RIGHT; } [[nodiscard]] auto isCooling() const -> bool { return firing_state_ == State::COOLING_LEFT || firing_state_ == State::COOLING_UP || firing_state_ == State::COOLING_RIGHT; }
[[nodiscard]] auto isRecoiling() const -> bool { return firing_state_ == State::RECOILING_LEFT || firing_state_ == State::RECOILING_UP || firing_state_ == State::RECOILING_RIGHT; } [[nodiscard]] auto isRecoiling() const -> bool { return firing_state_ == State::RECOILING_LEFT || firing_state_ == State::RECOILING_UP || firing_state_ == State::RECOILING_RIGHT; }
@@ -175,7 +188,7 @@ class Player {
// Setters inline // Setters inline
void setController(int index) { controller_index_ = index; } void setController(int index) { controller_index_ = index; }
void setCantFireCounter(int counter) { recoiling_state_duration_ = cant_fire_counter_ = counter; } void startFiringSystem(int cooldown_frames); // Método público para iniciar disparo
void setFiringState(State state) { firing_state_ = state; } void setFiringState(State state) { firing_state_ = state; }
void setInvulnerableCounter(int value) { invulnerable_counter_ = value; } void setInvulnerableCounter(int value) { invulnerable_counter_ = value; }
void setName(const std::string &name) { name_ = name; } void setName(const std::string &name) { name_ = name; }
@@ -193,15 +206,27 @@ class Player {
[[nodiscard]] auto getUsesKeyboard() const -> bool { return uses_keyboard_; } [[nodiscard]] auto getUsesKeyboard() const -> bool { return uses_keyboard_; }
private: private:
// --- Constantes --- // --- Constantes de física y movimiento ---
static constexpr int POWERUP_COUNTER = 1500; // Duración del estado PowerUp
static constexpr int INVULNERABLE_COUNTER = 200; // Duración del estado invulnerable
static constexpr size_t INVULNERABLE_TEXTURE = 3; // Textura usada durante invulnerabilidad
static constexpr float BASE_SPEED = 1.5F; // Velocidad base del jugador static constexpr float BASE_SPEED = 1.5F; // Velocidad base del jugador
// --- Constantes de power-ups y estados especiales ---
static constexpr int POWERUP_COUNTER = 1500; // Duración del estado PowerUp (frames)
static constexpr int INVULNERABLE_COUNTER = 200; // Duración del estado invulnerable (frames)
static constexpr size_t INVULNERABLE_TEXTURE = 3; // Textura usada durante invulnerabilidad
// --- Constantes del sistema de disparo (obsoletas - usar nuevo sistema) ---
static constexpr int COOLING_DURATION = 50; // Duración del enfriamiento tras disparar static constexpr int COOLING_DURATION = 50; // Duración del enfriamiento tras disparar
static constexpr int COOLING_COMPLETE = 0; // Valor que indica enfriamiento completado static constexpr int COOLING_COMPLETE = 0; // Valor que indica enfriamiento completado
// --- Constantes de estados de espera ---
static constexpr int WAITING_COUNTER = 1000; // Tiempo de espera en estado de espera static constexpr int WAITING_COUNTER = 1000; // Tiempo de espera en estado de espera
// --- Constantes del nuevo sistema de disparo de dos líneas ---
static constexpr float AIMING_DURATION_FACTOR = 0.5f; // 50% del cooldown funcional
static constexpr float RECOILING_DURATION_MULTIPLIER = 4.0f; // 4 veces la duración de aiming
static constexpr float THREAT_POSE_DURATION = 833.33f; // 50 frames = ~833ms (duración base)
static constexpr float MIN_THREAT_POSE_DURATION = 100.0f; // Duración mínima para threat pose
// --- Objetos y punteros --- // --- Objetos y punteros ---
std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador
std::unique_ptr<AnimatedSprite> power_sprite_; // Sprite para dibujar el aura del jugador con el poder a tope std::unique_ptr<AnimatedSprite> power_sprite_; // Sprite para dibujar el aura del jugador con el poder a tope
@@ -222,9 +247,6 @@ class Player {
State firing_state_ = State::FIRING_NONE; // Estado del jugador al disparar State firing_state_ = State::FIRING_NONE; // Estado del jugador al disparar
State playing_state_ = State::WAITING; // Estado del jugador en el juego State playing_state_ = State::WAITING; // Estado del jugador en el juego
Uint32 continue_ticks_ = 0; // Variable para poder cambiar el contador de continue en función del tiempo
Uint32 name_entry_ticks_ = 0; // Variable para poder cambiar el contador de poner nombre en función del tiempo
Uint32 showing_name_ticks_ = 0; // Tiempo en el que se entra al estado SHOWING_NAME
float pos_x_ = 0.0F; // Posición en el eje X float pos_x_ = 0.0F; // Posición en el eje X
float default_pos_x_; // Posición inicial para el jugador float default_pos_x_; // Posición inicial para el jugador
float vel_x_ = 0.0F; // Cantidad de píxeles a desplazarse en el eje X float vel_x_ = 0.0F; // Cantidad de píxeles a desplazarse en el eje X
@@ -232,10 +254,36 @@ class Player {
int pos_y_ = 0; // Posición en el eje Y int pos_y_ = 0; // Posición en el eje Y
int default_pos_y_; // Posición inicial para el jugador int default_pos_y_; // Posición inicial para el jugador
int vel_y_ = 0; // Cantidad de píxeles a desplazarse en el eje Y int vel_y_ = 0; // Cantidad de píxeles a desplazarse en el eje Y
int cant_fire_counter_ = 0; // Contador durante el cual no puede disparar float invulnerable_time_accumulator_ = 0.0f; // Acumulador de tiempo para invulnerabilidad (time-based)
int recoiling_state_counter_ = 0; // Contador para la animación del estado de retroceso float power_up_time_accumulator_ = 0.0f; // Acumulador de tiempo para power-up (time-based)
int recoiling_state_duration_ = 0; // Número de frames que dura el estado de retroceso float continue_time_accumulator_ = 0.0f; // Acumulador de tiempo para continue counter (time-based)
int cooling_state_counter_ = 0; // Contador para la animación del estado cooling float name_entry_time_accumulator_ = 0.0f; // Acumulador de tiempo para name entry counter (time-based)
float showing_name_time_accumulator_ = 0.0f; // Acumulador de tiempo para showing name (time-based)
float waiting_time_accumulator_ = 0.0f; // Acumulador de tiempo para waiting movement (time-based)
float step_time_accumulator_ = 0.0f; // Acumulador de tiempo para step counter (time-based)
// ========================================
// NUEVO SISTEMA DE DISPARO DE DOS LÍNEAS
// ========================================
// LÍNEA 1: SISTEMA FUNCIONAL (CanFire)
float fire_cooldown_timer_ = 0.0f; // Tiempo restante hasta poder disparar otra vez
bool can_fire_new_system_ = true; // true si puede disparar ahora mismo
// LÍNEA 2: SISTEMA VISUAL (Animaciones)
enum class VisualFireState {
NORMAL, // Brazo en posición neutral
AIMING, // Brazo alzado (disparando)
RECOILING, // Brazo en retroceso
THREAT_POSE // Posición amenazante
};
VisualFireState visual_fire_state_ = VisualFireState::NORMAL;
float visual_state_timer_ = 0.0f; // Tiempo en el estado visual actual
float aiming_duration_ = 0.0f; // Duración del estado AIMING
float recoiling_duration_ = 0.0f; // Duración del estado RECOILING
int invulnerable_counter_ = INVULNERABLE_COUNTER; // Contador para la invulnerabilidad int invulnerable_counter_ = INVULNERABLE_COUNTER; // Contador para la invulnerabilidad
int score_ = 0; // Puntos del jugador int score_ = 0; // Puntos del jugador
int coffees_ = 0; // Indica cuántos cafés lleva acumulados int coffees_ = 0; // Indica cuántos cafés lleva acumulados
@@ -243,8 +291,8 @@ class Player {
int power_up_x_offset_ = 0; // Desplazamiento del sprite de PowerUp respecto al sprite del jugador int power_up_x_offset_ = 0; // Desplazamiento del sprite de PowerUp respecto al sprite del jugador
int continue_counter_ = 10; // Contador para poder continuar int continue_counter_ = 10; // Contador para poder continuar
int controller_index_ = 0; // Índice del array de mandos que utilizará para moverse int controller_index_ = 0; // Índice del array de mandos que utilizará para moverse
int name_entry_idle_counter_ = 0; // Contador para poner nombre float name_entry_idle_time_accumulator_ = 0.0f; // Tiempo idle acumulado para poner nombre (milisegundos)
int name_entry_total_counter_ = 0; // Segundos totales que lleva acumulados poniendo nombre float name_entry_total_time_accumulator_ = 0.0f; // Tiempo total acumulado poniendo nombre (milisegundos)
int step_counter_ = 0; // Cuenta los pasos para los estados en los que camina automáticamente int step_counter_ = 0; // Cuenta los pasos para los estados en los que camina automáticamente
int credits_used_ = 0; // Indica el número de veces que ha continuado int credits_used_ = 0; // Indica el número de veces que ha continuado
int waiting_counter_ = 0; // Contador para el estado de espera int waiting_counter_ = 0; // Contador para el estado de espera
@@ -259,23 +307,39 @@ class Player {
// --- Métodos internos --- // --- Métodos internos ---
void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador
void shiftSprite(); // Recoloca el sprite void shiftSprite(); // Recoloca el sprite
void updateInvulnerable(); // Monitoriza el estado de invulnerabilidad void updateInvulnerable(float deltaTime); // Monitoriza el estado de invulnerabilidad (time-based)
void updateContinueCounter(); // Actualiza el contador de continue void updateContinueCounter(float deltaTime); // Actualiza el contador de continue (time-based)
void updateEnterNameCounter(); // Actualiza el contador de entrar nombre void updateEnterNameCounter(float deltaTime); // Actualiza el contador de entrar nombre (time-based)
void updateShowingName(); // Actualiza el estado SHOWING_NAME void updateShowingName(float deltaTime); // Actualiza el estado SHOWING_NAME (time-based)
void decNameEntryCounter(); // Decrementa el contador de entrar nombre void decNameEntryCounter(); // Decrementa el contador de entrar nombre
void updateScoreboard(); // Actualiza el panel del marcador void updateScoreboard(); // Actualiza el panel del marcador
void setScoreboardMode(Scoreboard::Mode mode) const; // Cambia el modo del marcador void setScoreboardMode(Scoreboard::Mode mode) const; // Cambia el modo del marcador
void playSound(const std::string &name) const; // Hace sonar un sonido void playSound(const std::string &name) const; // Hace sonar un sonido
[[nodiscard]] auto isRenderable() const -> bool; // Indica si se puede dibujar el objeto [[nodiscard]] auto isRenderable() const -> bool; // Indica si se puede dibujar el objeto
void addScoreToScoreBoard() const; // Añade una puntuación a la tabla de records void addScoreToScoreBoard() const; // Añade una puntuación a la tabla de records
void handleFiringCooldown(); // Gestiona el tiempo de espera después de disparar antes de permitir otro disparo
void handleRecoilAndCooling(); // Procesa simultáneamente el retroceso del arma y la transición al estado de enfriamiento si aplica // --- Métodos del sistema de disparo de dos líneas ---
void handleCoolingState(); // Actualiza la lógica interna mientras el sistema está en estado de enfriamiento void updateFireSystem(float deltaTime); // Método principal del nuevo sistema de disparo
void transitionToRecoiling(); // Cambia el estado actual al de retroceso después de disparar void updateFunctionalLine(float deltaTime); // Actualiza la línea funcional (CanFire)
void transitionToCooling(); // Cambia el estado actual al de enfriamiento (por ejemplo, tras una ráfaga o sobrecalentamiento) void updateVisualLine(float deltaTime); // Actualiza la línea visual (Animaciones)
void completeCooling(); // Finaliza el proceso de enfriamiento y restablece el estado listo para disparar void startFiring(int cooldown_frames); // Inicia un nuevo disparo en ambas líneas
void handlePlayingMovement(); // Gestiona el movimiento del personaje u objeto durante el estado de juego activo void updateFiringStateFromVisual(); // Sincroniza firing_state_ con visual_fire_state_
void transitionToRecoilingNew(); // Transición AIMING → RECOILING
void transitionToThreatPose(); // Transición RECOILING → THREAT_POSE
void transitionToNormalNew(); // Transición THREAT_POSE → NORMAL
// --- Métodos del sistema de disparo obsoleto ---
void handleFiringCooldown(); // Gestiona el tiempo de espera después de disparar (frame-based)
void handleFiringCooldown(float deltaTime); // Gestiona el tiempo de espera después de disparar (time-based)
void handleRecoilAndCooling(); // Procesa retroceso y enfriamiento (frame-based)
void handleRecoilAndCooling(float deltaTime); // Procesa retroceso y enfriamiento (time-based)
void handleCoolingState(); // Actualiza estado de enfriamiento (frame-based)
void handleCoolingState(float deltaTime); // Actualiza estado de enfriamiento (time-based)
void transitionToRecoiling(); // Transición a retroceso (sistema obsoleto)
void transitionToCooling(); // Transición a enfriamiento (sistema obsoleto)
void completeCooling(); // Finaliza enfriamiento (sistema obsoleto)
void handlePlayingMovement(); // Gestiona el movimiento del personaje u objeto durante el estado de juego activo (frame-based)
void handlePlayingMovement(float deltaTime); // Gestiona el movimiento del personaje u objeto durante el estado de juego activo (time-based)
void handleRecoverMovement(); // Comprueba si ha acabado la animación void handleRecoverMovement(); // Comprueba si ha acabado la animación
void handleRollingMovement(); // Actualiza la lógica de movimiento de "rodar" (posiblemente tras impacto o acción especial) void handleRollingMovement(); // Actualiza la lógica de movimiento de "rodar" (posiblemente tras impacto o acción especial)
void handleRollingBoundaryCollision(); // Detecta y maneja colisiones del objeto rodante con los límites de la pantalla void handleRollingBoundaryCollision(); // Detecta y maneja colisiones del objeto rodante con los límites de la pantalla
@@ -288,12 +352,15 @@ class Player {
void handleEnteringScreen(); // Lógica para entrar en una nueva pantalla, posiblemente con animación o retraso void handleEnteringScreen(); // Lógica para entrar en una nueva pantalla, posiblemente con animación o retraso
void handlePlayer1Entering(); // Controla la animación o posición de entrada del Jugador 1 en pantalla void handlePlayer1Entering(); // Controla la animación o posición de entrada del Jugador 1 en pantalla
void handlePlayer2Entering(); // Controla la animación o posición de entrada del Jugador 2 en pantalla void handlePlayer2Entering(); // Controla la animación o posición de entrada del Jugador 2 en pantalla
void handleCreditsMovement(); // Movimiento general en la pantalla de créditos (desplazamiento vertical u horizontal) void handleCreditsMovement(); // Movimiento general en la pantalla de créditos (frame-based)
void handleCreditsMovement(float deltaTime); // Movimiento general en la pantalla de créditos (time-based)
void handleCreditsRightMovement(); // Lógica específica para mover los créditos hacia la derecha void handleCreditsRightMovement(); // Lógica específica para mover los créditos hacia la derecha
void handleCreditsLeftMovement(); // Lógica específica para mover los créditos hacia la izquierda void handleCreditsLeftMovement(); // Lógica específica para mover los créditos hacia la izquierda
void handleWaitingMovement(); // Controla la animación del jugador saludando void handleWaitingMovement(); // Controla la animación del jugador saludando (frame-based)
void handleWaitingMovement(float deltaTime); // Controla la animación del jugador saludando (time-based)
void updateWalkingStateForCredits(); // Actualiza el estado de caminata de algún personaje u elemento animado en los créditos void updateWalkingStateForCredits(); // Actualiza el estado de caminata de algún personaje u elemento animado en los créditos
void setInputBasedOnPlayerId(); // Asocia las entradas de control en función del identificador del jugador (teclas, mando, etc.) void setInputBasedOnPlayerId(); // Asocia las entradas de control en función del identificador del jugador (teclas, mando, etc.)
void updateStepCounter(); // Incrementa o ajusta el contador de pasos para animaciones o mecánicas relacionadas con movimiento void updateStepCounter(); // Incrementa o ajusta el contador de pasos (frame-based)
void updateStepCounter(float deltaTime); // Incrementa o ajusta el contador de pasos (time-based)
[[nodiscard]] auto computeAnimation() const -> std::pair<std::string, SDL_FlipMode>; // Calcula la animacion de moverse y disparar del jugador [[nodiscard]] auto computeAnimation() const -> std::pair<std::string, SDL_FlipMode>; // Calcula la animacion de moverse y disparar del jugador
}; };

View File

@@ -78,36 +78,46 @@ Credits::~Credits() {
Options::gamepad_manager.clearPlayers(); Options::gamepad_manager.clearPlayers();
} }
// Calcula el deltatime
auto Credits::calculateDeltaTime() -> float {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle principal // Bucle principal
void Credits::run() { void Credits::run() {
last_time_ = SDL_GetTicks();
while (Section::name == Section::Name::CREDITS) { while (Section::name == Section::Name::CREDITS) {
checkInput(); checkInput();
update(); const float delta_time = calculateDeltaTime();
update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
} }
// Actualiza las variables // Actualiza las variables (time-based)
void Credits::update() { void Credits::update(float deltaTime) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { const float multiplier = want_to_pass_ ? 4.0f : 1.0f;
ticks_ = SDL_GetTicks(); const float adjusted_delta_time = deltaTime * multiplier;
const int REPEAT = want_to_pass_ ? 4 : 1;
for (int i = 0; i < REPEAT; ++i) {
tiled_bg_->update();
cycleColors();
balloon_manager_->update();
updateTextureDstRects();
throwBalloons();
updatePlayers();
updateAllFades();
++counter_;
}
Screen::get()->update(); tiled_bg_->update(adjusted_delta_time);
cycleColors();
balloon_manager_->update(adjusted_delta_time);
updateTextureDstRects(adjusted_delta_time);
throwBalloons(adjusted_delta_time);
updatePlayers(adjusted_delta_time);
updateAllFades(adjusted_delta_time);
fillCanvas(); // Convertir deltaTime a factor de frame (asumiendo 60fps)
} const float frameFactor = adjusted_delta_time / (1000.0f / 60.0f);
counter_ += frameFactor;
Screen::get()->update();
fillCanvas();
Audio::update(); Audio::update();
} }
@@ -277,9 +287,9 @@ void Credits::fillCanvas() {
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
} }
// Actualiza el destino de los rectangulos de las texturas // Actualiza el destino de los rectangulos de las texturas (frame-based)
void Credits::updateTextureDstRects() { void Credits::updateTextureDstRects() {
if (counter_ % 10 == 0) { if (static_cast<int>(counter_) % 10 == 0) {
// Comprueba la posición de la textura con los titulos de credito // Comprueba la posición de la textura con los titulos de credito
if (credits_rect_dst_.y + credits_rect_dst_.h > play_area_.y) { if (credits_rect_dst_.y + credits_rect_dst_.h > play_area_.y) {
--credits_rect_dst_.y; --credits_rect_dst_.y;
@@ -306,7 +316,42 @@ void Credits::updateTextureDstRects() {
} }
} }
// Tira globos al escenario // Actualiza el destino de los rectangulos de las texturas (time-based)
void Credits::updateTextureDstRects(float deltaTime) {
constexpr float TEXTURE_UPDATE_INTERVAL = 10 * (1000.0f / 60.0f); // 166.67ms (cada 10 frames)
static float texture_accumulator = 0.0f;
texture_accumulator += deltaTime;
if (texture_accumulator >= TEXTURE_UPDATE_INTERVAL) {
texture_accumulator -= TEXTURE_UPDATE_INTERVAL;
// Comprueba la posición de la textura con los titulos de credito
if (credits_rect_dst_.y + credits_rect_dst_.h > play_area_.y) {
--credits_rect_dst_.y;
}
// Comprueba la posición de la textura con el mini_logo
if (mini_logo_rect_dst_.y == mini_logo_final_pos_) {
mini_logo_on_position_ = true;
// Si el jugador quiere pasar los titulos de credito, el fade se inicia solo
if (want_to_pass_) {
fading_ = true;
}
// Se activa el contador para evitar que la sección sea infinita
if (counter_prevent_endless_ == 1000) {
fading_ = true;
} else {
++counter_prevent_endless_;
}
} else {
--mini_logo_rect_dst_.y;
}
}
}
// Tira globos al escenario (frame-based)
void Credits::throwBalloons() { void Credits::throwBalloons() {
constexpr int SPEED = 200; constexpr int SPEED = 200;
const std::vector<int> SETS = {0, 63, 25, 67, 17, 75, 13, 50}; const std::vector<int> SETS = {0, 63, 25, 67, 17, 75, 13, 50};
@@ -315,12 +360,41 @@ void Credits::throwBalloons() {
return; return;
} }
if (counter_ % SPEED == 0) { if (static_cast<int>(counter_) % SPEED == 0) {
const int INDEX = (counter_ / SPEED) % SETS.size(); const int INDEX = (static_cast<int>(counter_) / SPEED) % SETS.size();
balloon_manager_->deployFormation(SETS.at(INDEX), -60); balloon_manager_->deployFormation(SETS.at(INDEX), -60);
} }
if (counter_ % (SPEED * 4) == 0 && counter_ > 0) { if (static_cast<int>(counter_) % (SPEED * 4) == 0 && counter_ > 0) {
balloon_manager_->createPowerBall();
}
}
// Tira globos al escenario (time-based)
void Credits::throwBalloons(float deltaTime) {
constexpr int SPEED = 200;
const std::vector<int> SETS = {0, 63, 25, 67, 17, 75, 13, 50};
constexpr float BALLOON_INTERVAL = SPEED * (1000.0f / 60.0f); // 3333.33ms (cada 200 frames)
constexpr float POWERBALL_INTERVAL = (SPEED * 4) * (1000.0f / 60.0f); // 13333.33ms (cada 800 frames)
if (counter_ > ((SETS.size() - 1) * SPEED) * 3) {
return;
}
static float balloon_accumulator = 0.0f;
static float powerball_accumulator = 0.0f;
balloon_accumulator += deltaTime;
powerball_accumulator += deltaTime;
if (balloon_accumulator >= BALLOON_INTERVAL) {
balloon_accumulator -= BALLOON_INTERVAL;
const int INDEX = (static_cast<int>(counter_ / SPEED)) % SETS.size();
balloon_manager_->deployFormation(SETS.at(INDEX), -60);
}
if (powerball_accumulator >= POWERBALL_INTERVAL && counter_ > 0) {
powerball_accumulator -= POWERBALL_INTERVAL;
balloon_manager_->createPowerBall(); balloon_manager_->createPowerBall();
} }
} }
@@ -392,12 +466,12 @@ void Credits::initPlayers() {
} }
} }
// Actualiza los rectangulos negros // Actualiza los rectangulos negros (frame-based)
void Credits::updateBlackRects() { void Credits::updateBlackRects() {
static int current_step_ = steps_; static int current_step_ = steps_;
if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1) { if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1) {
// Si los rectangulos superior e inferior no han llegado al centro // Si los rectangulos superior e inferior no han llegado al centro
if (counter_ % 4 == 0) { if (static_cast<int>(counter_) % 4 == 0) {
// Incrementa la altura del rectangulo superior // Incrementa la altura del rectangulo superior
top_black_rect_.h = std::min(top_black_rect_.h + 1, param.game.game_area.center_y - 1); top_black_rect_.h = std::min(top_black_rect_.h + 1, param.game.game_area.center_y - 1);
@@ -435,6 +509,57 @@ void Credits::updateBlackRects() {
} }
} }
// Actualiza los rectangulos negros (time-based)
void Credits::updateBlackRects(float deltaTime) {
static float current_step_ = static_cast<float>(steps_);
constexpr float BLACK_RECT_INTERVAL = 4 * (1000.0f / 60.0f); // 66.67ms (cada 4 frames)
static float black_rect_accumulator = 0.0f;
if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1) {
// Si los rectangulos superior e inferior no han llegado al centro
black_rect_accumulator += deltaTime;
if (black_rect_accumulator >= BLACK_RECT_INTERVAL) {
black_rect_accumulator -= BLACK_RECT_INTERVAL;
// Incrementa la altura del rectangulo superior
top_black_rect_.h = std::min(top_black_rect_.h + 1, param.game.game_area.center_y - 1);
// Incrementa la altura y modifica la posición del rectangulo inferior
++bottom_black_rect_.h;
bottom_black_rect_.y = std::max(bottom_black_rect_.y - 1, param.game.game_area.center_y + 1);
--current_step_;
setVolume(static_cast<int>(initial_volume_ * current_step_ / steps_));
}
} else {
// Si los rectangulos superior e inferior han llegado al centro
if (left_black_rect_.w != param.game.game_area.center_x && right_black_rect_.x != param.game.game_area.center_x) {
constexpr int SPEED = 2;
// Si los rectangulos izquierdo y derecho no han llegado al centro
// Incrementa la anchura del rectangulo situado a la izquierda
left_black_rect_.w = std::min(left_black_rect_.w + SPEED, param.game.game_area.center_x);
// Incrementa la anchura y modifica la posición del rectangulo situado a la derecha
right_black_rect_.w += SPEED;
right_black_rect_.x = std::max(right_black_rect_.x - SPEED, param.game.game_area.center_x);
--current_step_;
setVolume(static_cast<int>(initial_volume_ * current_step_ / steps_));
} else {
// Si los rectangulos izquierdo y derecho han llegado al centro
setVolume(0);
Audio::get()->stopMusic();
if (counter_pre_fade_ == 400) {
fade_out_->activate();
} else {
// Convertir deltaTime a factor de frame
const float frameFactor = deltaTime / (1000.0f / 60.0f);
counter_pre_fade_ += frameFactor;
}
}
}
}
// Actualiza el rectangulo rojo // Actualiza el rectangulo rojo
void Credits::updateRedRect() { void Credits::updateRedRect() {
border_rect_.x = left_black_rect_.x + left_black_rect_.w; border_rect_.x = left_black_rect_.x + left_black_rect_.w;
@@ -443,7 +568,7 @@ void Credits::updateRedRect() {
border_rect_.h = bottom_black_rect_.y - border_rect_.y + 1; border_rect_.h = bottom_black_rect_.y - border_rect_.y + 1;
} }
// Actualiza el estado de fade // Actualiza el estado de fade (frame-based)
void Credits::updateAllFades() { void Credits::updateAllFades() {
if (fading_) { if (fading_) {
updateBlackRects(); updateBlackRects();
@@ -461,6 +586,24 @@ void Credits::updateAllFades() {
} }
} }
// Actualiza el estado de fade (time-based)
void Credits::updateAllFades(float deltaTime) {
if (fading_) {
updateBlackRects(deltaTime);
updateRedRect();
}
fade_in_->update(); // Fade ya usa tiempo interno
if (fade_in_->hasEnded()) {
Audio::get()->playMusic("credits.ogg");
}
fade_out_->update(); // Fade ya usa tiempo interno
if (fade_out_->hasEnded()) {
Section::name = Section::Name::HI_SCORE_TABLE;
}
}
// Establece el nivel de volumen // Establece el nivel de volumen
void Credits::setVolume(int amount) { void Credits::setVolume(int amount) {
Options::audio.music.volume = std::clamp(amount, 0, 100); Options::audio.music.volume = std::clamp(amount, 0, 100);
@@ -508,10 +651,10 @@ void Credits::cycleColors() {
tiled_bg_->setColor(color_); tiled_bg_->setColor(color_);
} }
// Actualza los jugadores // Actualza los jugadores (time-based)
void Credits::updatePlayers() { void Credits::updatePlayers(float deltaTime) {
for (auto &player : players_) { for (auto &player : players_) {
player->update(); player->update(deltaTime);
} }
} }

View File

@@ -25,6 +25,11 @@ class Credits {
// --- Bucle principal --- // --- Bucle principal ---
void run(); void run();
private:
// --- Métodos del bucle principal ---
void update(float deltaTime); // Actualización principal de la lógica (time-based)
auto calculateDeltaTime() -> float; // Calcula el deltatime
private: private:
// --- Constantes de clase --- // --- Constantes de clase ---
static constexpr int PLAY_AREA_HEIGHT = 200; static constexpr int PLAY_AREA_HEIGHT = 200;
@@ -41,10 +46,10 @@ class Credits {
SDL_Texture *canvas_; // Textura donde se dibuja todo SDL_Texture *canvas_; // Textura donde se dibuja todo
// --- Temporización y contadores --- // --- Temporización y contadores ---
Uint64 ticks_ = 0; // Control de velocidad del programa Uint64 last_time_ = 0; // Último tiempo registrado para deltaTime
Uint32 counter_ = 0; // Contador principal de lógica float counter_ = 0; // Contador principal de lógica
Uint32 counter_pre_fade_ = 0; // Activación del fundido final float counter_pre_fade_ = 0; // Activación del fundido final
Uint32 counter_prevent_endless_ = 0; // Prevención de bucle infinito float counter_prevent_endless_ = 0; // Prevención de bucle infinito
// --- Variables de estado --- // --- Variables de estado ---
bool fading_ = false; // Estado del fade final bool fading_ = false; // Estado del fade final
@@ -101,8 +106,6 @@ class Credits {
// Borde para la ventana // Borde para la ventana
SDL_FRect border_rect_ = play_area_; // Delimitador de ventana SDL_FRect border_rect_ = play_area_; // Delimitador de ventana
// --- Métodos del bucle principal ---
void update(); // Actualización principal de la lógica
void render(); // Renderizado de la escena void render(); // Renderizado de la escena
static void checkEvents(); // Manejo de eventos static void checkEvents(); // Manejo de eventos
void checkInput(); // Procesamiento de entrada void checkInput(); // Procesamiento de entrada
@@ -110,19 +113,23 @@ class Credits {
// --- Métodos de renderizado --- // --- Métodos de renderizado ---
void fillTextTexture(); // Crear textura de texto de créditos void fillTextTexture(); // Crear textura de texto de créditos
void fillCanvas(); // Renderizar todos los sprites y fondos void fillCanvas(); // Renderizar todos los sprites y fondos
void updateTextureDstRects(); // Actualizar destinos de texturas
void renderPlayers(); // Renderiza los jugadores void renderPlayers(); // Renderiza los jugadores
// --- Métodos de lógica del juego --- // --- Métodos de lógica del juego ---
void throwBalloons(); // Lanzar globos al escenario void throwBalloons(); // Lanzar globos al escenario (frame-based)
void initPlayers(); // Inicializar jugadores void throwBalloons(float deltaTime); // Lanzar globos al escenario (time-based)
void updateAllFades(); // Actualizar estados de fade void initPlayers(); // Inicializar jugadores
void cycleColors(); // Cambiar colores de fondo void updateAllFades(); // Actualizar estados de fade (frame-based)
void updatePlayers(); // Actualza los jugadores void updateAllFades(float deltaTime); // Actualizar estados de fade (time-based)
void cycleColors(); // Cambiar colores de fondo
void updatePlayers(float deltaTime); // Actualza los jugadores (time-based)
// --- Métodos de interfaz --- // --- Métodos de interfaz ---
void updateBlackRects(); // Actualizar rectángulos negros (letterbox) void updateBlackRects(); // Actualizar rectángulos negros (letterbox) (frame-based)
void updateRedRect(); // Actualizar rectángulo rojo (borde) void updateBlackRects(float deltaTime); // Actualizar rectángulos negros (letterbox) (time-based)
void updateRedRect(); // Actualizar rectángulo rojo (borde)
void updateTextureDstRects(); // Actualizar destinos de texturas (frame-based)
void updateTextureDstRects(float deltaTime); // Actualizar destinos de texturas (time-based)
// --- Métodos de audio --- // --- Métodos de audio ---
static void setVolume(int amount); // Establecer volumen static void setVolume(int amount); // Establecer volumen

View File

@@ -79,7 +79,7 @@ Game::Game(Player::Id player_id, int current_stage, bool demo)
scoreboard_ = Scoreboard::get(); scoreboard_ = Scoreboard::get();
fade_in_->setColor(param.fade.color); fade_in_->setColor(param.fade.color);
fade_in_->setPreDuration(demo_.enabled ? 500 : 0); fade_in_->setPreDuration(demo_.enabled ? DEMO_FADE_PRE_DURATION_MS : 0);
fade_in_->setPostDuration(0); fade_in_->setPostDuration(0);
fade_in_->setType(Fade::Type::RANDOM_SQUARE2); fade_in_->setType(Fade::Type::RANDOM_SQUARE2);
fade_in_->setMode(Fade::Mode::IN); fade_in_->setMode(Fade::Mode::IN);
@@ -211,9 +211,9 @@ void Game::updateHiScore() {
} }
// Actualiza las variables del jugador // Actualiza las variables del jugador
void Game::updatePlayers() { void Game::updatePlayers(float deltaTime) {
for (auto &player : players_) { for (auto &player : players_) {
player->update(); player->update(deltaTime);
if (player->isPlaying()) { if (player->isPlaying()) {
// Comprueba la colisión entre el jugador y los globos // Comprueba la colisión entre el jugador y los globos
@@ -222,7 +222,7 @@ void Game::updatePlayers() {
// Si hay colisión // Si hay colisión
if (balloon) { if (balloon) {
// Si el globo está parado y el temporizador activo, lo explota // Si el globo está parado y el temporizador activo, lo explota
if (balloon->isStopped() && time_stopped_counter_ > 0) { if (balloon->isStopped() && time_stopped_timer_ > 0) {
balloon_manager_->popBalloon(balloon); balloon_manager_->popBalloon(balloon);
} }
// En caso contrario, el jugador ha sido golpeado por un globo activo // En caso contrario, el jugador ha sido golpeado por un globo activo
@@ -312,31 +312,28 @@ void Game::updateStage() {
} }
// Actualiza el estado de fin de la partida // Actualiza el estado de fin de la partida
void Game::updateGameStateGameOver() { void Game::updateGameStateGameOver(float deltaTime) {
fade_out_->update(); fade_out_->update();
updatePlayers(); updatePlayers(deltaTime);
updateScoreboard(); updateScoreboard();
updateBackground(); updateBackground(deltaTime);
balloon_manager_->update(); balloon_manager_->update(deltaTime);
tabe_->update(); tabe_->update(deltaTime);
updateBullets(); updateBullets(deltaTime);
updateItems(); updateItems(deltaTime);
updateSmartSprites(); updateSmartSprites(deltaTime);
updatePathSprites(); updatePathSprites(deltaTime);
updateTimeStopped(); updateTimeStopped(deltaTime);
checkBulletCollision(); checkBulletCollision();
cleanVectors(); cleanVectors();
if (game_over_counter_ > 0) { if (game_over_timer_ < GAME_OVER_DURATION_MS) {
if (game_over_counter_ == GAME_OVER_COUNTER) { handleGameOverEvents(); // Maneja eventos al inicio
createMessage({paths_.at(2), paths_.at(3)}, Resource::get()->getTexture("game_text_game_over"));
Audio::get()->fadeOutMusic(1000);
balloon_manager_->setBouncingSounds(true);
}
game_over_counter_--; game_over_timer_ += deltaTime; // Incremento time-based
if (game_over_counter_ == 150) { constexpr float FADE_TRIGGER_MS = GAME_OVER_DURATION_MS - (150.0f * (1000.0f / 60.0f)); // 2500ms antes del final
if (game_over_timer_ >= FADE_TRIGGER_MS && !fade_out_->isEnabled()) {
fade_out_->activate(); fade_out_->activate();
} }
} }
@@ -349,7 +346,7 @@ void Game::updateGameStateGameOver() {
} }
if (fade_out_->hasEnded()) { if (fade_out_->hasEnded()) {
if (game_completed_counter_ > 0) { if (game_completed_timer_ > 0) {
Section::name = Section::Name::CREDITS; // Los jugadores han completado el juego Section::name = Section::Name::CREDITS; // Los jugadores han completado el juego
} else { } else {
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
@@ -363,55 +360,28 @@ void Game::updateGameStateGameOver() {
} }
// Gestiona eventos para el estado del final del juego // Gestiona eventos para el estado del final del juego
void Game::updateGameStateCompleted() { void Game::updateGameStateCompleted(float deltaTime) {
constexpr int START_CELEBRATIONS = 400; updatePlayers(deltaTime);
constexpr int END_CELEBRATIONS = START_CELEBRATIONS + 300;
updatePlayers();
updateScoreboard(); updateScoreboard();
updateBackground(); updateBackground(deltaTime);
balloon_manager_->update(); balloon_manager_->update(deltaTime);
tabe_->update(); tabe_->update(deltaTime);
updateBullets(); updateBullets(deltaTime);
updateItems(); updateItems(deltaTime);
updateSmartSprites(); updateSmartSprites(deltaTime);
updatePathSprites(); updatePathSprites(deltaTime);
cleanVectors(); cleanVectors();
// Comienza las celebraciones // Maneja eventos del juego completado
// Muestra el mensaje de felicitación y da los puntos a los jugadores handleGameCompletedEvents();
if (game_completed_counter_ == START_CELEBRATIONS) {
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"));
for (auto &player : players_) {
if (player->isPlaying()) {
player->addScore(1000000, Options::settings.hi_score_table.back().score);
player->setPlayingState(Player::State::CELEBRATING);
} else {
player->setPlayingState(Player::State::GAME_OVER);
}
}
updateHiScore();
}
// Termina las celebraciones
if (game_completed_counter_ == END_CELEBRATIONS) {
for (auto &player : players_) {
if (player->isCelebrating()) {
player->setPlayingState(player->qualifiesForHighScore() ? Player::State::ENTERING_NAME_GAME_COMPLETED : Player::State::LEAVING_SCREEN);
}
}
}
// Si los jugadores ya no estan y no quedan mensajes en pantalla // Si los jugadores ya no estan y no quedan mensajes en pantalla
if (allPlayersAreGameOver() && path_sprites_.empty()) { if (allPlayersAreGameOver() && path_sprites_.empty()) {
setState(State::GAME_OVER); setState(State::GAME_OVER);
} }
// Incrementa el contador al final // Incrementa el acumulador al final
++game_completed_counter_; game_completed_timer_ += deltaTime;
} }
// Comprueba el estado del juego // Comprueba el estado del juego
@@ -627,9 +597,9 @@ void Game::handleBalloonDestruction(std::shared_ptr<Balloon> balloon, const std:
} }
// Mueve las balas activas // Mueve las balas activas
void Game::updateBullets() { void Game::updateBullets(float deltaTime) {
for (auto &bullet : bullets_) { for (auto &bullet : bullets_) {
if (bullet->update() == BulletMoveStatus::OUT) { if (bullet->update(deltaTime) == BulletMoveStatus::OUT) {
getPlayer(bullet->getOwner())->decScoreMultiplier(); getPlayer(bullet->getOwner())->decScoreMultiplier();
} }
} }
@@ -659,10 +629,10 @@ void Game::freeBullets() {
} }
// Actualiza los items // Actualiza los items
void Game::updateItems() { void Game::updateItems(float deltaTime) {
for (auto &item : items_) { for (auto &item : items_) {
if (item->isEnabled()) { if (item->isEnabled()) {
item->update(); item->update(deltaTime);
if (item->isOnFloor()) { if (item->isOnFloor()) {
playSound("title.wav"); playSound("title.wav");
screen_->shake(1, 2, 4); screen_->shake(1, 2, 4);
@@ -820,7 +790,7 @@ void Game::throwCoffee(int x, int y) {
smart_sprites_.back()->setDestX(x + (smart_sprites_.back()->getVelX() * 50)); smart_sprites_.back()->setDestX(x + (smart_sprites_.back()->getVelX() * 50));
smart_sprites_.back()->setDestY(param.game.height + 1); smart_sprites_.back()->setDestY(param.game.height + 1);
smart_sprites_.back()->setEnabled(true); smart_sprites_.back()->setEnabled(true);
smart_sprites_.back()->setFinishedCounter(1); smart_sprites_.back()->setFinishedDelay(0.0F);
smart_sprites_.back()->setSpriteClip(0, param.game.item_size, param.game.item_size, param.game.item_size); smart_sprites_.back()->setSpriteClip(0, param.game.item_size, param.game.item_size, param.game.item_size);
smart_sprites_.back()->setRotatingCenter({param.game.item_size / 2, param.game.item_size / 2}); smart_sprites_.back()->setRotatingCenter({param.game.item_size / 2, param.game.item_size / 2});
smart_sprites_.back()->setRotate(true); smart_sprites_.back()->setRotate(true);
@@ -829,9 +799,9 @@ void Game::throwCoffee(int x, int y) {
} }
// Actualiza los SmartSprites // Actualiza los SmartSprites
void Game::updateSmartSprites() { void Game::updateSmartSprites(float deltaTime) {
for (auto &sprite : smart_sprites_) { for (auto &sprite : smart_sprites_) {
sprite->update(); sprite->update(deltaTime);
} }
} }
@@ -843,9 +813,9 @@ void Game::renderSmartSprites() {
} }
// Actualiza los PathSprites // Actualiza los PathSprites
void Game::updatePathSprites() { void Game::updatePathSprites(float deltaTime) {
for (auto &sprite : path_sprites_) { for (auto &sprite : path_sprites_) {
sprite->update(); sprite->update(deltaTime);
} }
} }
@@ -887,21 +857,35 @@ void Game::handlePlayerCollision(std::shared_ptr<Player> &player, std::shared_pt
} }
} }
// Actualiza y comprueba el valor de la variable // Actualiza el estado del tiempo detenido
void Game::updateTimeStopped() { void Game::updateTimeStopped(float deltaTime) {
if (time_stopped_counter_ > 0) { static constexpr float WARNING_THRESHOLD_MS = 2000.0f; // 120 frames a 60fps
time_stopped_counter_--; static constexpr float CLOCK_SOUND_INTERVAL_MS = 500.0f; // 30 frames a 60fps
if (time_stopped_counter_ > 120) { static constexpr float COLOR_FLASH_INTERVAL_MS = 250.0f; // 15 frames a 60fps
if (time_stopped_counter_ % 30 == 0) {
if (time_stopped_timer_ > 0) {
time_stopped_timer_ -= deltaTime;
// Fase de advertencia (últimos 2 segundos)
if (time_stopped_timer_ <= WARNING_THRESHOLD_MS) {
static float last_sound_time = 0.0f;
last_sound_time += deltaTime;
if (last_sound_time >= CLOCK_SOUND_INTERVAL_MS) {
balloon_manager_->normalColorsToAllBalloons();
playSound("clock.wav");
last_sound_time = 0.0f;
} else if (last_sound_time >= COLOR_FLASH_INTERVAL_MS) {
balloon_manager_->reverseColorsToAllBalloons();
playSound("clock.wav"); playSound("clock.wav");
} }
} else { } else {
if (time_stopped_counter_ % 30 == 0) { // Fase normal - solo sonido ocasional
balloon_manager_->normalColorsToAllBalloons(); static float sound_timer = 0.0f;
playSound("clock.wav"); sound_timer += deltaTime;
} else if (time_stopped_counter_ % 30 == 15) { if (sound_timer >= CLOCK_SOUND_INTERVAL_MS) {
balloon_manager_->reverseColorsToAllBalloons();
playSound("clock.wav"); playSound("clock.wav");
sound_timer = 0.0f;
} }
} }
} else { } else {
@@ -909,18 +893,16 @@ void Game::updateTimeStopped() {
} }
} }
void Game::update() { // Actualiza toda la lógica del juego
if (SDL_GetTicks() - ticks_ > param.game.speed) { void Game::update(float deltaTime) {
ticks_ = SDL_GetTicks(); screen_->update();
screen_->update();
updateDemo(); updateDemo();
#ifdef RECORDING #ifdef RECORDING
updateRecording(); updateRecording();
#endif #endif
updateGameStates(); updateGameStates(deltaTime);
fillCanvas(); fillCanvas();
}
Audio::update(); Audio::update();
} }
@@ -938,26 +920,26 @@ void Game::render() {
} }
// Actualiza los estados del juego // Actualiza los estados del juego
void Game::updateGameStates() { void Game::updateGameStates(float deltaTime) {
if (!pause_manager_->isPaused()) { if (!pause_manager_->isPaused()) {
switch (state_) { switch (state_) {
case State::FADE_IN: case State::FADE_IN:
updateGameStateFadeIn(); updateGameStateFadeIn(deltaTime);
break; break;
case State::ENTERING_PLAYER: case State::ENTERING_PLAYER:
updateGameStateEnteringPlayer(); updateGameStateEnteringPlayer(deltaTime);
break; break;
case State::SHOWING_GET_READY_MESSAGE: case State::SHOWING_GET_READY_MESSAGE:
updateGameStateShowingGetReadyMessage(); updateGameStateShowingGetReadyMessage(deltaTime);
break; break;
case State::PLAYING: case State::PLAYING:
updateGameStatePlaying(); updateGameStatePlaying(deltaTime);
break; break;
case State::COMPLETED: case State::COMPLETED:
updateGameStateCompleted(); updateGameStateCompleted(deltaTime);
break; break;
case State::GAME_OVER: case State::GAME_OVER:
updateGameStateGameOver(); updateGameStateGameOver(deltaTime);
break; break;
default: default:
break; break;
@@ -966,8 +948,8 @@ void Game::updateGameStates() {
} }
// Actualiza el fondo // Actualiza el fondo
void Game::updateBackground() { void Game::updateBackground(float deltaTime) {
background_->update(); background_->update(deltaTime);
} }
// Dibuja los elementos de la zona de juego en su textura // Dibuja los elementos de la zona de juego en su textura
@@ -996,23 +978,34 @@ void Game::fillCanvas() {
void Game::enableTimeStopItem() { void Game::enableTimeStopItem() {
balloon_manager_->stopAllBalloons(); balloon_manager_->stopAllBalloons();
balloon_manager_->reverseColorsToAllBalloons(); balloon_manager_->reverseColorsToAllBalloons();
time_stopped_counter_ = TIME_STOPPED_COUNTER; time_stopped_timer_ = TIME_STOPPED_DURATION_MS;
} }
// Deshabilita el efecto del item de detener el tiempo // Deshabilita el efecto del item de detener el tiempo
void Game::disableTimeStopItem() { void Game::disableTimeStopItem() {
time_stopped_counter_ = 0; time_stopped_timer_ = 0;
balloon_manager_->startAllBalloons(); balloon_manager_->startAllBalloons();
balloon_manager_->normalColorsToAllBalloons(); balloon_manager_->normalColorsToAllBalloons();
} }
// Calcula el deltatime
auto Game::calculateDeltaTime() -> float {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle para el juego // Bucle para el juego
void Game::run() { void Game::run() {
last_time_ = SDL_GetTicks();
while (Section::name == Section::Name::GAME) { while (Section::name == Section::Name::GAME) {
#ifndef RECORDING #ifndef RECORDING
checkInput(); checkInput();
#endif #endif
update(); const float delta_time = calculateDeltaTime();
update(delta_time);
handleEvents(); // Tiene que ir antes del render handleEvents(); // Tiene que ir antes del render
render(); render();
} }
@@ -1329,7 +1322,7 @@ void Game::handleFireInput(const std::shared_ptr<Player> &player, BulletType bul
cant_fire_counter = NORMAL_COOLDOWN; cant_fire_counter = NORMAL_COOLDOWN;
} }
player->setCantFireCounter(cant_fire_counter); player->startFiringSystem(cant_fire_counter); // Sistema de disparo de dos líneas
} }
} }
@@ -1553,21 +1546,21 @@ void Game::initDifficultyVars() {
// Variables relacionadas con la dificultad // Variables relacionadas con la dificultad
switch (difficulty_) { switch (difficulty_) {
case Difficulty::Code::EASY: { case Difficulty::Code::EASY: {
balloon_manager_->setDefaultBalloonSpeed(Balloon::SPEED.at(0)); balloon_manager_->setDefaultBalloonSpeed(Balloon::GAME_TEMPO.at(0));
difficulty_score_multiplier_ = 0.5F; difficulty_score_multiplier_ = 0.5F;
scoreboard_->setColor(param.scoreboard.easy_color); scoreboard_->setColor(param.scoreboard.easy_color);
break; break;
} }
case Difficulty::Code::NORMAL: { case Difficulty::Code::NORMAL: {
balloon_manager_->setDefaultBalloonSpeed(Balloon::SPEED.at(0)); balloon_manager_->setDefaultBalloonSpeed(Balloon::GAME_TEMPO.at(0));
difficulty_score_multiplier_ = 1.0F; difficulty_score_multiplier_ = 1.0F;
scoreboard_->setColor(param.scoreboard.normal_color); scoreboard_->setColor(param.scoreboard.normal_color);
break; break;
} }
case Difficulty::Code::HARD: { case Difficulty::Code::HARD: {
balloon_manager_->setDefaultBalloonSpeed(Balloon::SPEED.at(4)); balloon_manager_->setDefaultBalloonSpeed(Balloon::GAME_TEMPO.at(4));
difficulty_score_multiplier_ = 1.5F; difficulty_score_multiplier_ = 1.5F;
scoreboard_->setColor(param.scoreboard.hard_color); scoreboard_->setColor(param.scoreboard.hard_color);
break; break;
@@ -1706,10 +1699,10 @@ void Game::updateRecording() {
#endif #endif
// Actualiza las variables durante dicho estado // Actualiza las variables durante dicho estado
void Game::updateGameStateFadeIn() { void Game::updateGameStateFadeIn(float deltaTime) {
fade_in_->update(); fade_in_->update();
updateScoreboard(); updateScoreboard();
updateBackground(); updateBackground(deltaTime);
if (fade_in_->hasEnded()) { if (fade_in_->hasEnded()) {
setState(State::ENTERING_PLAYER); setState(State::ENTERING_PLAYER);
balloon_manager_->createTwoBigBalloons(); balloon_manager_->createTwoBigBalloons();
@@ -1718,11 +1711,11 @@ void Game::updateGameStateFadeIn() {
} }
// Actualiza las variables durante dicho estado // Actualiza las variables durante dicho estado
void Game::updateGameStateEnteringPlayer() { void Game::updateGameStateEnteringPlayer(float deltaTime) {
balloon_manager_->update(); balloon_manager_->update(deltaTime);
updatePlayers(); updatePlayers(deltaTime);
updateScoreboard(); updateScoreboard();
updateBackground(); updateBackground(deltaTime);
for (const auto &player : players_) { for (const auto &player : players_) {
if (player->isPlaying()) { if (player->isPlaying()) {
setState(State::SHOWING_GET_READY_MESSAGE); setState(State::SHOWING_GET_READY_MESSAGE);
@@ -1733,8 +1726,8 @@ void Game::updateGameStateEnteringPlayer() {
} }
// Actualiza las variables durante dicho estado // Actualiza las variables durante dicho estado
void Game::updateGameStateShowingGetReadyMessage() { void Game::updateGameStateShowingGetReadyMessage(float deltaTime) {
updateGameStatePlaying(); updateGameStatePlaying(deltaTime);
if (path_sprites_.empty()) { if (path_sprites_.empty()) {
setState(State::PLAYING); setState(State::PLAYING);
} }
@@ -1745,24 +1738,24 @@ void Game::updateGameStateShowingGetReadyMessage() {
} }
// Actualiza las variables durante el transcurso normal del juego // Actualiza las variables durante el transcurso normal del juego
void Game::updateGameStatePlaying() { void Game::updateGameStatePlaying(float deltaTime) {
#ifdef _DEBUG #ifdef _DEBUG
if (auto_pop_balloons_) { if (auto_pop_balloons_) {
stage_manager_->addPower(5); stage_manager_->addPower(1);
} }
#endif #endif
updatePlayers(); updatePlayers(deltaTime);
checkPlayersStatusPlaying(); checkPlayersStatusPlaying();
updateScoreboard(); updateScoreboard();
updateBackground(); updateBackground(deltaTime);
balloon_manager_->update(); balloon_manager_->update(deltaTime);
tabe_->update(); tabe_->update(deltaTime);
updateBullets(); updateBullets(deltaTime);
updateItems(); updateItems(deltaTime);
updateStage(); updateStage();
updateSmartSprites(); updateSmartSprites(deltaTime);
updatePathSprites(); updatePathSprites(deltaTime);
updateTimeStopped(); updateTimeStopped(deltaTime);
updateHelper(); updateHelper();
checkBulletCollision(); checkBulletCollision();
updateMenace(); updateMenace();
@@ -1820,9 +1813,9 @@ void Game::checkAndUpdateBalloonSpeed() {
for (size_t i = 0; i < std::size(THRESHOLDS); ++i) { for (size_t i = 0; i < std::size(THRESHOLDS); ++i) {
// Si la velocidad actual del globo es la correspondiente al umbral "i" y el porcentaje de progreso ha superado ese umbral // Si la velocidad actual del globo es la correspondiente al umbral "i" y el porcentaje de progreso ha superado ese umbral
if (balloon_manager_->getBalloonSpeed() == Balloon::SPEED.at(i) && PERCENT > THRESHOLDS.at(i)) { if (balloon_manager_->getBalloonSpeed() == Balloon::GAME_TEMPO.at(i) && PERCENT > THRESHOLDS.at(i)) {
// Sube la velocidad al siguiente nivel (i + 1) // Sube la velocidad al siguiente nivel (i + 1)
balloon_manager_->setBalloonSpeed(Balloon::SPEED.at(i + 1)); balloon_manager_->setBalloonSpeed(Balloon::GAME_TEMPO.at(i + 1));
return; return;
} }
} }
@@ -1900,6 +1893,55 @@ void Game::onPauseStateChanged(bool is_paused) {
tabe_->pauseTimer(is_paused); tabe_->pauseTimer(is_paused);
} }
// Maneja eventos del juego completado usando flags para triggers únicos
void Game::handleGameCompletedEvents() {
constexpr float START_CELEBRATIONS_MS = 6667.0f; // 400 frames a 60fps
constexpr float END_CELEBRATIONS_MS = 11667.0f; // 700 frames a 60fps
// Inicio de celebraciones
static bool start_celebrations_triggered = false;
if (!start_celebrations_triggered && game_completed_timer_ >= START_CELEBRATIONS_MS) {
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"));
for (auto &player : players_) {
if (player->isPlaying()) {
player->addScore(1000000, Options::settings.hi_score_table.back().score);
player->setPlayingState(Player::State::CELEBRATING);
} else {
player->setPlayingState(Player::State::GAME_OVER);
}
}
updateHiScore();
start_celebrations_triggered = true;
}
// Fin de celebraciones
static bool end_celebrations_triggered = false;
if (!end_celebrations_triggered && game_completed_timer_ >= END_CELEBRATIONS_MS) {
for (auto &player : players_) {
if (player->isCelebrating()) {
player->setPlayingState(player->qualifiesForHighScore() ? Player::State::ENTERING_NAME_GAME_COMPLETED : Player::State::LEAVING_SCREEN);
}
}
fade_out_->activate();
end_celebrations_triggered = true;
}
}
// Maneja eventos de game over usando flag para trigger único
void Game::handleGameOverEvents() {
static bool game_over_triggered = false;
if (!game_over_triggered && game_over_timer_ == 0.0f) {
createMessage({paths_.at(2), paths_.at(3)}, Resource::get()->getTexture("game_text_game_over"));
Audio::get()->fadeOutMusic(1000);
balloon_manager_->setBouncingSounds(true);
game_over_triggered = true;
}
}
#ifdef _DEBUG #ifdef _DEBUG
// Comprueba los eventos en el modo DEBUG // Comprueba los eventos en el modo DEBUG
void Game::handleDebugEvents(const SDL_Event &event) { void Game::handleDebugEvents(const SDL_Event &event) {

View File

@@ -33,7 +33,22 @@ namespace Difficulty {
enum class Code; enum class Code;
} // namespace Difficulty } // namespace Difficulty
// --- Clase Game: gestor principal del juego --- // --- Clase Game: núcleo principal del gameplay ---
//
// Esta clase gestiona toda la lógica del juego durante las partidas activas,
// incluyendo mecánicas de juego, estados, objetos y sistemas de puntuación.
//
// Funcionalidades principales:
// • Gestión de jugadores: soporte para 1 o 2 jugadores simultáneos
// • Sistema de estados: fade-in, entrada, jugando, completado, game-over
// • Mecánicas de juego: globos, balas, ítems, power-ups y efectos especiales
// • Sistema de puntuación: scoreboard y tabla de récords
// • Efectos temporales: tiempo detenido, ayudas automáticas
// • Modo demo: reproducción automática para attract mode
// • Gestión de fases: progresión entre niveles y dificultad
//
// Utiliza un sistema de tiempo basado en milisegundos para garantizar
// comportamiento consistente independientemente del framerate.
class Game { class Game {
public: public:
// --- Constantes --- // --- Constantes ---
@@ -58,12 +73,13 @@ class Game {
GAME_OVER, // Fin del juego GAME_OVER, // Fin del juego
}; };
// --- Constantes internas --- // --- Constantes de tiempo (en milisegundos) ---
static constexpr int HELP_COUNTER = 1000; static constexpr float HELP_COUNTER_MS = 16667.0f; // Contador de ayuda (1000 frames a 60fps)
static constexpr int GAME_COMPLETED_START_FADE = 500; static constexpr float GAME_COMPLETED_START_FADE_MS = 8333.0f; // Inicio del fade al completar (500 frames)
static constexpr int GAME_COMPLETED_END = 700; static constexpr float GAME_COMPLETED_END_MS = 11667.0f; // Fin del juego completado (700 frames)
static constexpr int GAME_OVER_COUNTER = 350; static constexpr float GAME_OVER_DURATION_MS = 5833.0f; // Duración game over (350 frames)
static constexpr int TIME_STOPPED_COUNTER = 360; static constexpr float TIME_STOPPED_DURATION_MS = 6000.0f; // Duración del tiempo detenido (360 frames)
static constexpr int DEMO_FADE_PRE_DURATION_MS = 500; // 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;
@@ -77,7 +93,7 @@ class Game {
bool need_coffee{false}; // Indica si se necesitan cafes bool need_coffee{false}; // Indica si se necesitan cafes
bool need_coffee_machine{false}; // Indica si se necesita PowerUp bool need_coffee_machine{false}; // Indica si se necesita PowerUp
bool need_power_ball{false}; // Indica si se necesita una PowerBall bool need_power_ball{false}; // Indica si se necesita una PowerBall
int counter; // Contador para no dar ayudas consecutivas float counter; // Contador para no dar ayudas consecutivas
int item_disk_odds; // Probabilidad de aparición del objeto int item_disk_odds; // Probabilidad de aparición del objeto
int item_gavina_odds; // Probabilidad de aparición del objeto int item_gavina_odds; // Probabilidad de aparición del objeto
int item_pacmar_odds; // Probabilidad de aparición del objeto int item_pacmar_odds; // Probabilidad de aparición del objeto
@@ -86,7 +102,7 @@ class Game {
int item_coffee_machine_odds; // Probabilidad de aparición del objeto int item_coffee_machine_odds; // Probabilidad de aparición del objeto
Helper() Helper()
: counter(HELP_COUNTER), : counter(HELP_COUNTER_MS),
item_disk_odds(ITEM_POINTS_1_DISK_ODDS), item_disk_odds(ITEM_POINTS_1_DISK_ODDS),
item_gavina_odds(ITEM_POINTS_2_GAVINA_ODDS), item_gavina_odds(ITEM_POINTS_2_GAVINA_ODDS),
item_pacmar_odds(ITEM_POINTS_3_PACMAR_ODDS), item_pacmar_odds(ITEM_POINTS_3_PACMAR_ODDS),
@@ -134,14 +150,14 @@ class Game {
Demo demo_; // Variable con todas las variables relacionadas con el modo demo Demo demo_; // Variable con todas las variables relacionadas con el modo demo
Difficulty::Code difficulty_ = Options::settings.difficulty; // Dificultad del juego Difficulty::Code difficulty_ = Options::settings.difficulty; // Dificultad del juego
Helper helper_; // Variable para gestionar las ayudas Helper helper_; // Variable para gestionar las ayudas
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último tiempo registrado para deltaTime
bool coffee_machine_enabled_ = false; // Indica si hay una máquina de café en el terreno de juego bool coffee_machine_enabled_ = false; // Indica si hay una máquina de café en el terreno de juego
bool hi_score_achieved_ = false; // Indica si se ha superado la puntuación máxima bool hi_score_achieved_ = false; // Indica si se ha superado la puntuación máxima
float difficulty_score_multiplier_; // Multiplicador de puntos en función de la dificultad float difficulty_score_multiplier_ = 1.0f; // Multiplicador de puntos en función de la dificultad
int counter_ = 0; // Contador para el juego float counter_ = 0.0f; // Contador para el juego
int game_completed_counter_ = 0; // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más globos float game_completed_timer_ = 0.0f; // Acumulador de tiempo para el tramo final (milisegundos)
int game_over_counter_ = GAME_OVER_COUNTER; // Contador para el estado de fin de partida float game_over_timer_ = 0.0f; // Timer para el estado de fin de partida (milisegundos)
int time_stopped_counter_ = 0; // Temporizador para llevar la cuenta del tiempo detenido float time_stopped_timer_ = 0.0f; // Temporizador para llevar la cuenta del tiempo detenido
int menace_ = 0; // Nivel de amenaza actual int menace_ = 0; // Nivel de amenaza actual
int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos
State state_ = State::FADE_IN; // Estado State state_ = State::FADE_IN; // Estado
@@ -154,25 +170,26 @@ class Game {
#endif #endif
// --- Ciclo principal del juego --- // --- Ciclo principal del juego ---
void update(); // Actualiza la lógica principal del juego void update(float deltaTime); // Actualiza la lógica principal del juego
void render(); // Renderiza todos los elementos del juego auto calculateDeltaTime() -> float; // Calcula el deltatime
void handleEvents(); // Procesa los eventos del sistema en cola void render(); // Renderiza todos los elementos del juego
void checkState(); // Verifica y actualiza el estado actual del juego void handleEvents(); // Procesa los eventos del sistema en cola
void setState(State state); // Cambia el estado del juego void checkState(); // Verifica y actualiza el estado actual del juego
void cleanVectors(); // Limpia vectores de elementos deshabilitados void setState(State state); // Cambia el estado del juego
void cleanVectors(); // Limpia vectores de elementos deshabilitados
// --- Gestión de estados del juego --- // --- Gestión de estados del juego ---
void updateGameStates(); // Actualiza todos los estados del juego void updateGameStates(float deltaTime); // Actualiza todos los estados del juego
void updateGameStateFadeIn(); // Gestiona el estado de transición de entrada void updateGameStateFadeIn(float deltaTime); // Gestiona el estado de transición de entrada (time-based)
void updateGameStateEnteringPlayer(); // Gestiona el estado de entrada de jugador void updateGameStateEnteringPlayer(float deltaTime); // Gestiona el estado de entrada de jugador
void updateGameStateShowingGetReadyMessage(); // Gestiona el estado de mensaje "preparado" void updateGameStateShowingGetReadyMessage(float deltaTime); // Gestiona el estado de mensaje "preparado"
void updateGameStatePlaying(); // Gestiona el estado de juego activo void updateGameStatePlaying(float deltaTime); // Gestiona el estado de juego activo
void updateGameStateCompleted(); // Gestiona el estado de juego completado void updateGameStateCompleted(float deltaTime); // Gestiona el estado de juego completado
void updateGameStateGameOver(); // Gestiona el estado de fin de partida void updateGameStateGameOver(float deltaTime); // Gestiona el estado de fin de partida
// --- Gestión de jugadores --- // --- Gestión de jugadores ---
void initPlayers(Player::Id player_id); // Inicializa los datos de los jugadores void initPlayers(Player::Id player_id); // Inicializa los datos de los jugadores
void updatePlayers(); // Actualiza las variables y estados de los jugadores void updatePlayers(float deltaTime); // Actualiza las variables y estados de los jugadores
void renderPlayers(); // Renderiza todos los jugadores en pantalla void renderPlayers(); // Renderiza todos los jugadores en pantalla
void sortPlayersByZOrder(); // Reorganiza el orden de dibujado de jugadores void sortPlayersByZOrder(); // Reorganiza el orden de dibujado de jugadores
auto getPlayer(Player::Id id) -> std::shared_ptr<Player>; // Obtiene un jugador por su identificador auto getPlayer(Player::Id id) -> std::shared_ptr<Player>; // Obtiene un jugador por su identificador
@@ -209,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(); // Actualiza posición y estado de todas las balas 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
@@ -221,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(); // 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
@@ -229,21 +246,23 @@ class Game {
void destroyAllItems(); // Elimina todos los ítems activos de la pantalla void destroyAllItems(); // Elimina todos los ítems activos de la pantalla
// --- ítems especiales --- // --- ítems especiales ---
void enableTimeStopItem(); // Activa el efecto de detener el tiempo void enableTimeStopItem(); // Activa el efecto de detener el tiempo
void disableTimeStopItem(); // Desactiva el efecto de detener el tiempo void disableTimeStopItem(); // Desactiva el efecto de detener el tiempo
void updateTimeStopped(); // Actualiza el estado del tiempo detenido void updateTimeStopped(float deltaTime); // Actualiza el estado del tiempo detenido
void throwCoffee(int x, int y); // Crea efecto de café arrojado al ser golpeado void handleGameCompletedEvents(); // Maneja eventos del juego completado
void handleGameOverEvents(); // Maneja eventos de game over
void throwCoffee(int x, int y); // Crea efecto de café arrojado al ser golpeado
// --- Gestión de caída de ítems --- // --- Gestión de caída de ítems ---
void handleItemDrop(const std::shared_ptr<Balloon> &balloon, const std::shared_ptr<Player> &player); // Gestiona caída de ítem desde globo void handleItemDrop(const std::shared_ptr<Balloon> &balloon, const std::shared_ptr<Player> &player); // Gestiona caída de ítem desde globo
// --- Sprites inteligentes (smartsprites) --- // --- Sprites inteligentes (smartsprites) ---
void updateSmartSprites(); // Actualiza todos los sprites con lógica propia 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(); // 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
@@ -277,7 +296,7 @@ class Game {
// --- 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(); // Actualiza elementos del fondo 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

View File

@@ -33,7 +33,7 @@ HiScoreTable::HiScoreTable()
backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
fade_(std::make_unique<Fade>()), fade_(std::make_unique<Fade>()),
background_(std::make_unique<Background>()), background_(std::make_unique<Background>()),
ticks_(0), last_time_(0),
view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}), view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}),
fade_mode_(Fade::Mode::IN), fade_mode_(Fade::Mode::IN),
background_fade_color_(Color(0, 0, 0)) { background_fade_color_(Color(0, 0, 0)) {
@@ -53,17 +53,14 @@ HiScoreTable::~HiScoreTable() {
} }
// Actualiza las variables // Actualiza las variables
void HiScoreTable::update() { void HiScoreTable::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { Screen::get()->update(); // Actualiza el objeto screen
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen
updateSprites(); // Actualiza las posiciones de los sprites de texto updateSprites(delta_time); // Actualiza las posiciones de los sprites de texto
background_->update(); // Actualiza el fondo background_->update(delta_time); // Actualiza el fondo
updateFade(); // Gestiona el fade updateFade(delta_time); // Gestiona el fade
updateCounter(); // Gestiona el contador y sus eventos updateCounter(); // Gestiona el contador y sus eventos
fillTexture(); // Dibuja los sprites en la textura fillTexture(); // Dibuja los sprites en la textura
}
Audio::update(); Audio::update();
} }
@@ -117,20 +114,32 @@ void HiScoreTable::checkInput() {
GlobalInputs::check(); GlobalInputs::check();
} }
// Calcula el tiempo transcurrido desde el último frame
float HiScoreTable::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle para la pantalla de instrucciones // Bucle para la pantalla de instrucciones
void HiScoreTable::run() { void HiScoreTable::run() {
last_time_ = SDL_GetTicks();
Audio::get()->playMusic("title.ogg"); Audio::get()->playMusic("title.ogg");
while (Section::name == Section::Name::HI_SCORE_TABLE) { while (Section::name == Section::Name::HI_SCORE_TABLE) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
} }
// Gestiona el fade // Gestiona el fade
void HiScoreTable::updateFade() { void HiScoreTable::updateFade(float delta_time) {
fade_->update(); fade_->update(delta_time);
if (fade_->hasEnded() && fade_mode_ == Fade::Mode::IN) { if (fade_->hasEnded() && fade_mode_ == Fade::Mode::IN) {
fade_->reset(); fade_->reset();
@@ -249,7 +258,7 @@ void HiScoreTable::createSprites() {
} }
// Actualiza las posiciones de los sprites de texto // Actualiza las posiciones de los sprites de texto
void HiScoreTable::updateSprites() { void HiScoreTable::updateSprites(float delta_time) {
constexpr int INIT_COUNTER = 190; constexpr int INIT_COUNTER = 190;
const int COUNTER_BETWEEN_ENTRIES = 16; const int COUNTER_BETWEEN_ENTRIES = 16;
if (counter_ >= INIT_COUNTER) { if (counter_ >= INIT_COUNTER) {
@@ -262,7 +271,7 @@ void HiScoreTable::updateSprites() {
} }
} }
for (auto const &entry : entry_names_) { for (auto const &entry : entry_names_) {
entry->update(); entry->update(delta_time);
} }
glowEntryNames(); glowEntryNames();

View File

@@ -45,26 +45,27 @@ class HiScoreTable {
// --- Variables --- // --- Variables ---
Uint16 counter_ = 0; // Contador Uint16 counter_ = 0; // Contador
Uint64 ticks_; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla
Fade::Mode fade_mode_; // Modo de fade a utilizar Fade::Mode fade_mode_; // Modo de fade a utilizar
Color background_fade_color_; // Color de atenuación del fondo Color background_fade_color_; // Color de atenuación del fondo
std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables void update(float delta_time); // Actualiza las variables
void render(); // Pinta en pantalla void render(); // Pinta en pantalla
static void checkEvents(); // Comprueba los eventos static void checkEvents(); // Comprueba los eventos
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
static auto format(int number) -> std::string; // Convierte un entero a un string con separadores de miles static auto format(int number) -> std::string; // Convierte un entero a un string con separadores de miles
void fillTexture(); // Dibuja los sprites en la textura void fillTexture(); // Dibuja los sprites en la textura
void updateFade(); // Gestiona el fade void updateFade(float delta_time); // Gestiona el fade
void createSprites(); // Crea los sprites con los textos void createSprites(); // Crea los sprites con los textos
void updateSprites(); // Actualiza las posiciones de los sprites de texto void updateSprites(float delta_time); // Actualiza las posiciones de los sprites de texto
void initFade(); // Inicializa el fade void initFade(); // Inicializa el fade
void initBackground(); // Inicializa el fondo void initBackground(); // Inicializa el fondo
auto getEntryColor(int counter) -> Color; // Obtiene un color del vector de colores de entradas auto getEntryColor(int counter) -> Color; // Obtiene un color del vector de colores de entradas
void iniEntryColors(); // Inicializa los colores de las entradas void iniEntryColors(); // Inicializa los colores de las entradas
void glowEntryNames(); // Hace brillar los nombres de la tabla de records void glowEntryNames(); // Hace brillar los nombres de la tabla de records
void updateCounter(); // Gestiona el contador void updateCounter(); // Gestiona el contador
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
}; };

View File

@@ -204,18 +204,15 @@ void Instructions::fillBackbuffer() {
} }
// Actualiza las variables // Actualiza las variables
void Instructions::update() { void Instructions::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { Screen::get()->update(); // Actualiza el objeto screen
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen
counter_++; // Incrementa el contador counter_++; // Incrementa el contador
updateSprites(); // Actualiza los sprites updateSprites(); // Actualiza los sprites
updateBackbuffer(); // Gestiona la textura con los graficos updateBackbuffer(); // Gestiona la textura con los graficos
tiled_bg_->update(); // Actualiza el mosaico de fondo tiled_bg_->update(delta_time); // Actualiza el mosaico de fondo
fade_->update(); // Actualiza el objeto "fade" fade_->update(delta_time); // Actualiza el objeto "fade"
fillBackbuffer(); // Rellena el backbuffer fillBackbuffer(); // Rellena el backbuffer
}
Audio::update(); Audio::update();
} }
@@ -255,12 +252,24 @@ void Instructions::checkInput() {
GlobalInputs::check(); GlobalInputs::check();
} }
// Calcula el tiempo transcurrido desde el último frame
float Instructions::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle para la pantalla de instrucciones // Bucle para la pantalla de instrucciones
void Instructions::run() { void Instructions::run() {
last_time_ = SDL_GetTicks();
Audio::get()->playMusic("title.ogg"); Audio::get()->playMusic("title.ogg");
while (Section::name == Section::Name::INSTRUCTIONS) { while (Section::name == Section::Name::INSTRUCTIONS) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }

View File

@@ -63,7 +63,7 @@ class Instructions {
// --- Variables --- // --- Variables ---
int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla
SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista
float item_space_ = 2.0; // Espacio entre los items en pantalla float item_space_ = 2.0; // Espacio entre los items en pantalla
@@ -73,7 +73,7 @@ class Instructions {
bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables void update(float delta_time); // Actualiza las variables
void render(); // Pinta en pantalla void render(); // Pinta en pantalla
static void checkEvents(); // Comprueba los eventos static void checkEvents(); // Comprueba los eventos
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
@@ -82,7 +82,8 @@ class Instructions {
void iniSprites(); // Inicializa los sprites de los items void iniSprites(); // Inicializa los sprites de los items
void updateSprites(); // Actualiza los sprites void updateSprites(); // Actualiza los sprites
static auto initializeLines(int height) -> std::vector<Line>; // Inicializa las líneas animadas static auto initializeLines(int height) -> std::vector<Line>; // Inicializa las líneas animadas
static auto moveLines(std::vector<Line> &lines, int width, float duration, Uint32 start_delay) -> bool; // Mueve las líneas static auto moveLines(std::vector<Line> &lines, int width, float duration, Uint32 start_delay) -> bool; // Mueve las líneas (ya usa tiempo real)
static void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas static void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas
void updateBackbuffer(); // Gestiona la textura con los gráficos void updateBackbuffer(); // Gestiona la textura con los gráficos
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
}; };

View File

@@ -39,7 +39,7 @@ Intro::Intro()
initTexts(); initTexts();
// Configura el fondo // Configura el fondo
tiled_bg_->setSpeed(0.3F); tiled_bg_->setSpeed(TILED_BG_SPEED);
tiled_bg_->setColor(bg_color_); tiled_bg_->setColor(bg_color_);
} }
@@ -191,7 +191,7 @@ void Intro::updateScene5() {
// Acaba la ultima imagen // Acaba la ultima imagen
if (card_sprites_.at(5)->hasFinished() && texts_.at(8)->hasFinished()) { if (card_sprites_.at(5)->hasFinished() && texts_.at(8)->hasFinished()) {
state_ = State::POST; state_ = State::POST;
state_start_time_ = SDL_GetTicks(); state_start_time_ = SDL_GetTicks() / 1000.0f;
} }
} }
@@ -207,24 +207,21 @@ void Intro::switchText(int from_index, int to_index) {
} }
// Actualiza las variables del objeto // Actualiza las variables del objeto
void Intro::update() { void Intro::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { Screen::get()->update(); // Actualiza el objeto screen
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen
tiled_bg_->update(); // Actualiza el fondo tiled_bg_->update(delta_time); // Actualiza el fondo
switch (state_) { switch (state_) {
case State::SCENES: case State::SCENES:
updateSprites(); updateSprites(delta_time);
updateTexts(); updateTexts(delta_time);
updateScenes(); updateScenes();
break; break;
case State::POST: case State::POST:
updatePostState(); updatePostState();
break; break;
}
} }
Audio::update(); Audio::update();
@@ -253,12 +250,24 @@ void Intro::render() {
SCREEN->render(); // Vuelca el contenido del renderizador en pantalla SCREEN->render(); // Vuelca el contenido del renderizador en pantalla
} }
// Calcula el tiempo transcurrido desde el último frame
float Intro::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_) / 1000.0f; // Convertir ms a segundos
last_time_ = current_time;
return delta_time;
}
// Bucle principal // Bucle principal
void Intro::run() { void Intro::run() {
last_time_ = SDL_GetTicks();
Audio::get()->playMusic("intro.ogg", 0); Audio::get()->playMusic("intro.ogg", 0);
while (Section::name == Section::Name::INTRO) { while (Section::name == Section::Name::INTRO) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
@@ -277,7 +286,7 @@ void Intro::initSprites() {
// Constantes // Constantes
constexpr int TOTAL_SPRITES = TEXTURE_LIST.size(); constexpr int TOTAL_SPRITES = TEXTURE_LIST.size();
const float BORDER = 2.0F; const float BORDER = CARD_BORDER_SIZE;
auto texture = Resource::get()->getTexture(TEXTURE_LIST.front()); auto texture = Resource::get()->getTexture(TEXTURE_LIST.front());
const float CARD_WIDTH = texture->getWidth() + (BORDER * 2); const float CARD_WIDTH = texture->getWidth() + (BORDER * 2);
@@ -329,16 +338,16 @@ void Intro::initSprites() {
const float X_DEST = param.game.game_area.center_x - (CARD_WIDTH / 2); const float X_DEST = param.game.game_area.center_x - (CARD_WIDTH / 2);
const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4); const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4);
card_sprites_.at(0)->addPath(-CARD_WIDTH - 10, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeInOutExpo, 0); card_sprites_.at(0)->addPath(-CARD_WIDTH - CARD_OFFSET_MARGIN, X_DEST, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_NORMAL, easeInOutExpo, 0.0f);
card_sprites_.at(1)->addPath(param.game.width, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeOutBounce, 0); card_sprites_.at(1)->addPath(param.game.width, X_DEST, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_NORMAL, easeOutBounce, 0.0f);
card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 40, easeOutQuint, 0); card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0.0f);
card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, 300, easeInOutExpo, 0); card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0.0f);
card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 70, easeOutElastic, 0); card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0.0f);
card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 250, easeOutQuad, 450); card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG);
card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, 80, easeInElastic, 0); card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0.0f);
// Constantes // Constantes
const float DESP = 8; const float DESP = SHADOW_OFFSET;
const float SHADOW_SPRITE_WIDTH = CARD_WIDTH; const float SHADOW_SPRITE_WIDTH = CARD_WIDTH;
const float SHADOW_SPRITE_HEIGHT = CARD_HEIGHT; const float SHADOW_SPRITE_HEIGHT = CARD_HEIGHT;
@@ -380,13 +389,13 @@ void Intro::initSprites() {
const float S_X_DEST = X_DEST + DESP; const float S_X_DEST = X_DEST + DESP;
const float S_Y_DEST = Y_DEST + DESP; const float S_Y_DEST = Y_DEST + DESP;
shadow_sprites_.at(0)->addPath(param.game.height + 10, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 100, easeInOutExpo, 0); shadow_sprites_.at(0)->addPath(param.game.height + CARD_OFFSET_MARGIN, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_NORMAL, easeInOutExpo, 0.0f);
shadow_sprites_.at(1)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 100, easeOutBounce, 0); shadow_sprites_.at(1)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_NORMAL, easeOutBounce, 0.0f);
shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, 40, easeOutQuint, 0); shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0.0f);
shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 300, easeInOutExpo, 0); shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0.0f);
shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 70, easeOutElastic, 0); shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0.0f);
shadow_sprites_.at(5)->addPath(param.game.width, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, 250, easeOutQuad, 450); shadow_sprites_.at(5)->addPath(param.game.width, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG);
shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, 80, easeInElastic, 0); shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0.0f);
} }
// Inicializa los textos // Inicializa los textos
@@ -396,47 +405,47 @@ void Intro::initTexts() {
auto writer = std::make_unique<Writer>(Resource::get()->getText("04b_25_metal")); auto writer = std::make_unique<Writer>(Resource::get()->getText("04b_25_metal"));
writer->setPosX(0); writer->setPosX(0);
writer->setPosY(param.game.height - param.intro.text_distance_from_bottom); writer->setPosY(param.game.height - param.intro.text_distance_from_bottom);
writer->setKerning(-2); writer->setKerning(TEXT_KERNING);
writer->setEnabled(false); writer->setEnabled(false);
writer->setFinishedCounter(180); writer->setFinishedTimerS(TEXT_DISPLAY_DURATION_S);
texts_.push_back(std::move(writer)); texts_.push_back(std::move(writer));
} }
// Un dia qualsevol de l'any 2000 // Un dia qualsevol de l'any 2000
texts_.at(0)->setCaption(Lang::getText("[INTRO] 1")); texts_.at(0)->setCaption(Lang::getText("[INTRO] 1"));
texts_.at(0)->setSpeed(8); texts_.at(0)->setSpeedS(TEXT_SPEED_NORMAL);
// Tot esta tranquil a la UPV // Tot esta tranquil a la UPV
texts_.at(1)->setCaption(Lang::getText("[INTRO] 2")); texts_.at(1)->setCaption(Lang::getText("[INTRO] 2"));
texts_.at(1)->setSpeed(8); texts_.at(1)->setSpeedS(TEXT_SPEED_NORMAL);
// Fins que un desaprensiu... // Fins que un desaprensiu...
texts_.at(2)->setCaption(Lang::getText("[INTRO] 3")); texts_.at(2)->setCaption(Lang::getText("[INTRO] 3"));
texts_.at(2)->setSpeed(12); texts_.at(2)->setSpeedS(TEXT_SPEED_FAST);
// HEY! ME ANE A FERME UN CORTAET... // HEY! ME ANE A FERME UN CORTAET...
texts_.at(3)->setCaption(Lang::getText("[INTRO] 4")); texts_.at(3)->setCaption(Lang::getText("[INTRO] 4"));
texts_.at(3)->setSpeed(8); texts_.at(3)->setSpeedS(TEXT_SPEED_NORMAL);
// UAAAAAAAAAAAAA!!! // UAAAAAAAAAAAAA!!!
texts_.at(4)->setCaption(Lang::getText("[INTRO] 5")); texts_.at(4)->setCaption(Lang::getText("[INTRO] 5"));
texts_.at(4)->setSpeed(1); texts_.at(4)->setSpeedS(TEXT_SPEED_VERY_SLOW);
// Espera un moment... // Espera un moment...
texts_.at(5)->setCaption(Lang::getText("[INTRO] 6")); texts_.at(5)->setCaption(Lang::getText("[INTRO] 6"));
texts_.at(5)->setSpeed(16); texts_.at(5)->setSpeedS(TEXT_SPEED_VERY_FAST);
// Si resulta que no tinc solt! // Si resulta que no tinc solt!
texts_.at(6)->setCaption(Lang::getText("[INTRO] 7")); texts_.at(6)->setCaption(Lang::getText("[INTRO] 7"));
texts_.at(6)->setSpeed(2); texts_.at(6)->setSpeedS(TEXT_SPEED_SLOW);
// MERDA DE MAQUINA! // MERDA DE MAQUINA!
texts_.at(7)->setCaption(Lang::getText("[INTRO] 8")); texts_.at(7)->setCaption(Lang::getText("[INTRO] 8"));
texts_.at(7)->setSpeed(3); texts_.at(7)->setSpeedS(TEXT_SPEED_MEDIUM_SLOW);
// Blop... blop... blop... // Blop... blop... blop...
texts_.at(8)->setCaption(Lang::getText("[INTRO] 9")); texts_.at(8)->setCaption(Lang::getText("[INTRO] 9"));
texts_.at(8)->setSpeed(20); texts_.at(8)->setSpeedS(TEXT_SPEED_ULTRA_FAST);
for (auto &text : texts_) { for (auto &text : texts_) {
text->center(param.game.game_area.center_x); text->center(param.game.game_area.center_x);
@@ -444,20 +453,20 @@ void Intro::initTexts() {
} }
// Actualiza los sprites // Actualiza los sprites
void Intro::updateSprites() { void Intro::updateSprites(float delta_time) {
for (auto &sprite : card_sprites_) { for (auto &sprite : card_sprites_) {
sprite->update(); sprite->update(delta_time);
} }
for (auto &sprite : shadow_sprites_) { for (auto &sprite : shadow_sprites_) {
sprite->update(); sprite->update(delta_time);
} }
} }
// Actualiza los textos // Actualiza los textos
void Intro::updateTexts() { void Intro::updateTexts(float delta_time) {
for (auto &text : texts_) { for (auto &text : texts_) {
text->update(); text->updateS(delta_time); // Usar updateS para delta_time en segundos
} }
} }
@@ -476,12 +485,12 @@ void Intro::renderTexts() {
// Actualiza el estado POST // Actualiza el estado POST
void Intro::updatePostState() { void Intro::updatePostState() {
const Uint32 ELAPSED_TIME = SDL_GetTicks() - state_start_time_; const float ELAPSED_TIME = (SDL_GetTicks() / 1000.0f) - state_start_time_;
switch (post_state_) { switch (post_state_) {
case PostState::STOP_BG: case PostState::STOP_BG:
// EVENTO: Detiene el fondo después de 1 segundo // EVENTO: Detiene el fondo después del tiempo especificado
if (ELAPSED_TIME >= 1000) { if (ELAPSED_TIME >= POST_BG_STOP_DELAY_S) {
tiled_bg_->stopGracefully(); tiled_bg_->stopGracefully();
if (!bg_color_.IS_EQUAL_TO(param.title.bg_color)) { if (!bg_color_.IS_EQUAL_TO(param.title.bg_color)) {
@@ -494,13 +503,13 @@ void Intro::updatePostState() {
// Cambia de estado si el fondo se ha detenido y recuperado el color // Cambia de estado si el fondo se ha detenido y recuperado el color
if (tiled_bg_->isStopped() && bg_color_.IS_EQUAL_TO(param.title.bg_color)) { if (tiled_bg_->isStopped() && bg_color_.IS_EQUAL_TO(param.title.bg_color)) {
post_state_ = PostState::END; post_state_ = PostState::END;
state_start_time_ = SDL_GetTicks(); state_start_time_ = SDL_GetTicks() / 1000.0f;
} }
break; break;
case PostState::END: case PostState::END:
// Finaliza la intro después de 1 segundo // Finaliza la intro después del tiempo especificado
if (ELAPSED_TIME >= 1000) { if (ELAPSED_TIME >= POST_END_DELAY_S) {
Audio::get()->stopMusic(); Audio::get()->stopMusic();
Section::name = Section::Name::TITLE; Section::name = Section::Name::TITLE;
Section::options = Section::Options::TITLE_1; Section::options = Section::Options::TITLE_1;

View File

@@ -11,9 +11,22 @@
#include "tiled_bg.h" // Para TiledBG #include "tiled_bg.h" // Para TiledBG
#include "writer.h" // Para Writer #include "writer.h" // Para Writer
// --- Clase Intro: muestra la secuencia de introducción --- // --- Clase Intro: secuencia cinemática de introducción del juego ---
// Esta clase gestiona un estado del programa. Se encarga de mostrar la secuencia //
// de introducción. // Esta clase gestiona la secuencia de introducción narrativa del juego, mostrando
// una serie de escenas con imágenes, texto y efectos visuales sincronizados.
//
// Funcionalidades principales:
// • Sistema de escenas secuencial: 6 escenas con transiciones automáticas
// • Animaciones de tarjetas: efectos de entrada con diferentes tipos de easing
// • Texto narrativo: velocidades de escritura configurables por escena
// • Efectos visuales: sombras, bordes y transiciones de color
// • Audio sincronizado: música de fondo durante toda la secuencia
// • Estado POST: transición suave hacia el menú principal
//
// Todas las duraciones y velocidades están configuradas mediante constantes
// para facilitar el ajuste fino de la experiencia cinematográfica.
class Intro { class Intro {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
@@ -24,6 +37,37 @@ class Intro {
void run(); void run();
private: private:
// --- Constantes de tiempo (en segundos) ---
static constexpr float TEXT_DISPLAY_DURATION_S = 3.0f; // Duración de visualización de texto (180 frames a 60fps)
static constexpr float POST_BG_STOP_DELAY_S = 1.0f; // Retraso antes de detener el fondo
static constexpr float POST_END_DELAY_S = 1.0f; // Retraso antes de finalizar intro
// --- Constantes de layout ---
static constexpr float CARD_BORDER_SIZE = 2.0f; // Tamaño del borde de tarjetas
static constexpr float SHADOW_OFFSET = 8.0f; // Desplazamiento de sombra
static constexpr float TILED_BG_SPEED = 18.0f; // Velocidad del fondo mosaico (pixels/segundo)
static constexpr int TEXT_KERNING = -2; // Espaciado entre caracteres
// --- Constantes de velocidades de texto (segundos entre caracteres) ---
static constexpr float TEXT_SPEED_NORMAL = 0.133f; // Velocidad normal (8 frames * 16.67ms = 133ms)
static constexpr float TEXT_SPEED_FAST = 0.2f; // Velocidad rápida (12 frames * 16.67ms = 200ms)
static constexpr float TEXT_SPEED_VERY_SLOW = 0.0167f; // Velocidad muy lenta (1 frame * 16.67ms = 16.7ms)
static constexpr float TEXT_SPEED_VERY_FAST = 0.267f; // Velocidad muy rápida (16 frames * 16.67ms = 267ms)
static constexpr float TEXT_SPEED_SLOW = 0.033f; // Velocidad lenta (2 frames * 16.67ms = 33ms)
static constexpr float TEXT_SPEED_MEDIUM_SLOW = 0.05f; // Velocidad medio-lenta (3 frames * 16.67ms = 50ms)
static constexpr float TEXT_SPEED_ULTRA_FAST = 0.333f; // Velocidad ultra rápida (20 frames * 16.67ms = 333ms)
// --- Constantes de animaciones de tarjetas (duraciones en segundos) ---
static constexpr float CARD_ANIM_DURATION_NORMAL = 100.0f / 60.0f; // ≈ 1.6667 s
static constexpr float CARD_ANIM_DURATION_FAST = 40.0f / 60.0f; // ≈ 0.6667 s
static constexpr float CARD_ANIM_DURATION_MEDIUM = 70.0f / 60.0f; // ≈ 1.1667 s
static constexpr float CARD_ANIM_DURATION_SHORT = 80.0f / 60.0f; // ≈ 1.3333 s
static constexpr float CARD_ANIM_DURATION_SLOW = 250.0f / 60.0f; // ≈ 4.1667 s
static constexpr float CARD_ANIM_DURATION_VERY_SLOW = 300.0f / 60.0f; // ≈ 5.0000 s
static constexpr float CARD_ANIM_DELAY_LONG = 0.45f; // Retraso largo antes de animación
static constexpr float CARD_OFFSET_MARGIN = 10.0f; // Margen fuera de pantalla
// --- Estados internos --- // --- Estados internos ---
enum class State { enum class State {
SCENES, SCENES,
@@ -42,27 +86,28 @@ class Intro {
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
// --- Variables --- // --- Variables ---
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
int scene_ = 0; // Indica qué escena está activa int scene_ = 0; // Indica qué escena está activa
State state_ = State::SCENES; // Estado principal de la intro State state_ = State::SCENES; // Estado principal de la intro
PostState post_state_ = PostState::STOP_BG; // Estado POST PostState post_state_ = PostState::STOP_BG; // Estado POST
Uint32 state_start_time_; // Tiempo de inicio del estado actual float state_start_time_; // Tiempo de inicio del estado actual (segundos)
Color bg_color_ = param.intro.bg_color; // Color de fondo Color bg_color_ = param.intro.bg_color; // Color de fondo
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables del objeto void update(float delta_time); // Actualiza las variables del objeto
void render(); // Dibuja el objeto en pantalla void render(); // Dibuja el objeto en pantalla
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
static void checkEvents(); // Comprueba los eventos static void checkEvents(); // Comprueba los eventos
void updateScenes(); // Actualiza las escenas de la intro void updateScenes(); // Actualiza las escenas de la intro
void initSprites(); // Inicializa las imágenes void initSprites(); // Inicializa las imágenes
void initTexts(); // Inicializa los textos void initTexts(); // Inicializa los textos
void updateSprites(); // Actualiza los sprites void updateSprites(float delta_time); // Actualiza los sprites
void updateTexts(); // Actualiza los textos void updateTexts(float delta_time); // Actualiza los textos
void renderSprites(); // Dibuja los sprites void renderSprites(); // Dibuja los sprites
void renderTexts(); // Dibuja los textos void renderTexts(); // Dibuja los textos
static void renderTextRect(); // Dibuja el rectangulo de fondo del texto; static void renderTextRect(); // Dibuja el rectangulo de fondo del texto;
void updatePostState(); // Actualiza el estado POST void updatePostState(); // Actualiza el estado POST
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
// --- Métodos para manejar cada escena individualmente --- // --- Métodos para manejar cada escena individualmente ---
void updateScene0(); void updateScene0();

View File

@@ -28,38 +28,38 @@ Logo::Logo()
dest_.y = param.game.game_area.center_y - jail_texture_->getHeight() / 2; dest_.y = param.game.game_area.center_y - jail_texture_->getHeight() / 2;
since_sprite_->setPosition(SDL_FRect{ since_sprite_->setPosition(SDL_FRect{
static_cast<float>((param.game.width - since_texture_->getWidth()) / 2), static_cast<float>((param.game.width - since_texture_->getWidth()) / 2),
static_cast<float>(83 + jail_texture_->getHeight() + 5), static_cast<float>(SINCE_SPRITE_Y_OFFSET + jail_texture_->getHeight() + LOGO_SPACING),
static_cast<float>(since_texture_->getWidth()), static_cast<float>(since_texture_->getWidth()),
static_cast<float>(since_texture_->getHeight())}); static_cast<float>(since_texture_->getHeight())});
since_sprite_->setY(dest_.y + jail_texture_->getHeight() + 5); since_sprite_->setY(dest_.y + jail_texture_->getHeight() + LOGO_SPACING);
since_sprite_->setSpriteClip(0, 0, since_texture_->getWidth(), since_texture_->getHeight()); since_sprite_->setSpriteClip(0, 0, since_texture_->getWidth(), since_texture_->getHeight());
since_texture_->setColor(0x00, 0x00, 0x00); since_texture_->setColor(SPECTRUM_BLACK.r, SPECTRUM_BLACK.g, SPECTRUM_BLACK.b);
// Crea los sprites de cada linea // Crea los sprites de cada linea
for (int i = 0; i < jail_texture_->getHeight(); ++i) { for (int i = 0; i < jail_texture_->getHeight(); ++i) {
auto temp = std::make_unique<Sprite>(jail_texture_, 0, i, jail_texture_->getWidth(), 1); auto temp = std::make_unique<Sprite>(jail_texture_, 0, i, jail_texture_->getWidth(), SPRITE_LINE_HEIGHT);
temp->setSpriteClip(0, i, jail_texture_->getWidth(), 1); temp->setSpriteClip(0, i, jail_texture_->getWidth(), SPRITE_LINE_HEIGHT);
const int POS_X = (i % 2 == 0) ? param.game.width + (i * 3) : -jail_texture_->getWidth() - (i * 3); const int POS_X = (i % 2 == 0) ? param.game.width + (i * LINE_OFFSET_FACTOR) : -jail_texture_->getWidth() - (i * LINE_OFFSET_FACTOR);
temp->setX(POS_X); temp->setX(POS_X);
temp->setY(dest_.y + i); temp->setY(dest_.y + i);
jail_sprite_.push_back(std::move(temp)); jail_sprite_.push_back(std::move(temp));
} }
// Inicializa el vector de colores // Inicializa el vector de colores con la paleta ZX Spectrum
color_.emplace_back(0x00, 0x00, 0x00); // Black color_.emplace_back(SPECTRUM_BLACK);
color_.emplace_back(0x00, 0x00, 0xd8); // Blue color_.emplace_back(SPECTRUM_BLUE);
color_.emplace_back(0xd8, 0x00, 0x00); // Red color_.emplace_back(SPECTRUM_RED);
color_.emplace_back(0xd8, 0x00, 0xd8); // Magenta color_.emplace_back(SPECTRUM_MAGENTA);
color_.emplace_back(0x00, 0xd8, 0x00); // Green color_.emplace_back(SPECTRUM_GREEN);
color_.emplace_back(0x00, 0xd8, 0xd8); // Cyan color_.emplace_back(SPECTRUM_CYAN);
color_.emplace_back(0xd8, 0xd8, 0x00); // Yellow color_.emplace_back(SPECTRUM_YELLOW);
color_.emplace_back(0xFF, 0xFF, 0xFF); // Bright white color_.emplace_back(SPECTRUM_WHITE);
} }
// Destructor // Destructor
Logo::~Logo() { Logo::~Logo() {
jail_texture_->setColor(255, 255, 255); jail_texture_->setColor(RESET_COLOR.r, RESET_COLOR.g, RESET_COLOR.b);
since_texture_->setColor(255, 255, 255); since_texture_->setColor(RESET_COLOR.r, RESET_COLOR.g, RESET_COLOR.b);
Audio::get()->stopAllSounds(); Audio::get()->stopAllSounds();
Audio::get()->stopMusic(); Audio::get()->stopMusic();
} }
@@ -78,22 +78,30 @@ void Logo::checkInput() {
GlobalInputs::check(); GlobalInputs::check();
} }
// Gestiona el logo de JAILGAMES // Maneja la reproducción del sonido del logo
void Logo::updateJAILGAMES() { void Logo::handleSound() {
if (counter_ == 30) { static bool sound_triggered = false;
Audio::get()->playSound("logo.wav");
}
if (counter_ > 30) { if (!sound_triggered && elapsed_time_s_ >= SOUND_TRIGGER_TIME_S) {
for (int i = 0; i < (int)jail_sprite_.size(); ++i) { Audio::get()->playSound("logo.wav");
sound_triggered = true;
}
}
// Gestiona el logo de JAILGAMES
void Logo::updateJAILGAMES(float delta_time) {
if (elapsed_time_s_ > SOUND_TRIGGER_TIME_S) {
const float PIXELS_TO_MOVE = LOGO_SPEED_PX_PER_S * delta_time;
for (size_t i = 0; i < jail_sprite_.size(); ++i) {
if (jail_sprite_[i]->getX() != dest_.x) { if (jail_sprite_[i]->getX() != dest_.x) {
if (i % 2 == 0) { if (i % 2 == 0) {
jail_sprite_[i]->incX(-SPEED); jail_sprite_[i]->incX(-PIXELS_TO_MOVE);
if (jail_sprite_[i]->getX() < dest_.x) { if (jail_sprite_[i]->getX() < dest_.x) {
jail_sprite_[i]->setX(dest_.x); jail_sprite_[i]->setX(dest_.x);
} }
} else { } else {
jail_sprite_[i]->incX(SPEED); jail_sprite_[i]->incX(PIXELS_TO_MOVE);
if (jail_sprite_[i]->getX() > dest_.x) { if (jail_sprite_[i]->getX() > dest_.x) {
jail_sprite_[i]->setX(dest_.x); jail_sprite_[i]->setX(dest_.x);
} }
@@ -103,43 +111,41 @@ void Logo::updateJAILGAMES() {
} }
// Comprueba si ha terminado el logo // Comprueba si ha terminado el logo
if (counter_ == END_LOGO_COUNTER_MARK + POST_LOGO_DURATION) { if (elapsed_time_s_ >= END_LOGO_TIME_S + POST_LOGO_DURATION_S) {
Section::name = Section::Name::INTRO; Section::name = Section::Name::INTRO;
} }
} }
// Gestiona el color de las texturas // Gestiona el color de las texturas
void Logo::updateTextureColors() { void Logo::updateTextureColors(float delta_time) {
constexpr int INC = 4;
// Manejo de 'sinceTexture' // Manejo de 'sinceTexture'
for (int i = 0; i <= 7; ++i) { for (int i = 0; i <= MAX_SINCE_COLOR_INDEX; ++i) {
if (counter_ == SHOW_SINCE_SPRITE_COUNTER_MARK + INC * i) { const float target_time = SHOW_SINCE_SPRITE_TIME_S + COLOR_CHANGE_INTERVAL_S * i;
if (elapsed_time_s_ >= target_time && elapsed_time_s_ - delta_time < target_time) {
since_texture_->setColor(color_[i].r, color_[i].g, color_[i].b); since_texture_->setColor(color_[i].r, color_[i].g, color_[i].b);
} }
} }
// Manejo de 'jailTexture' y 'sinceTexture' en el fade // Manejo de 'jailTexture' y 'sinceTexture' en el fade
for (int i = 0; i <= 6; ++i) { for (int i = 0; i <= MAX_FADE_COLOR_INDEX; ++i) {
if (counter_ == INIT_FADE_COUNTER_MARK + INC * i) { const float target_time = INIT_FADE_TIME_S + COLOR_CHANGE_INTERVAL_S * i;
jail_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b); if (elapsed_time_s_ >= target_time && elapsed_time_s_ - delta_time < target_time) {
since_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b); jail_texture_->setColor(color_[MAX_FADE_COLOR_INDEX - i].r, color_[MAX_FADE_COLOR_INDEX - i].g, color_[MAX_FADE_COLOR_INDEX - i].b);
since_texture_->setColor(color_[MAX_FADE_COLOR_INDEX - i].r, color_[MAX_FADE_COLOR_INDEX - i].g, color_[MAX_FADE_COLOR_INDEX - i].b);
} }
} }
} }
// Actualiza las variables // Actualiza las variables
void Logo::update() { void Logo::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { elapsed_time_s_ += delta_time; // Acumula el tiempo transcurrido
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen
updateJAILGAMES(); // Actualiza el logo de JAILGAMES Screen::get()->update(); // Actualiza el objeto screen
updateTextureColors(); // Actualiza los colores de las texturas Audio::update(); // Actualiza el objeto audio
++counter_; // Gestiona el contador
}
Audio::update(); handleSound(); // Maneja la reproducción del sonido
updateTextureColors(delta_time); // Actualiza los colores de las texturas
updateJAILGAMES(delta_time); // Actualiza el logo de JAILGAMES
} }
// Dibuja en pantalla // Dibuja en pantalla
@@ -154,11 +160,23 @@ void Logo::render() {
SCREEN->render(); SCREEN->render();
} }
// Calcula el tiempo transcurrido desde el último frame
float Logo::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_) / 1000.0f; // Convertir ms a segundos
last_time_ = current_time;
return delta_time;
}
// Bucle para el logo del juego // Bucle para el logo del juego
void Logo::run() { void Logo::run() {
last_time_ = SDL_GetTicks();
while (Section::name == Section::Name::LOGO) { while (Section::name == Section::Name::LOGO) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
@@ -171,7 +189,7 @@ void Logo::renderJAILGAMES() {
sprite->render(); sprite->render();
} }
if (counter_ >= SHOW_SINCE_SPRITE_COUNTER_MARK) { if (elapsed_time_s_ >= SHOW_SINCE_SPRITE_TIME_S) {
since_sprite_->render(); since_sprite_->render();
} }
} }

View File

@@ -10,12 +10,21 @@
class Texture; class Texture;
// --- Clase Logo: dibuja el logo de JAILGAMES con efectos visuales --- // --- Clase Logo: pantalla de presentación de JAILGAMES con efectos retro ---
// Esta clase gestiona un estado del programa. Se encarga de dibujar por pantalla el //
// logo de "JAILGAMES" utilizando un sencillo efecto consistente en generar un sprite por // Esta clase gestiona el estado inicial del programa, mostrando el logo corporativo
// cada línea del bitmap que forma la palabra "JAILGAMES". Posteriormente realiza una // de JAILGAMES con efectos visuales inspirados en el ZX Spectrum.
// modulación de color sobre la textura para simular un fade to black al estilo //
// ZX Spectrum. // Funcionalidades principales:
// • Animación de convergencia: cada línea del logo entra desde los laterales
// • Efectos de color: transiciones automáticas usando la paleta ZX Spectrum
// • Audio sincronizado: reproduce sonido del logo en momento específico
// • Transición temporal: duración controlada con paso automático al siguiente estado
// • Sistema delta-time: animaciones suaves independientes del framerate
//
// La clase utiliza un sistema de tiempo basado en segundos para garantizar
// consistencia visual en diferentes velocidades de procesamiento.
class Logo { class Logo {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
@@ -26,12 +35,35 @@ class Logo {
void run(); void run();
private: private:
// --- Constantes --- // --- Constantes de tiempo (en segundos) ---
static constexpr int SHOW_SINCE_SPRITE_COUNTER_MARK = 70; // Tiempo del contador en el que empieza a verse el sprite de "SINCE 1998" static constexpr float SOUND_TRIGGER_TIME_S = 0.5f; // Tiempo para activar el sonido del logo
static constexpr int INIT_FADE_COUNTER_MARK = 300; // Tiempo del contador cuando inicia el fade a negro static constexpr float SHOW_SINCE_SPRITE_TIME_S = 1.167f; // Tiempo para mostrar el sprite "SINCE 1998"
static constexpr int END_LOGO_COUNTER_MARK = 400; // Tiempo del contador para terminar el logo static constexpr float INIT_FADE_TIME_S = 5.0f; // Tiempo de inicio del fade a negro
static constexpr int POST_LOGO_DURATION = 20; // Tiempo que dura el logo con el fade al máximo static constexpr float END_LOGO_TIME_S = 6.668f; // Tiempo de finalización del logo
static constexpr int SPEED = 8; // Velocidad de desplazamiento de cada línea static constexpr float POST_LOGO_DURATION_S = 0.333f; // Duración adicional después del fade
static constexpr float LOGO_SPEED_PX_PER_S = 480.0f; // Velocidad de desplazamiento (píxeles por segundo) - 8.0f/16.67f*1000
static constexpr float COLOR_CHANGE_INTERVAL_S = 0.0667f; // Intervalo entre cambios de color (~4 frames a 60fps)
// --- Constantes de layout ---
static constexpr int SINCE_SPRITE_Y_OFFSET = 83; // Posición Y base del sprite "Since 1998"
static constexpr int LOGO_SPACING = 5; // Espaciado entre elementos del logo
static constexpr int LINE_OFFSET_FACTOR = 3; // Factor de desplazamiento inicial por línea
static constexpr int SPRITE_LINE_HEIGHT = 1; // Altura de cada línea sprite
// --- Constantes de colores ---
static constexpr int MAX_SINCE_COLOR_INDEX = 7; // Índice máximo para colores del sprite "Since"
static constexpr int MAX_FADE_COLOR_INDEX = 6; // Índice máximo para colores del fade
// --- Paleta ZX Spectrum para efectos de logo ---
static constexpr Color SPECTRUM_BLACK = Color(0x00, 0x00, 0x00); // Negro
static constexpr Color SPECTRUM_BLUE = Color(0x00, 0x00, 0xd8); // Azul
static constexpr Color SPECTRUM_RED = Color(0xd8, 0x00, 0x00); // Rojo
static constexpr Color SPECTRUM_MAGENTA = Color(0xd8, 0x00, 0xd8); // Magenta
static constexpr Color SPECTRUM_GREEN = Color(0x00, 0xd8, 0x00); // Verde
static constexpr Color SPECTRUM_CYAN = Color(0x00, 0xd8, 0xd8); // Cian
static constexpr Color SPECTRUM_YELLOW = Color(0xd8, 0xd8, 0x00); // Amarillo
static constexpr Color SPECTRUM_WHITE = Color(0xFF, 0xFF, 0xFF); // Blanco brillante
static constexpr Color RESET_COLOR = Color(255, 255, 255); // Color de reset
// --- Objetos y punteros --- // --- Objetos y punteros ---
std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998" std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998"
@@ -40,17 +72,19 @@ class Logo {
std::vector<std::unique_ptr<Sprite>> jail_sprite_; // Vector con los sprites de cada línea que forman el bitmap JAILGAMES std::vector<std::unique_ptr<Sprite>> jail_sprite_; // Vector con los sprites de cada línea que forman el bitmap JAILGAMES
// --- Variables --- // --- Variables ---
std::vector<Color> color_; // Vector con los colores para el fade std::vector<Color> color_; // Vector con los colores para el fade
int counter_ = 0; // Contador float elapsed_time_s_ = 0.0f; // Tiempo transcurrido en segundos
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
SDL_FPoint dest_; // Posición donde dibujar el logo SDL_FPoint dest_; // Posición donde dibujar el logo
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables void update(float delta_time); // Actualiza las variables
void render(); // Dibuja en pantalla void render(); // Dibuja en pantalla
static void checkEvents(); // Comprueba el manejador de eventos static void checkEvents(); // Comprueba el manejador de eventos
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
void updateJAILGAMES(); // Gestiona el logo de JAILGAMES void updateJAILGAMES(float delta_time); // Gestiona el logo de JAILGAMES
void renderJAILGAMES(); // Renderiza el logo de JAILGAMES void renderJAILGAMES(); // Renderiza el logo de JAILGAMES
void updateTextureColors(); // Gestiona el color de las texturas void updateTextureColors(float delta_time); // Gestiona el color de las texturas
void handleSound(); // Maneja la reproducción del sonido del logo
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
}; };

View File

@@ -42,7 +42,7 @@ Title::Title()
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::RANDOM)), tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::RANDOM)),
game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)), game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)),
mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))), mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))),
state_(TitleState::LOGO_ANIMATING), state_(State::LOGO_ANIMATING),
num_controllers_(Input::get()->getNumGamepads()) { num_controllers_(Input::get()->getNumGamepads()) {
// Configura objetos // Configura objetos
tiled_bg_->setColor(param.title.bg_color); tiled_bg_->setColor(param.title.bg_color);
@@ -60,16 +60,16 @@ Title::Title()
Section::attract_mode = IS_TITLE_TO_DEMO ? Section::AttractMode::TITLE_TO_LOGO : Section::AttractMode::TITLE_TO_DEMO; Section::attract_mode = IS_TITLE_TO_DEMO ? Section::AttractMode::TITLE_TO_LOGO : Section::AttractMode::TITLE_TO_DEMO;
// Define los anclajes de los elementos // Define los anclajes de los elementos
anchor_.mini_logo = (param.game.height / 5 * 4) + BLOCK; anchor_.mini_logo = (param.game.height / MINI_LOGO_Y_DIVISOR * MINI_LOGO_Y_FACTOR) + BLOCK;
mini_logo_sprite_->setY(anchor_.mini_logo); mini_logo_sprite_->setY(anchor_.mini_logo);
anchor_.copyright_text = anchor_.mini_logo + mini_logo_sprite_->getHeight() + 3; anchor_.copyright_text = anchor_.mini_logo + mini_logo_sprite_->getHeight() + COPYRIGHT_TEXT_SPACING;
} }
// Destructor // Destructor
Title::~Title() { Title::~Title() {
Audio::get()->stopAllSounds(); Audio::get()->stopAllSounds();
if (Section::name == Section::Name::LOGO) { if (Section::name == Section::Name::LOGO) {
Audio::get()->fadeOutMusic(300); Audio::get()->fadeOutMusic(MUSIC_FADE_OUT_SHORT_MS);
} }
// Desregistra los jugadores de Options // Desregistra los jugadores de Options
@@ -78,20 +78,27 @@ Title::~Title() {
} }
// Actualiza las variables del objeto // Actualiza las variables del objeto
void Title::update() { void Title::update(float deltaTime) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { Screen::get()->update();
ticks_ = SDL_GetTicks(); updateFade();
Screen::get()->update(); updateState(deltaTime);
updateStartPrompt();
updateFade(); for (auto& player : players_) {
updateState(); player->update(deltaTime);
updateStartPrompt();
updatePlayers();
} }
Audio::update(); Audio::update();
} }
// Calcula el tiempo transcurrido desde el último frame
float Title::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Dibuja el objeto en pantalla // Dibuja el objeto en pantalla
void Title::render() { void Title::render() {
static auto* const SCREEN = Screen::get(); static auto* const SCREEN = Screen::get();
@@ -136,7 +143,7 @@ void Title::handleDebugColorKeys(SDL_Keycode key) {
adjustColorComponent(key, color_); adjustColorComponent(key, color_);
counter_ = 0; counter_time_ = 0.0f;
tiled_bg_->setColor(color_); tiled_bg_->setColor(color_);
printColorValue(color_); printColorValue(color_);
} }
@@ -270,7 +277,7 @@ void Title::handleStartButtonPress(const Options::Gamepad* controller) {
} }
auto Title::canProcessStartButton() const -> bool { auto Title::canProcessStartButton() const -> bool {
return (state_ != TitleState::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP); return (state_ != State::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP);
} }
void Title::processPlayer1Start() { void Title::processPlayer1Start() {
@@ -289,22 +296,26 @@ void Title::processPlayer2Start() {
void Title::activatePlayerAndSetState(Player::Id player_id) { void Title::activatePlayerAndSetState(Player::Id player_id) {
getPlayer(player_id)->setPlayingState(Player::State::TITLE_ANIMATION); getPlayer(player_id)->setPlayingState(Player::State::TITLE_ANIMATION);
setState(TitleState::START_HAS_BEEN_PRESSED); setState(State::START_HAS_BEEN_PRESSED);
counter_ = 0; counter_time_ = 0.0f;
} }
// Bucle para el titulo del juego // Bucle para el titulo del juego
void Title::run() { void Title::run() {
last_time_ = SDL_GetTicks();
while (Section::name == Section::Name::TITLE) { while (Section::name == Section::Name::TITLE) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
} }
// Reinicia el contador interno // Reinicia el contador interno
void Title::resetCounter() { counter_ = 0; } void Title::resetCounter() { counter_time_ = 0.0f; }
// Intercambia la asignación de mandos a los jugadores // Intercambia la asignación de mandos a los jugadores
void Title::swapControllers() { void Title::swapControllers() {
@@ -366,22 +377,22 @@ void Title::updateFade() {
} }
// Actualiza el estado // Actualiza el estado
void Title::updateState() { void Title::updateState(float deltaTime) {
game_logo_->update(deltaTime);
tiled_bg_->update(deltaTime);
// Establece la lógica según el estado // Establece la lógica según el estado
switch (state_) { switch (state_) {
case TitleState::LOGO_ANIMATING: { case State::LOGO_ANIMATING: {
game_logo_->update();
if (game_logo_->hasFinished()) { if (game_logo_->hasFinished()) {
setState(TitleState::LOGO_FINISHED); setState(State::LOGO_FINISHED);
} }
break; break;
} }
case TitleState::LOGO_FINISHED: { case State::LOGO_FINISHED: {
++counter_; // Incrementa el contador counter_time_ += deltaTime;
game_logo_->update(); // Actualiza el logo con el título del juego
tiled_bg_->update(); // Actualiza el mosaico de fondo
if (counter_ == param.title.title_duration) { if (counter_time_ >= param.title.title_duration) {
// El menu ha hecho time out // El menu ha hecho time out
fade_->setPostDuration(0); fade_->setPostDuration(0);
fade_->activate(); fade_->activate();
@@ -389,12 +400,10 @@ void Title::updateState() {
} }
break; break;
} }
case TitleState::START_HAS_BEEN_PRESSED: { case State::START_HAS_BEEN_PRESSED: {
++counter_; // Incrementa el contador counter_time_ += deltaTime;
game_logo_->update(); // Actualiza el logo con el título del juego
tiled_bg_->update(); // Actualiza el mosaico de fondo
if (counter_ == 100) { if (counter_time_ >= START_PRESSED_DELAY_MS) {
fade_->activate(); fade_->activate();
} }
break; break;
@@ -406,22 +415,16 @@ void Title::updateState() {
} }
void Title::updateStartPrompt() { void Title::updateStartPrompt() {
constexpr Uint32 LOGO_BLINK_PERIOD = 833; // milisegundos
constexpr Uint32 LOGO_BLINK_ON_TIME = 583; // 833 - 250
constexpr Uint32 START_BLINK_PERIOD = 167;
constexpr Uint32 START_BLINK_ON_TIME = 83; // 167 - 83
Uint32 time_ms = SDL_GetTicks(); Uint32 time_ms = SDL_GetTicks();
bool condition_met = false; bool condition_met = false;
switch (state_) { switch (state_) {
case TitleState::LOGO_FINISHED: case State::LOGO_FINISHED:
condition_met = (time_ms % LOGO_BLINK_PERIOD) >= (LOGO_BLINK_PERIOD - LOGO_BLINK_ON_TIME); condition_met = (time_ms % LOGO_BLINK_PERIOD_MS) >= (LOGO_BLINK_PERIOD_MS - LOGO_BLINK_ON_TIME_MS);
break; break;
case TitleState::START_HAS_BEEN_PRESSED: case State::START_HAS_BEEN_PRESSED:
condition_met = (time_ms % START_BLINK_PERIOD) >= (START_BLINK_PERIOD - START_BLINK_ON_TIME); condition_met = (time_ms % START_BLINK_PERIOD_MS) >= (START_BLINK_PERIOD_MS - START_BLINK_ON_TIME_MS);
break; break;
default: default:
@@ -445,7 +448,7 @@ void Title::renderStartPrompt() {
} }
void Title::renderCopyright() { void Title::renderCopyright() {
if (state_ != TitleState::LOGO_ANIMATING) { if (state_ != State::LOGO_ANIMATING) {
// Mini logo // Mini logo
mini_logo_sprite_->render(); mini_logo_sprite_->render();
@@ -462,20 +465,20 @@ void Title::renderCopyright() {
} }
// Cambia el estado // Cambia el estado
void Title::setState(TitleState state) { void Title::setState(State state) {
if (state_ == state) { if (state_ == state) {
return; return;
} }
state_ = state; state_ = state;
switch (state_) { switch (state_) {
case TitleState::LOGO_ANIMATING: case State::LOGO_ANIMATING:
break; break;
case TitleState::LOGO_FINISHED: case State::LOGO_FINISHED:
Audio::get()->playMusic("title.ogg"); Audio::get()->playMusic("title.ogg");
break; break;
case TitleState::START_HAS_BEEN_PRESSED: case State::START_HAS_BEEN_PRESSED:
Audio::get()->fadeOutMusic(1500); Audio::get()->fadeOutMusic(MUSIC_FADE_OUT_LONG_MS);
break; break;
} }
} }
@@ -544,13 +547,6 @@ void Title::initPlayers() {
} }
} }
// Actualza los jugadores
void Title::updatePlayers() {
for (auto& player : players_) {
player->update();
}
}
// Renderiza los jugadores // Renderiza los jugadores
void Title::renderPlayers() { void Title::renderPlayers() {
for (auto const& player : players_) { for (auto const& player : players_) {

View File

@@ -19,11 +19,22 @@ namespace Options {
struct Gamepad; struct Gamepad;
} // namespace Options } // namespace Options
// --- Constantes --- // --- Clase Title: pantalla de título y menú principal del juego ---
constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner"; // Texto de copyright //
constexpr bool ALLOW_TITLE_ANIMATION_SKIP = false; // Permite saltar la animación del título // Esta clase gestiona la pantalla de título del juego, incluyendo el menú principal
// y la transición entre diferentes modos de juego.
// --- Clase Title: gestiona el estado de título/menú principal del juego --- //
// Funcionalidades principales:
// • Logo animado: muestra y anima el logotipo principal del juego
// • Selección de jugadores: permite iniciar partidas de 1 o 2 jugadores
// • Modo attract: cicla automáticamente entre título y demo
// • Efectos visuales: parpadeos, transiciones y efectos de fondo
// • Gestión de controles: soporte para teclado y múltiples gamepads
// • Timeouts automáticos: transición automática si no hay interacción
// • Debug de colores: herramientas de depuración para ajustes visuales
//
// La clase utiliza un sistema de tiempo basado en milisegundos para garantizar
// comportamiento consistente independientemente del framerate.
class Title { class Title {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
@@ -34,8 +45,28 @@ class Title {
void run(); void run();
private: private:
// --- Constantes de tiempo (en milisegundos) ---
static constexpr float START_PRESSED_DELAY_MS = 1666.67f; // Tiempo antes de fade tras pulsar start (100 frames a 60fps)
static constexpr int MUSIC_FADE_OUT_LONG_MS = 1500; // Fade out largo de música
static constexpr int MUSIC_FADE_OUT_SHORT_MS = 300; // Fade out corto de música
// --- Constantes de parpadeo ---
static constexpr Uint32 LOGO_BLINK_PERIOD_MS = 833; // Período de parpadeo del logo
static constexpr Uint32 LOGO_BLINK_ON_TIME_MS = 583; // Tiempo encendido del logo (833-250)
static constexpr Uint32 START_BLINK_PERIOD_MS = 167; // Período de parpadeo del start
static constexpr Uint32 START_BLINK_ON_TIME_MS = 83; // Tiempo encendido del start (167-83)
// --- Constantes de layout ---
static constexpr int MINI_LOGO_Y_DIVISOR = 5; // Divisor para posición Y del mini logo
static constexpr int MINI_LOGO_Y_FACTOR = 4; // Factor para posición Y del mini logo
static constexpr int COPYRIGHT_TEXT_SPACING = 3; // Espaciado del texto de copyright
// --- Constantes de texto y configuración ---
static constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner"; // Texto de copyright
static constexpr bool ALLOW_TITLE_ANIMATION_SKIP = false; // Permite saltar la animación del título
// --- Enums --- // --- Enums ---
enum class TitleState { enum class State {
LOGO_ANIMATING, // El logo está animándose LOGO_ANIMATING, // El logo está animándose
LOGO_FINISHED, // El logo ha terminado de animarse LOGO_FINISHED, // El logo ha terminado de animarse
START_HAS_BEEN_PRESSED, // Se ha pulsado el botón de start START_HAS_BEEN_PRESSED, // Se ha pulsado el botón de start
@@ -59,19 +90,20 @@ class Title {
Anchor anchor_; // Anclas para definir la posición de los elementos del título Anchor anchor_; // Anclas para definir la posición de los elementos del título
Section::Name next_section_; // Siguiente sección a cargar Section::Name next_section_; // Siguiente sección a cargar
Section::Options selection_ = Section::Options::TITLE_TIME_OUT; // Opción elegida en el título Section::Options selection_ = Section::Options::TITLE_TIME_OUT; // Opción elegida en el título
TitleState state_; // Estado actual de la sección State state_; // Estado actual de la sección
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
int counter_ = 0; // Temporizador para la pantalla de título float counter_time_ = 0.0f; // Temporizador para la pantalla de título (en milisegundos)
int num_controllers_; // Número de mandos conectados int num_controllers_; // Número de mandos conectados
bool should_render_start_prompt_ = false; // Indica si se muestra el texto de PRESS START BUTTON TO PLAY bool should_render_start_prompt_ = false; // Indica si se muestra el texto de PRESS START BUTTON TO PLAY
bool player1_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 1 bool player1_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 1
bool player2_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 2 bool player2_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 2
// --- Ciclo de vida del título --- // --- Ciclo de vida del título ---
void update(); // Actualiza las variables del objeto void update(float deltaTime); // Actualiza las variables del objeto
void updateState(); // Actualiza el estado actual del título float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
void setState(TitleState state); // Cambia el estado del título void updateState(float deltaTime); // Actualiza el estado actual del título
void resetCounter(); // Reinicia el contador interno void setState(State state); // Cambia el estado del título
void resetCounter(); // Reinicia el contador interno
// --- Entrada de usuario --- // --- Entrada de usuario ---
void checkEvents(); // Comprueba los eventos void checkEvents(); // Comprueba los eventos

View File

@@ -2,12 +2,12 @@
#include "moving_sprite.h" // Para MovingSprite #include "moving_sprite.h" // Para MovingSprite
// Actualiza la posición y comprueba si ha llegado a su destino // Actualiza la posición y comprueba si ha llegado a su destino (time-based)
void SmartSprite::update() { void SmartSprite::update(float deltaTime) {
if (enabled_) { if (enabled_) {
MovingSprite::update(); MovingSprite::update(deltaTime);
checkMove(); checkMove();
checkFinished(); checkFinished(deltaTime);
} }
} }
@@ -71,16 +71,19 @@ void SmartSprite::checkMove() {
} }
} }
// Comprueba si ha terminado // Comprueba si ha terminado (time-based)
void SmartSprite::checkFinished() { void SmartSprite::checkFinished(float deltaTime) {
// Comprueba si ha llegado a su destino // Comprueba si ha llegado a su destino
on_destination_ = (getPosX() == dest_x_ && getPosY() == dest_y_); on_destination_ = (getPosX() == dest_x_ && getPosY() == dest_y_);
if (on_destination_) { if (on_destination_) {
if (finished_counter_ == 0) { if (finished_delay_ms_ == 0.0f) {
finished_ = true; finished_ = true;
} else { } else {
--finished_counter_; finished_timer_ += deltaTime;
if (finished_timer_ >= finished_delay_ms_) {
finished_ = true;
}
} }
} }
} }

View File

@@ -16,8 +16,8 @@ class SmartSprite : public AnimatedSprite {
~SmartSprite() override = default; ~SmartSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
void update() override; // Actualiza la posición y comprueba si ha llegado a su destino void update(float deltaTime) override; // Actualiza la posición y comprueba si ha llegado a su destino (time-based)
void render() override; // Dibuja el sprite void render() override; // Dibuja el sprite
// --- Getters --- // --- Getters ---
auto getDestX() const -> int { return dest_x_; } // Obtiene la posición de destino en X auto getDestX() const -> int { return dest_x_; } // Obtiene la posición de destino en X
@@ -26,21 +26,22 @@ class SmartSprite : public AnimatedSprite {
auto hasFinished() const -> bool { return finished_; } // Indica si ya ha terminado auto hasFinished() const -> bool { return finished_; } // Indica si ya ha terminado
// --- Setters --- // --- Setters ---
void setFinishedCounter(int value) { finished_counter_ = value; } // Establece el contador para deshabilitarlo void setFinishedDelay(float value) { finished_delay_ms_ = value; } // Establece el retraso para deshabilitarlo (ms)
void setDestX(int x) { dest_x_ = x; } // Establece la posición de destino en X void setDestX(int x) { dest_x_ = x; } // Establece la posición de destino en X
void setDestY(int y) { dest_y_ = y; } // Establece la posición de destino en Y void setDestY(int y) { dest_y_ = y; } // Establece la posición de destino en Y
void setEnabled(bool value) { enabled_ = value; } // Habilita o deshabilita el objeto void setEnabled(bool value) { enabled_ = value; } // Habilita o deshabilita el objeto
private: private:
// --- Variables de estado --- // --- Variables de estado ---
int dest_x_ = 0; // Posición de destino en el eje X int dest_x_ = 0; // Posición de destino en el eje X
int dest_y_ = 0; // Posición de destino en el eje Y int dest_y_ = 0; // Posición de destino en el eje Y
int finished_counter_ = 0; // Contador para deshabilitarlo float finished_delay_ms_ = 0.0f; // Retraso para deshabilitarlo (ms)
float finished_timer_ = 0.0f; // Timer acumulado (ms)
bool on_destination_ = false; // Indica si está en el destino bool on_destination_ = false; // Indica si está en el destino
bool finished_ = false; // Indica si ya ha terminado bool finished_ = false; // Indica si ya ha terminado
bool enabled_ = false; // Indica si el objeto está habilitado bool enabled_ = false; // Indica si el objeto está habilitado
// --- Métodos internos --- // --- Métodos internos ---
void checkFinished(); // Comprueba si ha terminado void checkFinished(float deltaTime); // Comprueba si ha terminado (time-based)
void checkMove(); // Comprueba el movimiento void checkMove(); // Comprueba el movimiento
}; };

View File

@@ -17,12 +17,12 @@ Tabe::Tabe()
: sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("tabe.png"), Resource::get()->getAnimation("tabe.ani"))), : sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("tabe.png"), Resource::get()->getAnimation("tabe.ani"))),
timer_(Timer(param.tabe.min_spawn_time, param.tabe.max_spawn_time)) {} timer_(Timer(param.tabe.min_spawn_time, param.tabe.max_spawn_time)) {}
// Actualiza la lógica // Actualiza la lógica (time-based)
void Tabe::update() { void Tabe::update(float deltaTime) {
if (enabled_ && !timer_.is_paused) { if (enabled_ && !timer_.is_paused) {
sprite_->update(); sprite_->update(deltaTime);
move(); move(deltaTime);
updateState(); updateState(deltaTime);
} }
timer_.update(); timer_.update();
@@ -38,11 +38,14 @@ void Tabe::render() {
} }
} }
// Mueve el objeto // Mueve el objeto (time-based)
void Tabe::move() { void Tabe::move(float deltaTime) {
// Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
float frameFactor = deltaTime / (1000.0f / 60.0f);
const int X = static_cast<int>(x_); const int X = static_cast<int>(x_);
speed_ += accel_; speed_ += accel_ * frameFactor;
x_ += speed_; x_ += speed_ * frameFactor;
fly_distance_ -= std::abs(X - static_cast<int>(x_)); fly_distance_ -= std::abs(X - static_cast<int>(x_));
// Comprueba si sale por los bordes // Comprueba si sale por los bordes
@@ -77,7 +80,8 @@ void Tabe::move() {
if (fly_distance_ <= 0) { if (fly_distance_ <= 0) {
if (waiting_counter_ > 0) { if (waiting_counter_ > 0) {
accel_ = speed_ = 0.0F; accel_ = speed_ = 0.0F;
--waiting_counter_; waiting_counter_ -= frameFactor;
if (waiting_counter_ < 0) waiting_counter_ = 0;
} else { } else {
constexpr int CHOICES = 4; constexpr int CHOICES = 4;
const std::array<Direction, CHOICES> LEFT = { const std::array<Direction, CHOICES> LEFT = {
@@ -175,11 +179,13 @@ void Tabe::setState(State state) {
} }
} }
// Actualiza el estado // Actualiza el estado (time-based)
void Tabe::updateState() { void Tabe::updateState(float deltaTime) {
if (state_ == State::HIT) { if (state_ == State::HIT) {
--hit_counter_; // Convertir deltaTime (milisegundos) a factor de frame (asumiendo 60fps)
if (hit_counter_ == 0) { float frameFactor = deltaTime / (1000.0f / 60.0f);
hit_counter_ -= frameFactor;
if (hit_counter_ <= 0) {
setState(State::FLY); setState(State::FLY);
} }
} }

View File

@@ -26,8 +26,8 @@ class Tabe {
~Tabe() = default; ~Tabe() = default;
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la lógica void update(float deltaTime); // Actualiza la lógica (time-based)
void render(); // Dibuja el objeto void render(); // Dibuja el objeto
void enable(); // Habilita el objeto void enable(); // Habilita el objeto
void setState(State state); // Establece el estado void setState(State state); // Establece el estado
auto tryToGetBonus() -> bool; // Intenta obtener el bonus auto tryToGetBonus() -> bool; // Intenta obtener el bonus
@@ -130,21 +130,21 @@ class Tabe {
float speed_ = 0.0F; // Velocidad de movimiento float speed_ = 0.0F; // Velocidad de movimiento
float accel_ = 0.0F; // Aceleración float accel_ = 0.0F; // Aceleración
int fly_distance_ = 0; // Distancia de vuelo int fly_distance_ = 0; // Distancia de vuelo
int waiting_counter_ = 0; // Tiempo que pasa quieto float waiting_counter_ = 0; // Tiempo que pasa quieto
bool enabled_ = false; // Indica si el objeto está activo bool enabled_ = false; // Indica si el objeto está activo
Direction direction_ = Direction::TO_THE_LEFT; // Dirección actual Direction direction_ = Direction::TO_THE_LEFT; // Dirección actual
Direction destiny_ = Direction::TO_THE_LEFT; // Destino Direction destiny_ = Direction::TO_THE_LEFT; // Destino
State state_ = State::FLY; // Estado actual State state_ = State::FLY; // Estado actual
int hit_counter_ = 0; // Contador para el estado HIT float hit_counter_ = 0; // Contador para el estado HIT
int number_of_hits_ = 0; // Cantidad de disparos recibidos int number_of_hits_ = 0; // Cantidad de disparos recibidos
bool has_bonus_ = true; // Indica si aún tiene el bonus para soltar bool has_bonus_ = true; // Indica si aún tiene el bonus para soltar
Timer timer_; // Temporizador para gestionar la aparición Timer timer_; // Temporizador para gestionar la aparición
// --- Métodos internos --- // --- Métodos internos ---
void move(); // Mueve el objeto void move(float deltaTime); // Mueve el objeto (time-based)
void shiftSprite() { sprite_->setPos(x_, y_); } // Actualiza la posición del sprite void shiftSprite() { sprite_->setPos(x_, y_); } // Actualiza la posición del sprite
void setRandomFlyPath(Direction direction, int length); // Establece un vuelo aleatorio void setRandomFlyPath(Direction direction, int length); // Establece un vuelo aleatorio
void updateState(); // Actualiza el estado void updateState(float deltaTime); // Actualiza el estado (time-based)
void updateTimer(); // Actualiza el temporizador void updateTimer(); // Actualiza el temporizador
void disable(); // Deshabilita el objeto void disable(); // Deshabilita el objeto
}; };

View File

@@ -3,7 +3,7 @@
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_DestroyTexture, SDL_FRect, SDL_GetRenderTarget, SDL_RenderTexture, SDL_PixelFormat, SDL_TextureAccess #include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_DestroyTexture, SDL_FRect, SDL_GetRenderTarget, SDL_RenderTexture, SDL_PixelFormat, SDL_TextureAccess
#include <algorithm> #include <algorithm>
#include <cmath> // Para sin #include <cmath> // Para sin, pow
#include <cstdlib> // Para rand #include <cstdlib> // Para rand
#include <memory> // Para allocator, unique_ptr, make_unique #include <memory> // Para allocator, unique_ptr, make_unique
#include <numbers> // Para pi #include <numbers> // Para pi
@@ -81,10 +81,10 @@ void TiledBG::render() {
SDL_RenderTexture(renderer_, canvas_, &window_, &pos_); SDL_RenderTexture(renderer_, canvas_, &window_, &pos_);
} }
// Actualiza la lógica de la clase // Actualiza la lógica de la clase (time-based)
void TiledBG::update() { void TiledBG::update(float delta_time) {
updateDesp(); updateDesp(delta_time);
updateStop(); updateStop(delta_time);
switch (mode_) { switch (mode_) {
case TiledBGMode::DIAGONAL: { case TiledBGMode::DIAGONAL: {
@@ -107,17 +107,20 @@ void TiledBG::update() {
} }
} }
// Detiene el desplazamiento de forma ordenada // Detiene el desplazamiento de forma ordenada (time-based)
void TiledBG::updateStop() { void TiledBG::updateStop(float delta_time) {
if (stopping_) { if (stopping_) {
const int UMBRAL = 20 * speed_; // Ajusta este valor según la precisión deseada const int UMBRAL = STOP_THRESHOLD_FACTOR * speed_;
// Desacelerar si estamos cerca de completar el ciclo (ventana a punto de regresar a 0) // Desacelerar si estamos cerca de completar el ciclo (ventana a punto de regresar a 0)
if (window_.x >= TILE_WIDTH - UMBRAL) { if (window_.x >= TILE_WIDTH - UMBRAL) {
speed_ /= 1.05F; // Reduce gradualmente la velocidad // Aplicar desaceleración time-based
float frame_rate = 60.0F;
float deceleration_per_ms = std::pow(DECELERATION_FACTOR, frame_rate * delta_time / 1000.0F);
speed_ /= deceleration_per_ms;
// Asegura que no baje demasiado // Asegura que no baje demasiado
speed_ = std::max(speed_, 0.1F); speed_ = std::max(speed_, MIN_SPEED);
} }
// Si estamos en 0, detener // Si estamos en 0, detener

View File

@@ -24,8 +24,8 @@ class TiledBG {
~TiledBG(); ~TiledBG();
// --- Métodos principales --- // --- Métodos principales ---
void render(); // Pinta la clase en pantalla void render(); // Pinta la clase en pantalla
void update(); // Actualiza la lógica de la clase void update(float delta_time); // Actualiza la lógica de la clase
// --- Configuración --- // --- Configuración ---
void setSpeed(float speed) { speed_ = speed; } // Establece la velocidad void setSpeed(float speed) { speed_ = speed; } // Establece la velocidad
@@ -37,8 +37,11 @@ class TiledBG {
private: private:
// --- Constantes --- // --- Constantes ---
static constexpr int TILE_WIDTH = 64; // Ancho del tile static constexpr int TILE_WIDTH = 64; // Ancho del tile
static constexpr int TILE_HEIGHT = 64; // Alto del tile static constexpr int TILE_HEIGHT = 64; // Alto del tile
static constexpr float STOP_THRESHOLD_FACTOR = 20.0f; // Factor para umbral de parada
static constexpr float DECELERATION_FACTOR = 1.05f; // Factor de desaceleración
static constexpr float MIN_SPEED = 0.1f; // Velocidad mínima
// --- Objetos y punteros --- // --- Objetos y punteros ---
SDL_Renderer *renderer_; // El renderizador de la ventana SDL_Renderer *renderer_; // El renderizador de la ventana
@@ -54,7 +57,7 @@ class TiledBG {
bool stopping_ = false; // Indica si se está deteniendo bool stopping_ = false; // Indica si se está deteniendo
// --- Métodos internos --- // --- Métodos internos ---
void fillTexture(); // Rellena la textura con el contenido void fillTexture(); // Rellena la textura con el contenido
void updateDesp() { desp_ += speed_; } // Actualiza el desplazamiento void updateDesp(float delta_time) { desp_ += speed_ * delta_time; } // Actualiza el desplazamiento (time-based)
void updateStop(); // Detiene el desplazamiento de forma ordenada void updateStop(float delta_time); // Detiene el desplazamiento de forma ordenada (time-based)
}; };

View File

@@ -2,16 +2,15 @@
#include "text.h" // Para Text #include "text.h" // Para Text
// Actualiza el objeto // Actualiza el objeto (delta_time en ms)
void Writer::update() { void Writer::update(float delta_time) {
if (enabled_) { if (enabled_) {
if (!completed_) { if (!completed_) {
// No completado // No completado
if (writing_counter_ > 0) { writing_timer_ += delta_time;
writing_counter_--; if (writing_timer_ >= speed_interval_) {
} else {
index_++; index_++;
writing_counter_ = speed_; writing_timer_ = 0.0f;
} }
if (index_ == length_) { if (index_ == length_) {
@@ -19,14 +18,18 @@ void Writer::update() {
} }
} else { } else {
// Completado // Completado
finished_ = enabled_counter_ <= 0; enabled_timer_ += delta_time;
if (!finished_) { finished_ = enabled_timer_ >= enabled_timer_target_;
enabled_counter_--;
}
} }
} }
} }
// Actualiza el objeto (delta_time en segundos)
void Writer::updateS(float delta_time) {
// Convierte segundos a milisegundos y usa la lógica normal
update(delta_time * 1000.0f);
}
// Dibuja el objeto en pantalla // Dibuja el objeto en pantalla
void Writer::render() const { void Writer::render() const {
if (enabled_) { if (enabled_) {
@@ -55,10 +58,19 @@ void Writer::setCaption(const std::string &text) {
length_ = text.length(); length_ = text.length();
} }
// Establece el valor de la variable // Establece el valor de la variable (frames)
void Writer::setSpeed(int value) { void Writer::setSpeed(int value) {
speed_ = value; // Convierte frames a milisegundos (frames * 16.67ms)
writing_counter_ = value; constexpr float FRAME_TIME_MS = 16.67f;
speed_interval_ = static_cast<float>(value) * FRAME_TIME_MS;
writing_timer_ = 0.0f;
}
// Establece la velocidad en segundos entre caracteres
void Writer::setSpeedS(float value) {
// Convierte segundos a milisegundos para consistencia interna
speed_interval_ = value * 1000.0f;
writing_timer_ = 0.0f;
} }
// Establece el valor de la variable // Establece el valor de la variable
@@ -71,9 +83,16 @@ auto Writer::isEnabled() const -> bool {
return enabled_; return enabled_;
} }
// Establece el valor de la variable // Establece el temporizador para deshabilitar el objeto (en milisegundos)
void Writer::setFinishedCounter(int time) { void Writer::setFinishedTimerMs(float time_ms) {
enabled_counter_ = time; enabled_timer_target_ = time_ms;
enabled_timer_ = 0.0f;
}
// Establece el temporizador para deshabilitar el objeto (en segundos)
void Writer::setFinishedTimerS(float time_s) {
enabled_timer_target_ = time_s * 1000.0f; // Convertir segundos a milisegundos
enabled_timer_ = 0.0f;
} }
// Centra la cadena de texto a un punto X // Centra la cadena de texto a un punto X

View File

@@ -15,17 +15,20 @@ class Writer {
~Writer() = default; ~Writer() = default;
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza el objeto void update(float delta_time); // Actualiza el objeto (delta_time en ms)
void render() const; // Dibuja el objeto en pantalla void updateS(float delta_time); // Actualiza el objeto (delta_time en segundos)
void render() const; // Dibuja el objeto en pantalla
// --- Setters --- // --- Setters ---
void setPosX(int value); // Establece la posición X void setPosX(int value); // Establece la posición X
void setPosY(int value); // Establece la posición Y void setPosY(int value); // Establece la posición Y
void setKerning(int value); // Establece el kerning (espaciado entre caracteres) void setKerning(int value); // Establece el kerning (espaciado entre caracteres)
void setCaption(const std::string &text); // Establece el texto a escribir void setCaption(const std::string &text); // Establece el texto a escribir
void setSpeed(int value); // Establece la velocidad de escritura void setSpeed(int value); // Establece la velocidad de escritura (frames)
void setSpeedS(float value); // Establece la velocidad de escritura (segundos entre caracteres)
void setEnabled(bool value); // Habilita o deshabilita el objeto void setEnabled(bool value); // Habilita o deshabilita el objeto
void setFinishedCounter(int time); // Establece el temporizador para deshabilitar el objeto void setFinishedTimerMs(float time_ms); // Establece el temporizador para deshabilitar el objeto (en ms)
void setFinishedTimerS(float time_s); // Establece el temporizador para deshabilitar el objeto (en segundos)
void center(int x); // Centra la cadena de texto a un punto X void center(int x); // Centra la cadena de texto a un punto X
@@ -38,16 +41,17 @@ class Writer {
std::shared_ptr<Text> text_; // Objeto encargado de escribir el texto std::shared_ptr<Text> text_; // Objeto encargado de escribir el texto
// --- Variables de estado --- // --- Variables de estado ---
std::string caption_; // El texto para escribir std::string caption_; // El texto para escribir
int pos_x_ = 0; // Posición en el eje X donde empezar a escribir el texto int pos_x_ = 0; // Posición en el eje X donde empezar a escribir el texto
int pos_y_ = 0; // Posición en el eje Y donde empezar a escribir el texto int pos_y_ = 0; // Posición en el eje Y donde empezar a escribir el texto
int kerning_ = 0; // Kerning del texto, es decir, espaciado entre caracteres int kerning_ = 0; // Kerning del texto, es decir, espaciado entre caracteres
int speed_ = 0; // Velocidad de escritura float speed_interval_ = 0.0f; // Intervalo entre caracteres (ms para compatibilidad)
int writing_counter_ = 0; // Temporizador de escritura para cada caracter float writing_timer_ = 0.0f; // Temporizador de escritura para cada caracter
int index_ = 0; // Posición del texto que se está escribiendo int index_ = 0; // Posición del texto que se está escribiendo
int length_ = 0; // Longitud de la cadena a escribir int length_ = 0; // Longitud de la cadena a escribir
int enabled_counter_ = 0; // Temporizador para deshabilitar el objeto float enabled_timer_ = 0.0f; // Temporizador para deshabilitar el objeto
bool completed_ = false; // Indica si se ha escrito todo el texto float enabled_timer_target_ = 0.0f; // Tiempo objetivo para deshabilitar el objeto
bool enabled_ = false; // Indica si el objeto está habilitado bool completed_ = false; // Indica si se ha escrito todo el texto
bool finished_ = false; // Indica si ya ha terminado bool enabled_ = false; // Indica si el objeto está habilitado
bool finished_ = false; // Indica si ya ha terminado
}; };