Compare commits

23 Commits

Author SHA1 Message Date
2819b3628e el fitxer de parametres es guarda en options/config.txt 2025-08-17 21:42:26 +02:00
1e9e664012 creat param_red.txt
afegides guardes en setParams
2025-08-17 21:29:49 +02:00
0c8b39cee7 els items ja no tenen vel_x = 0 2025-08-17 20:42:55 +02:00
d7b3af5ab8 en el modo demo asignava cafes al jugador que no jugava i eixia saludant amb la camisa que no toca 2025-08-17 20:22:53 +02:00
5e5227305f arreglat bug en el estat pre del fade 2025-08-17 20:17:55 +02:00
3fc15a9512 afegit el fade RANDOM_SQAURE2
canviat els timings del fade a milisegons
2025-08-17 19:34:48 +02:00
e774e0e8ad modificat el efecte de invulnerabilitat per a que vaja menguant 2025-08-17 17:21:03 +02:00
a95776e6c7 retocat el audio de recover 2025-08-17 17:01:49 +02:00
0428ff26d5 options.h ara gasta defaults.h 2025-08-17 16:28:30 +02:00
fc3e2deb1f Item fix: alguns items es quedaven engantxats en la part de dalt 2025-08-17 16:18:48 +02:00
65ca17f938 afegit i parametritzat outline per als textos dels items 2025-08-17 16:07:16 +02:00
ff2a51a507 noves coses chules en la clase text 2025-08-17 14:25:17 +02:00
fe0083abd4 afagit a param el color de la camiseta per defecte 2025-08-17 13:25:10 +02:00
1c058694fd afegit a param el color de outline dels jugadors 2025-08-17 12:58:20 +02:00
cb0c3266d5 proposta 1 de versio RC2 2025-08-17 10:54:45 +02:00
8ddc5d94f1 clang-tidy 2025-08-17 10:20:41 +02:00
b359a73d50 He tornat a deixar el progres del fondo que fa tope en la ultima fase, no en el final del joc 2025-08-17 08:20:38 +02:00
142603db71 hi_score_table: faltava posar el fondo en modo manual
game: redefinides les prioritats de dibuixat -> jugadors altra volta al front
2025-08-17 08:08:48 +02:00
1ec272f017 . 2025-08-17 01:10:57 +02:00
327987447d passant linters a vore si trobe variables sense inicialitzar 2025-08-17 00:23:59 +02:00
ada5025c65 claude: arreglos d'estil 2025-08-16 19:48:32 +02:00
1ced698093 ja guarda la configuració del mando amb els boto-triggers 2025-08-16 18:13:55 +02:00
2a4c47a6ca ja permet mapejar botons tipo trigger 2025-08-16 18:03:32 +02:00
118 changed files with 3339 additions and 2457 deletions

View File

@@ -6,7 +6,14 @@ BreakBeforeBraces: Attach # Llaves en la misma línea
AllowShortIfStatementsOnASingleLine: true AllowShortIfStatementsOnASingleLine: true
AllowShortBlocksOnASingleLine: true AllowShortBlocksOnASingleLine: true
AllowShortFunctionsOnASingleLine: All AllowShortFunctionsOnASingleLine: All
AlignOperands: false AlignOperands: DontAlign
AlignAfterOpenBracket: DontAlign AlignAfterOpenBracket: DontAlign
BinPackArguments: false BinPackArguments: false
BinPackParameters: false BinPackParameters: false
ContinuationIndentWidth: 4
ConstructorInitializerIndentWidth: 4
IndentWrappedFunctionNames: false
Cpp11BracedListStyle: true
BreakConstructorInitializers: BeforeComma
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false

View File

@@ -1,8 +1,21 @@
Checks: > Checks: >
readability-*, readability-*,
modernize-*, modernize-*,
performance-*,
bugprone-unchecked-optional-access,
bugprone-sizeof-expression,
bugprone-suspicious-missing-comma,
bugprone-suspicious-index,
bugprone-undefined-memory-manipulation,
bugprone-use-after-move,
bugprone-out-of-bound-access,
-readability-identifier-length, -readability-identifier-length,
-readability-magic-numbers -readability-magic-numbers,
-bugprone-narrowing-conversions,
-performance-enum-size,
-performance-inefficient-string-concatenation,
-bugprone-integer-division,
-bugprone-easily-swappable-parameters,
WarningsAsErrors: '*' WarningsAsErrors: '*'
# Solo incluir archivos de tu código fuente # Solo incluir archivos de tu código fuente

View File

@@ -13,6 +13,7 @@ DATA|${PREFIX}/data/config/formations.txt
DATA|${PREFIX}/data/config/gamecontrollerdb.txt DATA|${PREFIX}/data/config/gamecontrollerdb.txt
DATA|${PREFIX}/data/config/param_320x240.txt DATA|${PREFIX}/data/config/param_320x240.txt
DATA|${PREFIX}/data/config/param_320x256.txt DATA|${PREFIX}/data/config/param_320x256.txt
DATA|${PREFIX}/data/config/param_red.txt
DATA|${PREFIX}/data/config/pools.txt DATA|${PREFIX}/data/config/pools.txt
DATA|${PREFIX}/data/config/stages.txt DATA|${PREFIX}/data/config/stages.txt
DEMODATA|${PREFIX}/data/config/demo1.bin DEMODATA|${PREFIX}/data/config/demo1.bin
@@ -56,12 +57,12 @@ SOUND|${PREFIX}/data/sound/tabe_hit.wav
SOUND|${PREFIX}/data/sound/tabe.wav SOUND|${PREFIX}/data/sound/tabe.wav
SOUND|${PREFIX}/data/sound/title.wav SOUND|${PREFIX}/data/sound/title.wav
SOUND|${PREFIX}/data/sound/voice_aw_aw_aw.wav SOUND|${PREFIX}/data/sound/voice_aw_aw_aw.wav
SOUND|${PREFIX}/data/sound/voice_brbrbr.wav
SOUND|${PREFIX}/data/sound/voice_coffee.wav SOUND|${PREFIX}/data/sound/voice_coffee.wav
SOUND|${PREFIX}/data/sound/voice_credit_thankyou.wav SOUND|${PREFIX}/data/sound/voice_credit_thankyou.wav
SOUND|${PREFIX}/data/sound/voice_get_ready.wav SOUND|${PREFIX}/data/sound/voice_get_ready.wav
SOUND|${PREFIX}/data/sound/voice_no.wav SOUND|${PREFIX}/data/sound/voice_no.wav
SOUND|${PREFIX}/data/sound/voice_power_up.wav SOUND|${PREFIX}/data/sound/voice_power_up.wav
SOUND|${PREFIX}/data/sound/voice_recover.wav
SOUND|${PREFIX}/data/sound/voice_thankyou.wav SOUND|${PREFIX}/data/sound/voice_thankyou.wav
SOUND|${PREFIX}/data/sound/walk.wav SOUND|${PREFIX}/data/sound/walk.wav
@@ -177,6 +178,7 @@ BITMAP|${PREFIX}/data/font/04b_25_grey.png
BITMAP|${PREFIX}/data/font/04b_25_metal.png BITMAP|${PREFIX}/data/font/04b_25_metal.png
BITMAP|${PREFIX}/data/font/04b_25_reversed_2x.png BITMAP|${PREFIX}/data/font/04b_25_reversed_2x.png
BITMAP|${PREFIX}/data/font/04b_25_reversed.png BITMAP|${PREFIX}/data/font/04b_25_reversed.png
BITMAP|${PREFIX}/data/font/04b_25_white.png
BITMAP|${PREFIX}/data/font/04b_25.png BITMAP|${PREFIX}/data/font/04b_25.png
BITMAP|${PREFIX}/data/font/8bithud.png BITMAP|${PREFIX}/data/font/8bithud.png
BITMAP|${PREFIX}/data/font/aseprite.png BITMAP|${PREFIX}/data/font/aseprite.png

View File

@@ -2,26 +2,26 @@
# Formato: PARAMETRO VALOR # Formato: PARAMETRO VALOR
# --- GAME --- # --- GAME ---
game.item_size 20 # Tamaño de los items del juego (en píxeles) game.item_size 20 # Tamaño de los items del juego (en píxeles)
game.width 320 # Ancho de la resolución nativa del juego (en píxeles) game.item_text_outline_color E0E0E0F0 # Color del outline del texto de los items (RGBA hex)
game.height 240 # Alto de la resolución nativa del juego (en píxeles) game.width 320 # Ancho de la resolución nativa del juego (en píxeles)
game.play_area.rect.x 0 # Posición X de la zona jugable game.height 240 # Alto de la resolución nativa del juego (en píxeles)
game.play_area.rect.y 0 # Posición Y de la zona jugable game.play_area.rect.x 0 # Posición X de la zona jugable
game.play_area.rect.w 320 # Ancho de la zona jugable game.play_area.rect.y 0 # Posición Y de la zona jugable
game.play_area.rect.h 200 # Alto de la zona jugable game.play_area.rect.w 320 # Ancho 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.play_area.rect.h 200 # Alto de la zona jugable
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop 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
# --- FADE --- # --- FADE ---
fade.color 1F2B30 # Color hexadecimal para el efecto de fundido fade.color 1F2B30 # Color hexadecimal para el efecto de fundido
fade.num_squares_width 160 # Número de cuadrados en el eje X para el fundido fade.num_squares_width 64 # Número de cuadrados en el eje X para el fundido
fade.num_squares_height 120 # Número de cuadrados en el eje Y para el fundido fade.num_squares_height 48 # Número de cuadrados en el eje Y para el fundido
fade.random_squares_delay 1 # Delay entre aparición de cuadrados aleatorios (frames) fade.random_squares_duration_ms 1200 # Duración del fade en milisegundos
fade.random_squares_mult 500 # Multiplicador para la velocidad de aparición aleatoria fade.post_duration_ms 500 # Duración tras el fundido en milisegundos
fade.post_duration 80 # Duración tras el fundido (frames) fade.venetian_size 12 # Tamaño de las bandas para el efecto veneciano (en píxeles)
fade.venetian_size 12 # Tamaño de las bandas para el efecto veneciano (en píxeles)
# --- SCOREBOARD --- # --- SCOREBOARD ---
scoreboard.rect.x 0 # Posición X del marcador scoreboard.rect.x 0 # Posición X del marcador
@@ -84,8 +84,8 @@ service_menu.window_message.title_color 6496C8FF # Color del tít
service_menu.window_message.text_color DCDCDCFF # Color del texto en ventanas de mensaje (RGBA hexadecimal) service_menu.window_message.text_color DCDCDCFF # Color del texto en ventanas de mensaje (RGBA hexadecimal)
service_menu.window_message.padding 15.0f # Espaciado interno de ventanas de mensaje (píxeles) service_menu.window_message.padding 15.0f # Espaciado interno de ventanas de mensaje (píxeles)
service_menu.window_message.line_spacing 5.0f # Espaciado entre líneas de texto (píxeles) service_menu.window_message.line_spacing 5.0f # Espaciado entre líneas de texto (píxeles)
service_menu.window_message.title_separator_spacing 10.0f # Espaciado entre título y contenido (píxeles) service_menu.window_message.title_separator_spacing 20.0f # Espaciado entre título y contenido (píxeles)
service_menu.window_message.min_width 250.0f # Ancho mínimo de ventanas de mensaje (píxeles) service_menu.window_message.min_width 200.0f # Ancho mínimo de ventanas de mensaje (píxeles)
service_menu.window_message.min_height 32.0f # Alto mínimo de ventanas de mensaje (píxeles) service_menu.window_message.min_height 32.0f # Alto mínimo de ventanas de mensaje (píxeles)
service_menu.window_message.max_width_ratio 0.8f # Ratio máximo de ancho respecto a pantalla (0.0-1.0) service_menu.window_message.max_width_ratio 0.8f # Ratio máximo de ancho respecto a pantalla (0.0-1.0)
service_menu.window_message.max_height_ratio 0.8f # Ratio máximo de alto respecto a pantalla (0.0-1.0) service_menu.window_message.max_height_ratio 0.8f # Ratio máximo de alto respecto a pantalla (0.0-1.0)
@@ -110,6 +110,18 @@ tabe.min_spawn_time 2.0f # Tiempo mínimo en minutos para que aparezca el Tabe
tabe.max_spawn_time 3.0f # Tiempo máximo en minutos para que aparezca el Tabe tabe.max_spawn_time 3.0f # Tiempo máximo en minutos para que aparezca el Tabe
# --- PLAYER --- # --- PLAYER ---
# Jugador 1 - Camiseta por defecto
player.default_shirt[0].darkest 028ECFFF # Tono más oscuro - bordes y contornos (Jugador 1, por defecto)
player.default_shirt[0].dark 0297DBFF # Tono oscuro - sombras (Jugador 1, por defecto)
player.default_shirt[0].base 029FE8FF # Tono principal - color base (Jugador 1, por defecto)
player.default_shirt[0].light 03A9F4FF # Tono claro - zonas iluminadas (Jugador 1, por defecto)
# Jugador 2 - Camiseta por defecto
player.default_shirt[1].darkest 8E8E8EFF # Tono más oscuro - bordes y contornos (Jugador 2, por defecto)
player.default_shirt[1].dark AEADADFF # Tono oscuro - sombras (Jugador 2, por defecto)
player.default_shirt[1].base E4E4E4FF # Tono principal - color base (Jugador 2, por defecto)
player.default_shirt[1].light F7F1F1FF # Tono claro - zonas iluminadas (Jugador 2, por defecto)
# Jugador 1 - Camiseta con 1 café # Jugador 1 - Camiseta con 1 café
player.one_coffee_shirt[0].darkest 3D9C70FF # Tono más oscuro - bordes y contornos (Jugador 1, 1 café) player.one_coffee_shirt[0].darkest 3D9C70FF # Tono más oscuro - bordes y contornos (Jugador 1, 1 café)
player.one_coffee_shirt[0].dark 4FA370FF # Tono oscuro - sombras (Jugador 1, 1 café) player.one_coffee_shirt[0].dark 4FA370FF # Tono oscuro - sombras (Jugador 1, 1 café)
@@ -132,4 +144,8 @@ player.one_coffee_shirt[1].light 55EF8DFF # Tono claro - zonas iluminadas
player.two_coffee_shirt[1].darkest E08500FF # Tono más oscuro - bordes y contornos (Jugador 2, 2 cafés) player.two_coffee_shirt[1].darkest E08500FF # Tono más oscuro - bordes y contornos (Jugador 2, 2 cafés)
player.two_coffee_shirt[1].dark FA7D00FF # Tono oscuro - sombras (Jugador 2, 2 cafés) player.two_coffee_shirt[1].dark FA7D00FF # Tono oscuro - sombras (Jugador 2, 2 cafés)
player.two_coffee_shirt[1].base FAA200FF # Tono principal - color base (Jugador 2, 2 cafés) player.two_coffee_shirt[1].base FAA200FF # Tono principal - color base (Jugador 2, 2 cafés)
player.two_coffee_shirt[1].light FA8500FF # Tono claro - zonas iluminadas (Jugador 2, 2 cafés) player.two_coffee_shirt[1].light FA8500FF # Tono claro - zonas iluminadas (Jugador 2, 2 cafés)
# Colores de contorno de los jugadores
player.outline_color[0] 66323FFF # Color del contorno del sprite del Jugador 1
player.outline_color[1] 422028FF # Color del contorno del sprite del Jugador 2

View File

@@ -2,26 +2,26 @@
# Formato: PARAMETRO VALOR # Formato: PARAMETRO VALOR
# --- GAME --- # --- GAME ---
game.item_size 20 # Tamaño de los items del juego (en píxeles) game.item_size 20 # Tamaño de los items del juego (en píxeles)
game.width 320 # Ancho de la resolución nativa del juego (en píxeles) game.item_text_outline_color E0E0E0F0 # Color del outline del texto de los items (RGBA hex)
game.height 256 # Alto de la resolución nativa del juego (en píxeles) game.width 320 # Ancho de la resolución nativa del juego (en píxeles)
game.play_area.rect.x 0 # Posición X de la zona jugable game.height 256 # Alto de la resolución nativa del juego (en píxeles)
game.play_area.rect.y 0 # Posición Y de la zona jugable game.play_area.rect.x 0 # Posición X de la zona jugable
game.play_area.rect.w 320 # Ancho de la zona jugable game.play_area.rect.y 0 # Posición Y de la zona jugable
game.play_area.rect.h 216 # Alto de la zona jugable game.play_area.rect.w 320 # Ancho 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.play_area.rect.h 216 # Alto de la zona jugable
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop 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
# --- FADE --- # --- FADE ---
fade.color 1F2B30 # Color hexadecimal para el efecto de fundido fade.color 1F2B30 # Color hexadecimal para el efecto de fundido
fade.num_squares_width 160 # Número de cuadrados en el eje X para el fundido fade.num_squares_width 64 # Número de cuadrados en el eje X para el fundido
fade.num_squares_height 128 # Número de cuadrados en el eje Y para el fundido fade.num_squares_height 48 # Número de cuadrados en el eje Y para el fundido
fade.random_squares_delay 1 # Delay entre aparición de cuadrados aleatorios (frames) fade.random_squares_duration_ms 1200 # Duración del fade en milisegundos
fade.random_squares_mult 500 # Multiplicador para la velocidad de aparición aleatoria fade.post_duration_ms 500 # Duración tras el fundido en milisegundos
fade.post_duration 80 # Duración tras el fundido (frames) fade.venetian_size 12 # Tamaño de las bandas para el efecto veneciano (en píxeles)
fade.venetian_size 12 # Tamaño de las bandas para el efecto veneciano (en píxeles)
# --- SCOREBOARD --- # --- SCOREBOARD ---
scoreboard.rect.x 0 # Posición X del marcador scoreboard.rect.x 0 # Posición X del marcador
@@ -84,8 +84,8 @@ service_menu.window_message.title_color 6496C8FF # Color del tít
service_menu.window_message.text_color DCDCDCFF # Color del texto en ventanas de mensaje (RGBA hexadecimal) service_menu.window_message.text_color DCDCDCFF # Color del texto en ventanas de mensaje (RGBA hexadecimal)
service_menu.window_message.padding 15.0f # Espaciado interno de ventanas de mensaje (píxeles) service_menu.window_message.padding 15.0f # Espaciado interno de ventanas de mensaje (píxeles)
service_menu.window_message.line_spacing 5.0f # Espaciado entre líneas de texto (píxeles) service_menu.window_message.line_spacing 5.0f # Espaciado entre líneas de texto (píxeles)
service_menu.window_message.title_separator_spacing 10.0f # Espaciado entre título y contenido (píxeles) service_menu.window_message.title_separator_spacing 20.0f # Espaciado entre título y contenido (píxeles)
service_menu.window_message.min_width 250.0f # Ancho mínimo de ventanas de mensaje (píxeles) service_menu.window_message.min_width 200.0f # Ancho mínimo de ventanas de mensaje (píxeles)
service_menu.window_message.min_height 32.0f # Alto mínimo de ventanas de mensaje (píxeles) service_menu.window_message.min_height 32.0f # Alto mínimo de ventanas de mensaje (píxeles)
service_menu.window_message.max_width_ratio 0.8f # Ratio máximo de ancho respecto a pantalla (0.0-1.0) service_menu.window_message.max_width_ratio 0.8f # Ratio máximo de ancho respecto a pantalla (0.0-1.0)
service_menu.window_message.max_height_ratio 0.8f # Ratio máximo de alto respecto a pantalla (0.0-1.0) service_menu.window_message.max_height_ratio 0.8f # Ratio máximo de alto respecto a pantalla (0.0-1.0)
@@ -109,6 +109,18 @@ tabe.min_spawn_time 2.0f # Tiempo mínimo en segundos para que aparezca el Ta
tabe.max_spawn_time 3.0f # Tiempo máximo en segundos para que aparezca el Tabe tabe.max_spawn_time 3.0f # Tiempo máximo en segundos para que aparezca el Tabe
# --- PLAYER --- # --- PLAYER ---
# Jugador 1 - Camiseta por defecto
player.default_shirt[0].darkest 028ECFFF # Tono más oscuro - bordes y contornos (Jugador 1, por defecto)
player.default_shirt[0].dark 0297DBFF # Tono oscuro - sombras (Jugador 1, por defecto)
player.default_shirt[0].base 029FE8FF # Tono principal - color base (Jugador 1, por defecto)
player.default_shirt[0].light 03A9F4FF # Tono claro - zonas iluminadas (Jugador 1, por defecto)
# Jugador 2 - Camiseta por defecto
player.default_shirt[1].darkest 8E8E8EFF # Tono más oscuro - bordes y contornos (Jugador 2, por defecto)
player.default_shirt[1].dark AEADADFF # Tono oscuro - sombras (Jugador 2, por defecto)
player.default_shirt[1].base E4E4E4FF # Tono principal - color base (Jugador 2, por defecto)
player.default_shirt[1].light F7F1F1FF # Tono claro - zonas iluminadas (Jugador 2, por defecto)
# Jugador 1 - Camiseta con 1 café # Jugador 1 - Camiseta con 1 café
player.one_coffee_shirt[0].darkest 3D9C70FF # Tono más oscuro - bordes y contornos (Jugador 1, 1 café) player.one_coffee_shirt[0].darkest 3D9C70FF # Tono más oscuro - bordes y contornos (Jugador 1, 1 café)
player.one_coffee_shirt[0].dark 4FA370FF # Tono oscuro - sombras (Jugador 1, 1 café) player.one_coffee_shirt[0].dark 4FA370FF # Tono oscuro - sombras (Jugador 1, 1 café)
@@ -131,4 +143,8 @@ player.one_coffee_shirt[1].light 55EF8DFF # Tono claro - zonas iluminadas
player.two_coffee_shirt[1].darkest E08500FF # Tono más oscuro - bordes y contornos (Jugador 2, 2 cafés) player.two_coffee_shirt[1].darkest E08500FF # Tono más oscuro - bordes y contornos (Jugador 2, 2 cafés)
player.two_coffee_shirt[1].dark FA7D00FF # Tono oscuro - sombras (Jugador 2, 2 cafés) player.two_coffee_shirt[1].dark FA7D00FF # Tono oscuro - sombras (Jugador 2, 2 cafés)
player.two_coffee_shirt[1].base FAA200FF # Tono principal - color base (Jugador 2, 2 cafés) player.two_coffee_shirt[1].base FAA200FF # Tono principal - color base (Jugador 2, 2 cafés)
player.two_coffee_shirt[1].light FA8500FF # Tono claro - zonas iluminadas (Jugador 2, 2 cafés) player.two_coffee_shirt[1].light FA8500FF # Tono claro - zonas iluminadas (Jugador 2, 2 cafés)
# Colores de contorno de los jugadores
player.outline_color[0] 66323FFF # Color del contorno del sprite del Jugador 1
player.outline_color[1] 422028FF # Color del contorno del sprite del Jugador 2

150
data/config/param_red.txt Normal file
View File

@@ -0,0 +1,150 @@
# Coffee Crisis Arcade Edition - Fichero de parametros - RED THEME
# Formato: PARAMETRO VALOR
# --- GAME ---
game.item_size 20 # Tamaño de los items del juego (en píxeles)
game.item_text_outline_color FFB8B8F0 # Color del outline del texto de los items (RGBA hex) - Rojo claro
game.width 320 # Ancho de la resolución nativa del juego (en píxeles)
game.height 256 # Alto de la resolución nativa del juego (en píxeles)
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.w 320 # Ancho 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_total_time 60 # 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_ms 500 # Cantidad de milisegundos que dura el hit_stop
# --- FADE ---
fade.color 5C1F1F # Color hexadecimal para el efecto de fundido - Rojo oscuro
fade.num_squares_width 64 # Número de cuadrados en el eje X para el fundido
fade.num_squares_height 48 # Número de cuadrados en el eje Y para el fundido
fade.random_squares_duration_ms 1200 # Duración del fade en milisegundos
fade.post_duration_ms 500 # Duración tras el fundido en milisegundos
fade.venetian_size 12 # Tamaño de las bandas para el efecto veneciano (en píxeles)
# --- SCOREBOARD ---
scoreboard.rect.x 0 # Posición X del marcador
scoreboard.rect.y 216 # Posición Y del marcador
scoreboard.rect.w 320 # Ancho del marcador
scoreboard.rect.h 40 # Alto del marcador
scoreboard.separator_autocolor true # ¿El separador usa color automático?
scoreboard.separator_color 2B0D0D # Color del separador - Rojo muy oscuro
scoreboard.easy_color 8B4A2F # Color para la dificultad fácil - Marrón rojizo
scoreboard.normal_color 6B2F2F # Color para la dificultad normal - Rojo medio
scoreboard.hard_color A73030 # Color para la dificultad difícil - Rojo fuerte
scoreboard.text_autocolor true # ¿El texto usa color automático?
scoreboard.text_color1 FFE6E6 # Color principal del texto del marcador - Blanco rosado
scoreboard.text_color2 FFE6E6 # Color secundario del texto del marcador - Blanco rosado
scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás (segundos)
# --- TITLE ---
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.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.bg_color 8B4A3A # Color de fondo en la sección titulo - Marrón rojizo
# --- BACKGROUND ---
background.attenuate_color FF4A3A40 # Color de atenuación del fondo (RGBA hexadecimal) - Blanco rosado
# --- BALLOONS ---
balloon.settings[0].vel 2.75f # Velocidad inicial del globo 1
balloon.settings[0].grav 0.09f # Gravedad aplicada al globo 1
balloon.settings[1].vel 3.70f # Velocidad inicial del globo 2
balloon.settings[1].grav 0.10f # Gravedad aplicada al globo 2
balloon.settings[2].vel 4.70f # Velocidad inicial del globo 3
balloon.settings[2].grav 0.10f # Gravedad aplicada al globo 3
balloon.settings[3].vel 5.45f # Velocidad inicial del globo 4
balloon.settings[3].grav 0.10f # Gravedad aplicada al globo 4
balloon.color[0] orange # Color de creación del globo normal
balloon.color[1] red # Color del globo normal
balloon.color[2] blue # Color de creación del globo que rebota
balloon.color[3] green # Color del globo que rebota
balloon.bouncing_sound false # Indica si los globos hacer sonido al rebotar
# --- NOTIFICATION ---
notification.pos_v TOP # Posición vertical de la notificación (TOP/BOTTOM)
notification.pos_h LEFT # Posición horizontal de la notificación (LEFT/RIGHT)
notification.sound false # ¿La notificación reproduce sonido?
notification.color 4D1A1A # Color de fondo de la notificación - Rojo oscuro
# --- SERVICE MENU ---
service_menu.title_color FF9966 # Color del título del menú de servicio - Naranja claro
service_menu.text_color FFE6E6 # Color del texto del menú de servicio - Blanco rosado
service_menu.selected_color FFB366 # Color de la opción seleccionada - Naranja dorado
service_menu.bg_color 331A0AF5 # Color de fondo del menú de servicio - Marrón rojizo oscuro con alpha
service_menu.drop_shadow false # ¿El menú de servicio tiene sombra?
service_menu.window_message.bg_color 4D1A1AF0 # Color de fondo de ventanas - Rojo oscuro con alpha
service_menu.window_message.border_color CC6633FF # Color del borde de ventanas - Naranja rojizo
service_menu.window_message.title_color CC6633FF # Color del título en ventanas - Naranja rojizo
service_menu.window_message.text_color FFE6E6FF # Color del texto en ventanas - Blanco rosado
service_menu.window_message.padding 15.0f # Espaciado interno de ventanas de mensaje (píxeles)
service_menu.window_message.line_spacing 5.0f # Espaciado entre líneas de texto (píxeles)
service_menu.window_message.title_separator_spacing 20.0f # Espaciado entre título y contenido (píxeles)
service_menu.window_message.min_width 200.0f # Ancho mínimo de ventanas de mensaje (píxeles)
service_menu.window_message.min_height 32.0f # Alto mínimo de ventanas de mensaje (píxeles)
service_menu.window_message.max_width_ratio 0.8f # Ratio máximo de ancho respecto a pantalla (0.0-1.0)
service_menu.window_message.max_height_ratio 0.8f # Ratio máximo de alto respecto a pantalla (0.0-1.0)
service_menu.window_message.text_safety_margin 15.0f # Margen de seguridad para el texto (píxeles)
service_menu.window_message.animation_duration 0.3f # Duración de animaciones de ventanas (segundos)
# --- INTRO ---
intro.bg_color B8664D # Color de fondo de la intro - Naranja tierra
intro.card_color FFE6CC # Color de las tarjetas en la intro - Crema rojizo
intro.shadow_color 66000080 # Color de la sombra de las tarjetas - Rojo muy oscuro con alpha
intro.text_distance_from_bottom 48 # Posición del texto desde la parte inferior
# --- DEBUG ---
debug.color FF6666 # Color para elementos de depuración - Rojo claro
# --- RESOURCE ---
resource.color FFE6E6 # Color de recursos - Blanco rosado
# --- TABE ---
tabe.min_spawn_time 2.0f # Tiempo mínimo en segundos para que aparezca el Tabe
tabe.max_spawn_time 3.0f # Tiempo máximo en segundos para que aparezca el Tabe
# --- PLAYER ---
# Jugador 1 - Camiseta por defecto (tonos rojos)
player.default_shirt[0].darkest B33030FF # Tono más oscuro - Rojo fuerte
player.default_shirt[0].dark CC4040FF # Tono oscuro - Rojo medio
player.default_shirt[0].base E65050FF # Tono principal - Rojo claro
player.default_shirt[0].light FF6666FF # Tono claro - Rojo muy claro
# Jugador 2 - Camiseta por defecto (tonos naranjas)
player.default_shirt[1].darkest B36030FF # Tono más oscuro - Naranja oscuro
player.default_shirt[1].dark CC7540FF # Tono oscuro - Naranja medio
player.default_shirt[1].base E68A50FF # Tono principal - Naranja claro
player.default_shirt[1].light FF9966FF # Tono claro - Naranja muy claro
# Jugador 1 - Camiseta con 1 café (tonos rojizos más intensos)
player.one_coffee_shirt[0].darkest 8B2635FF # Tono más oscuro - Rojo granate
player.one_coffee_shirt[0].dark A53040FF # Tono oscuro - Rojo granate claro
player.one_coffee_shirt[0].base BF3A50FF # Tono principal - Rojo vibrante
player.one_coffee_shirt[0].light D94460FF # Tono claro - Rojo vibrante claro
# Jugador 1 - Camiseta con 2 cafés (tonos naranja dorado)
player.two_coffee_shirt[0].darkest CC6600FF # Tono más oscuro - Naranja dorado oscuro
player.two_coffee_shirt[0].dark E6750AFF # Tono oscuro - Naranja dorado
player.two_coffee_shirt[0].base FF8514FF # Tono principal - Naranja dorado claro
player.two_coffee_shirt[0].light FF991EFF # Tono claro - Naranja dorado muy claro
# Jugador 2 - Camiseta con 1 café (tonos terracotas)
player.one_coffee_shirt[1].darkest A0472DFF # Tono más oscuro - Terracota oscuro
player.one_coffee_shirt[1].dark B8553AFF # Tono oscuro - Terracota medio
player.one_coffee_shirt[1].base D06347FF # Tono principal - Terracota claro
player.one_coffee_shirt[1].light E87154FF # Tono claro - Terracota muy claro
# Jugador 2 - Camiseta con 2 cafés (tonos naranjas cálidos)
player.two_coffee_shirt[1].darkest CC5500FF # Tono más oscuro - Naranja intenso
player.two_coffee_shirt[1].dark E66600FF # Tono oscuro - Naranja fuerte
player.two_coffee_shirt[1].base FF7700FF # Tono principal - Naranja brillante
player.two_coffee_shirt[1].light FF8800FF # Tono claro - Naranja muy brillante
# Colores de contorno de los jugadores
player.outline_color[0] 994D33FF # Color del contorno del Jugador 1 - Marrón rojizo
player.outline_color[1] 664433FF # Color del contorno del Jugador 2 - Marrón tierra

BIN
data/font/04b_25_white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Binary file not shown.

BIN
define_buttons.o Normal file

Binary file not shown.

BIN
input.o Normal file

Binary file not shown.

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# 🏁 Ruta base del proyecto
BASE_DIR="/home/sergio/gitea/coffee_crisis_arcade_edition"
# 📁 Ruta al build
BUILD_DIR="$BASE_DIR/build"
# 📄 Archivo de mapping personalizado
MAPPING_FILE="$BASE_DIR/linux_utils/sdl3_mapping.imp"
# 📦 Generar compile_commands.json
echo "🔧 Generando compile_commands.json..."
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -S "$BASE_DIR" -B "$BUILD_DIR"

44
linux_utils/run_clang-tidy.sh Executable file
View File

@@ -0,0 +1,44 @@
#!/bin/bash
# Script para ejecutar clang-tidy en múltiples directorios
# Uso: ./run_clang-tidy.sh
# Lista de rutas donde ejecutar clang-tidy
PATHS=(
"/home/sergio/gitea/coffee_crisis_arcade_edition/source"
"/home/sergio/gitea/coffee_crisis_arcade_edition/source/sections"
"/home/sergio/gitea/coffee_crisis_arcade_edition/source/ui"
)
# Ruta del directorio build (relativa desde donde se ejecuta el script)
BUILD_DIR="/home/sergio/gitea/coffee_crisis_arcade_edition/build/"
# Función para procesar un directorio
process_directory() {
local dir="$1"
echo "=== Procesando directorio: $dir ==="
# Verificar que el directorio existe
if [[ ! -d "$dir" ]]; then
echo "Error: El directorio $dir no existe"
return 1
fi
# Cambiar al directorio y ejecutar find con -maxdepth 1 para un solo nivel
cd "$dir" || return 1
# Buscar archivos .cpp, .h, .hpp solo en el nivel actual (no subdirectorios)
find . -maxdepth 1 \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) | \
xargs -P4 -I{} bash -c 'echo "Procesando: {}"; clang-tidy {} -p '"$BUILD_DIR"' --fix'
echo "=== Completado: $dir ==="
echo
}
# Procesar cada directorio en la lista
for path in "${PATHS[@]}"; do
process_directory "$path"
done
echo "¡Proceso completado para todos los directorios!"

View File

@@ -35,7 +35,7 @@ auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffe
// Constructor // Constructor
AnimatedSprite::AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path) AnimatedSprite::AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path)
: MovingSprite(texture) { : MovingSprite(std::move(texture)) {
// Carga las animaciones // Carga las animaciones
if (!file_path.empty()) { if (!file_path.empty()) {
auto buffer = loadAnimationsFromFile(file_path); auto buffer = loadAnimationsFromFile(file_path);
@@ -45,7 +45,7 @@ AnimatedSprite::AnimatedSprite(std::shared_ptr<Texture> texture, const std::stri
// Constructor // Constructor
AnimatedSprite::AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations) AnimatedSprite::AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations)
: MovingSprite(texture) { : MovingSprite(std::move(texture)) {
if (!animations.empty()) { if (!animations.empty()) {
loadFromAnimationsFileBuffer(animations); loadFromAnimationsFileBuffer(animations);
} }
@@ -176,7 +176,7 @@ void AnimatedSprite::loadFromAnimationsFileBuffer(const AnimationsFileBuffer& so
// Procesa una línea de configuración // Procesa una línea de configuración
void AnimatedSprite::processConfigLine(const std::string& line, AnimationConfig& config) { void AnimatedSprite::processConfigLine(const std::string& line, AnimationConfig& config) {
size_t pos = line.find("="); size_t pos = line.find('=');
if (pos == std::string::npos) { if (pos == std::string::npos) {
return; return;
} }
@@ -230,7 +230,7 @@ auto AnimatedSprite::processAnimationBlock(const AnimationsFileBuffer& source, s
// Procesa un parámetro individual de animación // Procesa un parámetro individual de animación
void AnimatedSprite::processAnimationParameter(const std::string& line, Animation& animation, const AnimationConfig& config) { void AnimatedSprite::processAnimationParameter(const std::string& line, Animation& animation, const AnimationConfig& config) {
size_t pos = line.find("="); size_t pos = line.find('=');
if (pos == std::string::npos) { if (pos == std::string::npos) {
return; return;
} }

View File

@@ -7,14 +7,15 @@
#include <memory> // Para allocator, shared_ptr #include <memory> // Para allocator, shared_ptr
#include <string> // Para string, hash #include <string> // Para string, hash
#include <unordered_map> // Para unordered_map #include <unordered_map> // Para unordered_map
#include <vector> // Para vector #include <utility>
#include <vector> // Para vector
#include "moving_sprite.h" // Para MovingSprite #include "moving_sprite.h" // Para MovingSprite
// Declaración adelantada // Declaración adelantada
class Texture; class Texture;
// Estructura de Animación // --- Estructuras ---
struct Animation { struct Animation {
static constexpr int DEFAULT_SPEED = 5; static constexpr int DEFAULT_SPEED = 5;
@@ -37,19 +38,19 @@ struct AnimationConfig {
int max_tiles = 1; int max_tiles = 1;
}; };
// Alias de tipo para buffer de animaciones // --- Tipos ---
using AnimationsFileBuffer = std::vector<std::string>; using AnimationsFileBuffer = std::vector<std::string>; // Buffer de animaciones
// Carga las animaciones desde un fichero en un vector de strings // --- Funciones ---
auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer; auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer; // Carga las animaciones desde un fichero
// Clase AnimatedSprite: Sprite animado que hereda de MovingSprite // --- Clase AnimatedSprite: sprite animado que hereda de MovingSprite ---
class AnimatedSprite : public MovingSprite { class AnimatedSprite : public MovingSprite {
public: public:
// --- Constructores y destructor --- // --- Constructores y destructor ---
AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path); AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path);
AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations); AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations);
explicit AnimatedSprite(std::shared_ptr<Texture> texture) : MovingSprite(texture) {} explicit AnimatedSprite(std::shared_ptr<Texture> texture) : MovingSprite(std::move(texture)) {}
~AnimatedSprite() override = default; ~AnimatedSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
@@ -70,12 +71,10 @@ class AnimatedSprite : public MovingSprite {
auto getAnimationIndex(const std::string& name) -> int; // Obtiene el índice de una animación por nombre auto getAnimationIndex(const std::string& name) -> int; // Obtiene el índice de una animación por nombre
protected: protected:
// --- Datos de animación --- // --- Variables de estado ---
std::vector<Animation> animations_; // Vector de animaciones disponibles std::vector<Animation> animations_; // Vector de animaciones disponibles
int current_animation_ = 0; // Índice de la animación activa std::unordered_map<std::string, int> animation_indices_; // Mapa para búsqueda rápida por nombre
int current_animation_ = 0; // Índice de la animación activa
// --- Mapa para búsqueda rápida de animaciones por nombre ---
std::unordered_map<std::string, int> animation_indices_;
// --- Métodos internos --- // --- Métodos internos ---
void animate(); // Calcula el frame correspondiente a la animación void animate(); // Calcula el frame correspondiente a la animación

View File

@@ -92,7 +92,7 @@ void Asset::loadFromFile(const std::string &config_file_path, const std::string
} }
try { try {
std::string type_str = parts[0]; const std::string &type_str = parts[0];
std::string path = parts[1]; std::string path = parts[1];
// Valores por defecto // Valores por defecto

View File

@@ -1,25 +1,25 @@
#pragma once #pragma once
#include <string> #include <string> // Para string
#include <unordered_map> #include <unordered_map> // Para unordered_map
#include <utility> #include <utility> // Para move
#include <vector> #include <vector> // Para vector
// Clase Asset: gestor optimizado de recursos (singleton) // --- Clase Asset: gestor optimizado de recursos (singleton) ---
class Asset { class Asset {
public: public:
// Tipos de recursos // --- Enums ---
enum class Type : int { enum class Type : int {
BITMAP, BITMAP, // Imágenes
MUSIC, MUSIC, // Música
SOUND, SOUND, // Sonidos
FONT, FONT, // Fuentes
LANG, LANG, // Idiomas
DATA, DATA, // Datos
DEMODATA, DEMODATA, // Datos de demo
ANIMATION, ANIMATION, // Animaciones
PALETTE, PALETTE, // Paletas
SIZE, SIZE, // Tamaño
}; };
// --- Métodos de singleton --- // --- Métodos de singleton ---
@@ -38,9 +38,9 @@ class Asset {
[[nodiscard]] auto exists(const std::string &filename) const -> bool; // Nueva función para verificar existencia [[nodiscard]] auto exists(const std::string &filename) const -> bool; // Nueva función para verificar existencia
private: private:
// --- Estructura interna para almacenar información de cada recurso --- // --- Estructuras privadas ---
struct Item { struct Item {
std::string file; // Ruta completa del archivo (mantener nombre original) std::string file; // Ruta completa del archivo
Type type; // Tipo de recurso Type type; // Tipo de recurso
bool required; // Indica si el archivo es obligatorio bool required; // Indica si el archivo es obligatorio
@@ -49,21 +49,22 @@ class Asset {
}; };
// --- Variables internas --- // --- Variables internas ---
std::unordered_map<std::string, Item> file_list_; // Mapa para búsqueda O(1) (mantener nombre original) std::unordered_map<std::string, Item> file_list_; // Mapa para búsqueda O(1)
std::string executable_path_; // Ruta del ejecutable std::string executable_path_; // Ruta del ejecutable
// --- Métodos internos --- // --- Métodos internos ---
[[nodiscard]] static auto checkFile(const std::string &path) -> bool; [[nodiscard]] static auto checkFile(const std::string &path) -> bool; // Verifica si un archivo existe
[[nodiscard]] static auto getTypeName(Type type) -> std::string; [[nodiscard]] static auto getTypeName(Type type) -> std::string; // Obtiene el nombre del tipo
[[nodiscard]] static auto parseAssetType(const std::string &type_str) -> Type; [[nodiscard]] static auto parseAssetType(const std::string &type_str) -> Type; // Convierte string a tipo
void addToMap(const std::string &file_path, Type type, bool required, bool absolute); void addToMap(const std::string &file_path, Type type, bool required, bool absolute); // Añade archivo al mapa
[[nodiscard]] static auto replaceVariables(const std::string &path, const std::string &prefix, const std::string &system_folder) -> std::string; [[nodiscard]] static auto replaceVariables(const std::string &path, const std::string &prefix, const std::string &system_folder) -> std::string; // Reemplaza variables en la ruta
static auto parseOptions(const std::string &options, bool &required, bool &absolute) -> void; static auto parseOptions(const std::string &options, bool &required, bool &absolute) -> void; // Parsea opciones
// --- Patrón Singleton --- // --- Constructores y destructor privados (singleton) ---
explicit Asset(std::string executable_path) explicit Asset(std::string executable_path) // Constructor privado
: executable_path_(std::move(executable_path)) {} : executable_path_(std::move(executable_path)) {}
~Asset() = default; ~Asset() = default; // Destructor privado
static Asset *instance; // --- Instancia singleton ---
static Asset *instance; // Instancia única de Asset
}; };

View File

@@ -3,19 +3,20 @@
#include <string> // Para string #include <string> // Para string
#include <utility> // Para move #include <utility> // Para move
// Clase Audio: gestor de audio (singleton) // --- Clase Audio: gestor de audio (singleton) ---
class Audio { class Audio {
public: public:
// --- Enums ---
enum class Group : int { enum class Group : int {
ALL = -1, ALL = -1, // Todos los grupos
GAME = 0, GAME = 0, // Sonidos del juego
INTERFACE = 1 INTERFACE = 1 // Sonidos de la interfaz
}; };
// --- Constantes --- // --- Constantes ---
static constexpr int MAX_VOLUME = 100; static constexpr int MAX_VOLUME = 100; // Volumen máximo
static constexpr int MIN_VOLUME = 0; static constexpr int MIN_VOLUME = 0; // Volumen mínimo
static constexpr int FREQUENCY = 48000; static constexpr int FREQUENCY = 48000; // Frecuencia de audio
// --- Métodos de singleton --- // --- Métodos de singleton ---
static void init(); // Inicializa el objeto Audio static void init(); // Inicializa el objeto Audio
@@ -60,12 +61,14 @@ class Audio {
void setMusicVolume(int volume) const; // Ajustar volumen de música void setMusicVolume(int volume) const; // Ajustar volumen de música
private: private:
// --- Enums privados ---
enum class MusicState { enum class MusicState {
PLAYING, PLAYING, // Reproduciendo música
PAUSED, PAUSED, // Música pausada
STOPPED, STOPPED, // Música detenida
}; };
// --- Estructuras privadas ---
struct Music { struct Music {
MusicState state; // Estado actual de la música (reproduciendo, detenido, en pausa) MusicState state; // Estado actual de la música (reproduciendo, detenido, en pausa)
std::string name; // Última pista de música reproducida std::string name; // Última pista de música reproducida
@@ -79,20 +82,19 @@ class Audio {
: state(init_state), name(std::move(init_name)), loop(init_loop) {} : state(init_state), name(std::move(init_name)), loop(init_loop) {}
}; };
Music music_; // --- Variables de estado ---
Music music_; // Estado de la música
// --- Variables de Estado ---
bool enabled_ = true; // Estado general del audio bool enabled_ = true; // Estado general del audio
bool sound_enabled_ = true; // Estado de los efectos de sonido bool sound_enabled_ = true; // Estado de los efectos de sonido
bool music_enabled_ = true; // Estado de la música bool music_enabled_ = true; // Estado de la música
// --- Inicializa SDL Audio --- // --- Métodos internos ---
void initSDLAudio(); void initSDLAudio(); // Inicializa SDL Audio
// --- Patrón Singleton --- // --- Constructores y destructor privados (singleton) ---
Audio(); // Constructor privado Audio(); // Constructor privado
~Audio(); // Destructor privado ~Audio(); // Destructor privado
// --- Singleton --- // --- Instancia singleton ---
static Audio *instance; static Audio *instance; // Instancia única de Audio
}; };

View File

@@ -5,6 +5,7 @@
#include <algorithm> // Para clamp, max #include <algorithm> // Para clamp, max
#include <cmath> // Para M_PI, cos, sin #include <cmath> // Para M_PI, cos, sin
#include <utility>
#include "moving_sprite.h" // Para MovingSprite #include "moving_sprite.h" // Para MovingSprite
#include "param.h" // Para Param, ParamBackground, param #include "param.h" // Para Param, ParamBackground, param
@@ -15,11 +16,7 @@
// Constructor // Constructor
Background::Background(float total_progress_to_complete) Background::Background(float total_progress_to_complete)
: total_progress_to_complete_(total_progress_to_complete), : renderer_(Screen::get()->getRenderer()),
progress_per_stage_(total_progress_to_complete_ / STAGES),
sun_completion_progress_(total_progress_to_complete_ * SUN_COMPLETION_FACTOR),
renderer_(Screen::get()->getRenderer()),
buildings_texture_(Resource::get()->getTexture("game_buildings.png")), buildings_texture_(Resource::get()->getTexture("game_buildings.png")),
top_clouds_texture_(Resource::get()->getTexture("game_clouds1.png")), top_clouds_texture_(Resource::get()->getTexture("game_clouds1.png")),
@@ -29,9 +26,13 @@ Background::Background(float total_progress_to_complete)
sun_texture_(Resource::get()->getTexture("game_sun.png")), sun_texture_(Resource::get()->getTexture("game_sun.png")),
moon_texture_(Resource::get()->getTexture("game_moon.png")), moon_texture_(Resource::get()->getTexture("game_moon.png")),
total_progress_to_complete_(total_progress_to_complete),
progress_per_stage_(total_progress_to_complete_ / STAGES),
sun_completion_progress_(total_progress_to_complete_ * SUN_COMPLETION_FACTOR),
rect_(SDL_FRect{0, 0, static_cast<float>(gradients_texture_->getWidth() / 2), static_cast<float>(gradients_texture_->getHeight() / 2)}), rect_(SDL_FRect{0, 0, static_cast<float>(gradients_texture_->getWidth() / 2), static_cast<float>(gradients_texture_->getHeight() / 2)}),
src_rect_({0, 0, 320, 240}), src_rect_({.x = 0, .y = 0, .w = 320, .h = 240}),
dst_rect_({0, 0, 320, 240}), dst_rect_({.x = 0, .y = 0, .w = 320, .h = 240}),
attenuate_color_(Color(param.background.attenuate_color.r, param.background.attenuate_color.g, param.background.attenuate_color.b)), attenuate_color_(Color(param.background.attenuate_color.r, param.background.attenuate_color.g, param.background.attenuate_color.b)),
alpha_color_texture_(param.background.attenuate_color.a), alpha_color_texture_(param.background.attenuate_color.a),
@@ -58,17 +59,17 @@ void Background::initializePaths() {
// Inicializa los rectángulos de gradientes y nubes // Inicializa los rectángulos de gradientes y nubes
void Background::initializeRects() { void Background::initializeRects() {
gradient_rect_[0] = {0, 0, rect_.w, rect_.h}; gradient_rect_[0] = {.x = 0, .y = 0, .w = rect_.w, .h = rect_.h};
gradient_rect_[1] = {rect_.w, 0, rect_.w, rect_.h}; gradient_rect_[1] = {.x = rect_.w, .y = 0, .w = rect_.w, .h = rect_.h};
gradient_rect_[2] = {0, rect_.h, rect_.w, rect_.h}; gradient_rect_[2] = {.x = 0, .y = rect_.h, .w = rect_.w, .h = rect_.h};
gradient_rect_[3] = {rect_.w, rect_.h, rect_.w, rect_.h}; gradient_rect_[3] = {.x = rect_.w, .y = rect_.h, .w = rect_.w, .h = rect_.h};
const float TOP_CLOUDS_TEXTURE_HEIGHT = top_clouds_texture_->getHeight() / 4; const float TOP_CLOUDS_TEXTURE_HEIGHT = top_clouds_texture_->getHeight() / 4;
const float BOTTOM_CLOUDS_TEXTURE_HEIGHT = bottom_clouds_texture_->getHeight() / 4; const float BOTTOM_CLOUDS_TEXTURE_HEIGHT = bottom_clouds_texture_->getHeight() / 4;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
top_clouds_rect_[i] = {0, i * TOP_CLOUDS_TEXTURE_HEIGHT, static_cast<float>(top_clouds_texture_->getWidth()), TOP_CLOUDS_TEXTURE_HEIGHT}; top_clouds_rect_[i] = {.x = 0, .y = i * TOP_CLOUDS_TEXTURE_HEIGHT, .w = static_cast<float>(top_clouds_texture_->getWidth()), .h = TOP_CLOUDS_TEXTURE_HEIGHT};
bottom_clouds_rect_[i] = {0, i * BOTTOM_CLOUDS_TEXTURE_HEIGHT, static_cast<float>(bottom_clouds_texture_->getWidth()), BOTTOM_CLOUDS_TEXTURE_HEIGHT}; bottom_clouds_rect_[i] = {.x = 0, .y = i * BOTTOM_CLOUDS_TEXTURE_HEIGHT, .w = static_cast<float>(bottom_clouds_texture_->getWidth()), .h = BOTTOM_CLOUDS_TEXTURE_HEIGHT};
} }
} }
@@ -171,7 +172,7 @@ void Background::incrementProgress(float amount) {
// Establece la progresión absoluta // Establece la progresión absoluta
void Background::setProgress(float absolute_progress) { void Background::setProgress(float absolute_progress) {
float old_progress = progress_; float old_progress = progress_;
progress_ = std::clamp(absolute_progress, 0.0f, total_progress_to_complete_); progress_ = std::clamp(absolute_progress, 0.0F, total_progress_to_complete_);
// Notifica el cambio si hay callback y el progreso cambió // Notifica el cambio si hay callback y el progreso cambió
if (progress_callback_ && progress_ != old_progress) { if (progress_callback_ && progress_ != old_progress) {
@@ -187,11 +188,11 @@ void Background::setState(State new_state) {
// Reinicia la progresión // Reinicia la progresión
void Background::reset() { void Background::reset() {
float old_progress = progress_; float old_progress = progress_;
progress_ = 0.0f; progress_ = 0.0F;
state_ = State::NORMAL; state_ = State::NORMAL;
manual_mode_ = false; manual_mode_ = false;
gradient_number_ = 0; gradient_number_ = 0;
transition_ = 0.0f; transition_ = 0.0F;
sun_index_ = 0; sun_index_ = 0;
moon_index_ = 0; moon_index_ = 0;
@@ -208,7 +209,7 @@ void Background::setManualMode(bool manual) {
// Establece callback para sincronización automática // Establece callback para sincronización automática
void Background::setProgressCallback(ProgressCallback callback) { void Background::setProgressCallback(ProgressCallback callback) {
progress_callback_ = callback; progress_callback_ = std::move(callback);
} }
// Elimina el callback // Elimina el callback
@@ -224,8 +225,8 @@ void Background::setCloudsSpeed(float value) {
// Las nubes inferiores van a la mitad de velocidad que las superiores // Las nubes inferiores van a la mitad de velocidad que las superiores
top_clouds_sprite_a_->setVelX(value); top_clouds_sprite_a_->setVelX(value);
top_clouds_sprite_b_->setVelX(value); top_clouds_sprite_b_->setVelX(value);
bottom_clouds_sprite_a_->setVelX(value / 2.0f); bottom_clouds_sprite_a_->setVelX(value / 2.0F);
bottom_clouds_sprite_b_->setVelX(value / 2.0f); bottom_clouds_sprite_b_->setVelX(value / 2.0F);
} }
// Establece el degradado de fondo // Establece el degradado de fondo
@@ -262,19 +263,19 @@ void Background::updateProgression() {
} }
// Calcula la transición de los diferentes fondos // Calcula la transición de los diferentes fondos
const float gradient_number_float = std::min(progress_ / progress_per_stage_, 3.0F); const float GRADIENT_NUMBER_FLOAT = std::min(progress_ / progress_per_stage_, 3.0F);
const float percent = gradient_number_float - static_cast<int>(gradient_number_float); const float PERCENT = GRADIENT_NUMBER_FLOAT - static_cast<int>(GRADIENT_NUMBER_FLOAT);
gradient_number_ = static_cast<size_t>(gradient_number_float); gradient_number_ = static_cast<size_t>(GRADIENT_NUMBER_FLOAT);
transition_ = percent; transition_ = PERCENT;
// Calcula la posición del sol // Calcula la posición del sol
const float sun_progression = std::min(progress_ / sun_completion_progress_, 1.0f); const float SUN_PROGRESSION = std::min(progress_ / sun_completion_progress_, 1.0F);
sun_index_ = static_cast<size_t>(sun_progression * (sun_path_.size() - 1)); sun_index_ = static_cast<size_t>(SUN_PROGRESSION * (sun_path_.size() - 1));
// Calcula la posición de la luna // Calcula la posición de la luna
const float moon_progression = std::min(progress_ / total_progress_to_complete_, 1.0f); const float MOON_PROGRESSION = std::min(progress_ / total_progress_to_complete_, 1.0F);
moon_index_ = static_cast<size_t>(moon_progression * (moon_path_.size() - 1)); moon_index_ = static_cast<size_t>(MOON_PROGRESSION * (moon_path_.size() - 1));
// Actualiza la velocidad de las nubes // Actualiza la velocidad de las nubes
updateCloudsSpeed(); updateCloudsSpeed();
@@ -294,22 +295,22 @@ void Background::updateCloudsSpeed() {
if (state_ == State::COMPLETED) { if (state_ == State::COMPLETED) {
float completion_factor = (progress_ - MINIMUM_COMPLETED_PROGRESS) / float completion_factor = (progress_ - MINIMUM_COMPLETED_PROGRESS) /
(total_progress_to_complete_ - MINIMUM_COMPLETED_PROGRESS); (total_progress_to_complete_ - MINIMUM_COMPLETED_PROGRESS);
completion_factor = std::max(0.1f, completion_factor); completion_factor = std::max(0.1F, completion_factor);
base_clouds_speed *= completion_factor; base_clouds_speed *= completion_factor;
} }
// Aplicar velocidades diferentes para nubes superiores e inferiores // Aplicar velocidades diferentes para nubes superiores e inferiores
const float top_clouds_speed = base_clouds_speed; const float TOP_CLOUDS_SPEED = base_clouds_speed;
const float bottom_clouds_speed = base_clouds_speed / 2.0f; const float BOTTOM_CLOUDS_SPEED = base_clouds_speed / 2.0F;
// Aplicar las velocidades a los sprites correspondientes // Aplicar las velocidades a los sprites correspondientes
top_clouds_sprite_a_->setVelX(top_clouds_speed); top_clouds_sprite_a_->setVelX(TOP_CLOUDS_SPEED);
top_clouds_sprite_b_->setVelX(top_clouds_speed); top_clouds_sprite_b_->setVelX(TOP_CLOUDS_SPEED);
bottom_clouds_sprite_a_->setVelX(bottom_clouds_speed); bottom_clouds_sprite_a_->setVelX(BOTTOM_CLOUDS_SPEED);
bottom_clouds_sprite_b_->setVelX(bottom_clouds_speed); bottom_clouds_sprite_b_->setVelX(BOTTOM_CLOUDS_SPEED);
// Guardar la velocidad principal // Guardar la velocidad principal
clouds_speed_ = top_clouds_speed; clouds_speed_ = TOP_CLOUDS_SPEED;
} }
// Actualiza las nubes // Actualiza las nubes
@@ -478,7 +479,7 @@ void Background::createSunPath() {
const int NUM_STEPS = static_cast<int>((M_PI - M_PI / 2) / STEP) + 1; const int NUM_STEPS = static_cast<int>((M_PI - M_PI / 2) / STEP) + 1;
for (int i = 0; i < NUM_STEPS; ++i) { for (int i = 0; i < NUM_STEPS; ++i) {
double theta = M_PI / 2 + i * STEP; double theta = M_PI / 2 + (i * STEP);
float x = CENTER_X + (RADIUS * cos(theta)); float x = CENTER_X + (RADIUS * cos(theta));
float y = CENTER_Y - (RADIUS * sin(theta)); float y = CENTER_Y - (RADIUS * sin(theta));
sun_path_.push_back({x, y}); sun_path_.push_back({x, y});
@@ -501,9 +502,9 @@ void Background::createMoonPath() {
constexpr double STEP = 0.01; constexpr double STEP = 0.01;
const int NUM_STEPS = static_cast<int>((M_PI / 2) / STEP) + 1; const int NUM_STEPS = static_cast<int>((M_PI / 2) / STEP) + 1;
constexpr float FREEZE_PERCENTAGE = 0.2f; // Porcentaje final del recorrido que se mantiene fijo constexpr float FREEZE_PERCENTAGE = 0.2F; // Porcentaje final del recorrido que se mantiene fijo
const int FREEZE_START_INDEX = static_cast<int>(NUM_STEPS * (1.0f - FREEZE_PERCENTAGE)); const int FREEZE_START_INDEX = static_cast<int>(NUM_STEPS * (1.0F - FREEZE_PERCENTAGE));
for (int i = 0; i < NUM_STEPS; ++i) { for (int i = 0; i < NUM_STEPS; ++i) {
double theta = i * STEP; double theta = i * STEP;

View File

@@ -4,7 +4,7 @@
#include <array> // Para array #include <array> // Para array
#include <cstddef> // Para size_t #include <cstddef> // Para size_t
#include <functional> // Para std::function #include <functional> // Para function
#include <memory> // Para unique_ptr, shared_ptr #include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector #include <vector> // Para vector
@@ -14,140 +14,109 @@ class MovingSprite;
class Sprite; class Sprite;
class Texture; class Texture;
/* // --- Clase Background: gestiona el fondo de la sección jugable ---
Esta clase gestiona el fondo que aparece en la sección jugable.
Maneja internamente su progresión a través de diferentes estados del día/noche,
controlando las transiciones entre gradientes, posiciones del sol/luna y velocidad de nubes.
Estados:
- NORMAL: Progresión normal del día
- COMPLETED: Reducción gradual de la actividad (nubes más lentas)
Métodos clave:
- incrementProgress() -> Avanza la progresión del fondo
- setState() -> Cambia el estado del fondo
- setColor/setAlpha -> Efectos de atenuación
*/
class Background { class Background {
public: public:
// Enumeración de estados // --- Enums ---
enum class State { enum class State {
NORMAL, NORMAL, // Progresión normal del día
COMPLETED COMPLETED // Reducción gradual de la actividad
}; };
// Constructor y Destructor // --- Tipos ---
Background(float total_progress_to_complete = 6100.0f); using ProgressCallback = std::function<void(float)>; // Callback para sincronización
~Background();
// Actualización y renderizado // --- Constructor y destructor ---
Background(float total_progress_to_complete = 6100.0F); // Constructor principal
~Background(); // Destructor
// --- Métodos principales ---
void update(); // Actualiza la lógica del objeto void update(); // Actualiza la lógica del objeto
void render(); // Dibuja el objeto void render(); // Dibuja el objeto
void reset(); // Reinicia la progresión
// Configuración de posición // --- Configuración ---
void setPos(SDL_FRect pos); // Establece la posición del objeto void setPos(SDL_FRect pos); // Establece la posición del objeto
void setState(State new_state); // Cambia el estado del fondo
// Control de progresión
void incrementProgress(float amount = 1.0f); // Incrementa la progresión interna
void setProgress(float absolute_progress); // Establece la progresión absoluta
void setState(State new_state); // Cambia el estado del fondo
void reset(); // Reinicia la progresión
// Sistema de callback para sincronización automática
using ProgressCallback = std::function<void(float)>;
void setProgressCallback(ProgressCallback callback); // Establece callback para sincronización void setProgressCallback(ProgressCallback callback); // Establece callback para sincronización
void removeProgressCallback(); // Elimina el callback void removeProgressCallback(); // Elimina el callback
void setManualMode(bool manual); // Activa/desactiva el modo manual
void setCloudsSpeed(float value); // Ajusta la velocidad de las nubes
void setGradientNumber(int value); // Establece el degradado de fondo
void setTransition(float value); // Ajusta la transición entre texturas
void setSunProgression(float progress); // Establece la posición del sol
void setMoonProgression(float progress); // Establece la posición de la luna
void setColor(Color color); // Establece el color de atenuación
void setAlpha(int alpha); // Ajusta la transparencia del fondo
// Configuración manual (para uso fuera del juego principal) // --- Control de progresión ---
void setManualMode(bool manual); // Activa/desactiva el modo manual void incrementProgress(float amount = 1.0F); // Incrementa la progresión interna
void setCloudsSpeed(float value); // Ajusta la velocidad de las nubes void setProgress(float absolute_progress); // Establece la progresión absoluta
void setGradientNumber(int value); // Establece el degradado de fondo
void setTransition(float value); // Ajusta la transición entre texturas
void setSunProgression(float progress); // Establece la posición del sol
void setMoonProgression(float progress); // Establece la posición de la luna
// Configuración de efectos visuales // --- Getters ---
void setColor(Color color); // Establece el color de atenuación [[nodiscard]] auto getProgress() const -> float { return progress_; } // Obtiene el progreso actual
void setAlpha(int alpha); // Ajusta la transparencia del fondo [[nodiscard]] auto getState() const -> State { return state_; } // Obtiene el estado actual
[[nodiscard]] auto getCurrentGradient() const -> int { return static_cast<int>(gradient_number_); } // Obtiene el gradiente actual
// Getters para información del estado
float getProgress() const { return progress_; }
State getState() const { return state_; }
int getCurrentGradient() const { return static_cast<int>(gradient_number_); }
private: private:
// Constantes de configuración // --- Constantes ---
static constexpr size_t STAGES = 4; static constexpr size_t STAGES = 4; // Número de etapas
static constexpr float COMPLETED_REDUCTION_RATE = 25.0f; static constexpr float COMPLETED_REDUCTION_RATE = 25.0F; // Tasa de reducción completada
static constexpr float MINIMUM_COMPLETED_PROGRESS = 200.0f; static constexpr float MINIMUM_COMPLETED_PROGRESS = 200.0F; // Progreso mínimo completado
static constexpr float SUN_COMPLETION_FACTOR = 0.5f; // El sol completa su recorrido a la mitad del progreso total static constexpr float SUN_COMPLETION_FACTOR = 0.5F; // Factor de completado del sol
// Configuración paramétrica // --- Objetos y punteros ---
const float total_progress_to_complete_; SDL_Renderer *renderer_; // Renderizador de la ventana
const float progress_per_stage_; SDL_Texture *canvas_; // Textura para componer el fondo
const float sun_completion_progress_; SDL_Texture *color_texture_; // Textura para atenuar el fondo
std::shared_ptr<Texture> buildings_texture_; // Textura de edificios
std::shared_ptr<Texture> top_clouds_texture_; // Textura de nubes superiores
std::shared_ptr<Texture> bottom_clouds_texture_; // Textura de nubes inferiores
std::shared_ptr<Texture> grass_texture_; // Textura de hierba
std::shared_ptr<Texture> gradients_texture_; // Textura de gradientes
std::shared_ptr<Texture> sun_texture_; // Textura del sol
std::shared_ptr<Texture> moon_texture_; // Textura de la luna
std::unique_ptr<MovingSprite> top_clouds_sprite_a_; // Sprite de nubes superiores A
std::unique_ptr<MovingSprite> top_clouds_sprite_b_; // Sprite de nubes superiores B
std::unique_ptr<MovingSprite> bottom_clouds_sprite_a_; // Sprite de nubes inferiores A
std::unique_ptr<MovingSprite> bottom_clouds_sprite_b_; // Sprite de nubes inferiores B
std::unique_ptr<Sprite> buildings_sprite_; // Sprite de edificios
std::unique_ptr<Sprite> gradient_sprite_; // Sprite de gradiente
std::unique_ptr<Sprite> grass_sprite_; // Sprite de hierba
std::unique_ptr<Sprite> sun_sprite_; // Sprite del sol
std::unique_ptr<Sprite> moon_sprite_; // Sprite de la luna
// Objetos y punteros // --- Variables de configuración ---
SDL_Renderer *renderer_; // Renderizador de la ventana const float total_progress_to_complete_; // Progreso total para completar
const float progress_per_stage_; // Progreso por etapa
const float sun_completion_progress_; // Progreso de completado del sol
ProgressCallback progress_callback_; // Callback para notificar cambios de progreso
// Texturas // --- Variables de estado ---
std::shared_ptr<Texture> buildings_texture_; std::vector<SDL_FPoint> sun_path_; // Recorrido del sol
std::shared_ptr<Texture> top_clouds_texture_; std::vector<SDL_FPoint> moon_path_; // Recorrido de la luna
std::shared_ptr<Texture> bottom_clouds_texture_;
std::shared_ptr<Texture> grass_texture_;
std::shared_ptr<Texture> gradients_texture_;
std::shared_ptr<Texture> sun_texture_;
std::shared_ptr<Texture> moon_texture_;
// Sprites
std::unique_ptr<MovingSprite> top_clouds_sprite_a_;
std::unique_ptr<MovingSprite> top_clouds_sprite_b_;
std::unique_ptr<MovingSprite> bottom_clouds_sprite_a_;
std::unique_ptr<MovingSprite> bottom_clouds_sprite_b_;
std::unique_ptr<Sprite> buildings_sprite_;
std::unique_ptr<Sprite> gradient_sprite_;
std::unique_ptr<Sprite> grass_sprite_;
std::unique_ptr<Sprite> sun_sprite_;
std::unique_ptr<Sprite> moon_sprite_;
// Buffers de renderizado
SDL_Texture *canvas_; // Textura para componer el fondo
SDL_Texture *color_texture_; // Textura para atenuar el fondo
// Variables de estado y progresión
State state_ = State::NORMAL;
float progress_ = 0.0f; // Progresión interna (0 a total_progress_to_complete_)
bool manual_mode_ = false; // Si está en modo manual, no actualiza automáticamente
ProgressCallback progress_callback_; // Callback para notificar cambios de progreso
// Variables de renderizado
SDL_FRect rect_; // Tamaño del objeto
SDL_FRect src_rect_; // Parte del objeto para copiar en pantalla
SDL_FRect dst_rect_; // Posición en pantalla donde se copia el objeto
std::array<SDL_FRect, STAGES> gradient_rect_; // Fondos degradados std::array<SDL_FRect, STAGES> gradient_rect_; // Fondos degradados
std::array<SDL_FRect, 4> top_clouds_rect_; // Nubes superiores std::array<SDL_FRect, 4> top_clouds_rect_; // Nubes superiores
std::array<SDL_FRect, 4> bottom_clouds_rect_; // Nubes inferiores std::array<SDL_FRect, 4> bottom_clouds_rect_; // Nubes inferiores
SDL_FRect rect_; // Tamaño del objeto
SDL_FRect src_rect_; // Parte del objeto para copiar en pantalla
SDL_FRect dst_rect_; // Posición en pantalla donde se copia el objeto
Color attenuate_color_; // Color de atenuación Color attenuate_color_; // Color de atenuación
State state_ = State::NORMAL; // Estado actual
float progress_ = 0.0F; // Progresión interna
float clouds_speed_ = 0; // Velocidad de las nubes
float transition_ = 0; // Porcentaje de transición
size_t gradient_number_ = 0; // Índice de fondo degradado
size_t counter_ = 0; // Contador interno
size_t alpha_color_texture_ = 0; // Transparencia de atenuación
size_t previous_alpha_color_texture_ = 0; // Transparencia anterior
size_t sun_index_ = 0; // Índice del recorrido del sol
size_t moon_index_ = 0; // Índice del recorrido de la luna
int base_ = 0; // Posición base del fondo
Uint8 alpha_ = 0; // Transparencia entre fases
bool manual_mode_ = false; // Si está en modo manual
std::vector<SDL_FPoint> sun_path_; // Recorrido del sol // --- Métodos internos ---
std::vector<SDL_FPoint> moon_path_; // Recorrido de la luna
float clouds_speed_ = 0; // Velocidad de las nubes
float transition_ = 0; // Porcentaje de transición
size_t gradient_number_ = 0; // Índice de fondo degradado
size_t counter_ = 0; // Contador interno
size_t alpha_color_texture_ = 0; // Transparencia de atenuación
size_t previous_alpha_color_texture_ = 0; // Transparencia anterior
size_t sun_index_ = 0; // Índice del recorrido del sol
size_t moon_index_ = 0; // Índice del recorrido de la luna
int base_ = 0; // Posición base del fondo
Uint8 alpha_ = 0; // Transparencia entre fases
// Métodos internos
void initializePaths(); // Inicializa las rutas del sol y la luna void initializePaths(); // Inicializa las rutas del sol y la luna
void initializeRects(); // Inicializa los rectángulos de gradientes y nubes void initializeRects(); // Inicializa los rectángulos de gradientes y nubes
void initializeSprites(); // Crea los sprites void initializeSprites(); // Crea los sprites

View File

@@ -11,7 +11,7 @@
#include "texture.h" // Para Texture #include "texture.h" // Para Texture
// Constructor // Constructor
Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float speed, Uint16 creation_timer, SDL_FRect play_area, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation) Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float speed, Uint16 creation_timer, SDL_FRect play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation)
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)), : sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
x_(x), x_(x),
y_(y), y_(y),
@@ -44,7 +44,7 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
} }
case Type::FLOATER: { case Type::FLOATER: {
default_vy_ = max_vy_ = vy_ = fabs(vx_ * 2.0F); default_vy_ = max_vy_ = vy_ = std::fabs(vx_ * 2.0F);
gravity_ = 0.00F; gravity_ = 0.00F;
const int INDEX = static_cast<int>(size_); const int INDEX = static_cast<int>(size_);

View File

@@ -38,17 +38,18 @@ class Balloon {
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;
// --- Enums ---
enum class Size : Uint8 { enum class Size : Uint8 {
SMALL = 0, SMALL = 0, // Tamaño pequeño
MEDIUM = 1, MEDIUM = 1, // Tamaño mediano
LARGE = 2, LARGE = 2, // Tamaño grande
EXTRALARGE = 3, EXTRALARGE = 3, // Tamaño extra grande
}; };
enum class Type : Uint8 { enum class Type : Uint8 {
BALLOON = 0, BALLOON = 0, // Globo normal
FLOATER = 1, FLOATER = 1, // Globo flotante
POWERBALL = 2, POWERBALL = 2, // Globo de poder
}; };
// --- Constructores y destructor --- // --- Constructores y destructor ---
@@ -61,7 +62,7 @@ class Balloon {
float speed, float speed,
Uint16 creation_timer, Uint16 creation_timer,
SDL_FRect play_area, SDL_FRect play_area,
std::shared_ptr<Texture> texture, const std::shared_ptr<Texture>& texture,
const std::vector<std::string>& animation); const std::vector<std::string>& animation);
~Balloon() = default; ~Balloon() = default;

View File

@@ -13,7 +13,7 @@
// --- Clase BalloonFormations --- // --- Clase BalloonFormations ---
class BalloonFormations { class BalloonFormations {
public: public:
// --- Estructuras de datos --- // --- Estructuras ---
struct SpawnParams { struct SpawnParams {
int x = 0; // Posición en el eje X donde crear el globo int x = 0; // Posición en el eje X donde crear el globo
int y = 0; // Posición en el eje Y donde crear el globo int y = 0; // Posición en el eje Y donde crear el globo
@@ -41,8 +41,8 @@ class BalloonFormations {
Formation() = default; Formation() = default;
}; };
// Vector de índices a formaciones // --- Types ---
using Pool = std::vector<int>; using Pool = std::vector<int>; // Vector de índices a formaciones
// --- Constructor y destructor --- // --- Constructor y destructor ---
BalloonFormations() { BalloonFormations() {
@@ -79,28 +79,20 @@ class BalloonFormations {
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 = 200; // Tiempo base de creación de los globos para las formaciones
// --- Datos --- // --- Variables ---
std::vector<Formation> formations_; // Vector con todas las formaciones disponibles std::vector<Formation> formations_; // Vector con todas las formaciones disponibles
std::vector<Pool> pools_; // Vector de pools, cada pool contiene índices a formaciones std::vector<Pool> pools_; // Vector de pools, cada pool contiene índices a formaciones
// --- Inicialización de formaciones --- // --- Métodos internos ---
void initFormations(); // Inicializa la lista principal de formaciones de globos disponibles void initFormations(); // Inicializa la lista principal de formaciones de globos disponibles
void initFormationPools(); // Carga los pools desde archivo de configuración void initFormationPools(); // Carga los pools desde archivo de configuración
// --- Carga y análisis de datos ---
auto loadFormationsFromFile(const std::string& filename, const std::map<std::string, float>& variables) -> bool; auto loadFormationsFromFile(const std::string& filename, const std::map<std::string, float>& variables) -> bool;
auto parseBalloonLine(const std::string& line, const std::map<std::string, float>& variables) -> std::optional<SpawnParams>; auto parseBalloonLine(const std::string& line, const std::map<std::string, float>& variables) -> std::optional<SpawnParams>;
auto loadPoolsFromFile(const std::string& filename) -> bool; // Nueva función para cargar pools auto loadPoolsFromFile(const std::string& filename) -> bool; // Nueva función para cargar pools
auto parsePoolLine(const std::string& line) -> std::optional<std::pair<int, std::vector<int>>>; // Nueva función para parsear líneas de pools auto parsePoolLine(const std::string& line) -> std::optional<std::pair<int, std::vector<int>>>; // Nueva función para parsear líneas de pools
// --- Evaluación de expresiones ---
auto evaluateExpression(const std::string& expr, const std::map<std::string, float>& variables) -> float; auto evaluateExpression(const std::string& expr, const std::map<std::string, float>& variables) -> float;
auto evaluateSimpleExpression(const std::string& expr, const std::map<std::string, float>& variables) -> float; auto evaluateSimpleExpression(const std::string& expr, const std::map<std::string, float>& variables) -> float;
// --- Utilidades ---
static auto trim(const std::string& str) -> std::string; static auto trim(const std::string& str) -> std::string;
// --- Generación de variantes ---
void createFloaterVariants(); void createFloaterVariants();
void loadDefaultFormations(); void loadDefaultFormations();
void loadDefaultPools(); // Nueva función para pools por defecto void loadDefaultPools(); // Nueva función para pools por defecto

View File

@@ -17,9 +17,7 @@
// Constructor // Constructor
BalloonManager::BalloonManager(IStageInfo *stage_info) BalloonManager::BalloonManager(IStageInfo *stage_info)
: explosions_(std::make_unique<Explosions>()), : explosions_(std::make_unique<Explosions>()), balloon_formations_(std::make_unique<BalloonFormations>()), stage_info_(stage_info) { init(); }
balloon_formations_(std::make_unique<BalloonFormations>()),
stage_info_(stage_info) { init(); }
// Inicializa // Inicializa
void BalloonManager::init() { void BalloonManager::init() {
@@ -64,7 +62,7 @@ void BalloonManager::init() {
// Actualiza // Actualiza
void BalloonManager::update() { void BalloonManager::update() {
for (auto balloon : balloons_) { for (const auto &balloon : balloons_) {
balloon->update(); balloon->update();
} }
updateBalloonDeployCounter(); updateBalloonDeployCounter();
@@ -134,8 +132,8 @@ void BalloonManager::deployFormation(int formation_id, int y) {
// Vacia del vector de globos los globos que ya no sirven // Vacia del vector de globos los globos que ya no sirven
void BalloonManager::freeBalloons() { void BalloonManager::freeBalloons() {
auto it = std::remove_if(balloons_.begin(), balloons_.end(), [](const auto &balloon) { return !balloon->isEnabled(); }); auto result = std::ranges::remove_if(balloons_, [](const auto &balloon) { return !balloon->isEnabled(); });
balloons_.erase(it, balloons_.end()); balloons_.erase(result.begin(), balloons_.end());
} }
// Actualiza la variable enemyDeployCounter // Actualiza la variable enemyDeployCounter
@@ -226,7 +224,7 @@ void BalloonManager::setBalloonSpeed(float speed) {
} }
// Explosiona un globo. Lo destruye y crea otros dos si es el caso // Explosiona un globo. Lo destruye y crea otros dos si es el caso
auto BalloonManager::popBalloon(std::shared_ptr<Balloon> balloon) -> int { auto BalloonManager::popBalloon(const std::shared_ptr<Balloon> &balloon) -> int {
stage_info_->addPower(1); stage_info_->addPower(1);
int score = 0; int score = 0;
@@ -291,7 +289,7 @@ auto BalloonManager::destroyAllBalloons() -> int {
} }
balloon_deploy_counter_ = 300; balloon_deploy_counter_ = 300;
Screen::get()->flash(FLASH_COLOR, 3); Screen::get()->flash(Colors::FLASH, 3);
Screen::get()->shake(); Screen::get()->shake();
return score; return score;
@@ -336,7 +334,7 @@ void BalloonManager::createTwoBigBalloons() {
// Crea una disposición de globos aleatoria // Crea una disposición de globos aleatoria
void BalloonManager::createRandomBalloons() { void BalloonManager::createRandomBalloons() {
const int NUM_BALLOONS = 2 + rand() % 4; const int NUM_BALLOONS = 2 + (rand() % 4);
for (int i = 0; i < NUM_BALLOONS; ++i) { for (int i = 0; i < NUM_BALLOONS; ++i) {
const float X = param.game.game_area.rect.x + (rand() % static_cast<int>(param.game.game_area.rect.w)) - Balloon::WIDTH.at(3); const float X = param.game.game_area.rect.x + (rand() % static_cast<int>(param.game.game_area.rect.w)) - Balloon::WIDTH.at(3);
const int Y = param.game.game_area.rect.y + (rand() % 50); const int Y = param.game.game_area.rect.y + (rand() % 50);

View File

@@ -17,35 +17,36 @@
class Texture; class Texture;
// --- Types ---
using Balloons = std::vector<std::shared_ptr<Balloon>>; using Balloons = std::vector<std::shared_ptr<Balloon>>;
// Clase BalloonManager // --- Clase BalloonManager: gestiona todos los globos del juego ---
class BalloonManager { class BalloonManager {
public: public:
// Constructor y Destructor // --- Constructor y destructor ---
BalloonManager(IStageInfo *stage_info); BalloonManager(IStageInfo *stage_info);
~BalloonManager() = default; ~BalloonManager() = default;
// Actualización y Renderizado // --- Métodos principales ---
void update(); // Actualiza el estado de los globos void update(); // Actualiza el estado de los globos
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
// Creación de formaciones enemigas // --- Creación de formaciones enemigas ---
void deployRandomFormation(int stage); // Crea una formación de globos aleatoria void deployRandomFormation(int stage); // Crea una formación de globos aleatoria
void deployFormation(int formation_id); // Crea una formación específica void deployFormation(int formation_id); // Crea una formación específica
void deployFormation(int formation_id, int y); // Crea una formación específica con coordenadas void deployFormation(int formation_id, int y); // Crea una formación específica con coordenadas
// Creación de globos // --- Creación de globos ---
auto createBalloon(float x, int y, Balloon::Type type, Balloon::Size size, float velx, float speed, int creation_timer) -> std::shared_ptr<Balloon>; // Crea un nuevo globo auto createBalloon(float x, int y, Balloon::Type type, Balloon::Size size, float velx, float speed, int creation_timer) -> std::shared_ptr<Balloon>; // Crea un nuevo globo
void createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction); // Crea un globo a partir de otro void createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction); // Crea un globo a partir de otro
void createPowerBall(); // Crea una PowerBall void createPowerBall(); // Crea una PowerBall
void createTwoBigBalloons(); // Crea dos globos grandes void createTwoBigBalloons(); // Crea dos globos grandes
void createRandomBalloons(); // Crea una disposición aleatoria de globos void createRandomBalloons(); // Crea una disposición aleatoria de globos
// Control de velocidad y despliegue // --- Control de velocidad y despliegue ---
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
@@ -53,61 +54,61 @@ class BalloonManager {
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
// Manipulación de globos existentes // --- Manipulación de globos existentes ---
auto popBalloon(std::shared_ptr<Balloon> balloon) -> int; // Explosiona un globo, creando otros si aplica auto popBalloon(const std::shared_ptr<Balloon> &balloon) -> int; // Explosiona un globo, creando otros si aplica
auto destroyBalloon(std::shared_ptr<Balloon> &balloon) -> int; // Explosiona un globo sin crear otros auto destroyBalloon(std::shared_ptr<Balloon> &balloon) -> int; // Explosiona un globo sin crear otros
auto destroyAllBalloons() -> int; // Destruye todos los globos auto destroyAllBalloons() -> int; // Destruye todos los globos
void stopAllBalloons(); // Detiene el movimiento de los globos void stopAllBalloons(); // Detiene el movimiento de los globos
void startAllBalloons(); // Reactiva el movimiento de los globos void startAllBalloons(); // Reactiva el movimiento de los globos
// Cambios de apariencia // --- Cambios de apariencia ---
void reverseColorsToAllBalloons(); // Invierte los colores de los globos void reverseColorsToAllBalloons(); // Invierte los colores de los globos
void normalColorsToAllBalloons(); // Restaura los colores originales void normalColorsToAllBalloons(); // Restaura los colores originales
// Configuración de sonido // --- Configuración de sonido ---
void setSounds(bool value); // Activa o desactiva los sonidos de los globos void setSounds(bool value); // Activa o desactiva los sonidos de los globos
void setBouncingSounds(bool value); // Activa o desactiva los sonidos de rebote los globos void setBouncingSounds(bool value); // Activa o desactiva los sonidos de rebote los globos
void setPoppingSounds(bool value); // Activa o desactiva los sonidos de los globos al explotar void setPoppingSounds(bool value); // Activa o desactiva los sonidos de los globos al explotar
// Configuración de juego // --- Configuración de juego ---
void setPlayArea(SDL_FRect play_area) { play_area_ = play_area; }; // Define el área de juego void setPlayArea(SDL_FRect play_area) { play_area_ = play_area; }; // Define el área de juego
void setCreationTimeEnabled(bool value) { creation_time_enabled_ = value; }; // Activa o desactiva el tiempo de creación de globos void setCreationTimeEnabled(bool value) { creation_time_enabled_ = value; }; // Activa o desactiva el tiempo de creación de globos
void enableBalloonDeployment(bool value) { can_deploy_balloons_ = value; }; // Activa o desactiva la generación de globos void enableBalloonDeployment(bool value) { can_deploy_balloons_ = value; }; // Activa o desactiva la generación de globos
// Obtención de información // --- Getters ---
auto getMenace() -> int; // Obtiene el nivel de amenaza generado por los globos auto getMenace() -> int; // Obtiene el nivel de amenaza generado por los globos
[[nodiscard]] auto getBalloonSpeed() const -> float { return balloon_speed_; } [[nodiscard]] auto getBalloonSpeed() const -> float { return balloon_speed_; }
auto getBalloons() -> Balloons & { return balloons_; } auto getBalloons() -> Balloons & { return balloons_; }
[[nodiscard]] auto getNumBalloons() const -> int { return balloons_.size(); } [[nodiscard]] auto getNumBalloons() const -> int { return balloons_.size(); }
private: private:
// --- Constantes ---
static const int DEFAULT_BALLOON_DEPLOY_COUNTER = 300; static const int DEFAULT_BALLOON_DEPLOY_COUNTER = 300;
Balloons balloons_; // Vector con los globos activos // --- Objetos y punteros ---
std::unique_ptr<Explosions> explosions_; // Objeto para gestionar explosiones Balloons balloons_; // Vector con los globos activos
std::unique_ptr<BalloonFormations> balloon_formations_; // Objeto para manejar formaciones enemigas std::unique_ptr<Explosions> explosions_; // Objeto para gestionar explosiones
std::unique_ptr<BalloonFormations> balloon_formations_; // Objeto para manejar formaciones enemigas
std::vector<std::shared_ptr<Texture>> balloon_textures_; // Texturas de los globos std::vector<std::shared_ptr<Texture>> balloon_textures_; // Texturas de los globos
std::vector<std::shared_ptr<Texture>> explosions_textures_; // Texturas de explosiones std::vector<std::shared_ptr<Texture>> explosions_textures_; // Texturas de explosiones
std::vector<std::vector<std::string>> balloon_animations_; // Animaciones de los globos std::vector<std::vector<std::string>> balloon_animations_; // Animaciones de los globos
std::vector<std::vector<std::string>> explosions_animations_; // Animaciones de las explosiones std::vector<std::vector<std::string>> explosions_animations_; // Animaciones de las explosiones
IStageInfo *stage_info_; // Informacion de la pantalla actual
// Variables de control de globos // --- Variables de estado ---
SDL_FRect play_area_ = param.game.play_area.rect;
float balloon_speed_ = Balloon::SPEED.at(0); float balloon_speed_ = Balloon::SPEED.at(0);
float default_balloon_speed_ = Balloon::SPEED.at(0); float default_balloon_speed_ = Balloon::SPEED.at(0);
int balloon_deploy_counter_ = 0; int balloon_deploy_counter_ = 0;
bool power_ball_enabled_ = false;
int power_ball_counter_ = 0; int power_ball_counter_ = 0;
int last_balloon_deploy_ = 0; int last_balloon_deploy_ = 0;
SDL_FRect play_area_ = param.game.play_area.rect; bool power_ball_enabled_ = false;
bool creation_time_enabled_ = true; bool creation_time_enabled_ = true;
bool can_deploy_balloons_ = true; bool can_deploy_balloons_ = true;
bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar
bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar
bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido
IStageInfo *stage_info_; // Informacion de la pantalla actual
// Metodos privados // --- Métodos internos ---
void init(); void init();
}; };

View File

@@ -9,10 +9,10 @@
// Constructor // Constructor
Bullet::Bullet(float x, float y, BulletType bullet_type, bool powered, Player::Id owner) Bullet::Bullet(float x, float y, BulletType bullet_type, bool powered, Player::Id owner)
: sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("bullet.png"), Resource::get()->getAnimation("bullet.ani"))), : sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("bullet.png"), Resource::get()->getAnimation("bullet.ani"))),
pos_x_(x),
pos_y_(y),
bullet_type_(bullet_type), bullet_type_(bullet_type),
owner_(owner) { owner_(owner),
pos_x_(x),
pos_y_(y) {
vel_x_ = calculateVelocity(bullet_type_); vel_x_ = calculateVelocity(bullet_type_);
sprite_->setCurrentAnimation(buildAnimationString(bullet_type_, powered)); sprite_->setCurrentAnimation(buildAnimationString(bullet_type_, powered));

View File

@@ -9,65 +9,62 @@
#include "player.h" // Para Player #include "player.h" // Para Player
#include "utils.h" // Para Circle #include "utils.h" // Para Circle
// Tipos de balas // --- Enums ---
enum class BulletType : Uint8 { enum class BulletType : Uint8 {
UP, UP, // Bala hacia arriba
LEFT, LEFT, // Bala hacia la izquierda
RIGHT, RIGHT, // Bala hacia la derecha
NONE NONE // Sin bala
}; };
// Resultado del movimiento de la bala
enum class BulletMoveStatus : Uint8 { enum class BulletMoveStatus : Uint8 {
OK = 0, OK = 0, // Movimiento normal
OUT = 1 OUT = 1 // Fuera de los límites
}; };
// Clase Bullet // --- Clase Bullet: representa una bala del jugador ---
class Bullet { class Bullet {
public: public:
// Constantes // --- Constantes ---
static constexpr float WIDTH = 12.0F; static constexpr float WIDTH = 12.0F; // Anchura de la bala
static constexpr float HEIGHT = 12.0F; static constexpr float HEIGHT = 12.0F; // Altura de la bala
// Constructor y Destructor // --- Constructor y destructor ---
Bullet(float x, float y, BulletType bullet_type, bool powered, Player::Id owner); Bullet(float x, float y, BulletType bullet_type, bool powered, Player::Id owner); // Constructor principal
~Bullet() = default; ~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() -> BulletMoveStatus; // Actualiza el estado del objeto
void disable(); // Desactiva la bala
// Estado de la bala // --- Getters ---
[[nodiscard]] auto isEnabled() const -> bool; // Comprueba si está activa [[nodiscard]] auto isEnabled() const -> bool; // Comprueba si está activa
void disable(); // Desactiva la bala
// Getters
[[nodiscard]] auto getOwner() const -> Player::Id; // Devuelve el identificador del dueño [[nodiscard]] auto getOwner() const -> Player::Id; // Devuelve el identificador del dueño
auto getCollider() -> Circle &; // Devuelve el círculo de colisión auto getCollider() -> Circle &; // Devuelve el círculo de colisión
private: private:
// Constantes // --- Constantes ---
static constexpr float VEL_Y = -3.0F; static constexpr float VEL_Y = -3.0F; // Velocidad vertical
static constexpr float VEL_X_LEFT = -2.0F; static constexpr float VEL_X_LEFT = -2.0F; // Velocidad izquierda
static constexpr float VEL_X_RIGHT = 2.0F; static constexpr float VEL_X_RIGHT = 2.0F; // Velocidad derecha
static constexpr float VEL_X_CENTER = 0.0F; static constexpr float VEL_X_CENTER = 0.0F; // Velocidad central
// Propiedades // --- Objetos y punteros ---
std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos
float pos_x_; // Posición en el eje X // --- Variables de estado ---
float pos_y_; // Posición en el eje Y Circle collider_; // Círculo de colisión
float vel_x_; // Velocidad en el eje X
BulletType bullet_type_; // Tipo de bala BulletType bullet_type_; // Tipo de bala
Player::Id owner_; // Identificador del dueño Player::Id owner_; // Identificador del dueño
Circle collider_; // Círculo de colisión float pos_x_; // Posición en el eje X
float pos_y_; // Posición en el eje Y
float vel_x_; // Velocidad en el eje X
// 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() -> BulletMoveStatus; // Mueve la bala y devuelve su estado
static auto calculateVelocity(BulletType bullet_type) -> float; // Calcula la velocidad horizontal de la bala basada en su tipo 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 basado en el tipo de bala y si está potenciada static auto buildAnimationString(BulletType bullet_type, bool powered) -> std::string; // Construye el string de animación
}; };

View File

@@ -42,22 +42,8 @@ auto Color::fromHex(const std::string &hex_str) -> Color {
return Color(r, g, b, a); return Color(r, g, b, a);
} }
// Obtiene un color del vector de colores imitando al Coche Fantástico // Implementaciones de métodos estáticos de Color
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color { constexpr auto Color::rgbToHsv(Color color) -> HSV {
int cycle_length = colors.size() * 2 - 2;
size_t n = counter % cycle_length;
size_t index;
if (n < colors.size()) {
index = n; // Avanza: 0,1,2,3
} else {
index = 2 * (colors.size() - 1) - n; // Retrocede: 2,1
}
return colors[index];
}
constexpr auto rgbToHsv(Color color) -> HSV {
float r = color.r / 255.0F; float r = color.r / 255.0F;
float g = color.g / 255.0F; float g = color.g / 255.0F;
float b = color.b / 255.0F; float b = color.b / 255.0F;
@@ -84,10 +70,10 @@ constexpr auto rgbToHsv(Color color) -> HSV {
float s = (max <= 0.0F) ? 0.0F : delta / max; float s = (max <= 0.0F) ? 0.0F : delta / max;
float v = max; float v = max;
return {h, s, v}; return {.h = h, .s = s, .v = v};
} }
constexpr auto hsvToRgb(HSV hsv) -> Color { constexpr auto Color::hsvToRgb(HSV hsv) -> Color {
float c = hsv.v * hsv.s; float c = hsv.v * hsv.s;
float x = c * (1 - std::abs(std::fmod(hsv.h / 60.0F, 2) - 1)); float x = c * (1 - std::abs(std::fmod(hsv.h / 60.0F, 2) - 1));
float m = hsv.v - c; float m = hsv.v - c;
@@ -128,55 +114,73 @@ constexpr auto hsvToRgb(HSV hsv) -> Color {
static_cast<uint8_t>(roundf((b + m) * 255))); static_cast<uint8_t>(roundf((b + m) * 255)));
} }
auto generateMirroredCycle(Color base, ColorCycleStyle style) -> ColorCycle { // Implementaciones del namespace Colors
ColorCycle result{}; namespace Colors {
HSV base_hsv = rgbToHsv(base); // Obtiene un color del vector de colores imitando al Coche Fantástico
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color {
int cycle_length = (colors.size() * 2) - 2;
size_t n = counter % cycle_length;
for (size_t i = 0; i < COLOR_CYCLE_SIZE; ++i) { size_t index;
float t = static_cast<float>(i) / (COLOR_CYCLE_SIZE - 1); // 0 → 1 if (n < colors.size()) {
float hue_shift = 0.0F; index = n; // Avanza: 0,1,2,3
float sat_shift = 0.0F; } else {
float val_shift = 0.0F; index = 2 * (colors.size() - 1) - n; // Retrocede: 2,1
switch (style) {
case ColorCycleStyle::SUBTLE_PULSE:
// Solo brillo suave
val_shift = 0.07F * sinf(t * M_PI);
break;
case ColorCycleStyle::HUE_WAVE:
// Oscilación leve de tono
hue_shift = 15.0F * (t - 0.5F) * 2.0F;
val_shift = 0.05F * sinf(t * M_PI);
break;
case ColorCycleStyle::VIBRANT:
// Cambios fuertes en tono y brillo
hue_shift = 35.0F * sinf(t * M_PI);
val_shift = 0.2F * sinf(t * M_PI);
sat_shift = -0.2F * sinf(t * M_PI);
break;
case ColorCycleStyle::DARKEN_GLOW:
// Se oscurece al centro
val_shift = -0.15F * sinf(t * M_PI);
break;
case ColorCycleStyle::LIGHT_FLASH:
// Se ilumina al centro
val_shift = 0.25F * sinf(t * M_PI);
break;
} }
HSV adjusted = { return colors[index];
fmodf(base_hsv.h + hue_shift + 360.0F, 360.0F),
fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)),
fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))};
Color c = hsvToRgb(adjusted);
result[i] = c;
result[2 * COLOR_CYCLE_SIZE - 1 - i] = c; // espejo
} }
return result; auto generateMirroredCycle(Color base, ColorCycleStyle style) -> Cycle {
Cycle result{};
HSV base_hsv = Color::rgbToHsv(base);
for (size_t i = 0; i < CYCLE_SIZE; ++i) {
float t = static_cast<float>(i) / (CYCLE_SIZE - 1); // 0 → 1
float hue_shift = 0.0F;
float sat_shift = 0.0F;
float val_shift = 0.0F;
switch (style) {
case ColorCycleStyle::SUBTLE_PULSE:
// Solo brillo suave
val_shift = 0.07F * sinf(t * M_PI);
break;
case ColorCycleStyle::HUE_WAVE:
// Oscilación leve de tono
hue_shift = 15.0F * (t - 0.5F) * 2.0F;
val_shift = 0.05F * sinf(t * M_PI);
break;
case ColorCycleStyle::VIBRANT:
// Cambios fuertes en tono y brillo
hue_shift = 35.0F * sinf(t * M_PI);
val_shift = 0.2F * sinf(t * M_PI);
sat_shift = -0.2F * sinf(t * M_PI);
break;
case ColorCycleStyle::DARKEN_GLOW:
// Se oscurece al centro
val_shift = -0.15F * sinf(t * M_PI);
break;
case ColorCycleStyle::LIGHT_FLASH:
// Se ilumina al centro
val_shift = 0.25F * sinf(t * M_PI);
break;
}
HSV adjusted = {
.h = fmodf(base_hsv.h + hue_shift + 360.0F, 360.0F),
.s = fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)),
.v = fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))};
Color c = Color::hsvToRgb(adjusted);
result[i] = c;
result[(2 * CYCLE_SIZE) - 1 - i] = c; // espejo
}
return result;
}
} }

View File

@@ -9,12 +9,14 @@
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector #include <vector> // Para vector
// --- Constantes --- // --- Estructura HSV: define un color en formato HSV ---
constexpr size_t COLOR_CYCLE_SIZE = 6; // Mitad del ciclo espejado struct HSV {
float h; // Matiz (Hue)
float s; // Saturación (Saturation)
float v; // Valor (Value)
};
// --- Estructuras y tipos --- // --- Estructura Color: define un color RGBA ---
// Estructura para definir un color RGBA
struct Color { struct Color {
private: private:
static constexpr int DEFAULT_LIGHTEN_AMOUNT = 50; static constexpr int DEFAULT_LIGHTEN_AMOUNT = 50;
@@ -62,6 +64,10 @@ struct Color {
// Método estático para crear Color desde string hexadecimal // Método estático para crear Color desde string hexadecimal
static auto fromHex(const std::string &hex_str) -> Color; static auto fromHex(const std::string &hex_str) -> Color;
// Conversiones de formato de color
[[nodiscard]] constexpr static auto rgbToHsv(Color color) -> HSV;
[[nodiscard]] constexpr static auto hsvToRgb(HSV hsv) -> Color;
[[nodiscard]] constexpr auto IS_EQUAL_TO(const Color &other) const -> bool { [[nodiscard]] constexpr auto IS_EQUAL_TO(const Color &other) const -> bool {
return r == other.r && g == other.g && b == other.b && a == other.a; return r == other.r && g == other.g && b == other.b && a == other.a;
} }
@@ -83,7 +89,7 @@ struct Color {
} }
// Convierte el color a un entero de 32 bits en formato RGBA // Convierte el color a un entero de 32 bits en formato RGBA
[[nodiscard]] constexpr auto toUint32() const -> Uint32 { [[nodiscard]] constexpr auto TO_UINT32() const -> Uint32 {
return (static_cast<Uint32>(r) << 24) | return (static_cast<Uint32>(r) << 24) |
(static_cast<Uint32>(g) << 16) | (static_cast<Uint32>(g) << 16) |
(static_cast<Uint32>(b) << 8) | (static_cast<Uint32>(b) << 8) |
@@ -91,12 +97,7 @@ struct Color {
} }
}; };
// Estructura para definir un color HSV // --- Enum ColorCycleStyle: define estilos de ciclo de color ---
struct HSV {
float h, s, v;
};
// Estructura para definir el ciclo de color
enum class ColorCycleStyle { enum class ColorCycleStyle {
SUBTLE_PULSE, // Variación leve en brillo (por defecto) SUBTLE_PULSE, // Variación leve en brillo (por defecto)
HUE_WAVE, // Variación suave en tono (sin verde) HUE_WAVE, // Variación suave en tono (sin verde)
@@ -105,23 +106,27 @@ enum class ColorCycleStyle {
LIGHT_FLASH // Ilumina hacia el centro y regresa LIGHT_FLASH // Ilumina hacia el centro y regresa
}; };
// --- Alias --- // --- Namespace Colors: constantes y utilidades de color ---
using ColorCycle = std::array<Color, 2 * COLOR_CYCLE_SIZE>; namespace Colors {
// --- Constantes ---
constexpr size_t CYCLE_SIZE = 6; // Mitad del ciclo espejado
// --- Colores predefinidos --- // --- Alias ---
constexpr Color NO_TEXT_COLOR = Color(0XFF, 0XFF, 0XFF); using Cycle = std::array<Color, 2 * CYCLE_SIZE>;
constexpr Color SHADOW_TEXT_COLOR = Color(0X43, 0X43, 0X4F);
constexpr Color TITLE_SHADOW_TEXT_COLOR = Color(0x14, 0x87, 0xc4);
constexpr Color ORANGE_TEXT_COLOR = Color(0XFF, 0X7A, 0X00);
constexpr Color FLASH_COLOR = Color(0XFF, 0XFF, 0XFF); // --- Colores predefinidos ---
constexpr Color NO_COLOR_MOD = Color(0XFF, 0XFF, 0XFF);
constexpr Color SHADOW_TEXT = Color(0X43, 0X43, 0X4F);
constexpr Color TITLE_SHADOW_TEXT = Color(0x14, 0x87, 0xc4);
constexpr Color ORANGE_TEXT = Color(0XFF, 0X7A, 0X00);
constexpr Color BLUE_SKY_COLOR = Color(0X02, 0X88, 0XD1); constexpr Color FLASH = Color(0XFF, 0XFF, 0XFF);
constexpr Color PINK_SKY_COLOR = Color(0XFF, 0X6B, 0X97);
constexpr Color GREEN_SKY_COLOR = Color(0X00, 0X79, 0X6B);
// --- Funciones --- constexpr Color BLUE_SKY = Color(0X02, 0X88, 0XD1);
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color; constexpr Color PINK_SKY = Color(0XFF, 0X6B, 0X97);
constexpr auto rgbToHsv(Color color) -> HSV; constexpr Color GREEN_SKY = Color(0X00, 0X79, 0X6B);
constexpr auto hsvToRgb(HSV hsv) -> Color;
auto generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SUBTLE_PULSE) -> ColorCycle; // --- Funciones ---
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color;
auto generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SUBTLE_PULSE) -> Cycle;
}

View File

@@ -1,47 +1,49 @@
#pragma once #pragma once
#include <SDL3/SDL.h> // Para SDL_ScaleMode
#include <array> #include <array>
#include "color.h" #include "color.h"
#include "ui/notifier.h" // Para Notifier::Position #include "ui/notifier.h" // Para Notifier::Position
// Configuración centralizada con todos los valores por defecto del juego // --- Namespace GameDefaults: configuración centralizada con valores por defecto del juego ---
namespace GameDefaults { namespace GameDefaults {
// --- GAME --- // --- GAME ---
namespace Game { 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 = 10;
constexpr int NAME_ENTRY_TOTAL_TIME = 60; constexpr int NAME_ENTRY_TOTAL_TIME = 60;
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
// Play area por defecto // Play area por defecto
constexpr float PLAY_AREA_X = 0.0f; constexpr float PLAY_AREA_X = 0.0F;
constexpr float PLAY_AREA_Y = 0.0f; constexpr float PLAY_AREA_Y = 0.0F;
constexpr float PLAY_AREA_W = 320.0f; constexpr float PLAY_AREA_W = 320.0F;
constexpr float PLAY_AREA_H = 216.0f; constexpr float PLAY_AREA_H = 216.0F;
} // namespace Game } // namespace Game
// --- FADE --- // --- FADE ---
namespace Fade { namespace Fade {
constexpr const char* COLOR = "1F2B30"; constexpr const char* COLOR = "1F2B30";
constexpr float NUM_SQUARES_WIDTH = 160.0f; constexpr float NUM_SQUARES_WIDTH = 160.0F;
constexpr float NUM_SQUARES_HEIGHT = 128.0f; constexpr float NUM_SQUARES_HEIGHT = 128.0F;
constexpr int RANDOM_SQUARES_DELAY = 1; constexpr int RANDOM_SQUARES_DURATION_MS = 1;
constexpr int RANDOM_SQUARES_MULT = 500; constexpr int POST_DURATION_MS = 80;
constexpr int POST_DURATION = 80; constexpr float VENETIAN_SIZE = 12.0F;
constexpr float VENETIAN_SIZE = 12.0f;
} // namespace Fade } // namespace Fade
// --- SCOREBOARD --- // --- SCOREBOARD ---
namespace Scoreboard { namespace Scoreboard {
constexpr float RECT_X = 0.0f; constexpr float RECT_X = 0.0F;
constexpr float RECT_Y = 216.0f; constexpr float RECT_Y = 216.0F;
constexpr float RECT_W = 320.0f; constexpr float RECT_W = 320.0F;
constexpr float RECT_H = 40.0f; constexpr float RECT_H = 40.0F;
constexpr bool SEPARATOR_AUTOCOLOR = true; constexpr bool SEPARATOR_AUTOCOLOR = true;
constexpr const char* SEPARATOR_COLOR = "0D1A2B"; constexpr const char* SEPARATOR_COLOR = "0D1A2B";
constexpr const char* EASY_COLOR = "4B692F"; constexpr const char* EASY_COLOR = "4B692F";
@@ -73,18 +75,22 @@ namespace Balloon {
struct BalloonSettings { struct BalloonSettings {
float vel; float vel;
float grav; float grav;
constexpr BalloonSettings(float v, float g) : vel(v), grav(g) {} constexpr BalloonSettings(float v, float g)
: vel(v), grav(g) {}
}; };
constexpr std::array<BalloonSettings, 4> SETTINGS = {{ constexpr std::array<BalloonSettings, 4> SETTINGS = {{
BalloonSettings(2.75f, 0.09f), // Globo 0 BalloonSettings(2.75F, 0.09F), // Globo 0
BalloonSettings(3.70f, 0.10f), // Globo 1 BalloonSettings(3.70F, 0.10F), // Globo 1
BalloonSettings(4.70f, 0.10f), // Globo 2 BalloonSettings(4.70F, 0.10F), // Globo 2
BalloonSettings(5.45f, 0.10f) // Globo 3 BalloonSettings(5.45F, 0.10F) // Globo 3
}}; }};
constexpr std::array<const char*, 4> COLORS = { constexpr std::array<const char*, 4> COLORS = {
"blue", "orange", "red", "green"}; "blue",
"orange",
"red",
"green"};
constexpr bool BOUNCING_SOUND = false; constexpr bool BOUNCING_SOUND = false;
} // namespace Balloon } // namespace Balloon
@@ -111,15 +117,15 @@ constexpr const char* BG_COLOR = "141E32F0"; // Color(20, 30, 50, 240)
constexpr const char* BORDER_COLOR = "6496C8FF"; // Color(100, 150, 200, 255) constexpr const char* BORDER_COLOR = "6496C8FF"; // Color(100, 150, 200, 255)
constexpr const char* TITLE_COLOR = "6496C8FF"; // Color(100, 150, 200, 255) constexpr const char* TITLE_COLOR = "6496C8FF"; // Color(100, 150, 200, 255)
constexpr const char* TEXT_COLOR = "DCDCDCFF"; // Color(220, 220, 220, 255) constexpr const char* TEXT_COLOR = "DCDCDCFF"; // Color(220, 220, 220, 255)
constexpr float PADDING = 15.0f; constexpr float PADDING = 15.0F;
constexpr float LINE_SPACING = 5.0f; constexpr float LINE_SPACING = 5.0F;
constexpr float TITLE_SEPARATOR_SPACING = 10.0f; // Cambiado a float constexpr float TITLE_SEPARATOR_SPACING = 10.0F; // Cambiado a float
constexpr float MIN_WIDTH = 250.0f; constexpr float MIN_WIDTH = 250.0F;
constexpr float MIN_HEIGHT = 32.0f; // Cambiado a float constexpr float MIN_HEIGHT = 32.0F; // Cambiado a float
constexpr float MAX_WIDTH_RATIO = 0.8f; // Nuevo constexpr float MAX_WIDTH_RATIO = 0.8F; // Nuevo
constexpr float MAX_HEIGHT_RATIO = 0.8f; // Nuevo constexpr float MAX_HEIGHT_RATIO = 0.8F; // Nuevo
constexpr float TEXT_SAFETY_MARGIN = 15.0f; constexpr float TEXT_SAFETY_MARGIN = 15.0F;
constexpr float ANIMATION_DURATION = 0.3f; constexpr float ANIMATION_DURATION = 0.3F;
} // namespace WindowMessage } // namespace WindowMessage
} // namespace ServiceMenu } // namespace ServiceMenu
@@ -143,12 +149,26 @@ constexpr const char* COLOR = "FFFFFF";
// --- TABE --- // --- TABE ---
namespace Tabe { namespace Tabe {
constexpr float MIN_SPAWN_TIME = 2.0f; constexpr float MIN_SPAWN_TIME = 2.0F;
constexpr float MAX_SPAWN_TIME = 3.0f; constexpr float MAX_SPAWN_TIME = 3.0F;
} // namespace Tabe } // namespace Tabe
// --- PLAYER --- // --- PLAYER ---
namespace Player { namespace Player {
namespace DefaultShirt {
// Player 0 (Jugador 1)
constexpr const char* PLAYER0_DARKEST = "028ECFFF"; // 2, 142, 207, 255
constexpr const char* PLAYER0_DARK = "0297DBFF"; // 2, 151, 219, 255
constexpr const char* PLAYER0_BASE = "029FE8FF"; // 2, 159, 232, 255
constexpr const char* PLAYER0_LIGHT = "03A9F4FF"; // 3, 169, 244, 255
// Player 1 (Jugador 2)
constexpr const char* PLAYER1_DARKEST = "8E8E8EFF"; // 142, 142, 142, 255
constexpr const char* PLAYER1_DARK = "AEADADFF"; // 174, 173, 173, 255
constexpr const char* PLAYER1_BASE = "E4E4E4FF"; // 228, 228, 228, 255
constexpr const char* PLAYER1_LIGHT = "F7F1F1FF"; // 247, 241, 241, 255
} // namespace DefaultShirt
namespace OneCoffeeShirt { namespace OneCoffeeShirt {
// Player 0 (Jugador 1) // Player 0 (Jugador 1)
constexpr const char* PLAYER0_DARKEST = "3D9C70FF"; // 61, 156, 112, 255 constexpr const char* PLAYER0_DARKEST = "3D9C70FF"; // 61, 156, 112, 255
@@ -176,5 +196,45 @@ constexpr const char* PLAYER1_DARK = "FA7D00FF"; // 250, 125, 0, 255
constexpr const char* PLAYER1_BASE = "FAA200FF"; // 250, 162, 0, 255 constexpr const char* PLAYER1_BASE = "FAA200FF"; // 250, 162, 0, 255
constexpr const char* PLAYER1_LIGHT = "FA8500FF"; // 250, 133, 0, 255 constexpr const char* PLAYER1_LIGHT = "FA8500FF"; // 250, 133, 0, 255
} // namespace TwoCoffeeShirt } // namespace TwoCoffeeShirt
namespace OutlineColor {
// Player 0 (Jugador 1)
constexpr const char* PLAYER0 = "66323FFF";
// Player 1 (Jugador 2)
constexpr const char* PLAYER1 = "422028FF";
} // namespace OutlineColor
} // namespace Player } // namespace Player
// --- OPTIONS ---
namespace Options {
// Window
constexpr const char* WINDOW_CAPTION = "Coffee Crisis Arcade Edition";
constexpr int WINDOW_ZOOM = 2;
constexpr int WINDOW_MAX_ZOOM = 2;
// Video
constexpr SDL_ScaleMode VIDEO_SCALE_MODE = SDL_ScaleMode::SDL_SCALEMODE_NEAREST;
constexpr bool VIDEO_FULLSCREEN = false;
constexpr bool VIDEO_VSYNC = true;
constexpr bool VIDEO_INTEGER_SCALE = true;
constexpr bool VIDEO_SHADERS = false;
// Music
constexpr bool MUSIC_ENABLED = true;
constexpr int MUSIC_VOLUME = 100;
// Sound
constexpr bool SOUND_ENABLED = true;
constexpr int SOUND_VOLUME = 100;
// Audio
constexpr bool AUDIO_ENABLED = true;
constexpr int AUDIO_VOLUME = 100;
// Settings
constexpr bool SETTINGS_AUTOFIRE = true;
constexpr bool SETTINGS_SHUTDOWN_ENABLED = false;
constexpr const char* PARAMS_FILE = "param_320x256.txt";
} // namespace Options
} // namespace GameDefaults } // namespace GameDefaults

View File

@@ -19,7 +19,7 @@ DefineButtons::DefineButtons()
clearButtons(); clearButtons();
auto gamepads = input_->getGamepads(); auto gamepads = input_->getGamepads();
for (auto gamepad : gamepads) { for (const auto &gamepad : gamepads) {
controller_names_.emplace_back(Input::getControllerName(gamepad)); controller_names_.emplace_back(Input::getControllerName(gamepad));
} }
@@ -72,6 +72,9 @@ void DefineButtons::handleEvents(const SDL_Event &event) {
case SDL_EVENT_GAMEPAD_BUTTON_UP: case SDL_EVENT_GAMEPAD_BUTTON_UP:
checkEnd(); checkEnd();
break; break;
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
doControllerAxisMotion(event.gaxis);
break;
default: default:
break; break;
} }
@@ -86,6 +89,8 @@ auto DefineButtons::enable(Options::Gamepad *options_gamepad) -> bool {
index_button_ = 0; index_button_ = 0;
message_shown_ = false; message_shown_ = false;
closing_ = false; closing_ = false;
l2_was_pressed_ = false;
r2_was_pressed_ = false;
clearButtons(); clearButtons();
updateWindowMessage(); updateWindowMessage();
@@ -104,6 +109,8 @@ void DefineButtons::disable() {
finished_ = false; finished_ = false;
message_shown_ = false; message_shown_ = false;
closing_ = false; closing_ = false;
l2_was_pressed_ = false;
r2_was_pressed_ = false;
if (window_message_) { if (window_message_) {
window_message_->hide(); window_message_->hide();
@@ -119,15 +126,65 @@ void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent &event)
const auto BUTTON = static_cast<SDL_GamepadButton>(event.button); const auto BUTTON = static_cast<SDL_GamepadButton>(event.button);
if (checkButtonNotInUse(BUTTON)) { if (checkButtonNotInUse(BUTTON)) {
buttons_.at(index_button_).button = BUTTON; buttons_.at(index_button_).button = static_cast<int>(BUTTON);
incIndexButton(); incIndexButton();
updateWindowMessage(); updateWindowMessage();
} }
} }
void DefineButtons::doControllerAxisMotion(const SDL_GamepadAxisEvent &event) {
auto gamepad = input_->getGamepad(event.which);
if (!gamepad || gamepad != options_gamepad_->instance) {
return;
}
// Solo manejamos L2 y R2 como botones con lógica de transición
if (event.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER) {
bool l2_is_pressed_now = event.value > 16384;
// Solo actuar en la transición de no presionado a presionado
if (l2_is_pressed_now && !l2_was_pressed_) {
const auto TRIGGER_BUTTON = Input::TRIGGER_L2_AS_BUTTON;
if (checkTriggerNotInUse(TRIGGER_BUTTON)) {
buttons_.at(index_button_).button = TRIGGER_BUTTON;
incIndexButton();
updateWindowMessage();
}
}
// Detectar liberación del trigger para llamar checkEnd()
if (!l2_is_pressed_now && l2_was_pressed_) {
checkEnd();
}
l2_was_pressed_ = l2_is_pressed_now;
} else if (event.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
bool r2_is_pressed_now = event.value > 16384;
// Solo actuar en la transición de no presionado a presionado
if (r2_is_pressed_now && !r2_was_pressed_) {
const auto TRIGGER_BUTTON = Input::TRIGGER_R2_AS_BUTTON;
if (checkTriggerNotInUse(TRIGGER_BUTTON)) {
buttons_.at(index_button_).button = TRIGGER_BUTTON;
incIndexButton();
updateWindowMessage();
}
}
// Detectar liberación del trigger para llamar checkEnd()
if (!r2_is_pressed_now && r2_was_pressed_) {
checkEnd();
}
r2_was_pressed_ = r2_is_pressed_now;
}
}
void DefineButtons::bindButtons(Options::Gamepad *options_gamepad) { void DefineButtons::bindButtons(Options::Gamepad *options_gamepad) {
for (const auto &button : buttons_) { for (const auto &button : buttons_) {
Input::bindGameControllerButton(options_gamepad->instance, button.action, button.button); Input::bindGameControllerButton(options_gamepad->instance, button.action, static_cast<SDL_GamepadButton>(button.button));
} }
Input::bindGameControllerButton(options_gamepad->instance, Input::Action::SM_SELECT, Input::Action::FIRE_LEFT); Input::bindGameControllerButton(options_gamepad->instance, Input::Action::SM_SELECT, Input::Action::FIRE_LEFT);
@@ -148,13 +205,19 @@ auto DefineButtons::checkButtonNotInUse(SDL_GamepadButton button) -> bool {
}); });
} }
auto DefineButtons::checkTriggerNotInUse(int trigger_button) -> bool {
return std::ranges::all_of(buttons_, [trigger_button](const auto &b) {
return b.button != trigger_button;
});
}
void DefineButtons::clearButtons() { void DefineButtons::clearButtons() {
buttons_.clear(); buttons_.clear();
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_LEFT"), Input::Action::FIRE_LEFT, SDL_GAMEPAD_BUTTON_INVALID); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_LEFT"), Input::Action::FIRE_LEFT, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_UP"), Input::Action::FIRE_CENTER, SDL_GAMEPAD_BUTTON_INVALID); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_UP"), Input::Action::FIRE_CENTER, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_RIGHT"), Input::Action::FIRE_RIGHT, SDL_GAMEPAD_BUTTON_INVALID); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_RIGHT"), Input::Action::FIRE_RIGHT, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] START"), Input::Action::START, SDL_GAMEPAD_BUTTON_INVALID); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] START"), Input::Action::START, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] SERVICE_MENU"), Input::Action::SERVICE, SDL_GAMEPAD_BUTTON_INVALID); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] SERVICE_MENU"), Input::Action::SERVICE, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
} }
void DefineButtons::checkEnd() { void DefineButtons::checkEnd() {

View File

@@ -15,57 +15,63 @@ namespace Options {
struct Gamepad; struct Gamepad;
} }
// --- Clase DefineButtons: configuración de botones de gamepad ---
class DefineButtons { class DefineButtons {
public: public:
// --- Estructuras ---
struct Button { struct Button {
std::string label; std::string label;
Input::Action action; Input::Action action;
SDL_GamepadButton button; int button;
Button(std::string label, Input::Action action, SDL_GamepadButton button) Button(std::string label, Input::Action action, int button)
: label(std::move(label)), action(action), button(button) {} : label(std::move(label)), action(action), button(button) {}
}; };
// --- Constructor y destructor ---
DefineButtons(); DefineButtons();
~DefineButtons() = default; ~DefineButtons() = default;
// --- Métodos principales ---
void render(); void render();
void update(); void update();
void handleEvents(const SDL_Event &event); void handleEvents(const SDL_Event &event);
auto enable(Options::Gamepad *options_gamepad) -> bool; auto enable(Options::Gamepad *options_gamepad) -> bool;
void disable(); void disable();
// --- Getters ---
[[nodiscard]] auto isReadyToClose() const -> bool; [[nodiscard]] auto isReadyToClose() const -> bool;
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; } [[nodiscard]] auto isEnabled() const -> bool { return enabled_; }
[[nodiscard]] auto isFinished() const -> bool { return finished_; } [[nodiscard]] auto isFinished() const -> bool { return finished_; }
private: private:
// Constante para cuánto tiempo mostrar el mensaje (en frames) // --- Constantes ---
static constexpr size_t MESSAGE_DISPLAY_FRAMES = 120; // ~2 segundos a 60fps static constexpr size_t MESSAGE_DISPLAY_FRAMES = 120; // Cuánto tiempo mostrar el mensaje (en frames) ~2 segundos a 60fps
// Punteros // --- Objetos y punteros ---
Input *input_ = nullptr; // Entrada del usuario Input *input_ = nullptr; // Entrada del usuario
Options::Gamepad *options_gamepad_ = nullptr; // Opciones del gamepad Options::Gamepad *options_gamepad_ = nullptr; // Opciones del gamepad
std::unique_ptr<WindowMessage> window_message_; // Mensaje de ventana std::unique_ptr<WindowMessage> window_message_; // Mensaje de ventana
// Vectores y strings // --- Variables de estado ---
std::vector<Button> buttons_; // Lista de botones std::vector<Button> buttons_; // Lista de botones
std::vector<std::string> controller_names_; // Nombres de los controladores std::vector<std::string> controller_names_; // Nombres de los controladores
size_t index_button_ = 0; // Índice del botón seleccionado
size_t message_timer_ = 0; // Contador de frames para el mensaje
bool enabled_ = false; // Flag para indicar si está activo
bool finished_ = false; // Flag para indicar si ha terminado
bool closing_ = false; // Flag para indicar que está cerrando
bool message_shown_ = false; // Flag para indicar que ya mostró el mensaje
bool l2_was_pressed_ = false; // Estado anterior del trigger L2
bool r2_was_pressed_ = false; // Estado anterior del trigger R2
// size_t // --- Métodos internos ---
size_t index_button_ = 0; // Índice del botón seleccionado
size_t message_timer_ = 0; // Contador de frames para el mensaje
// bools
bool enabled_ = false; // Flag para indicar si está activo
bool finished_ = false; // Flag para indicar si ha terminado
bool closing_ = false; // Flag para indicar que está cerrando
bool message_shown_ = false; // Flag para indicar que ya mostró el mensaje
// Métodos
void incIndexButton(); void incIndexButton();
void doControllerButtonDown(const SDL_GamepadButtonEvent &event); void doControllerButtonDown(const SDL_GamepadButtonEvent &event);
void doControllerAxisMotion(const SDL_GamepadAxisEvent &event);
void bindButtons(Options::Gamepad *options_gamepad); void bindButtons(Options::Gamepad *options_gamepad);
auto checkButtonNotInUse(SDL_GamepadButton button) -> bool; auto checkButtonNotInUse(SDL_GamepadButton button) -> bool;
auto checkTriggerNotInUse(int trigger_button) -> bool;
void clearButtons(); void clearButtons();
void checkEnd(); void checkEnd();
void updateWindowMessage(); void updateWindowMessage();

View File

@@ -8,9 +8,9 @@ static std::vector<Info> difficulties_list;
void init() { void init() {
difficulties_list = { difficulties_list = {
{Code::EASY, "Easy"}, {.code = Code::EASY, .name = "Easy"},
{Code::NORMAL, "Normal"}, {.code = Code::NORMAL, .name = "Normal"},
{Code::HARD, "Hard"}}; {.code = Code::HARD, .name = "Hard"}};
} }
auto getDifficulties() -> std::vector<Info>& { auto getDifficulties() -> std::vector<Info>& {

View File

@@ -1,52 +1,28 @@
#pragma once #pragma once
#include <string> #include <string> // Para string
#include <vector> #include <vector> // Para vector
namespace Difficulty { namespace Difficulty {
/** // --- Enums ---
* @brief Códigos que identifican unívocamente cada nivel de dificultad.
*/
enum class Code { enum class Code {
EASY = 0, EASY = 0, // Dificultad fácil
NORMAL = 1, NORMAL = 1, // Dificultad normal
HARD = 2, HARD = 2, // Dificultad difícil
}; };
/** // --- Estructuras ---
* @brief Estructura que asocia un código de dificultad con su nombre traducible.
*/
struct Info { struct Info {
Code code; Code code; // Código de dificultad
std::string name; std::string name; // Nombre traducible
}; };
// --- Interfaz Pública --- // --- Funciones ---
void init(); // Inicializa la lista de dificultades con sus valores por defecto
/** auto getDifficulties() -> std::vector<Info>&; // Devuelve una referencia al vector de todas las dificultades
* @brief Inicializa la lista de dificultades con sus valores por defecto. auto getNameFromCode(Code code) -> std::string; // Obtiene el nombre de una dificultad a partir de su código
*/ auto getCodeFromName(const std::string& name) -> Code; // Obtiene el código de una dificultad a partir de su nombre
void init();
/**
* @brief Devuelve una referencia al vector de todas las dificultades para su lectura o modificación.
* @return Referencia a `std::vector<Info>&`.
*/
auto getDifficulties() -> std::vector<Info>&;
/**
* @brief Obtiene el nombre de una dificultad a partir de su código.
* @param code El código de la dificultad.
* @return El nombre de la dificultad.
*/
auto getNameFromCode(Code code) -> std::string;
/**
* @brief Obtiene el código de una dificultad a partir de su nombre.
* @param name El nombre de la dificultad.
* @return El código de la dificultad.
*/
auto getCodeFromName(const std::string& name) -> Code;
} // namespace Difficulty } // namespace Difficulty

View File

@@ -124,9 +124,9 @@ void Director::close() {
void Director::loadParams() { void Director::loadParams() {
// Carga los parametros para configurar el juego // Carga los parametros para configurar el juego
#ifdef ANBERNIC #ifdef ANBERNIC
const std::string paramFilePath = asset->get("param_320x240.txt"); const std::string PARAM_FILE_PATH = Asset::get()->get("param_320x240.txt");
#else #else
const std::string PARAM_FILE_PATH = overrides.param_file == "--320x240" ? Asset::get()->get("param_320x240.txt") : Asset::get()->get("param_320x256.txt"); const std::string PARAM_FILE_PATH = Asset::get()->get(Options::settings.params_file);
#endif #endif
loadParamsFromFile(PARAM_FILE_PATH); loadParamsFromFile(PARAM_FILE_PATH);
} }
@@ -188,7 +188,7 @@ void Director::createSystemFolder(const std::string &folder) {
if (result != SystemUtils::Result::SUCCESS) { if (result != SystemUtils::Result::SUCCESS) {
std::cerr << "Error creando carpeta del sistema: " std::cerr << "Error creando carpeta del sistema: "
<< SystemUtils::resultToString(result) << std::endl; << SystemUtils::resultToString(result) << '\n';
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
@@ -321,7 +321,7 @@ void Director::shutdownSystem(bool should_shutdown) {
auto result = SystemShutdown::shutdownSystem(5, true); // 5 segundos, forzar apps auto result = SystemShutdown::shutdownSystem(5, true); // 5 segundos, forzar apps
if (result != SystemShutdown::ShutdownResult::SUCCESS) { if (result != SystemShutdown::ShutdownResult::SUCCESS) {
std::cerr << SystemShutdown::resultToString(result) << std::endl; std::cerr << SystemShutdown::resultToString(result) << '\n';
} }
} }
} }

View File

@@ -7,6 +7,7 @@ namespace Lang {
enum class Code : int; enum class Code : int;
} }
// --- Clase Director: gestor principal de la aplicación ---
class Director { class Director {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---

View File

@@ -42,7 +42,7 @@ void EnterName::incPosition() {
if (position_ >= NAME_SIZE) { if (position_ >= NAME_SIZE) {
position_ = NAME_SIZE; // Mantenemos en el índice máximo válido. position_ = NAME_SIZE; // Mantenemos en el índice máximo válido.
position_overflow_ = true; // Activamos el flag de overflow. position_overflow_ = true; // Activamos el flag de overflow.
} else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGHT } else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGTH
{ {
// Copiamos el índice del carácter anterior si es posible. // Copiamos el índice del carácter anterior si es posible.
character_index_[position_] = character_index_[position_ - 1]; character_index_[position_] = character_index_[position_ - 1];
@@ -74,7 +74,7 @@ void EnterName::decPosition() {
// character_index_[position_] = 0; // character_index_[position_] = 0;
} }
// Si position_ es menor que NAME_LENGHT, aseguramos que el overflow esté desactivado. // Si position_ es menor que NAME_LENGTH, aseguramos que el overflow esté desactivado.
if (position_ < NAME_SIZE) { if (position_ < NAME_SIZE) {
position_overflow_ = false; position_overflow_ = false;
} }

View File

@@ -6,10 +6,10 @@
#include "utils.h" // Para trim #include "utils.h" // Para trim
// Tamaño máximo del nombre // --- Constantes ---
constexpr size_t NAME_SIZE = 5; constexpr size_t NAME_SIZE = 5; // Tamaño máximo del nombre
// Clase EnterName // --- Clase EnterName: gestor de entrada de nombre del jugador ---
class EnterName { class EnterName {
public: public:
EnterName(); EnterName();
@@ -29,11 +29,12 @@ class EnterName {
[[nodiscard]] auto getPositionOverflow() const -> bool { return position_overflow_; } // Indica si la posición excede el límite [[nodiscard]] auto getPositionOverflow() const -> bool { return position_overflow_; } // Indica si la posición excede el límite
private: private:
// --- Variables de estado ---
std::string character_list_; // Lista de caracteres permitidos std::string character_list_; // Lista de caracteres permitidos
std::string name_; // Nombre en proceso std::string name_; // Nombre en proceso
std::array<int, NAME_SIZE> character_index_; // Índices a "character_list_"
size_t position_ = 0; // Índice del carácter que se edita size_t position_ = 0; // Índice del carácter que se edita
bool position_overflow_ = false; // Flag para exceder límite bool position_overflow_ = false; // Flag para exceder límite
std::array<int, NAME_SIZE> character_index_; // Índices a "character_list_"
void updateNameFromCharacterIndex(); // Actualiza "name_" según "character_index_" void updateNameFromCharacterIndex(); // Actualiza "name_" según "character_index_"
void initCharacterIndex(const std::string &name); // Inicializa índices desde el nombre void initCharacterIndex(const std::string &name); // Inicializa índices desde el nombre

View File

@@ -24,7 +24,7 @@ void Explosions::render() {
} }
// Añade texturas al objeto // Añade texturas al objeto
void Explosions::addTexture(int size, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation) { void Explosions::addTexture(int size, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation) {
textures_.emplace_back(size, texture, animation); textures_.emplace_back(size, texture, animation);
} }

View File

@@ -1,15 +1,15 @@
#pragma once #pragma once
#include <memory> // Para unique_ptr, shared_ptr #include <memory> // Para unique_ptr, shared_ptr
#include <string> // Para string #include <string> // Para string
#include <utility> #include <utility> // Para move
#include <vector> // Para vector #include <vector> // Para vector
#include "animated_sprite.h" // Para AnimatedSprite #include "animated_sprite.h" // Para AnimatedSprite
class Texture; class Texture;
// Estructura para almacenar la información de una textura de explosión // --- Estructura ExplosionTexture: almacena información de una textura de explosión ---
struct ExplosionTexture { struct ExplosionTexture {
int size; // Tamaño de la explosión int size; // Tamaño de la explosión
std::shared_ptr<Texture> texture; // Textura para la explosión std::shared_ptr<Texture> texture; // Textura para la explosión
@@ -19,35 +19,27 @@ struct ExplosionTexture {
: size(sz), texture(std::move(tex)), animation(anim) {} : size(sz), texture(std::move(tex)), animation(anim) {}
}; };
// Clase Explosions // --- Clase Explosions: gestor de explosiones ---
class Explosions { class Explosions {
public: public:
// Constructor y destructor // --- Constructor y destructor ---
Explosions() = default; Explosions() = default; // Constructor por defecto
~Explosions() = default; ~Explosions() = default; // Destructor por defecto
// Actualiza la lógica de la clase // --- Métodos principales ---
void update(); void update(); // Actualiza la lógica de la clase
void render(); // Dibuja el objeto en pantalla
// Dibuja el objeto en pantalla // --- Configuración ---
void render(); void addTexture(int size, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation); // Añade texturas al objeto
void add(int x, int y, int size); // Añade una explosión
// Añade texturas al objeto
void addTexture(int size, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation);
// Añade una explosión
void add(int x, int y, int size);
private: private:
// Vector con las texturas a utilizar // --- Variables de estado ---
std::vector<ExplosionTexture> textures_; std::vector<ExplosionTexture> textures_; // Vector con las texturas a utilizar
std::vector<std::unique_ptr<AnimatedSprite>> explosions_; // Lista con todas las explosiones
// Lista con todas las explosiones // --- Métodos internos ---
std::vector<std::unique_ptr<AnimatedSprite>> explosions_; void freeExplosions(); // Vacía el vector de elementos finalizados
auto getIndexBySize(int size) -> int; // Busca una textura a partir del tamaño
// Vacia el vector de elementos finalizados
void freeExplosions();
// Busca una textura a partir del tamaño
auto getIndexBySize(int size) -> int;
}; };

View File

@@ -27,32 +27,40 @@ Fade::~Fade() {
// Inicializa las variables // Inicializa las variables
void Fade::init() { void Fade::init() {
type_ = FadeType::CENTER; type_ = Type::CENTER;
mode_ = FadeMode::OUT; mode_ = Mode::OUT;
counter_ = 0; counter_ = 0;
r_ = 0; r_ = 0;
g_ = 0; g_ = 0;
b_ = 0; b_ = 0;
a_ = 0; a_ = 0;
post_duration_ = 0; post_duration_ = 0;
post_counter_ = 0; post_start_time_ = 0;
pre_duration_ = 0; pre_duration_ = 0;
pre_counter_ = 0; pre_start_time_ = 0;
num_squares_width_ = param.fade.num_squares_width; num_squares_width_ = param.fade.num_squares_width;
num_squares_height_ = param.fade.num_squares_height; num_squares_height_ = param.fade.num_squares_height;
fade_random_squares_delay_ = param.fade.random_squares_delay; random_squares_duration_ = param.fade.random_squares_duration_ms; // Usar como duración en ms
fade_random_squares_mult_ = param.fade.random_squares_mult; square_transition_duration_ = random_squares_duration_ / 4; // 25% del tiempo total para la transición individual
random_squares_start_time_ = 0;
} }
// Resetea algunas variables para volver a hacer el fade sin perder ciertos parametros // Resetea algunas variables para volver a hacer el fade sin perder ciertos parametros
void Fade::reset() { void Fade::reset() {
state_ = FadeState::NOT_ENABLED; state_ = State::NOT_ENABLED;
counter_ = 0; counter_ = 0;
post_start_time_ = 0;
pre_start_time_ = 0;
} }
// Pinta una transición en pantalla // Pinta una transición en pantalla
void Fade::render() { void Fade::render() {
if (state_ != FadeState::NOT_ENABLED) { if (state_ != State::NOT_ENABLED) {
// Para fade IN terminado, no renderizar (auto-desactivación visual)
if (state_ == State::FINISHED && mode_ == Mode::IN) {
return;
}
SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr); SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr);
} }
} }
@@ -60,13 +68,13 @@ void Fade::render() {
// Actualiza las variables internas // Actualiza las variables internas
void Fade::update() { void Fade::update() {
switch (state_) { switch (state_) {
case FadeState::PRE: case State::PRE:
updatePreState(); updatePreState();
break; break;
case FadeState::FADING: case State::FADING:
updateFadingState(); updateFadingState();
break; break;
case FadeState::POST: case State::POST:
updatePostState(); updatePostState();
break; break;
default: default:
@@ -75,25 +83,36 @@ void Fade::update() {
} }
void Fade::updatePreState() { void Fade::updatePreState() {
if (pre_counter_ == pre_duration_) { // Sistema basado en tiempo únicamente
state_ = FadeState::FADING; Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_;
} else {
pre_counter_++; if (elapsed_time >= static_cast<Uint32>(pre_duration_)) {
state_ = State::FADING;
// CRÍTICO: Reinicializar tiempo de inicio para tipos que usan random_squares_start_time_
if (type_ == Type::RANDOM_SQUARE2 || type_ == Type::DIAGONAL) {
random_squares_start_time_ = SDL_GetTicks();
}
} }
} }
void Fade::updateFadingState() { void Fade::updateFadingState() {
switch (type_) { switch (type_) {
case FadeType::FULLSCREEN: case Type::FULLSCREEN:
updateFullscreenFade(); updateFullscreenFade();
break; break;
case FadeType::CENTER: case Type::CENTER:
updateCenterFade(); updateCenterFade();
break; break;
case FadeType::RANDOM_SQUARE: case Type::RANDOM_SQUARE:
updateRandomSquareFade(); updateRandomSquareFade();
break; break;
case FadeType::VENETIAN: case Type::RANDOM_SQUARE2:
updateRandomSquare2Fade();
break;
case Type::DIAGONAL:
updateDiagonalFade();
break;
case Type::VENETIAN:
updateVenetianFade(); updateVenetianFade();
break; break;
default: default:
@@ -102,23 +121,36 @@ void Fade::updateFadingState() {
counter_++; counter_++;
} }
void Fade::changeToPostState() {
state_ = State::POST;
post_start_time_ = SDL_GetTicks();
}
void Fade::updatePostState() { void Fade::updatePostState() {
if (post_counter_ == post_duration_) { // Sistema basado en tiempo únicamente
state_ = FadeState::FINISHED; Uint32 elapsed_time = SDL_GetTicks() - post_start_time_;
} else {
post_counter_++; if (elapsed_time >= static_cast<Uint32>(post_duration_)) {
state_ = State::FINISHED;
} }
cleanBackbuffer(r_, g_, b_, a_);
// Mantener el alpha final correcto para cada tipo de fade
Uint8 post_alpha = a_;
if (type_ == Type::RANDOM_SQUARE2 || type_ == Type::DIAGONAL) {
post_alpha = (mode_ == Mode::OUT) ? 255 : 0;
}
cleanBackbuffer(r_, g_, b_, post_alpha);
} }
void Fade::updateFullscreenFade() { void Fade::updateFullscreenFade() {
// Modifica la transparencia // Modifica la transparencia
a_ = mode_ == FadeMode::OUT ? std::min(counter_ * 4, 255) : 255 - std::min(counter_ * 4, 255); a_ = mode_ == Mode::OUT ? std::min(counter_ * 4, 255) : 255 - std::min(counter_ * 4, 255);
SDL_SetTextureAlphaMod(backbuffer_, a_); SDL_SetTextureAlphaMod(backbuffer_, a_);
// Comprueba si ha terminado // Comprueba si ha terminado
if (counter_ >= 255 / 4) { if (counter_ >= 255 / 4) {
state_ = FadeState::POST; changeToPostState();
} }
} }
@@ -127,8 +159,8 @@ void Fade::updateCenterFade() {
// Comprueba si ha terminado // Comprueba si ha terminado
if ((counter_ * 4) > param.game.height) { if ((counter_ * 4) > param.game.height) {
state_ = FadeState::POST;
a_ = 255; a_ = 255;
changeToPostState();
} }
} }
@@ -151,20 +183,243 @@ void Fade::drawCenterFadeRectangles() {
} }
void Fade::updateRandomSquareFade() { void Fade::updateRandomSquareFade() {
if (counter_ % fade_random_squares_delay_ == 0) { Uint32 elapsed_time = SDL_GetTicks() - random_squares_start_time_;
drawRandomSquares(); float progress = static_cast<float>(elapsed_time) / random_squares_duration_;
}
value_ = calculateValue(0, (num_squares_width_ * num_squares_height_), (counter_ * fade_random_squares_mult_ / fade_random_squares_delay_)); // Calcula cuántos cuadrados deberían estar activos
int total_squares = num_squares_width_ * num_squares_height_;
int active_squares = static_cast<int>(progress * total_squares);
active_squares = std::min(active_squares, total_squares);
// Dibuja los cuadrados activos
drawRandomSquares(active_squares);
value_ = calculateValue(0, total_squares, active_squares);
// Comprueba si ha terminado // Comprueba si ha terminado
if (counter_ * fade_random_squares_mult_ / fade_random_squares_delay_ >= if (elapsed_time >= static_cast<Uint32>(random_squares_duration_)) {
num_squares_width_ * num_squares_height_) { changeToPostState();
state_ = FadeState::POST;
} }
} }
void Fade::drawRandomSquares() { void Fade::updateRandomSquare2Fade() {
Uint32 elapsed_time = SDL_GetTicks() - random_squares_start_time_;
int total_squares = num_squares_width_ * num_squares_height_;
// Calcula el tiempo de activación: total - tiempo que necesitan los últimos cuadrados
int activation_time = random_squares_duration_ - square_transition_duration_;
activation_time = std::max(activation_time, square_transition_duration_); // Mínimo igual a la duración de transición
// Lógica diferente según el modo
int squares_to_activate = 0;
if (mode_ == Mode::OUT) {
// OUT: Activa cuadrados gradualmente
if (elapsed_time < static_cast<Uint32>(activation_time)) {
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
squares_to_activate = static_cast<int>(activation_progress * total_squares);
} else {
squares_to_activate = total_squares; // Activar todos
}
// Activa nuevos cuadrados y guarda su tiempo de activación
for (int i = 0; i < squares_to_activate && i < total_squares; ++i) {
if (square_age_[i] == -1) {
square_age_[i] = elapsed_time; // Guarda el tiempo de activación
}
}
} else {
// IN: Todos los cuadrados empiezan activos desde el inicio
squares_to_activate = total_squares;
// Activa cuadrados gradualmente con tiempo de inicio escalonado
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
int squares_starting_transition = static_cast<int>(activation_progress * total_squares);
// Asegurar que al menos 1 cuadrado se active desde el primer frame
squares_starting_transition = std::max(squares_starting_transition, 1);
squares_starting_transition = std::min(squares_starting_transition, total_squares);
for (int i = 0; i < squares_starting_transition; ++i) {
if (square_age_[i] == -1) {
square_age_[i] = elapsed_time; // Empieza la transición a transparente
}
}
}
drawRandomSquares2();
value_ = calculateValue(0, total_squares, squares_to_activate);
// Comprueba si ha terminado - todos los cuadrados han completado su transición
bool all_completed = (squares_to_activate >= total_squares);
if (all_completed) {
// Verificar que todos han completado su transición individual
for (int i = 0; i < total_squares; ++i) {
if (square_age_[i] >= 0) { // Cuadrado activado
Uint32 square_elapsed = elapsed_time - square_age_[i];
if (square_elapsed < static_cast<Uint32>(square_transition_duration_)) {
all_completed = false;
break;
}
}
}
if (all_completed) {
// Pintar textura final: OUT opaca, IN transparente
Uint8 final_alpha = (mode_ == Mode::OUT) ? 255 : 0;
cleanBackbuffer(r_, g_, b_, final_alpha);
changeToPostState();
}
}
}
void Fade::updateDiagonalFade() {
Uint32 elapsed_time = SDL_GetTicks() - random_squares_start_time_;
int total_squares = num_squares_width_ * num_squares_height_;
// Calcula el tiempo de activación: total - tiempo que necesitan los últimos cuadrados
int activation_time = random_squares_duration_ - square_transition_duration_;
activation_time = std::max(activation_time, square_transition_duration_);
// Calcula cuántas diagonales deberían estar activas
int max_diagonal = num_squares_width_ + num_squares_height_ - 1; // Número total de diagonales
int active_diagonals = 0;
if (mode_ == Mode::OUT) {
// OUT: Activa diagonales gradualmente desde esquina superior izquierda
if (elapsed_time < static_cast<Uint32>(activation_time)) {
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
active_diagonals = static_cast<int>(activation_progress * max_diagonal);
} else {
active_diagonals = max_diagonal; // Activar todas
}
// Activa cuadrados por diagonales
for (int diagonal = 0; diagonal < active_diagonals; ++diagonal) {
activateDiagonal(diagonal, elapsed_time);
}
} else {
// IN: Todas las diagonales empiezan activas, van desapareciendo
active_diagonals = max_diagonal;
// Activa diagonales gradualmente para transición
if (elapsed_time < static_cast<Uint32>(activation_time)) {
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
int diagonals_starting_transition = static_cast<int>(activation_progress * max_diagonal);
for (int diagonal = 0; diagonal < diagonals_starting_transition; ++diagonal) {
activateDiagonal(diagonal, elapsed_time);
}
} else {
// Activar transición en todas las diagonales restantes
for (int diagonal = 0; diagonal < max_diagonal; ++diagonal) {
activateDiagonal(diagonal, elapsed_time);
}
}
}
drawDiagonal();
value_ = calculateValue(0, total_squares, active_diagonals * (total_squares / max_diagonal));
// Comprueba si ha terminado - todas las diagonales activadas y último cuadrado completó transición
bool all_completed = (active_diagonals >= max_diagonal);
if (all_completed) {
// Verificar que todos han completado su transición individual
for (int i = 0; i < total_squares; ++i) {
if (square_age_[i] >= 0) { // Cuadrado activado
Uint32 square_elapsed = elapsed_time - square_age_[i];
if (square_elapsed < static_cast<Uint32>(square_transition_duration_)) {
all_completed = false;
break;
}
}
}
if (all_completed) {
// Pintar textura final: OUT opaca, IN transparente
Uint8 final_alpha = (mode_ == Mode::OUT) ? 255 : 0;
cleanBackbuffer(r_, g_, b_, final_alpha);
changeToPostState();
}
}
}
void Fade::activateDiagonal(int diagonal_index, Uint32 current_time) {
// Para cada diagonal, activamos los cuadrados que pertenecen a esa diagonal
// Diagonal 0: (0,0)
// Diagonal 1: (1,0), (0,1)
// Diagonal 2: (2,0), (1,1), (0,2)
// etc.
for (int x = 0; x < num_squares_width_; ++x) {
int y = diagonal_index - x;
// Verificar que y está dentro de los límites
if (y >= 0 && y < num_squares_height_) {
// Convertir coordenadas (x,y) a índice en el vector
int index = y * num_squares_width_ + x;
if (index >= 0 && index < static_cast<int>(square_age_.size())) {
if (square_age_[index] == -1) {
square_age_[index] = current_time; // Guarda el tiempo de activación
}
}
}
}
}
void Fade::drawDiagonal() {
auto *temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
// CRÍTICO: Limpiar la textura antes de dibujar
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_RenderClear(renderer_);
SDL_BlendMode blend_mode;
SDL_GetRenderDrawBlendMode(renderer_, &blend_mode);
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); // Usar BLEND para alpha
Uint32 current_time = SDL_GetTicks() - random_squares_start_time_;
// Lógica unificada: sobre textura transparente, pintar cuadrados según su estado
for (size_t i = 0; i < square_.size(); ++i) {
Uint8 current_alpha = 0;
if (square_age_[i] == -1) {
// Cuadrado no activado
if (mode_ == Mode::OUT) {
current_alpha = 0; // OUT: transparente si no activado
} else {
current_alpha = a_; // IN: opaco si no activado
}
} else {
// Cuadrado activado - calculamos progreso
Uint32 square_elapsed = current_time - square_age_[i];
float progress = std::min(static_cast<float>(square_elapsed) / square_transition_duration_, 1.0f);
if (mode_ == Mode::OUT) {
current_alpha = static_cast<Uint8>(progress * a_); // 0 → 255
} else {
current_alpha = static_cast<Uint8>((1.0f - progress) * a_); // 255 → 0
}
}
if (current_alpha > 0) {
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, current_alpha);
SDL_RenderFillRect(renderer_, &square_[i]);
}
}
SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
SDL_SetRenderTarget(renderer_, temp);
}
void Fade::drawRandomSquares(int active_count) {
auto *temp = SDL_GetRenderTarget(renderer_); auto *temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_); SDL_SetRenderTarget(renderer_, backbuffer_);
@@ -173,13 +428,56 @@ void Fade::drawRandomSquares() {
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE); SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_); SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_);
const int INDEX = std::min(counter_ / fade_random_squares_delay_, // Dibuja solo los cuadrados activos
(num_squares_width_ * num_squares_height_) - 1); for (int i = 0; i < active_count && i < static_cast<int>(square_.size()); ++i) {
SDL_RenderFillRect(renderer_, &square_[i]);
}
for (int i = 0; i < fade_random_squares_mult_; ++i) { SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
const int INDEX2 = std::min(INDEX * fade_random_squares_mult_ + i, SDL_SetRenderTarget(renderer_, temp);
static_cast<int>(square_.size()) - 1); }
SDL_RenderFillRect(renderer_, &square_[INDEX2]);
void Fade::drawRandomSquares2() {
auto *temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, backbuffer_);
// CRÍTICO: Limpiar la textura antes de dibujar
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_RenderClear(renderer_);
SDL_BlendMode blend_mode;
SDL_GetRenderDrawBlendMode(renderer_, &blend_mode);
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); // Usar BLEND para alpha
Uint32 current_time = SDL_GetTicks() - random_squares_start_time_;
// Lógica unificada: sobre textura transparente, pintar cuadrados según su estado
for (size_t i = 0; i < square_.size(); ++i) {
Uint8 current_alpha = 0;
if (square_age_[i] == -1) {
// Cuadrado no activado
if (mode_ == Mode::OUT) {
current_alpha = 0; // OUT: transparente si no activado
} else {
current_alpha = a_; // IN: opaco si no activado
}
} else {
// Cuadrado activado - calculamos progreso
Uint32 square_elapsed = current_time - square_age_[i];
float progress = std::min(static_cast<float>(square_elapsed) / square_transition_duration_, 1.0f);
if (mode_ == Mode::OUT) {
current_alpha = static_cast<Uint8>(progress * a_); // 0 → 255
} else {
current_alpha = static_cast<Uint8>((1.0f - progress) * a_); // 255 → 0
}
}
if (current_alpha > 0) {
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, current_alpha);
SDL_RenderFillRect(renderer_, &square_[i]);
}
} }
SDL_SetRenderDrawBlendMode(renderer_, blend_mode); SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
@@ -192,7 +490,7 @@ void Fade::updateVenetianFade() {
updateVenetianRectangles(); updateVenetianRectangles();
calculateVenetianProgress(); calculateVenetianProgress();
} else { } else {
state_ = FadeState::POST; changeToPostState();
} }
} }
@@ -233,31 +531,30 @@ void Fade::calculateVenetianProgress() {
// Activa el fade // Activa el fade
void Fade::activate() { void Fade::activate() {
// Si ya está habilitado, no hay que volverlo a activar // Si ya está habilitado, no hay que volverlo a activar
if (state_ != FadeState::NOT_ENABLED) { if (state_ != State::NOT_ENABLED) {
return; return;
} }
state_ = FadeState::PRE; state_ = State::PRE;
counter_ = 0; counter_ = 0;
post_counter_ = 0; pre_start_time_ = SDL_GetTicks();
pre_counter_ = 0;
switch (type_) { switch (type_) {
case FadeType::FULLSCREEN: { case Type::FULLSCREEN: {
// Pinta el backbuffer_ de color sólido // Pinta el backbuffer_ de color sólido
cleanBackbuffer(r_, g_, b_, 255); cleanBackbuffer(r_, g_, b_, 255);
break; break;
} }
case FadeType::CENTER: { case Type::CENTER: {
rect1_ = {0, 0, param.game.width, 0}; rect1_ = {.x = 0, .y = 0, .w = param.game.width, .h = 0};
rect2_ = {0, 0, param.game.width, 0}; rect2_ = {.x = 0, .y = 0, .w = param.game.width, .h = 0};
a_ = 64; a_ = 64;
break; break;
} }
case FadeType::RANDOM_SQUARE: { case Type::RANDOM_SQUARE: {
rect1_ = {0, 0, static_cast<float>(param.game.width / num_squares_width_), static_cast<float>(param.game.height / num_squares_height_)}; rect1_ = {.x = 0, .y = 0, .w = static_cast<float>(param.game.width / num_squares_width_), .h = static_cast<float>(param.game.height / num_squares_height_)};
square_.clear(); square_.clear();
// Añade los cuadrados al vector // Añade los cuadrados al vector
@@ -278,26 +575,96 @@ void Fade::activate() {
} }
// Limpia la textura // Limpia la textura
a_ = mode_ == FadeMode::OUT ? 0 : 255; a_ = mode_ == Mode::OUT ? 0 : 255;
cleanBackbuffer(r_, g_, b_, a_); cleanBackbuffer(r_, g_, b_, a_);
// Deja el color listo para usar // Deja el color listo para usar
a_ = mode_ == FadeMode::OUT ? 255 : 0; a_ = mode_ == Mode::OUT ? 255 : 0;
// Inicializa el tiempo de inicio
random_squares_start_time_ = SDL_GetTicks();
break; break;
} }
case FadeType::VENETIAN: { case Type::RANDOM_SQUARE2: {
rect1_ = {.x = 0, .y = 0, .w = static_cast<float>(param.game.width / num_squares_width_), .h = static_cast<float>(param.game.height / num_squares_height_)};
square_.clear();
square_age_.clear();
// Añade los cuadrados al vector
for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) {
rect1_.x = (i % num_squares_width_) * rect1_.w;
rect1_.y = (i / num_squares_width_) * rect1_.h;
square_.push_back(rect1_);
square_age_.push_back(-1); // -1 indica cuadrado no activado aún
}
// Desordena el vector de cuadrados y edades
auto num = num_squares_width_ * num_squares_height_;
while (num > 1) {
auto num_arreu = rand() % num;
SDL_FRect temp_rect = square_[num_arreu];
int temp_age = square_age_[num_arreu];
square_[num_arreu] = square_[num - 1];
square_age_[num_arreu] = square_age_[num - 1];
square_[num - 1] = temp_rect;
square_age_[num - 1] = temp_age;
num--;
}
// Textura inicial: OUT transparente, IN opaca
Uint8 initial_alpha = (mode_ == Mode::OUT) ? 0 : 255;
cleanBackbuffer(r_, g_, b_, initial_alpha);
// Deja el color listo para usar (alpha target para los cuadrados)
a_ = 255; // Siempre usar 255 como alpha target
// Inicializa el tiempo de inicio y recalcula la duración de transición
random_squares_start_time_ = SDL_GetTicks();
square_transition_duration_ = std::max(random_squares_duration_ / 4, 100); // Mínimo 100ms
break;
}
case Type::DIAGONAL: {
rect1_ = {.x = 0, .y = 0, .w = static_cast<float>(param.game.width / num_squares_width_), .h = static_cast<float>(param.game.height / num_squares_height_)};
square_.clear();
square_age_.clear();
// Añade los cuadrados al vector en orden (sin desordenar)
for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) {
rect1_.x = (i % num_squares_width_) * rect1_.w;
rect1_.y = (i / num_squares_width_) * rect1_.h;
square_.push_back(rect1_);
square_age_.push_back(-1); // -1 indica cuadrado no activado aún
}
// Textura inicial: OUT transparente, IN opaca
Uint8 initial_alpha = (mode_ == Mode::OUT) ? 0 : 255;
cleanBackbuffer(r_, g_, b_, initial_alpha);
// Deja el color listo para usar (alpha target para los cuadrados)
a_ = 255; // Siempre usar 255 como alpha target
// Inicializa el tiempo de inicio y recalcula la duración de transición
random_squares_start_time_ = SDL_GetTicks();
square_transition_duration_ = std::max(random_squares_duration_ / 4, 100); // Mínimo 100ms
break;
}
case Type::VENETIAN: {
// Limpia la textura // Limpia la textura
a_ = mode_ == FadeMode::OUT ? 0 : 255; a_ = mode_ == Mode::OUT ? 0 : 255;
cleanBackbuffer(r_, g_, b_, a_); cleanBackbuffer(r_, g_, b_, a_);
// Deja el color listo para usar // Deja el color listo para usar
a_ = mode_ == FadeMode::OUT ? 255 : 0; a_ = mode_ == Mode::OUT ? 255 : 0;
// Añade los cuadrados al vector // Añade los cuadrados al vector
square_.clear(); square_.clear();
rect1_ = {0, 0, param.game.width, 0}; rect1_ = {.x = 0, .y = 0, .w = param.game.width, .h = 0};
const int MAX = param.game.height / param.fade.venetian_size; const int MAX = param.game.height / param.fade.venetian_size;
for (int i = 0; i < MAX; ++i) { for (int i = 0; i < MAX; ++i) {

View File

@@ -6,31 +6,32 @@
struct Color; struct Color;
// Tipos de fundido // --- Clase Fade: gestor de transiciones de fundido ---
enum class FadeType : Uint8 {
FULLSCREEN = 0,
CENTER = 1,
RANDOM_SQUARE = 2,
VENETIAN = 3,
};
// Modos de fundido
enum class FadeMode : Uint8 {
IN = 0,
OUT = 1,
};
// Estados del objeto
enum class FadeState : Uint8 {
NOT_ENABLED = 0,
PRE = 1,
FADING = 2,
POST = 3,
FINISHED = 4,
};
class Fade { class Fade {
public: public:
// --- Enums ---
enum class Type : Uint8 {
FULLSCREEN = 0, // Fundido de pantalla completa
CENTER = 1, // Fundido desde el centro
RANDOM_SQUARE = 2, // Fundido con cuadrados aleatorios
RANDOM_SQUARE2 = 3, // Fundido con cuadrados aleatorios (variante 2)
DIAGONAL = 4, // Fundido diagonal desde esquina superior izquierda
VENETIAN = 5, // Fundido tipo persiana veneciana
};
enum class Mode : Uint8 {
IN = 0, // Fundido de entrada
OUT = 1, // Fundido de salida
};
enum class State : Uint8 {
NOT_ENABLED = 0, // No activado
PRE = 1, // Estado previo
FADING = 2, // Fundiendo
POST = 3, // Estado posterior
FINISHED = 4, // Finalizado
};
// --- Constructores y destructor --- // --- Constructores y destructor ---
Fade(); Fade();
~Fade(); ~Fade();
@@ -42,17 +43,17 @@ class Fade {
void activate(); // Activa el fade void activate(); // Activa el fade
// --- Configuración --- // --- Configuración ---
void setColor(Uint8 r, Uint8 g, Uint8 b); void setColor(Uint8 r, Uint8 g, Uint8 b); // Establece el color RGB del fade
void setColor(Color color); void setColor(Color color); // Establece el color del fade
void setType(FadeType type) { type_ = type; } void setType(Type type) { type_ = type; } // Establece el tipo de fade
void setMode(FadeMode mode) { mode_ = mode; } void setMode(Mode mode) { mode_ = mode; } // Establece el modo de fade
void setPostDuration(int value) { post_duration_ = value; } void setPostDuration(int value) { post_duration_ = value; } // Duración posterior al fade en milisegundos
void setPreDuration(int value) { pre_duration_ = value; } void setPreDuration(int value) { pre_duration_ = value; } // Duración previa al fade en milisegundos
// --- Getters --- // --- Getters ---
[[nodiscard]] auto getValue() const -> int { return value_; } [[nodiscard]] auto getValue() const -> int { return value_; }
[[nodiscard]] auto isEnabled() const -> bool { return state_ != FadeState::NOT_ENABLED; } [[nodiscard]] auto isEnabled() const -> bool { return state_ != State::NOT_ENABLED; }
[[nodiscard]] auto hasEnded() const -> bool { return state_ == FadeState::FINISHED; } [[nodiscard]] auto hasEnded() const -> bool { return state_ == State::FINISHED; }
private: private:
// --- Objetos y punteros --- // --- Objetos y punteros ---
@@ -60,28 +61,24 @@ class Fade {
SDL_Texture *backbuffer_; // Backbuffer para efectos SDL_Texture *backbuffer_; // Backbuffer para efectos
// --- Variables de estado --- // --- Variables de estado ---
FadeType type_; // Tipo de fade std::vector<SDL_FRect> square_; // Vector de cuadrados
FadeMode mode_; // Modo de fade std::vector<int> square_age_; // Edad de cada cuadrado (para RANDOM_SQUARE2)
FadeState state_ = FadeState::NOT_ENABLED; // Estado actual SDL_FRect rect1_, rect2_; // Rectángulos para efectos
Uint16 counter_; // Contador interno Type type_; // Tipo de fade
Mode mode_; // Modo de fade
// --- Parámetros de color y geometría --- State state_ = State::NOT_ENABLED; // Estado actual
Uint8 r_, g_, b_, a_; // Color del fade Uint16 counter_; // Contador interno
SDL_FRect rect1_, rect2_; // Rectángulos para efectos Uint8 r_, g_, b_, a_; // Color del fade (RGBA)
int num_squares_width_; // Cuadrados en horizontal
// --- Parámetros para RANDOM_SQUARE --- int num_squares_height_; // Cuadrados en vertical
int num_squares_width_; // Cuadrados en horizontal Uint32 random_squares_start_time_; // Tiempo de inicio del fade de cuadrados
int num_squares_height_; // Cuadrados en vertical int random_squares_duration_; // Duración total en milisegundos
std::vector<SDL_FRect> square_; // Vector de cuadrados int square_transition_duration_; // Duración de transición de cada cuadrado en ms
int fade_random_squares_delay_; // Delay entre cuadrados int post_duration_ = 0; // Duración posterior en milisegundos
int fade_random_squares_mult_; // Cuadrados por paso Uint32 post_start_time_ = 0; // Tiempo de inicio del estado POST
int pre_duration_ = 0; // Duración previa en milisegundos
// --- Temporizadores --- Uint32 pre_start_time_ = 0; // Tiempo de inicio del estado PRE
int post_duration_ = 0, post_counter_ = 0; int value_ = 0; // Estado del fade (0-100)
int pre_duration_ = 0, pre_counter_ = 0;
// --- Valor de progreso ---
int value_ = 0; // Estado del fade (0-100)
// --- Inicialización y limpieza --- // --- Inicialización y limpieza ---
void init(); // Inicializa variables void init(); // Inicializa variables
@@ -94,17 +91,23 @@ class Fade {
void updatePreState(); // Actualiza el estado previo al fade void updatePreState(); // Actualiza el estado previo al fade
void updateFadingState(); // Actualiza el estado durante el fade void updateFadingState(); // Actualiza el estado durante el fade
void updatePostState(); // Actualiza el estado posterior al fade void updatePostState(); // Actualiza el estado posterior al fade
void changeToPostState(); // Cambia al estado POST e inicializa el tiempo
// --- Efectos de fundido (fade) --- // --- Efectos de fundido (fade) ---
void updateFullscreenFade(); // Actualiza el fundido de pantalla completa void updateFullscreenFade(); // Actualiza el fundido de pantalla completa
void updateCenterFade(); // Actualiza el fundido desde el centro void updateCenterFade(); // Actualiza el fundido desde el centro
void updateRandomSquareFade(); // Actualiza el fundido con cuadrados aleatorios void updateRandomSquareFade(); // Actualiza el fundido con cuadrados aleatorios
void updateRandomSquare2Fade(); // Actualiza el fundido con cuadrados aleatorios (variante 2)
void updateDiagonalFade(); // Actualiza el fundido diagonal
void updateVenetianFade(); // Actualiza el fundido tipo persiana veneciana void updateVenetianFade(); // Actualiza el fundido tipo persiana veneciana
void updateVenetianRectangles(); // Actualiza los rectángulos del efecto veneciano void updateVenetianRectangles(); // Actualiza los rectángulos del efecto veneciano
void calculateVenetianProgress(); // Calcula el progreso del efecto veneciano void calculateVenetianProgress(); // Calcula el progreso del efecto veneciano
// --- Dibujo de efectos visuales --- // --- Dibujo de efectos visuales ---
void drawCenterFadeRectangles(); // Dibuja los rectángulos del fundido central void drawCenterFadeRectangles(); // Dibuja los rectángulos del fundido central
void drawRandomSquares(); // Dibuja los cuadrados aleatorios del fundido void drawRandomSquares(int active_count = -1); // Dibuja los cuadrados aleatorios del fundido
void drawRandomSquares2(); // Dibuja los cuadrados con transición de color (RANDOM_SQUARE2)
void drawDiagonal(); // Dibuja los cuadrados con patrón diagonal
void activateDiagonal(int diagonal_index, Uint32 current_time); // Activa una diagonal específica
void drawVenetianBlinds(); // Dibuja las persianas venecianas del fundido void drawVenetianBlinds(); // Dibuja las persianas venecianas del fundido
}; };

View File

@@ -16,7 +16,7 @@
constexpr int ZOOM_FACTOR = 5; constexpr int ZOOM_FACTOR = 5;
constexpr int FLASH_DELAY = 3; constexpr int FLASH_DELAY = 3;
constexpr int FLASH_LENGHT = FLASH_DELAY + 3; constexpr int FLASH_LENGTH = FLASH_DELAY + 3;
// Constructor // Constructor
GameLogo::GameLogo(int x, int y) GameLogo::GameLogo(int x, int y)
@@ -34,7 +34,7 @@ GameLogo::GameLogo(int x, int y)
// Inicializa las variables // Inicializa las variables
void GameLogo::init() { void GameLogo::init() {
const auto XP = x_ - coffee_texture_->getWidth() / 2; const auto XP = x_ - (coffee_texture_->getWidth() / 2);
const auto DESP = getInitialVerticalDesp(); const auto DESP = getInitialVerticalDesp();
// Configura texturas // Configura texturas
@@ -236,7 +236,7 @@ void GameLogo::finishArcadeEditionMoving() {
void GameLogo::playTitleEffects() { void GameLogo::playTitleEffects() {
Audio::get()->playSound("title.wav"); Audio::get()->playSound("title.wav");
Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY); Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGTH, FLASH_DELAY);
Screen::get()->shake(); Screen::get()->shake();
} }

View File

@@ -8,7 +8,7 @@
class Texture; class Texture;
// Clase GameLogo // --- Clase GameLogo: gestor del logo del juego ---
class GameLogo { class GameLogo {
public: public:
// --- Constructores y destructor --- // --- Constructores y destructor ---
@@ -24,30 +24,31 @@ class GameLogo {
[[nodiscard]] auto hasFinished() const -> bool; // Indica si ha terminado la animación [[nodiscard]] auto hasFinished() const -> bool; // Indica si ha terminado la animación
private: private:
// --- Tipos internos --- // --- Enums ---
enum class Status { enum class Status {
DISABLED, DISABLED, // Deshabilitado
MOVING, MOVING, // En movimiento
SHAKING, SHAKING, // Temblando
FINISHED, FINISHED, // Terminado
}; };
// --- 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
int lenght = 8; // Cantidad de desplazamientos a realizar int length = 8; // Cantidad de desplazamientos a realizar
int remaining = lenght; // 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
int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento 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)
: desp(d), delay(de), lenght(l), remaining(l), counter(de), origin(o) {} : desp(d), delay(de), length(l), remaining(l), counter(de), origin(o) {}
void init(int d, int de, int l, int o) { void init(int d, int de, int l, int o) {
desp = d; desp = d;
delay = de; delay = de;
lenght = l; length = l;
remaining = l; remaining = l;
counter = de; counter = de;
origin = o; origin = o;
@@ -67,14 +68,13 @@ class GameLogo {
std::unique_ptr<Sprite> arcade_edition_sprite_; // Sprite de "Arcade Edition" std::unique_ptr<Sprite> arcade_edition_sprite_; // Sprite de "Arcade Edition"
// --- Variables de estado --- // --- Variables de estado ---
float x_; // Posición X del logo Shake shake_; // Efecto de agitación
float y_; // Posición Y del logo
float zoom_ = 1.0F; // Zoom aplicado al texto "ARCADE EDITION"
int post_finished_counter_ = 1; // Contador final tras animaciones
Status coffee_crisis_status_ = Status::DISABLED; // Estado de "COFFEE CRISIS" Status coffee_crisis_status_ = Status::DISABLED; // Estado de "COFFEE CRISIS"
Status arcade_edition_status_ = Status::DISABLED; // Estado de "ARCADE EDITION" Status arcade_edition_status_ = Status::DISABLED; // Estado de "ARCADE EDITION"
Shake shake_; // Efecto de agitación float x_; // Posición X del logo
float y_; // Posición Y del logo
float zoom_ = 1.0F; // Zoom aplicado al texto "ARCADE EDITION"
int post_finished_counter_ = 1; // Contador final tras animaciones
// --- Inicialización --- // --- Inicialización ---
void init(); // Inicializa las variables void init(); // Inicializa las variables

View File

@@ -8,6 +8,7 @@
#include "external/json.hpp" #include "external/json.hpp"
#include "input_types.h" // Solo incluimos los tipos compartidos #include "input_types.h" // Solo incluimos los tipos compartidos
// --- Estructuras ---
struct GamepadConfig { struct GamepadConfig {
std::string name; // Nombre del dispositivo std::string name; // Nombre del dispositivo
std::string path; // Ruta física del dispositivo std::string path; // Ruta física del dispositivo
@@ -29,12 +30,14 @@ struct GamepadConfig {
} }
}; };
using GamepadConfigs = std::vector<GamepadConfig>; // --- Tipos ---
using GamepadConfigs = std::vector<GamepadConfig>; // Vector de configuraciones de gamepad
// --- Clase GamepadConfigManager: gestor de configuraciones de gamepad ---
class GamepadConfigManager { class GamepadConfigManager {
public: public:
// Escribir vector de GamepadConfig a archivo JSON // --- Métodos estáticos ---
static auto writeToJson(const GamepadConfigs& configs, const std::string& filename) -> bool { static auto writeToJson(const GamepadConfigs& configs, const std::string& filename) -> bool { // Escribir configuraciones a JSON
try { try {
nlohmann::json j; nlohmann::json j;
j["gamepads"] = nlohmann::json::array(); j["gamepads"] = nlohmann::json::array();

View File

@@ -2,7 +2,8 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
// --- Namespace GlobalEvents: maneja eventos globales del juego ---
namespace GlobalEvents { namespace GlobalEvents {
// Comprueba los eventos que se pueden producir en cualquier sección del juego // --- Funciones ---
void handle(const SDL_Event &event); void handle(const SDL_Event &event); // Comprueba los eventos que se pueden producir en cualquier sección del juego
} // namespace GlobalEvents } // namespace GlobalEvents

View File

@@ -1,5 +1,6 @@
#include "global_inputs.h" #include "global_inputs.h"
#include <algorithm> // Para std::ranges::any_of
#include <functional> // Para function #include <functional> // Para function
#include <memory> // Para allocator, shared_ptr #include <memory> // Para allocator, shared_ptr
#include <string> // Para operator+, char_traits, string, to_string #include <string> // Para operator+, char_traits, string, to_string
@@ -145,11 +146,11 @@ auto checkServiceButton() -> bool {
} }
// Mandos // Mandos
for (auto gamepad : Input::get()->getGamepads()) { if (std::ranges::any_of(Input::get()->getGamepads(), [](const auto& gamepad) {
if (Input::get()->checkAction(Input::Action::SERVICE, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) { return Input::get()->checkAction(Input::Action::SERVICE, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad);
toggleServiceMenu(); })) {
return true; toggleServiceMenu();
} return true;
} }
return false; return false;
@@ -176,14 +177,13 @@ auto checkSystemInputs() -> bool {
#endif #endif
}; };
for (const auto& [action, func] : ACTIONS) { return std::ranges::any_of(ACTIONS, [](const auto& pair) {
if (Input::get()->checkAction(action, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) { if (Input::get()->checkAction(pair.first, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
func(); pair.second();
return true; return true;
} }
} return false;
});
return false;
} }
// Comprueba el resto de entradas // Comprueba el resto de entradas

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
// --- Namespace GlobalInputs: gestiona inputs globales del juego ---
namespace GlobalInputs { namespace GlobalInputs {
// Comprueba los inputs que se pueden introducir en cualquier sección del juego // --- Funciones ---
auto check() -> bool; auto check() -> bool; // Comprueba los inputs que se pueden introducir en cualquier sección del juego
} // namespace GlobalInputs } // namespace GlobalInputs

View File

@@ -7,55 +7,44 @@
#include "sprite.h" // Para Sprite #include "sprite.h" // Para Sprite
#include "texture.h" // Para Texture #include "texture.h" // Para Texture
// Estructura que representa una colisión o impacto visual // --- Estructura Hit: representa una colisión o impacto visual ---
struct Hit { struct Hit {
private:
// Indica si el Hit está activo o no
bool enabled_{false};
// Sprite asociado al Hit, gestionado con un puntero único
std::unique_ptr<Sprite> sprite_;
public: public:
// Elimina el constructor por defecto para obligar a pasar una textura // --- Constructor ---
Hit() = delete; Hit() = delete; // Elimina el constructor por defecto para obligar a pasar una textura
explicit Hit(const std::shared_ptr<Texture>& texture) // Constructor con textura obligatoria
// Constructor que obliga a pasar una textura compartida para crear el Sprite
// Esto evita que se pueda crear un Hit sin recursos gráficos válidos
explicit Hit(std::shared_ptr<Texture> texture)
: sprite_(std::make_unique<Sprite>(texture)) {} : sprite_(std::make_unique<Sprite>(texture)) {}
// Establece la posición del Sprite en el espacio // --- Métodos principales ---
void setPos(SDL_FPoint position) { void create(SDL_FPoint position) { // Crea un "Hit" en la posición especificada
SDL_FPoint centered_position = {position.x - (sprite_->getWidth() / 2), position.y - (sprite_->getHeight() / 2)};
sprite_->setPosition(centered_position);
}
// Activa o desactiva el Hit
void enable(bool value) {
enabled_ = value;
}
// Consulta si el Hit está activo
[[nodiscard]] auto isEnabled() const -> bool {
return enabled_;
}
// Crea un "Hit" en la posición especificada
void create(SDL_FPoint position) {
setPos(position); setPos(position);
enable(true); enable(true);
} }
void render() { // Dibuja el hit
// Dibuja el hit
void render() {
if (enabled_) { if (enabled_) {
sprite_->render(); sprite_->render();
} }
} }
void disable() { // Deshabilita el hit
// Deshabilita el hit
void disable() {
enabled_ = false; enabled_ = false;
} }
// --- Configuración ---
void setPos(SDL_FPoint position) { // Establece la posición del Sprite en el espacio
SDL_FPoint centered_position = {position.x - (sprite_->getWidth() / 2), position.y - (sprite_->getHeight() / 2)};
sprite_->setPosition(centered_position);
}
void enable(bool value) { // Activa o desactiva el Hit
enabled_ = value;
}
// --- Getters ---
[[nodiscard]] auto isEnabled() const -> bool { // Consulta si el Hit está activo
return enabled_;
}
private:
// --- Variables de estado ---
std::unique_ptr<Sprite> sprite_; // Sprite asociado al Hit
bool enabled_{false}; // Indica si el Hit está activo
}; };

View File

@@ -36,21 +36,21 @@ void Input::bindKey(Action action, SDL_Scancode code) {
} }
// Asigna inputs a botones del mando // Asigna inputs a botones del mando
void Input::bindGameControllerButton(std::shared_ptr<Gamepad> gamepad, Action action, SDL_GamepadButton button) { void Input::bindGameControllerButton(const std::shared_ptr<Gamepad> &gamepad, Action action, SDL_GamepadButton button) {
if (gamepad != nullptr) { if (gamepad != nullptr) {
gamepad->bindings[action].button = button; gamepad->bindings[action].button = button;
} }
} }
// Asigna inputs a botones del mando // Asigna inputs a botones del mando
void Input::bindGameControllerButton(std::shared_ptr<Gamepad> gamepad, Action action_target, Action action_source) { void Input::bindGameControllerButton(const std::shared_ptr<Gamepad> &gamepad, Action action_target, Action action_source) {
if (gamepad != nullptr) { if (gamepad != nullptr) {
gamepad->bindings[action_target].button = gamepad->bindings[action_source].button; gamepad->bindings[action_target].button = gamepad->bindings[action_source].button;
} }
} }
// Comprueba si alguna acción está activa // Comprueba si alguna acción está activa
auto Input::checkAction(Action action, bool repeat, bool check_keyboard, std::shared_ptr<Gamepad> gamepad) -> bool { auto Input::checkAction(Action action, bool repeat, bool check_keyboard, const std::shared_ptr<Gamepad> &gamepad) -> bool {
bool success_keyboard = false; bool success_keyboard = false;
bool success_controller = false; bool success_controller = false;
@@ -65,6 +65,10 @@ auto Input::checkAction(Action action, bool repeat, bool check_keyboard, std::sh
if (gamepad != nullptr) { if (gamepad != nullptr) {
success_controller = checkAxisInput(action, gamepad, repeat); success_controller = checkAxisInput(action, gamepad, repeat);
if (!success_controller) {
success_controller = checkTriggerInput(action, gamepad, repeat);
}
if (!success_controller) { if (!success_controller) {
if (repeat) { // El usuario quiere saber si está pulsada (estado mantenido) if (repeat) { // El usuario quiere saber si está pulsada (estado mantenido)
success_controller = gamepad->bindings[action].is_held; success_controller = gamepad->bindings[action].is_held;
@@ -78,7 +82,7 @@ auto Input::checkAction(Action action, bool repeat, bool check_keyboard, std::sh
} }
// Comprueba si hay almenos una acción activa // Comprueba si hay almenos una acción activa
auto Input::checkAnyInput(bool check_keyboard, std::shared_ptr<Gamepad> gamepad) -> bool { auto Input::checkAnyInput(bool check_keyboard, const std::shared_ptr<Gamepad> &gamepad) -> bool {
// Obtenemos el número total de acciones posibles para iterar sobre ellas. // Obtenemos el número total de acciones posibles para iterar sobre ellas.
// --- Comprobación del Teclado --- // --- Comprobación del Teclado ---
@@ -118,7 +122,7 @@ auto Input::checkAnyButton(bool repeat) -> bool {
} }
// Comprueba los mandos // Comprueba los mandos
for (auto gamepad : gamepads_) { for (const auto &gamepad : gamepads_) {
if (checkAction(bi, repeat, DO_NOT_CHECK_KEYBOARD, gamepad)) { if (checkAction(bi, repeat, DO_NOT_CHECK_KEYBOARD, gamepad)) {
return true; return true;
} }
@@ -132,7 +136,9 @@ auto Input::checkAnyButton(bool repeat) -> bool {
auto Input::gameControllerFound() const -> bool { return !gamepads_.empty(); } auto Input::gameControllerFound() const -> bool { return !gamepads_.empty(); }
// Obten el nombre de un mando de juego // Obten el nombre de un mando de juego
auto Input::getControllerName(std::shared_ptr<Gamepad> gamepad) -> std::string { return gamepad == nullptr ? std::string() : gamepad->name; } auto Input::getControllerName(const std::shared_ptr<Gamepad> &gamepad) -> std::string {
return gamepad == nullptr ? std::string() : gamepad->name;
}
// Obtiene la lista de nombres de mandos // Obtiene la lista de nombres de mandos
auto Input::getControllerNames() const -> std::vector<std::string> { auto Input::getControllerNames() const -> std::vector<std::string> {
@@ -166,8 +172,8 @@ auto Input::getGamepadByName(const std::string &name) const -> std::shared_ptr<I
} }
// Obtiene el SDL_GamepadButton asignado a un action // Obtiene el SDL_GamepadButton asignado a un action
auto Input::getControllerBinding(std::shared_ptr<Gamepad> gamepad, Action action) -> SDL_GamepadButton { auto Input::getControllerBinding(const std::shared_ptr<Gamepad> &gamepad, Action action) -> SDL_GamepadButton {
return gamepad->bindings[action].button; return static_cast<SDL_GamepadButton>(gamepad->bindings[action].button);
} }
// Convierte un InputAction a std::string // Convierte un InputAction a std::string
@@ -202,7 +208,7 @@ auto Input::stringToInput(const std::string &name) -> Action {
} }
// Comprueba el eje del mando // Comprueba el eje del mando
auto Input::checkAxisInput(Action action, std::shared_ptr<Gamepad> gamepad, bool repeat) -> bool { auto Input::checkAxisInput(Action action, const std::shared_ptr<Gamepad> &gamepad, bool repeat) -> bool {
// Umbral para considerar el eje como activo // Umbral para considerar el eje como activo
bool axis_active_now = false; bool axis_active_now = false;
@@ -243,9 +249,57 @@ auto Input::checkAxisInput(Action action, std::shared_ptr<Gamepad> gamepad, bool
return false; return false;
} }
// Comprueba los triggers del mando como botones digitales
auto Input::checkTriggerInput(Action action, const std::shared_ptr<Gamepad> &gamepad, bool repeat) -> bool {
// Solo manejamos botones específicos que pueden ser triggers
if (gamepad->bindings[action].button != static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID)) {
// Solo procesamos L2 y R2 como triggers
int button = gamepad->bindings[action].button;
// Verificar si el botón mapeado corresponde a un trigger virtual
// (Para esto necesitamos valores especiales que representen L2/R2 como botones)
bool trigger_active_now = false;
// Usamos constantes especiales para L2 y R2 como botones
if (button == TRIGGER_L2_AS_BUTTON) { // L2 como botón
Sint16 trigger_value = SDL_GetGamepadAxis(gamepad->pad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER);
trigger_active_now = trigger_value > TRIGGER_THRESHOLD;
} else if (button == TRIGGER_R2_AS_BUTTON) { // R2 como botón
Sint16 trigger_value = SDL_GetGamepadAxis(gamepad->pad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
trigger_active_now = trigger_value > TRIGGER_THRESHOLD;
} else {
return false; // No es un trigger
}
// Referencia al binding correspondiente
auto &binding = gamepad->bindings[action];
if (repeat) {
// Si se permite repetir, simplemente devolvemos el estado actual
return trigger_active_now;
}
// Si no se permite repetir, aplicamos la lógica de transición
if (trigger_active_now && !binding.trigger_active) {
// Transición de inactivo a activo
binding.trigger_active = true;
return true;
}
if (!trigger_active_now && binding.trigger_active) {
// Transición de activo a inactivo
binding.trigger_active = false;
}
// Mantener el estado actual
return false;
}
return false;
}
void Input::addGamepadMappingsFromFile() { void Input::addGamepadMappingsFromFile() {
if (SDL_AddGamepadMappingsFromFile(gamepad_mappings_file_.c_str()) < 0) { if (SDL_AddGamepadMappingsFromFile(gamepad_mappings_file_.c_str()) < 0) {
std::cout << "Error, could not load " << gamepad_mappings_file_.c_str() << " file: " << SDL_GetError() << std::endl; std::cout << "Error, could not load " << gamepad_mappings_file_.c_str() << " file: " << SDL_GetError() << '\n';
} }
} }
@@ -280,6 +334,7 @@ void Input::resetInputStates() {
for (auto &binding : gamepad->bindings) { for (auto &binding : gamepad->bindings) {
binding.second.is_held = false; binding.second.is_held = false;
binding.second.just_pressed = false; binding.second.just_pressed = false;
binding.second.trigger_active = false;
} }
} }
} }
@@ -297,9 +352,9 @@ void Input::update() {
} }
// --- MANDOS --- // --- MANDOS ---
for (auto gamepad : gamepads_) { for (const auto &gamepad : gamepads_) {
for (auto &binding : gamepad->bindings) { for (auto &binding : gamepad->bindings) {
bool button_is_down_now = static_cast<int>(SDL_GetGamepadButton(gamepad->pad, binding.second.button)) != 0; bool button_is_down_now = static_cast<int>(SDL_GetGamepadButton(gamepad->pad, static_cast<SDL_GamepadButton>(binding.second.button))) != 0;
// El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo // El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo
binding.second.just_pressed = button_is_down_now && !binding.second.is_held; binding.second.just_pressed = button_is_down_now && !binding.second.is_held;
@@ -321,13 +376,13 @@ auto Input::handleEvent(const SDL_Event &event) -> std::string {
auto Input::addGamepad(int device_index) -> std::string { auto Input::addGamepad(int device_index) -> std::string {
SDL_Gamepad *pad = SDL_OpenGamepad(device_index); SDL_Gamepad *pad = SDL_OpenGamepad(device_index);
if (pad == nullptr) { if (pad == nullptr) {
std::cerr << "Error al abrir el gamepad: " << SDL_GetError() << std::endl; std::cerr << "Error al abrir el gamepad: " << SDL_GetError() << '\n';
return {}; return {};
} }
auto gamepad = std::make_shared<Gamepad>(pad); auto gamepad = std::make_shared<Gamepad>(pad);
auto name = gamepad->name; auto name = gamepad->name;
std::cout << "Gamepad connected (" << name << ")" << std::endl; std::cout << "Gamepad connected (" << name << ")" << '\n';
applyGamepadConfig(gamepad); applyGamepadConfig(gamepad);
saveGamepadConfigFromGamepad(gamepad); saveGamepadConfigFromGamepad(gamepad);
gamepads_.push_back(std::move(gamepad)); gamepads_.push_back(std::move(gamepad));
@@ -335,23 +390,23 @@ auto Input::addGamepad(int device_index) -> std::string {
} }
auto Input::removeGamepad(SDL_JoystickID id) -> std::string { auto Input::removeGamepad(SDL_JoystickID id) -> std::string {
auto it = std::find_if(gamepads_.begin(), gamepads_.end(), [id](const std::shared_ptr<Gamepad> &gamepad) { auto it = std::ranges::find_if(gamepads_, [id](const std::shared_ptr<Gamepad> &gamepad) {
return gamepad->instance_id == id; return gamepad->instance_id == id;
}); });
if (it != gamepads_.end()) { if (it != gamepads_.end()) {
std::string name = (*it)->name; std::string name = (*it)->name;
std::cout << "Gamepad disconnected (" << name << ")" << std::endl; std::cout << "Gamepad disconnected (" << name << ")" << '\n';
gamepads_.erase(it); gamepads_.erase(it);
return name + " DISCONNECTED"; return name + " DISCONNECTED";
} }
std::cerr << "No se encontró el gamepad con ID " << id << std::endl; std::cerr << "No se encontró el gamepad con ID " << id << '\n';
return {}; return {};
} }
void Input::printConnectedGamepads() const { void Input::printConnectedGamepads() const {
if (gamepads_.empty()) { if (gamepads_.empty()) {
std::cout << "No hay gamepads conectados." << std::endl; std::cout << "No hay gamepads conectados." << '\n';
return; return;
} }
@@ -359,7 +414,7 @@ void Input::printConnectedGamepads() const {
for (const auto &gamepad : gamepads_) { for (const auto &gamepad : gamepads_) {
std::string name = gamepad->name.empty() ? "Desconocido" : gamepad->name; std::string name = gamepad->name.empty() ? "Desconocido" : gamepad->name;
std::cout << " - ID: " << gamepad->instance_id std::cout << " - ID: " << gamepad->instance_id
<< ", Nombre: " << name << ")" << std::endl; << ", Nombre: " << name << ")" << '\n';
} }
} }
@@ -379,13 +434,13 @@ void Input::applyGamepadConfig(std::shared_ptr<Gamepad> gamepad) {
} }
// --- Buscar configuración por RUTA (path) --- // --- Buscar configuración por RUTA (path) ---
auto config_it = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepad](const GamepadConfig &config) { auto config_it = std::ranges::find_if(gamepad_configs_, [&gamepad](const GamepadConfig &config) {
return config.path == gamepad->path; return config.path == gamepad->path;
}); });
if (config_it != gamepad_configs_.end()) { if (config_it != gamepad_configs_.end()) {
// Se encontró una configuración específica para este puerto/dispositivo. La aplicamos. // Se encontró una configuración específica para este puerto/dispositivo. La aplicamos.
std::cout << "Applying custom config for gamepad at path: " << gamepad->path << std::endl; std::cout << "Applying custom config for gamepad at path: " << gamepad->path << '\n';
for (const auto &[action, button] : config_it->bindings) { for (const auto &[action, button] : config_it->bindings) {
if (gamepad->bindings.find(action) != gamepad->bindings.end()) { if (gamepad->bindings.find(action) != gamepad->bindings.end()) {
gamepad->bindings[action].button = button; gamepad->bindings[action].button = button;
@@ -401,7 +456,7 @@ void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
} }
// --- CAMBIO CLAVE: Buscar si ya existe una configuración por RUTA (path) --- // --- CAMBIO CLAVE: Buscar si ya existe una configuración por RUTA (path) ---
auto config_it = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepad](const GamepadConfig &config) { auto config_it = std::ranges::find_if(gamepad_configs_, [&gamepad](const GamepadConfig &config) {
return config.path == gamepad->path; return config.path == gamepad->path;
}); });
@@ -411,7 +466,7 @@ void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
// Copiar todos los bindings actuales del gamepad // Copiar todos los bindings actuales del gamepad
for (const auto &[action, buttonState] : gamepad->bindings) { for (const auto &[action, buttonState] : gamepad->bindings) {
new_config.bindings[action] = buttonState.button; new_config.bindings[action] = static_cast<SDL_GamepadButton>(buttonState.button);
} }
if (config_it != gamepad_configs_.end()) { if (config_it != gamepad_configs_.end()) {
@@ -434,7 +489,7 @@ void Input::setGamepadConfigsFile(const std::string &filename) {
// Método para obtener configuración de un gamepad específico (opcional) // Método para obtener configuración de un gamepad específico (opcional)
auto Input::getGamepadConfig(const std::string &gamepad_name) -> GamepadConfig * { auto Input::getGamepadConfig(const std::string &gamepad_name) -> GamepadConfig * {
auto config_it = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepad_name](const GamepadConfig &config) { auto config_it = std::ranges::find_if(gamepad_configs_, [&gamepad_name](const GamepadConfig &config) {
return config.name == gamepad_name; return config.name == gamepad_name;
}); });
@@ -443,7 +498,7 @@ auto Input::getGamepadConfig(const std::string &gamepad_name) -> GamepadConfig *
// Método para eliminar configuración de gamepad (opcional) // Método para eliminar configuración de gamepad (opcional)
auto Input::removeGamepadConfig(const std::string &gamepad_name) -> bool { auto Input::removeGamepadConfig(const std::string &gamepad_name) -> bool {
auto config_it = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepad_name](const GamepadConfig &config) { auto config_it = std::ranges::find_if(gamepad_configs_, [&gamepad_name](const GamepadConfig &config) {
return config.name == gamepad_name; return config.name == gamepad_name;
}); });

View File

@@ -11,18 +11,19 @@
#include "gamepad_config_manager.h" // Para GamepadConfig (ptr only), GamepadConfigs #include "gamepad_config_manager.h" // Para GamepadConfig (ptr only), GamepadConfigs
#include "input_types.h" // Para InputAction #include "input_types.h" // Para InputAction
// Clase Input: gestiona la entrada de teclado y mandos (singleton) // --- Clase Input: gestiona la entrada de teclado y mandos (singleton) ---
class Input { class Input {
public: public:
// --- Constantes --- // --- Constantes ---
static constexpr bool ALLOW_REPEAT = true; static constexpr bool ALLOW_REPEAT = true; // Permite repetición
static constexpr bool DO_NOT_ALLOW_REPEAT = false; static constexpr bool DO_NOT_ALLOW_REPEAT = false; // No permite repetición
static constexpr bool CHECK_KEYBOARD = true; // Comprueba teclado
static constexpr bool DO_NOT_CHECK_KEYBOARD = false; // No comprueba teclado
static constexpr int TRIGGER_L2_AS_BUTTON = 100; // L2 como botón
static constexpr int TRIGGER_R2_AS_BUTTON = 101; // R2 como botón
static constexpr bool CHECK_KEYBOARD = true; // --- Tipos ---
static constexpr bool DO_NOT_CHECK_KEYBOARD = false; using Action = InputAction; // Alias para mantener compatibilidad
// Alias para mantener compatibilidad con el código existente
using Action = InputAction;
// --- Estructuras --- // --- Estructuras ---
struct KeyState { struct KeyState {
@@ -35,12 +36,13 @@ class Input {
}; };
struct ButtonState { struct ButtonState {
SDL_GamepadButton button; // GameControllerButton asociado int button; // GameControllerButton asociado
bool is_held; // Está pulsada ahora mismo bool is_held; // Está pulsada ahora mismo
bool just_pressed; // Se acaba de pulsar en este fotograma bool just_pressed; // Se acaba de pulsar en este fotograma
bool axis_active; // Estado del eje bool axis_active; // Estado del eje
bool trigger_active{false}; // Estado del trigger como botón digital
ButtonState(SDL_GamepadButton btn = SDL_GAMEPAD_BUTTON_INVALID, bool is_held = false, bool just_pressed = false, bool axis_act = false) ButtonState(int btn = static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID), bool is_held = false, bool just_pressed = false, bool axis_act = false)
: button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act) {} : button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act) {}
}; };
@@ -102,23 +104,23 @@ class Input {
path(std::string(SDL_GetGamepadPath(pad))), path(std::string(SDL_GetGamepadPath(pad))),
bindings{ bindings{
// Movimiento del jugador // Movimiento del jugador
{Action::UP, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_UP)}, {Action::UP, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_DPAD_UP))},
{Action::DOWN, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_DOWN)}, {Action::DOWN, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_DPAD_DOWN))},
{Action::LEFT, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_LEFT)}, {Action::LEFT, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_DPAD_LEFT))},
{Action::RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_RIGHT)}, {Action::RIGHT, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_DPAD_RIGHT))},
// Disparo del jugador // Disparo del jugador
{Action::FIRE_LEFT, ButtonState(SDL_GAMEPAD_BUTTON_WEST)}, {Action::FIRE_LEFT, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_WEST))},
{Action::FIRE_CENTER, ButtonState(SDL_GAMEPAD_BUTTON_NORTH)}, {Action::FIRE_CENTER, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_NORTH))},
{Action::FIRE_RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_EAST)}, {Action::FIRE_RIGHT, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_EAST))},
// Interfaz // Interfaz
{Action::START, ButtonState(SDL_GAMEPAD_BUTTON_START)}, {Action::START, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_START))},
{Action::SERVICE, ButtonState(SDL_GAMEPAD_BUTTON_BACK)}, {Action::SERVICE, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_BACK))},
// Menu de servicio // Menu de servicio
{Action::SM_SELECT, ButtonState(SDL_GAMEPAD_BUTTON_WEST)}, {Action::SM_SELECT, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_WEST))},
{Action::SM_BACK, ButtonState(SDL_GAMEPAD_BUTTON_NORTH)}} {} {Action::SM_BACK, ButtonState(static_cast<int>(SDL_GAMEPAD_BUTTON_NORTH))}} {}
~Gamepad() { ~Gamepad() {
if (pad != nullptr) { if (pad != nullptr) {
@@ -128,11 +130,12 @@ class Input {
// Reasigna un botón a una acción // Reasigna un botón a una acción
void rebindAction(Action action, SDL_GamepadButton new_button) { void rebindAction(Action action, SDL_GamepadButton new_button) {
bindings[action] = new_button; bindings[action] = static_cast<int>(new_button);
} }
}; };
using Gamepads = std::vector<std::shared_ptr<Gamepad>>; // --- Tipos ---
using Gamepads = std::vector<std::shared_ptr<Gamepad>>; // Vector de gamepads
// --- Métodos de singleton --- // --- Métodos de singleton ---
static void init(const std::string &game_controller_db_path, const std::string &gamepad_configs_file); static void init(const std::string &game_controller_db_path, const std::string &gamepad_configs_file);
@@ -141,18 +144,18 @@ class Input {
// --- Métodos de configuración de controles --- // --- Métodos de configuración de controles ---
void bindKey(Action action, SDL_Scancode code); void bindKey(Action action, SDL_Scancode code);
static void bindGameControllerButton(std::shared_ptr<Gamepad> gamepad, Action action, SDL_GamepadButton button); static void bindGameControllerButton(const std::shared_ptr<Gamepad> &gamepad, Action action, SDL_GamepadButton button);
static void bindGameControllerButton(std::shared_ptr<Gamepad> gamepad, Action action_target, Action action_source); static void bindGameControllerButton(const std::shared_ptr<Gamepad> &gamepad, Action action_target, Action action_source);
// --- Métodos de consulta de entrada --- // --- Métodos de consulta de entrada ---
void update(); void update();
auto checkAction(Action action, bool repeat = true, bool check_keyboard = true, std::shared_ptr<Gamepad> gamepad = nullptr) -> bool; auto checkAction(Action action, bool repeat = true, bool check_keyboard = true, const std::shared_ptr<Gamepad> &gamepad = nullptr) -> bool;
auto checkAnyInput(bool check_keyboard = true, std::shared_ptr<Gamepad> gamepad = nullptr) -> bool; auto checkAnyInput(bool check_keyboard = true, const std::shared_ptr<Gamepad> &gamepad = nullptr) -> bool;
auto checkAnyButton(bool repeat = DO_NOT_ALLOW_REPEAT) -> bool; auto checkAnyButton(bool repeat = DO_NOT_ALLOW_REPEAT) -> bool;
// --- Métodos de gestión de mandos --- // --- Métodos de gestión de mandos ---
[[nodiscard]] auto gameControllerFound() const -> bool; [[nodiscard]] auto gameControllerFound() const -> bool;
static auto getControllerName(std::shared_ptr<Gamepad> gamepad) -> std::string; static auto getControllerName(const std::shared_ptr<Gamepad> &gamepad) -> std::string;
auto getControllerNames() const -> std::vector<std::string>; auto getControllerNames() const -> std::vector<std::string>;
[[nodiscard]] auto getNumGamepads() const -> int; [[nodiscard]] auto getNumGamepads() const -> int;
auto getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Gamepad>; auto getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Gamepad>;
@@ -160,7 +163,7 @@ class Input {
auto getGamepads() const -> const Gamepads & { return gamepads_; } auto getGamepads() const -> const Gamepads & { return gamepads_; }
// --- Métodos de consulta y utilidades --- // --- Métodos de consulta y utilidades ---
[[nodiscard]] static auto getControllerBinding(std::shared_ptr<Gamepad> gamepad, Action action) -> SDL_GamepadButton; [[nodiscard]] static auto getControllerBinding(const std::shared_ptr<Gamepad> &gamepad, Action action) -> SDL_GamepadButton;
[[nodiscard]] static auto inputToString(Action action) -> std::string; [[nodiscard]] static auto inputToString(Action action) -> std::string;
[[nodiscard]] static auto stringToInput(const std::string &name) -> Action; [[nodiscard]] static auto stringToInput(const std::string &name) -> Action;
@@ -178,6 +181,7 @@ class Input {
private: private:
// --- Constantes --- // --- Constantes ---
static constexpr Sint16 AXIS_THRESHOLD = 30000; static constexpr Sint16 AXIS_THRESHOLD = 30000;
static constexpr Sint16 TRIGGER_THRESHOLD = 16384; // Umbral para triggers (aproximadamente 50% del rango)
static constexpr std::array<Action, 4> BUTTON_INPUTS = {Action::FIRE_LEFT, Action::FIRE_CENTER, Action::FIRE_RIGHT, Action::START}; // Listado de los inputs para jugar que utilizan botones, ni palancas ni crucetas static constexpr std::array<Action, 4> BUTTON_INPUTS = {Action::FIRE_LEFT, Action::FIRE_CENTER, Action::FIRE_RIGHT, Action::START}; // Listado de los inputs para jugar que utilizan botones, ni palancas ni crucetas
// --- Variables internas --- // --- Variables internas ---
@@ -189,7 +193,8 @@ class Input {
// --- Métodos internos --- // --- Métodos internos ---
void initSDLGamePad(); void initSDLGamePad();
static auto checkAxisInput(Action action, std::shared_ptr<Gamepad> gamepad, bool repeat) -> bool; static auto checkAxisInput(Action action, const std::shared_ptr<Gamepad> &gamepad, bool repeat) -> bool;
static auto checkTriggerInput(Action action, const std::shared_ptr<Gamepad> &gamepad, bool repeat) -> bool;
auto addGamepad(int device_index) -> std::string; auto addGamepad(int device_index) -> std::string;
auto removeGamepad(SDL_JoystickID id) -> std::string; auto removeGamepad(SDL_JoystickID id) -> std::string;
void addGamepadMappingsFromFile(); void addGamepadMappingsFromFile();

View File

@@ -73,7 +73,9 @@ const std::unordered_map<SDL_GamepadButton, std::string> BUTTON_TO_STRING = {
{SDL_GAMEPAD_BUTTON_DPAD_UP, "DPAD_UP"}, {SDL_GAMEPAD_BUTTON_DPAD_UP, "DPAD_UP"},
{SDL_GAMEPAD_BUTTON_DPAD_DOWN, "DPAD_DOWN"}, {SDL_GAMEPAD_BUTTON_DPAD_DOWN, "DPAD_DOWN"},
{SDL_GAMEPAD_BUTTON_DPAD_LEFT, "DPAD_LEFT"}, {SDL_GAMEPAD_BUTTON_DPAD_LEFT, "DPAD_LEFT"},
{SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "DPAD_RIGHT"}}; {SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "DPAD_RIGHT"},
{static_cast<SDL_GamepadButton>(100), "L2_AS_BUTTON"},
{static_cast<SDL_GamepadButton>(101), "R2_AS_BUTTON"}};
const std::unordered_map<std::string, SDL_GamepadButton> STRING_TO_BUTTON = { const std::unordered_map<std::string, SDL_GamepadButton> STRING_TO_BUTTON = {
{"WEST", SDL_GAMEPAD_BUTTON_WEST}, {"WEST", SDL_GAMEPAD_BUTTON_WEST},
@@ -87,7 +89,9 @@ const std::unordered_map<std::string, SDL_GamepadButton> STRING_TO_BUTTON = {
{"DPAD_UP", SDL_GAMEPAD_BUTTON_DPAD_UP}, {"DPAD_UP", SDL_GAMEPAD_BUTTON_DPAD_UP},
{"DPAD_DOWN", SDL_GAMEPAD_BUTTON_DPAD_DOWN}, {"DPAD_DOWN", SDL_GAMEPAD_BUTTON_DPAD_DOWN},
{"DPAD_LEFT", SDL_GAMEPAD_BUTTON_DPAD_LEFT}, {"DPAD_LEFT", SDL_GAMEPAD_BUTTON_DPAD_LEFT},
{"DPAD_RIGHT", SDL_GAMEPAD_BUTTON_DPAD_RIGHT}}; {"DPAD_RIGHT", SDL_GAMEPAD_BUTTON_DPAD_RIGHT},
{"L2_AS_BUTTON", static_cast<SDL_GamepadButton>(100)},
{"R2_AS_BUTTON", static_cast<SDL_GamepadButton>(101)}};
const std::unordered_map<InputAction, InputAction> ACTION_TO_ACTION = { const std::unordered_map<InputAction, InputAction> ACTION_TO_ACTION = {
{InputAction::SM_SELECT, InputAction::FIRE_LEFT}, {InputAction::SM_SELECT, InputAction::FIRE_LEFT},

View File

@@ -5,8 +5,8 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
// Acciones de entrada posibles en el juego // --- Enums ---
enum class InputAction : int { enum class InputAction : int { // Acciones de entrada posibles en el juego
// Inputs de movimiento // Inputs de movimiento
UP, UP,
DOWN, DOWN,
@@ -47,9 +47,9 @@ enum class InputAction : int {
SIZE, SIZE,
}; };
// Mapas para convertir entre enums y strings // --- Variables ---
extern const std::unordered_map<InputAction, std::string> ACTION_TO_STRING; extern const std::unordered_map<InputAction, std::string> ACTION_TO_STRING; // Mapeo de acción a string
extern const std::unordered_map<std::string, InputAction> STRING_TO_ACTION; extern const std::unordered_map<std::string, InputAction> STRING_TO_ACTION; // Mapeo de string a acción
extern const std::unordered_map<SDL_GamepadButton, std::string> BUTTON_TO_STRING; extern const std::unordered_map<SDL_GamepadButton, std::string> BUTTON_TO_STRING; // Mapeo de botón a string
extern const std::unordered_map<std::string, SDL_GamepadButton> STRING_TO_BUTTON; extern const std::unordered_map<std::string, SDL_GamepadButton> STRING_TO_BUTTON; // Mapeo de string a botón
extern const std::unordered_map<InputAction, InputAction> ACTION_TO_ACTION; extern const std::unordered_map<InputAction, InputAction> ACTION_TO_ACTION; // Mapeo de acción a acción

View File

@@ -8,10 +8,8 @@
class Texture; // lines 6-6 class Texture; // lines 6-6
Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation) Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation)
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)), : sprite_(std::make_unique<AnimatedSprite>(texture, animation)), play_area_(play_area), type_(type) {
type_(type),
play_area_(play_area) {
switch (type) { switch (type) {
case ItemType::COFFEE_MACHINE: { case ItemType::COFFEE_MACHINE: {
width_ = COFFEE_MACHINE_WIDTH; width_ = COFFEE_MACHINE_WIDTH;
@@ -29,7 +27,15 @@ Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, std::shared_pt
height_ = param.game.item_size; height_ = param.game.item_size;
pos_x_ = x; pos_x_ = x;
pos_y_ = y; pos_y_ = y;
vel_x_ = -1.0F + ((rand() % 5) * 0.5F); // 6 velocidades: 3 negativas (-1.0, -0.66, -0.33) y 3 positivas (0.33, 0.66, 1.0)
const int direction = rand() % 6;
if (direction < 3) {
// Velocidades negativas: -1.0, -0.66, -0.33
vel_x_ = -1.0F + (direction * 0.33F);
} else {
// Velocidades positivas: 0.33, 0.66, 1.0
vel_x_ = 0.33F + ((direction - 3) * 0.33F);
}
vel_y_ = -4.0F; vel_y_ = -4.0F;
accel_y_ = 0.2F; accel_y_ = 0.2F;
collider_.r = width_ / 2; collider_.r = width_ / 2;
@@ -82,9 +88,9 @@ void Item::move() {
const float MAX_X = play_area_.w - width_; const float MAX_X = play_area_.w - width_;
pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);
// Si toca el borde lateral, invierte la velocidad horizontal // Si toca el borde lateral
if (pos_x_ == MIN_X || pos_x_ == MAX_X) { if (pos_x_ == MIN_X || pos_x_ == MAX_X) {
vel_x_ = -vel_x_; vel_x_ = -vel_x_; // Invierte la velocidad horizontal
} }
// Si colisiona por arriba, rebota (excepto la máquina de café) // Si colisiona por arriba, rebota (excepto la máquina de café)
@@ -92,8 +98,8 @@ void Item::move() {
// Corrige // Corrige
pos_y_ = param.game.play_area.rect.y; pos_y_ = param.game.play_area.rect.y;
// Invierte la velocidad // Fuerza la velocidad hacia abajo para evitar oscilaciones
vel_y_ = -vel_y_; vel_y_ = std::abs(vel_y_);
} }
// Si colisiona con la parte inferior // Si colisiona con la parte inferior
@@ -181,17 +187,17 @@ auto Item::getCoffeeMachineSpawn(int player_x, int item_width, int area_width, i
// Ambos lados disponibles, elegir aleatoriamente // Ambos lados disponibles, elegir aleatoriamente
if (rand() % 2 == 0) { if (rand() % 2 == 0) {
// Lado izquierdo // Lado izquierdo
return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND; return (rand() % (exclude_left - LEFT_BOUND)) + LEFT_BOUND;
} // Lado derecho } // Lado derecho
return rand() % (RIGHT_BOUND - exclude_right) + exclude_right; return (rand() % (RIGHT_BOUND - exclude_right)) + exclude_right;
} }
if (can_spawn_left) { if (can_spawn_left) {
// Solo lado izquierdo disponible // Solo lado izquierdo disponible
return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND; return (rand() % (exclude_left - LEFT_BOUND)) + LEFT_BOUND;
} }
if (can_spawn_right) { if (can_spawn_right) {
// Solo lado derecho disponible // Solo lado derecho disponible
return rand() % (RIGHT_BOUND - exclude_right) + exclude_right; return (rand() % (RIGHT_BOUND - exclude_right)) + exclude_right;
} // No hay espacio suficiente lejos del jugador } // No hay espacio suficiente lejos del jugador
// Por ahora, intentar spawn en el extremo más lejano posible // Por ahora, intentar spawn en el extremo más lejano posible
int distance_to_left = abs(player_x - LEFT_BOUND); int distance_to_left = abs(player_x - LEFT_BOUND);

View File

@@ -11,8 +11,7 @@
class Texture; class Texture;
// Tipos de objetos disponibles en el juego. // --- Enums ---
// Define los diferentes tipos de objetos que pueden existir en el juego.
enum class ItemType : int { enum class ItemType : int {
DISK = 1, // Disco DISK = 1, // Disco
GAVINA = 2, // Gavina GAVINA = 2, // Gavina
@@ -24,81 +23,57 @@ enum class ItemType : int {
NONE = 8, // Ninguno NONE = 8, // Ninguno
}; };
// Clase Item. // --- Clase Item: representa un objeto en el juego ---
// Representa un objeto en el juego, con sus propiedades y métodos para gestionar su comportamiento.
class Item { class Item {
public: public:
// Constantes // --- Constantes ---
static constexpr int COFFEE_MACHINE_WIDTH = 30; static constexpr int COFFEE_MACHINE_WIDTH = 30; // Anchura de la máquina de café
static constexpr int COFFEE_MACHINE_HEIGHT = 39; static constexpr int COFFEE_MACHINE_HEIGHT = 39; // Altura de la máquina de café
// Constructor. Inicializa un objeto Item con el tipo, posición, área de juego, textura y animación. // --- Constructor y destructor ---
Item(ItemType type, float x, float y, SDL_FRect &play_area, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation); 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
// Destructor. // --- Métodos principales ---
~Item() = default; void alignTo(int x); // Centra el objeto en la posición X indicada
void render(); // Renderiza el objeto en pantalla
void disable(); // Desactiva el objeto
void update(); // Actualiza la posición, animación y contadores
// Centra el objeto en la posición X indicada, asegurando que no se salga del área de juego. // --- Getters ---
void alignTo(int x); [[nodiscard]] auto getPosX() const -> float { return pos_x_; } // Obtiene la posición X
[[nodiscard]] auto getPosY() const -> float { return pos_y_; } // Obtiene la posición Y
// Renderiza el objeto en pantalla si está habilitado. [[nodiscard]] auto getWidth() const -> int { return width_; } // Obtiene la anchura
// Si el tiempo de vida es mayor que 200, renderiza el sprite. [[nodiscard]] auto getHeight() const -> int { return height_; } // Obtiene la altura
// Si es menor o igual a 200, renderiza el sprite de forma intermitente. [[nodiscard]] auto getType() const -> ItemType { return type_; } // Obtiene el tipo
void render(); [[nodiscard]] auto isEnabled() const -> bool { return enabled_; } // Verifica si está habilitado
[[nodiscard]] auto isOnFloor() const -> bool { return floor_collision_; } // Verifica si está en el suelo
// Desactiva el objeto estableciendo su estado enabled_ a false. auto getCollider() -> Circle & { return collider_; } // Obtiene el colisionador
void disable();
// Actualiza la posición, animación y contadores del objeto.
// Llama a move(), sprite_->update() y updateTimeToLive().
void update();
// Getters
[[nodiscard]] auto getPosX() const -> float { return pos_x_; }
[[nodiscard]] auto getPosY() const -> float { return pos_y_; }
[[nodiscard]] auto getWidth() const -> int { return width_; }
[[nodiscard]] auto getHeight() const -> int { return height_; }
[[nodiscard]] auto getType() const -> ItemType { return type_; }
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; }
[[nodiscard]] auto isOnFloor() const -> bool { return floor_collision_; }
auto getCollider() -> Circle & { return collider_; }
private: private:
// Objetos y punteros // --- Objetos y punteros ---
std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos del objeto std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos del objeto
// Variables de estado y físicas // --- Variables de estado ---
SDL_FRect play_area_; // Rectángulo con la zona de juego
Circle collider_; // Círculo de colisión del objeto
ItemType type_; // Tipo de objeto
float pos_x_; // Posición X del objeto float pos_x_; // Posición X del objeto
float pos_y_; // Posición Y del objeto float pos_y_; // Posición Y del objeto
int width_; // Ancho del objeto
int height_; // Alto del objeto
float vel_x_; // Velocidad en el eje X float vel_x_; // Velocidad en el eje X
float vel_y_; // Velocidad en el eje Y float vel_y_; // Velocidad en el eje Y
float accel_x_ = 0.0F; // Aceleración en el eje X float accel_x_ = 0.0F; // Aceleración en el eje X
float accel_y_; // Aceleración en el eje Y float accel_y_; // Aceleración en el eje Y
bool floor_collision_ = false; // Indica si el objeto colisiona con el suelo int width_; // Ancho del objeto
ItemType type_; // Tipo de objeto int height_; // Alto del objeto
bool enabled_ = true; // Indica si el objeto está habilitado
Circle collider_; // Círculo de colisión del objeto
SDL_FRect play_area_; // Rectángulo con la zona de juego
Uint16 time_to_live_ = 600; // Tiempo que el objeto está presente Uint16 time_to_live_ = 600; // Tiempo que el objeto está presente
bool floor_collision_ = false; // Indica si el objeto colisiona con el suelo
bool enabled_ = true; // Indica si el objeto está habilitado
// Alinea el círculo de colisión con la posición del objeto. // --- Métodos internos ---
// Actualiza las coordenadas X e Y del colisionador. void shiftColliders(); // Alinea el círculo de colisión con la posición del objeto
void shiftColliders(); void shiftSprite(); // Coloca el sprite en la posición del objeto
void move(); // Actualiza la posición y estados del objeto
// Coloca el sprite en la posición del objeto. void updateTimeToLive(); // Actualiza el contador de tiempo de vida
// Actualiza las coordenadas X e Y del sprite. 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é
void shiftSprite();
// Actualiza la posición y estados del objeto.
// Controla las colisiones con los límites del área de juego y actualiza sprite y colisionador.
void move();
// Actualiza el contador de tiempo de vida del objeto.
// Si el tiempo de vida es mayor a 0, lo decrementa. Si llega a 0, desactiva el objeto.
void updateTimeToLive();
// 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;
}; };

View File

@@ -1,17 +1,18 @@
#pragma once #pragma once
#include <string> // Para string, basic_string #include <string> // Para string, basic_string
#include <utility> #include <utility> // Para move
// --- Namespace Lang: gestión de idiomas y textos ---
namespace Lang { namespace Lang {
// --- Códigos de idioma soportados --- // --- Enums ---
enum class Code : int { enum class Code : int {
SPANISH = 0, SPANISH = 0, // Español
VALENCIAN = 1, VALENCIAN = 1, // Valenciano
ENGLISH = 2 ENGLISH = 2 // Inglés
}; };
// --- Estructura que representa un idioma --- // --- Estructuras ---
struct Language { struct Language {
Code code; // Código que identifica al idioma Code code; // Código que identifica al idioma
std::string name; // Nombre que identifica el idioma std::string name; // Nombre que identifica el idioma
@@ -21,7 +22,7 @@ struct Language {
: code(c), name(std::move(n)), file_name(std::move(fn)) {} : code(c), name(std::move(n)), file_name(std::move(fn)) {}
}; };
// --- Métodos --- // --- Funciones ---
auto loadFromFile(const std::string &file_path) -> bool; // Carga los textos desde el fichero JSON especificado auto loadFromFile(const std::string &file_path) -> bool; // Carga los textos desde el fichero JSON especificado
auto getText(const std::string &key) -> std::string; // Obtiene el texto por clave auto getText(const std::string &key) -> std::string; // Obtiene el texto por clave
auto getNextLangCode(Code current_lang) -> Code; // Obtiene el código del siguiente idioma (circular) auto getNextLangCode(Code current_lang) -> Code; // Obtiene el código del siguiente idioma (circular)

View File

@@ -17,5 +17,5 @@ auto main(int argc, char* argv[]) -> int {
auto director = std::make_unique<Director>(argc, std::span<char*>(argv, argc)); auto director = std::make_unique<Director>(argc, std::span<char*>(argv, argc));
// Bucle principal // Bucle principal
return director->run(); return Director::run();
} }

View File

@@ -36,9 +36,9 @@ auto ManageHiScoreTable::add(const HiScoreEntry &entry) -> int {
sort(); sort();
// Encontrar la posición del nuevo elemento // Encontrar la posición del nuevo elemento
auto it = std::find_if(table_.begin(), table_.end(), [&](const HiScoreEntry &e) { return e.name == entry.name && auto it = std::ranges::find_if(table_, [&](const HiScoreEntry &e) {
e.score == entry.score && return e.name == entry.name && e.score == entry.score && e.one_credit_complete == entry.one_credit_complete;
e.one_credit_complete == entry.one_credit_complete; }); });
int position = -1; int position = -1;
if (it != table_.end()) { if (it != table_.end()) {
@@ -66,7 +66,7 @@ void ManageHiScoreTable::sort() {
auto operator()(const HiScoreEntry &a, const HiScoreEntry &b) const -> bool { return a.score > b.score; } auto operator()(const HiScoreEntry &a, const HiScoreEntry &b) const -> bool { return a.score > b.score; }
} score_descending_comparator; } score_descending_comparator;
std::sort(table_.begin(), table_.end(), score_descending_comparator); std::ranges::sort(table_, score_descending_comparator);
} }
// Carga la tabla desde un fichero // Carga la tabla desde un fichero

View File

@@ -3,15 +3,7 @@
#include <string> // Para std::string #include <string> // Para std::string
#include <vector> // Para std::vector #include <vector> // Para std::vector
/* // --- Estructuras ---
Esta clase sirve para añadir elementos HiScoreEntry a un vector (tabla), de manera
que la tabla siempre está ordenada.
Además tiene un método para dejar la tabla con sus valores iniciales y métodos para
leer y escribir la tabla a un fichero.
*/
// --- Estructura para las entradas de la tabla de records ---
struct HiScoreEntry { struct HiScoreEntry {
std::string name; // Nombre std::string name; // Nombre
int score; // Puntuación int score; // Puntuación
@@ -22,7 +14,8 @@ struct HiScoreEntry {
: name(n.substr(0, 6)), score(s), one_credit_complete(occ) {} : name(n.substr(0, 6)), score(s), one_credit_complete(occ) {}
}; };
using Table = std::vector<HiScoreEntry>; // --- Tipos ---
using Table = std::vector<HiScoreEntry>; // Tabla de puntuaciones
// --- Clase ManageHiScoreTable --- // --- Clase ManageHiScoreTable ---
class ManageHiScoreTable { class ManageHiScoreTable {
@@ -30,10 +23,10 @@ class ManageHiScoreTable {
// --- Constantes --- // --- Constantes ---
static constexpr int NO_ENTRY = -1; static constexpr int NO_ENTRY = -1;
// Constructor y destructor // --- Constructor y destructor ---
explicit ManageHiScoreTable(Table &table) explicit ManageHiScoreTable(Table &table) // Constructor con referencia a tabla
: table_(table) {} : table_(table) {}
~ManageHiScoreTable() = default; ~ManageHiScoreTable() = default; // Destructor
// --- Métodos públicos --- // --- Métodos públicos ---
void clear(); // Resetea la tabla a los valores por defecto void clear(); // Resetea la tabla a los valores por defecto

View File

@@ -2,13 +2,14 @@
#include <SDL3/SDL.h> // Para Uint32, SDL_Event #include <SDL3/SDL.h> // Para Uint32, SDL_Event
// --- Namespace Mouse: gestión del ratón ---
namespace Mouse { namespace Mouse {
// --- Variables de estado del cursor --- // --- Variables de estado del cursor ---
extern Uint32 cursor_hide_time; // Tiempo en milisegundos para ocultar el cursor tras inactividad extern Uint32 cursor_hide_time; // Tiempo en milisegundos para ocultar el cursor tras inactividad
extern Uint32 last_mouse_move_time; // Última vez (en ms) que el ratón se movió extern Uint32 last_mouse_move_time; // Última vez (en ms) que el ratón se movió
extern bool cursor_visible; // Indica si el cursor está visible extern bool cursor_visible; // Indica si el cursor está visible
// --- Gestión de eventos y visibilidad --- // --- Funciones ---
void handleEvent(const SDL_Event &event); // Procesa eventos de ratón (movimiento, clic, etc.) void handleEvent(const SDL_Event &event); // Procesa eventos de ratón (movimiento, clic, etc.)
void updateCursorVisibility(); // Actualiza la visibilidad del cursor según la inactividad void updateCursorVisibility(); // Actualiza la visibilidad del cursor según la inactividad
} // namespace Mouse } // namespace Mouse

View File

@@ -1,32 +1,32 @@
#include "moving_sprite.h" #include "moving_sprite.h"
#include <utility>
#include "texture.h" // Para Texture #include "texture.h" // Para Texture
// Constructor // Constructor
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos, Rotate rotate, float zoom_w, float zoom_h, SDL_FlipMode flip) MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos, Rotate rotate, float horizontal_zoom, float vertical_zoom, SDL_FlipMode flip)
: Sprite(texture, pos), : Sprite(std::move(texture), pos),
rotate_(rotate),
flip_(flip),
x_(pos.x), x_(pos.x),
y_(pos.y), y_(pos.y),
rotate_(rotate), horizontal_zoom_(horizontal_zoom),
horizontal_zoom_(zoom_w), vertical_zoom_(vertical_zoom) {}
vertical_zoom_(zoom_h),
flip_(flip) {}
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos) MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos)
: Sprite(texture, pos), : Sprite(std::move(texture), pos),
flip_(SDL_FLIP_NONE),
x_(pos.x), x_(pos.x),
y_(pos.y), y_(pos.y),
horizontal_zoom_(1.0F), horizontal_zoom_(1.0F),
vertical_zoom_(1.0F), vertical_zoom_(1.0F) {}
flip_(SDL_FLIP_NONE) {}
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture) MovingSprite::MovingSprite(std::shared_ptr<Texture> texture)
: Sprite(texture), : Sprite(std::move(texture)),
flip_(SDL_FLIP_NONE),
horizontal_zoom_(1.0F), horizontal_zoom_(1.0F),
vertical_zoom_(1.0F), vertical_zoom_(1.0F) { Sprite::clear(); }
flip_(SDL_FLIP_NONE) { Sprite::clear(); }
// Reinicia todas las variables // Reinicia todas las variables
void MovingSprite::clear() { void MovingSprite::clear() {

View File

@@ -9,23 +9,21 @@
class Texture; class Texture;
// Clase MovingSprite. Añade movimiento y efectos de rotación, zoom y flip al sprite // --- Clase MovingSprite: añade movimiento y efectos de rotación, zoom y flip al sprite ---
class MovingSprite : public Sprite { class MovingSprite : public Sprite {
public: public:
// --- Estructura para la rotación --- // --- 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 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
SDL_FPoint center; // Centro de rotación SDL_FPoint center{.x = 0.0F, .y = 0.0F}; // Centro de rotación
Rotate() : center({0.0F, 0.0F}) {}
}; };
// --- Constructores y destructor --- // --- Constructores y destructor ---
MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos, MovingSprite::Rotate rotate, float zoom_w, float zoom_h, SDL_FlipMode flip); MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos, MovingSprite::Rotate rotate, float horizontal_zoom, float vertical_zoom, SDL_FlipMode flip);
MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos); MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos);
explicit MovingSprite(std::shared_ptr<Texture> texture); explicit MovingSprite(std::shared_ptr<Texture> texture);
~MovingSprite() override = default; ~MovingSprite() override = default;
@@ -36,58 +34,48 @@ class MovingSprite : public Sprite {
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
// --- Getters de posición y movimiento --- // --- Configuración ---
[[nodiscard]] auto getPosX() const -> float { return x_; } void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto
[[nodiscard]] auto getPosY() const -> float { return y_; } void setPos(float pos_x, float pos_y); // Establece la posición del objeto
[[nodiscard]] auto getVelX() const -> float { return vx_; } void setPosX(float pos_x); // Establece la posición X
[[nodiscard]] auto getVelY() const -> float { return vy_; } void setPosY(float pos_y); // Establece la posición Y
[[nodiscard]] auto getAccelX() const -> float { return ax_; } void setVelX(float value) { vx_ = value; } // Establece la velocidad X
[[nodiscard]] auto getAccelY() const -> float { return ay_; } void setVelY(float value) { vy_ = value; } // Establece la velocidad Y
void setAccelX(float value) { ax_ = value; } // Establece la aceleración X
void setAccelY(float value) { ay_ = value; } // Establece la aceleración Y
void setHorizontalZoom(float value) { horizontal_zoom_ = value; } // Establece el zoom horizontal
void setVerticalZoom(float value) { vertical_zoom_ = value; } // Establece el zoom vertical
void setAngle(double value) { rotate_.angle = value; } // Establece el ángulo
void setRotatingCenter(SDL_FPoint point) { rotate_.center = point; } // Establece el centro de rotación
void setRotate(bool enable); // Activa o desactiva el efecto de rotación
void setRotateSpeed(int value) { rotate_.speed = std::max(1, value); } // Establece la velocidad de rotación
void setRotateAmount(double value) { rotate_.amount = value; } // Establece la cantidad de rotación
void switchRotate() { rotate_.amount *= -1; } // Cambia el sentido de la rotación
void setFlip(SDL_FlipMode flip) { flip_ = flip; } // Establece el flip
void flip() { flip_ = (flip_ == SDL_FLIP_HORIZONTAL) ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL; } // Cambia el flip
// --- Setters de movimiento --- // --- Getters ---
void setVelX(float value) { vx_ = value; } [[nodiscard]] auto getPosX() const -> float { return x_; } // Obtiene la posición X
void setVelY(float value) { vy_ = value; } [[nodiscard]] auto getPosY() const -> float { return y_; } // Obtiene la posición Y
void setAccelX(float value) { ax_ = value; } [[nodiscard]] auto getVelX() const -> float { return vx_; } // Obtiene la velocidad X
void setAccelY(float value) { ay_ = value; } [[nodiscard]] auto getVelY() const -> float { return vy_; } // Obtiene la velocidad Y
[[nodiscard]] auto getAccelX() const -> float { return ax_; } // Obtiene la aceleración X
// --- Rotación --- [[nodiscard]] auto getAccelY() const -> float { return ay_; } // Obtiene la aceleración Y
[[nodiscard]] auto isRotating() const -> bool { return rotate_.enabled; } [[nodiscard]] auto isRotating() const -> bool { return rotate_.enabled; } // Verifica si está rotando
void setHorizontalZoom(float value) { horizontal_zoom_ = value; } auto getFlip() -> SDL_FlipMode { return flip_; } // Obtiene el flip
void setVerticalZoom(float value) { vertical_zoom_ = value; }
void setAngle(double value) { rotate_.angle = value; }
void setRotatingCenter(SDL_FPoint point) { rotate_.center = point; }
void setRotate(bool enable); // Activa o desactiva el efecto de rotación
void setRotateSpeed(int value) { rotate_.speed = std::max(1, value); }
void setRotateAmount(double value) { rotate_.amount = value; }
void switchRotate() { rotate_.amount *= -1; } // Cambia el sentido de la rotación
// --- Flip ---
void setFlip(SDL_FlipMode flip) { flip_ = flip; }
void flip() { flip_ = (flip_ == SDL_FLIP_HORIZONTAL) ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL; }
auto getFlip() -> SDL_FlipMode { return flip_; }
// --- Posición y tamaño ---
void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto
void setPos(float pos_x, float pos_y); // Establece la posición del objeto
void setPosX(float pos_x); // Establece la posición X
void setPosY(float pos_y); // Establece la posición Y
protected: protected:
// --- Variables de posición y movimiento --- // --- Variables de estado ---
float x_ = 0.0F; // Posición en el eje X
float y_ = 0.0F; // Posición en el eje Y
float vx_ = 0.0F; // Velocidad en el eje X. Cantidad de píxeles a desplazarse
float vy_ = 0.0F; // Velocidad en el eje Y. Cantidad de píxeles a desplazarse
float ax_ = 0.0F; // Aceleración en el eje X. Variación de la velocidad
float ay_ = 0.0F; // Aceleración en el eje Y. Variación de la velocidad
// --- Efectos visuales ---
Rotate rotate_; // Variables usadas para controlar la rotación del sprite Rotate rotate_; // Variables usadas para controlar la rotación del sprite
SDL_FlipMode flip_; // Indica cómo se voltea el sprite
float x_ = 0.0F; // Posición en el eje X
float y_ = 0.0F; // Posición en el eje Y
float vx_ = 0.0F; // Velocidad en el eje X
float vy_ = 0.0F; // Velocidad en el eje Y
float ax_ = 0.0F; // Aceleración en el eje X
float ay_ = 0.0F; // Aceleración en el eje Y
float horizontal_zoom_; // Zoom aplicado a la anchura float horizontal_zoom_; // Zoom aplicado a la anchura
float vertical_zoom_; // Zoom aplicado a la altura float vertical_zoom_; // Zoom aplicado a la altura
SDL_FlipMode flip_; // Indica cómo se voltea el 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

View File

@@ -7,6 +7,7 @@
#include <fstream> // Para basic_ostream, operator<<, basic_ostream::operator<<, basic_ofstream, basic_istream, basic_ifstream, ifstream, ofstream #include <fstream> // Para basic_ostream, operator<<, basic_ostream::operator<<, basic_ofstream, basic_istream, basic_ifstream, ifstream, ofstream
#include <functional> // Para function #include <functional> // Para function
#include <map> // Para map, operator==, _Rb_tree_const_iterator #include <map> // Para map, operator==, _Rb_tree_const_iterator
#include <ranges> // Para std::ranges::any_of
#include <stdexcept> // Para invalid_argument, out_of_range #include <stdexcept> // Para invalid_argument, out_of_range
#include <string> // Para char_traits, stoi, operator==, operator<<, allocator, string, basic_string, operator<=>, getline #include <string> // Para char_traits, stoi, operator==, operator<<, allocator, string, basic_string, operator<=>, getline
#include <utility> // Para swap, pair #include <utility> // Para swap, pair
@@ -65,7 +66,7 @@ auto loadFromFile() -> bool {
std::string line; std::string line;
while (std::getline(file, line)) { while (std::getline(file, line)) {
if (line.substr(0, 1) != "#") { if (line.substr(0, 1) != "#") {
int pos = line.find("="); int pos = line.find('=');
if (!set(line.substr(0, pos), line.substr(pos + 1, line.length()))) { if (!set(line.substr(0, pos), line.substr(pos + 1, line.length()))) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown parameter: %s", line.substr(0, pos).c_str()); SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown parameter: %s", line.substr(0, pos).c_str());
} }
@@ -133,6 +134,7 @@ auto saveToFile() -> bool {
file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n"; file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n";
file << "game.autofire=" << boolToString(settings.autofire) << "\n"; file << "game.autofire=" << boolToString(settings.autofire) << "\n";
file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n"; file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n";
file << "game.params_file=" << settings.params_file << "\n";
// Opciones de mandos // Opciones de mandos
file << "\n## CONTROLLERS\n"; file << "\n## CONTROLLERS\n";
@@ -207,6 +209,7 @@ auto set(const std::string& var, const std::string& value) -> bool {
}}, }},
{"game.autofire", [](const auto& val) { settings.autofire = stringToBool(val); }}, {"game.autofire", [](const auto& val) { settings.autofire = stringToBool(val); }},
{"game.shutdown_enabled", [](const auto& val) { settings.shutdown_enabled = stringToBool(val); }}, {"game.shutdown_enabled", [](const auto& val) { settings.shutdown_enabled = stringToBool(val); }},
{"game.params_file", [](const auto& val) { settings.params_file = val; }},
// Teclado // Teclado
{"keyboard.player", [](const auto& val) { keyboard.player_id = static_cast<Player::Id>(stoi(val)); }}}; {"keyboard.player", [](const auto& val) { keyboard.player_id = static_cast<Player::Id>(stoi(val)); }}};
@@ -392,12 +395,10 @@ void GamepadManager::clearUnassignedGamepadSlots() {
auto GamepadManager::isGamepadAssigned( auto GamepadManager::isGamepadAssigned(
const std::shared_ptr<Input::Gamepad>& physical_gamepad, const std::shared_ptr<Input::Gamepad>& physical_gamepad,
const std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) -> bool { const std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) -> bool {
for (const auto& assigned : assigned_instances) { return std::ranges::any_of(assigned_instances,
if (assigned == physical_gamepad) { [&physical_gamepad](const auto& assigned) {
return true; // Encontrado, por lo tanto, ya está asignado. return assigned == physical_gamepad;
} });
}
return false; // No se encontró en la lista.
} }
// Convierte un player id a texto segun Lang // Convierte un player id a texto segun Lang
@@ -413,7 +414,7 @@ auto playerIdToString(Player::Id player_id) -> std::string {
} }
// Convierte un texto a player id segun Lang // Convierte un texto a player id segun Lang
auto stringToPlayerId(std::string name) -> Player::Id { auto stringToPlayerId(const std::string& name) -> Player::Id {
if (name == Lang::getText("[SERVICE_MENU] PLAYER1")) { if (name == Lang::getText("[SERVICE_MENU] PLAYER1")) {
return Player::Id::PLAYER1; return Player::Id::PLAYER1;
} }

View File

@@ -11,83 +11,62 @@
#include <stdexcept> // Para out_of_range, invalid_argument #include <stdexcept> // Para out_of_range, invalid_argument
#include <string> // Para char_traits, string, allocator, operator==, swap, operator<<, basic_string, stoi #include <string> // Para char_traits, string, allocator, operator==, swap, operator<<, basic_string, stoi
#include <string_view> // Para string_view #include <string_view> // Para string_view
#include <utility> // Para swap
#include <vector> // Para vector #include <vector> // Para vector
#include "defaults.h" // Para GameDefaults
#include "difficulty.h" // Para Code #include "difficulty.h" // Para Code
#include "input.h" // Para Input #include "input.h" // Para Input
#include "lang.h" // Para Code #include "lang.h" // Para Code
#include "manage_hiscore_table.h" // Para ManageHiScoreTable, Table #include "manage_hiscore_table.h" // Para ManageHiScoreTable, Table
#include "player.h" // Para Player #include "player.h" // Para Player
// --- Namespace Options: gestión de configuración y opciones del juego ---
namespace Options { namespace Options {
// --- Opciones de ventana --- // --- Estructuras ---
struct Window { struct Window {
std::string caption; // Texto que aparece en la barra de título de la ventana std::string caption = GameDefaults::Options::WINDOW_CAPTION; // Texto que aparece en la barra de título de la ventana
int zoom{2}; // Valor por el que se multiplica el tamaño de la ventana int zoom = GameDefaults::Options::WINDOW_ZOOM; // Valor por el que se multiplica el tamaño de la ventana
int max_zoom{2}; // Tamaño máximo para que la ventana no sea mayor que la pantalla int max_zoom = GameDefaults::Options::WINDOW_MAX_ZOOM; // Tamaño máximo para que la ventana no sea mayor que la pantalla
// Constructor por defecto con valores iniciales
Window()
: caption("Coffee Crisis Arcade Edition") {}
}; };
// --- Opciones de vídeo ---
struct Video { struct Video {
SDL_ScaleMode scale_mode{SDL_ScaleMode::SDL_SCALEMODE_NEAREST}; // Filtro usado para el escalado de la imagen SDL_ScaleMode scale_mode = GameDefaults::Options::VIDEO_SCALE_MODE; // Filtro usado para el escalado de la imagen
bool fullscreen{false}; // Indica si se usa pantalla completa bool fullscreen = GameDefaults::Options::VIDEO_FULLSCREEN; // Indica si se usa pantalla completa
bool vsync{true}; // Indica si se usa vsync bool vsync = GameDefaults::Options::VIDEO_VSYNC; // Indica si se usa vsync
bool integer_scale{true}; // Indica si se usa escalado entero bool integer_scale = GameDefaults::Options::VIDEO_INTEGER_SCALE; // Indica si se usa escalado entero
bool shaders{false}; // Indica si se usan shaders para los filtros de vídeo bool shaders = GameDefaults::Options::VIDEO_SHADERS; // Indica si se usan shaders para los filtros de vídeo
std::string info; // Información sobre el modo de vídeo std::string info; // Información sobre el modo de vídeo
// Constructor por defecto con valores iniciales
Video() = default;
}; };
// --- Opciones de música ---
struct Music { struct Music {
bool enabled{true}; // Indica si la música suena o no bool enabled = GameDefaults::Options::MUSIC_ENABLED; // Indica si la música suena o no
int volume{100}; // Volumen de la música int volume = GameDefaults::Options::MUSIC_VOLUME; // Volumen de la música
// Constructor por defecto
Music() = default;
}; };
// --- Opciones de sonido ---
struct Sound { struct Sound {
bool enabled{true}; // Indica si los sonidos suenan o no bool enabled = GameDefaults::Options::SOUND_ENABLED; // Indica si los sonidos suenan o no
int volume{100}; // Volumen de los sonidos int volume = GameDefaults::Options::SOUND_VOLUME; // Volumen de los sonidos
// Constructor por defecto
Sound() = default;
}; };
// --- Opciones de audio ---
struct Audio { struct Audio {
Music music; // Opciones para la música Music music; // Opciones para la música
Sound sound; // Opciones para los efectos de sonido Sound sound; // Opciones para los efectos de sonido
bool enabled{true}; // Indica si el audio está activo o no bool enabled = GameDefaults::Options::AUDIO_ENABLED; // Indica si el audio está activo o no
int volume{100}; // Volumen general del audio int volume = GameDefaults::Options::AUDIO_VOLUME; // Volumen general del audio
// Constructor por defecto
Audio() = default;
}; };
// --- Opciones de configuración ---
struct Settings { struct Settings {
Difficulty::Code difficulty{Difficulty::Code::NORMAL}; // Dificultad del juego Difficulty::Code difficulty = Difficulty::Code::NORMAL; // Dificultad del juego
Lang::Code language{Lang::Code::VALENCIAN}; // Idioma usado en el juego Lang::Code language = Lang::Code::VALENCIAN; // Idioma usado en el juego
bool autofire{true}; // Indicador de autofire bool autofire = GameDefaults::Options::SETTINGS_AUTOFIRE; // Indicador de autofire
bool shutdown_enabled{false}; // Especifica si se puede apagar el sistema bool shutdown_enabled = GameDefaults::Options::SETTINGS_SHUTDOWN_ENABLED; // Especifica si se puede apagar el sistema
Table hi_score_table; // Tabla de mejores puntuaciones Table hi_score_table; // Tabla de mejores puntuaciones
std::vector<int> glowing_entries; // Últimas posiciones de entrada en la tabla std::vector<int> glowing_entries = {ManageHiScoreTable::NO_ENTRY, ManageHiScoreTable::NO_ENTRY}; // Últimas posiciones de entrada en la tabla
std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego
std::string controllers_file; // Ruta al fichero con las configuraciones de los mandos std::string controllers_file; // Ruta al fichero con las configuraciones de los mandos
std::string params_file = GameDefaults::Options::PARAMS_FILE; // Ruta al fichero de parámetros del juego
// Constructor por defecto con valores iniciales
Settings()
: glowing_entries({ManageHiScoreTable::NO_ENTRY, ManageHiScoreTable::NO_ENTRY}) {}
// Reinicia las últimas entradas de puntuación // Reinicia las últimas entradas de puntuación
void clearLastHiScoreEntries() { void clearLastHiScoreEntries() {
@@ -96,7 +75,6 @@ struct Settings {
} }
}; };
// --- Estructura para gamepad individual ---
struct Gamepad { struct Gamepad {
std::shared_ptr<Input::Gamepad> instance = nullptr; // Referencia al mando std::shared_ptr<Input::Gamepad> instance = nullptr; // Referencia al mando
std::string name; // Nombre del mando std::string name; // Nombre del mando
@@ -107,7 +85,7 @@ struct Gamepad {
: player_id(custom_player_id) {} : player_id(custom_player_id) {}
}; };
// --- Manager para los gamepads --- // --- Clases ---
class GamepadManager { class GamepadManager {
public: public:
void init() { void init() {
@@ -144,7 +122,7 @@ class GamepadManager {
const std::string& name) -> bool { const std::string& name) -> bool {
try { try {
auto& gamepad = getGamepad(player_id); auto& gamepad = getGamepad(player_id);
gamepad.instance = instance; gamepad.instance = std::move(instance);
gamepad.name = name; gamepad.name = name;
return true; return true;
} catch (const std::exception&) { } catch (const std::exception&) {
@@ -161,7 +139,7 @@ class GamepadManager {
} }
void resyncGamepadsWithPlayers() { void resyncGamepadsWithPlayers() {
for (auto player : players_) { for (const auto& player : players_) {
switch (player->getId()) { switch (player->getId()) {
case Player::Id::PLAYER1: case Player::Id::PLAYER1:
player->setGamepad(gamepads_[0].instance); player->setGamepad(gamepads_[0].instance);
@@ -226,11 +204,11 @@ class GamepadManager {
return false; return false;
} }
void addPlayer(std::shared_ptr<Player> player) { players_.push_back(player); } // Añade un jugador a la lista void addPlayer(const std::shared_ptr<Player>& player) { players_.push_back(player); } // Añade un jugador a la lista
void clearPlayers() { players_.clear(); } // Limpia la lista de jugadores void clearPlayers() { players_.clear(); } // Limpia la lista de jugadores
// Asigna el mando a un jugador // Asigna el mando a un jugador
void assignTo(Input::Gamepad gamepad, Player::Id player_id) { void assignTo(const Input::Gamepad& gamepad, Player::Id player_id) {
} }
// Asigna los mandos físicos basándose en la configuración actual de nombres. // Asigna los mandos físicos basándose en la configuración actual de nombres.
@@ -279,8 +257,8 @@ struct Keyboard {
Player::Id player_id = Player::Id::PLAYER1; // Jugador asociado al teclado Player::Id player_id = Player::Id::PLAYER1; // Jugador asociado al teclado
std::vector<std::shared_ptr<Player>> players; // Punteros a los jugadores std::vector<std::shared_ptr<Player>> players; // Punteros a los jugadores
void addPlayer(std::shared_ptr<Player> player) { players.push_back(player); } // Añade un jugador a la lista void addPlayer(const std::shared_ptr<Player>& player) { players.push_back(player); } // Añade un jugador a la lista
void clearPlayers() { players.clear(); } // Limpia la lista de jugadores void clearPlayers() { players.clear(); } // Limpia la lista de jugadores
// Asigna el teclado a un jugador // Asigna el teclado a un jugador
void assignTo(Player::Id player_id) { void assignTo(Player::Id player_id) {
@@ -291,17 +269,13 @@ struct Keyboard {
} }
}; };
// --- Opciones pendientes de aplicar ---
struct PendingChanges { struct PendingChanges {
Lang::Code new_language{Lang::Code::VALENCIAN}; // Idioma en espera de aplicar Lang::Code new_language = Lang::Code::VALENCIAN; // Idioma en espera de aplicar
Difficulty::Code new_difficulty{Difficulty::Code::NORMAL}; // Dificultad en espera de aplicar Difficulty::Code new_difficulty = Difficulty::Code::NORMAL; // Dificultad en espera de aplicar
bool has_pending_changes{false}; // Indica si hay cambios pendientes bool has_pending_changes = false; // Indica si hay cambios pendientes
// Constructor por defecto con valores iniciales
PendingChanges() = default;
}; };
// --- Variables globales --- // --- Variables ---
extern Window window; // Opciones de la ventana extern Window window; // Opciones de la ventana
extern Settings settings; // Opciones del juego extern Settings settings; // Opciones del juego
extern Video video; // Opciones de vídeo extern Video video; // Opciones de vídeo
@@ -310,7 +284,7 @@ extern GamepadManager gamepad_manager; // Manager de mandos para cada jugador
extern Keyboard keyboard; // Opciones para el teclado extern Keyboard keyboard; // Opciones para el teclado
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
// --- Funciones de configuración --- // --- Funciones ---
void init(); // Inicializa las opciones del programa void init(); // Inicializa las opciones del programa
void setConfigFile(const std::string& file_path); // Establece el fichero de configuración void setConfigFile(const std::string& file_path); // Establece el fichero de configuración
void setControllersFile(const std::string& file_path); // Establece el fichero de configuración de mandos void setControllersFile(const std::string& file_path); // Establece el fichero de configuración de mandos
@@ -321,7 +295,7 @@ void swapKeyboard();
void swapControllers(); // Intercambia los jugadores asignados a los dos primeros mandos void swapControllers(); // Intercambia los jugadores asignados a los dos primeros mandos
auto getPlayerWhoUsesKeyboard() -> Player::Id; // Averigua quién está usando el teclado auto getPlayerWhoUsesKeyboard() -> Player::Id; // Averigua quién está usando el teclado
auto playerIdToString(Player::Id player_id) -> std::string; // Convierte un player id a texto segun Lang auto playerIdToString(Player::Id player_id) -> std::string; // Convierte un player id a texto segun Lang
auto stringToPlayerId(std::string name) -> Player::Id; // Convierte un texto a player id segun Lang auto stringToPlayerId(const std::string& name) -> Player::Id; // Convierte un texto a player id segun Lang
void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables
void checkPendingChanges(); // Verifica si hay cambios pendientes void checkPendingChanges(); // Verifica si hay cambios pendientes
auto assignGamepadByName(const std::string& gamepad_name, Player::Id player_id) -> bool; // Buscar y asignar un mando disponible por nombre auto assignGamepadByName(const std::string& gamepad_name, Player::Id player_id) -> bool; // Buscar y asignar un mando disponible por nombre

View File

@@ -2,23 +2,23 @@
#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogError, SDL_LogInfo #include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogError, SDL_LogInfo
#include <fstream> // Para basic_istream, basic_ifstream, ifstream #include <fstream> // Para basic_istream, basic_ifstream, ifstream
#include <functional> #include <functional>
#include <sstream> // Para basic_istringstream #include <sstream> // Para basic_istringstream
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include <string> // Para operator==, stoi, char_traits, string, ope... #include <string> // Para operator==, stoi, char_traits, string, ope...
#include <unordered_map> #include <unordered_map>
#include "color.h" #include "color.h"
#include "utils.h"
#include "ui/notifier.h" // Para Notifier::Position #include "ui/notifier.h" // Para Notifier::Position
#include "utils.h"
// Variable global - ahora se inicializa automáticamente con valores por defecto // Variable global - ahora se inicializa automáticamente con valores por defecto
Param param; Param param;
// Declaraciones de funciones privadas // Declaraciones de funciones privadas
namespace { namespace {
auto setParams(const std::string& var, const std::string& value) -> bool; auto setParams(const std::string& var, const std::string& value) -> bool;
} }
// Implementación del método privado de Param // Implementación del método privado de Param
@@ -32,7 +32,7 @@ void Param::precalculateZones() {
game.play_area.third_quarter_y = game.play_area.rect.h / 4 * 3; game.play_area.third_quarter_y = game.play_area.rect.h / 4 * 3;
// gameArea - cálculos basados en width y height actuales // gameArea - cálculos basados en width y height actuales
game.game_area.rect = {0, 0, game.width, game.height}; game.game_area.rect = {.x = 0, .y = 0, .w = game.width, .h = game.height};
game.game_area.center_x = game.game_area.rect.w / 2; game.game_area.center_x = game.game_area.rect.w / 2;
game.game_area.first_quarter_x = game.game_area.rect.w / 4; game.game_area.first_quarter_x = game.game_area.rect.w / 4;
game.game_area.third_quarter_x = game.game_area.rect.w / 4 * 3; game.game_area.third_quarter_x = game.game_area.rect.w / 4 * 3;
@@ -45,7 +45,7 @@ void Param::precalculateZones() {
void loadParamsFromFile(const std::string& file_path) { void loadParamsFromFile(const std::string& file_path) {
// Los parámetros ya están inicializados con valores por defecto // Los parámetros ya están inicializados con valores por defecto
// Solo necesitamos abrir el archivo y sobrescribir los valores que aparezcan // Solo necesitamos abrir el archivo y sobrescribir los valores que aparezcan
std::ifstream file(file_path); std::ifstream file(file_path);
if (!file.is_open()) { if (!file.is_open()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo abrir el archivo %s", file_path.c_str()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo abrir el archivo %s", file_path.c_str());
@@ -57,7 +57,7 @@ void loadParamsFromFile(const std::string& file_path) {
std::string line; std::string line;
std::string param_name; std::string param_name;
std::string param_value; std::string param_value;
while (std::getline(file, line)) { while (std::getline(file, line)) {
// Elimina comentarios // Elimina comentarios
auto comment_pos = line.find('#'); auto comment_pos = line.find('#');
@@ -82,153 +82,194 @@ void loadParamsFromFile(const std::string& file_path) {
// Implementación local de setParams // Implementación local de setParams
namespace { namespace {
auto setParams(const std::string& var, const std::string& value) -> bool { auto setParams(const std::string& var, const std::string& value) -> bool {
// Mapas estáticos para diferentes tipos de parámetros // Mapas estáticos para diferentes tipos de parámetros
static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS = { static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS = {
{"game.width", [](const std::string& v) { param.game.width = std::stoi(v); }}, {"game.width", [](const std::string& v) { param.game.width = std::stoi(v); }},
{"game.height", [](const std::string& v) { param.game.height = std::stoi(v); }}, {"game.height", [](const std::string& v) { param.game.height = std::stoi(v); }},
{"game.item_size", [](const std::string& v) { param.game.item_size = std::stoi(v); }}, {"game.item_size", [](const std::string& v) { param.game.item_size = std::stoi(v); }},
{"game.play_area.rect.x", [](const std::string& v) { param.game.play_area.rect.x = std::stoi(v); }}, {"game.play_area.rect.x", [](const std::string& v) { param.game.play_area.rect.x = std::stoi(v); }},
{"game.play_area.rect.y", [](const std::string& v) { param.game.play_area.rect.y = std::stoi(v); }}, {"game.play_area.rect.y", [](const std::string& v) { param.game.play_area.rect.y = std::stoi(v); }},
{"game.play_area.rect.w", [](const std::string& v) { param.game.play_area.rect.w = std::stoi(v); }}, {"game.play_area.rect.w", [](const std::string& v) { param.game.play_area.rect.w = std::stoi(v); }},
{"game.play_area.rect.h", [](const std::string& v) { param.game.play_area.rect.h = std::stoi(v); }}, {"game.play_area.rect.h", [](const std::string& v) { param.game.play_area.rect.h = std::stoi(v); }},
{"game.name_entry_idle_time", [](const std::string& v) { param.game.name_entry_idle_time = std::stoi(v); }}, {"game.name_entry_idle_time", [](const std::string& v) { param.game.name_entry_idle_time = std::stoi(v); }},
{"game.name_entry_total_time", [](const std::string& v) { param.game.name_entry_total_time = std::stoi(v); }}, {"game.name_entry_total_time", [](const std::string& v) { param.game.name_entry_total_time = std::stoi(v); }},
{"game.hit_stop_ms", [](const std::string& v) { param.game.hit_stop_ms = std::stoi(v); }}, {"game.hit_stop_ms", [](const std::string& v) { param.game.hit_stop_ms = std::stoi(v); }},
{"fade.num_squares_width", [](const std::string& v) { param.fade.num_squares_width = std::stoi(v); }}, {"fade.num_squares_width", [](const std::string& v) { param.fade.num_squares_width = std::stoi(v); }},
{"fade.num_squares_height", [](const std::string& v) { param.fade.num_squares_height = std::stoi(v); }}, {"fade.num_squares_height", [](const std::string& v) { param.fade.num_squares_height = std::stoi(v); }},
{"fade.random_squares_delay", [](const std::string& v) { param.fade.random_squares_delay = std::stoi(v); }}, {"fade.random_squares_duration_ms", [](const std::string& v) { param.fade.random_squares_duration_ms = std::stoi(v); }},
{"fade.random_squares_mult", [](const std::string& v) { param.fade.random_squares_mult = std::stoi(v); }}, {"fade.post_duration_ms", [](const std::string& v) { param.fade.post_duration_ms = std::stoi(v); }},
{"fade.post_duration", [](const std::string& v) { param.fade.post_duration = std::stoi(v); }}, {"fade.venetian_size", [](const std::string& v) { param.fade.venetian_size = std::stoi(v); }},
{"fade.venetian_size", [](const std::string& v) { param.fade.venetian_size = std::stoi(v); }}, {"scoreboard.rect.x", [](const std::string& v) { param.scoreboard.rect.x = std::stoi(v); }},
{"scoreboard.rect.x", [](const std::string& v) { param.scoreboard.rect.x = std::stoi(v); }}, {"scoreboard.rect.y", [](const std::string& v) { param.scoreboard.rect.y = std::stoi(v); }},
{"scoreboard.rect.y", [](const std::string& v) { param.scoreboard.rect.y = std::stoi(v); }}, {"scoreboard.rect.w", [](const std::string& v) { param.scoreboard.rect.w = std::stoi(v); }},
{"scoreboard.rect.w", [](const std::string& v) { param.scoreboard.rect.w = std::stoi(v); }}, {"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.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); }}
};
static const std::unordered_map<std::string, std::function<void(const std::string&)>> COLOR_PARAMS = { static const std::unordered_map<std::string, std::function<void(const std::string&)>> COLOR_PARAMS = {
{"fade.color", [](const std::string& v) { param.fade.color = Color::fromHex(v); }}, {"fade.color", [](const std::string& v) { param.fade.color = Color::fromHex(v); }},
{"scoreboard.separator_color", [](const std::string& v) { param.scoreboard.separator_color = Color::fromHex(v); }}, {"scoreboard.separator_color", [](const std::string& v) { param.scoreboard.separator_color = Color::fromHex(v); }},
{"scoreboard.easy_color", [](const std::string& v) { param.scoreboard.easy_color = Color::fromHex(v); }}, {"scoreboard.easy_color", [](const std::string& v) { param.scoreboard.easy_color = Color::fromHex(v); }},
{"scoreboard.normal_color", [](const std::string& v) { param.scoreboard.normal_color = Color::fromHex(v); }}, {"scoreboard.normal_color", [](const std::string& v) { param.scoreboard.normal_color = Color::fromHex(v); }},
{"scoreboard.hard_color", [](const std::string& v) { param.scoreboard.hard_color = Color::fromHex(v); }}, {"scoreboard.hard_color", [](const std::string& v) { param.scoreboard.hard_color = Color::fromHex(v); }},
{"scoreboard.text_color1", [](const std::string& v) { param.scoreboard.text_color1 = Color::fromHex(v); }}, {"scoreboard.text_color1", [](const std::string& v) { param.scoreboard.text_color1 = Color::fromHex(v); }},
{"scoreboard.text_color2", [](const std::string& v) { param.scoreboard.text_color2 = Color::fromHex(v); }}, {"scoreboard.text_color2", [](const std::string& v) { param.scoreboard.text_color2 = Color::fromHex(v); }},
{"title.bg_color", [](const std::string& v) { param.title.bg_color = Color::fromHex(v); }}, {"title.bg_color", [](const std::string& v) { param.title.bg_color = Color::fromHex(v); }},
{"background.attenuate_color", [](const std::string& v) { param.background.attenuate_color = Color::fromHex(v); }}, {"background.attenuate_color", [](const std::string& v) { param.background.attenuate_color = Color::fromHex(v); }},
{"notification.color", [](const std::string& v) { param.notification.color = Color::fromHex(v); }}, {"notification.color", [](const std::string& v) { param.notification.color = Color::fromHex(v); }},
{"service_menu.title_color", [](const std::string& v) { param.service_menu.title_color = Color::fromHex(v); }}, {"service_menu.title_color", [](const std::string& v) { param.service_menu.title_color = Color::fromHex(v); }},
{"service_menu.text_color", [](const std::string& v) { param.service_menu.text_color = Color::fromHex(v); }}, {"service_menu.text_color", [](const std::string& v) { param.service_menu.text_color = Color::fromHex(v); }},
{"service_menu.selected_color", [](const std::string& v) { param.service_menu.selected_color = Color::fromHex(v); }}, {"service_menu.selected_color", [](const std::string& v) { param.service_menu.selected_color = Color::fromHex(v); }},
{"service_menu.bg_color", [](const std::string& v) { param.service_menu.bg_color = Color::fromHex(v); }}, {"service_menu.bg_color", [](const std::string& v) { param.service_menu.bg_color = Color::fromHex(v); }},
{"service_menu.window_message.bg_color", [](const std::string& v) { param.service_menu.window_message.bg_color = Color::fromHex(v); }}, {"service_menu.window_message.bg_color", [](const std::string& v) { param.service_menu.window_message.bg_color = Color::fromHex(v); }},
{"service_menu.window_message.border_color", [](const std::string& v) { param.service_menu.window_message.border_color = Color::fromHex(v); }}, {"service_menu.window_message.border_color", [](const std::string& v) { param.service_menu.window_message.border_color = Color::fromHex(v); }},
{"service_menu.window_message.title_color", [](const std::string& v) { param.service_menu.window_message.title_color = Color::fromHex(v); }}, {"service_menu.window_message.title_color", [](const std::string& v) { param.service_menu.window_message.title_color = Color::fromHex(v); }},
{"service_menu.window_message.text_color", [](const std::string& v) { param.service_menu.window_message.text_color = Color::fromHex(v); }}, {"service_menu.window_message.text_color", [](const std::string& v) { param.service_menu.window_message.text_color = Color::fromHex(v); }},
{"intro.bg_color", [](const std::string& v) { param.intro.bg_color = Color::fromHex(v); }}, {"intro.bg_color", [](const std::string& v) { param.intro.bg_color = Color::fromHex(v); }},
{"intro.card_color", [](const std::string& v) { param.intro.card_color = Color::fromHex(v); }}, {"intro.card_color", [](const std::string& v) { param.intro.card_color = Color::fromHex(v); }},
{"intro.shadow_color", [](const std::string& v) { param.intro.shadow_color = Color::fromHex(v); }}, {"intro.shadow_color", [](const std::string& v) { param.intro.shadow_color = Color::fromHex(v); }},
{"debug.color", [](const std::string& v) { param.debug.color = Color::fromHex(v); }}, {"debug.color", [](const std::string& v) { param.debug.color = Color::fromHex(v); }},
{"resource.color", [](const std::string& v) { param.resource.color = Color::fromHex(v); }}, {"resource.color", [](const std::string& v) { param.resource.color = Color::fromHex(v); }},
{"player.one_coffee_shirt[0].darkest", [](const std::string& v) { param.player.one_coffee_shirt[0].darkest = Color::fromHex(v); }}, {"game.item_text_outline_color", [](const std::string& v) { param.game.item_text_outline_color = Color::fromHex(v); }},
{"player.one_coffee_shirt[0].dark", [](const std::string& v) { param.player.one_coffee_shirt[0].dark = Color::fromHex(v); }}, {"player.default_shirt[0].darkest", [](const std::string& v) { param.player.default_shirt[0].darkest = Color::fromHex(v); }},
{"player.one_coffee_shirt[0].base", [](const std::string& v) { param.player.one_coffee_shirt[0].base = Color::fromHex(v); }}, {"player.default_shirt[0].dark", [](const std::string& v) { param.player.default_shirt[0].dark = Color::fromHex(v); }},
{"player.one_coffee_shirt[0].light", [](const std::string& v) { param.player.one_coffee_shirt[0].light = Color::fromHex(v); }}, {"player.default_shirt[0].base", [](const std::string& v) { param.player.default_shirt[0].base = Color::fromHex(v); }},
{"player.one_coffee_shirt[1].darkest", [](const std::string& v) { param.player.one_coffee_shirt[1].darkest = Color::fromHex(v); }}, {"player.default_shirt[0].light", [](const std::string& v) { param.player.default_shirt[0].light = Color::fromHex(v); }},
{"player.one_coffee_shirt[1].dark", [](const std::string& v) { param.player.one_coffee_shirt[1].dark = Color::fromHex(v); }}, {"player.default_shirt[1].darkest", [](const std::string& v) { param.player.default_shirt[1].darkest = Color::fromHex(v); }},
{"player.one_coffee_shirt[1].base", [](const std::string& v) { param.player.one_coffee_shirt[1].base = Color::fromHex(v); }}, {"player.default_shirt[1].dark", [](const std::string& v) { param.player.default_shirt[1].dark = Color::fromHex(v); }},
{"player.one_coffee_shirt[1].light", [](const std::string& v) { param.player.one_coffee_shirt[1].light = Color::fromHex(v); }}, {"player.default_shirt[1].base", [](const std::string& v) { param.player.default_shirt[1].base = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].darkest", [](const std::string& v) { param.player.two_coffee_shirt[0].darkest = Color::fromHex(v); }}, {"player.default_shirt[1].light", [](const std::string& v) { param.player.default_shirt[1].light = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].dark", [](const std::string& v) { param.player.two_coffee_shirt[0].dark = Color::fromHex(v); }}, {"player.one_coffee_shirt[0].darkest", [](const std::string& v) { param.player.one_coffee_shirt[0].darkest = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].base", [](const std::string& v) { param.player.two_coffee_shirt[0].base = Color::fromHex(v); }}, {"player.one_coffee_shirt[0].dark", [](const std::string& v) { param.player.one_coffee_shirt[0].dark = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].light", [](const std::string& v) { param.player.two_coffee_shirt[0].light = Color::fromHex(v); }}, {"player.one_coffee_shirt[0].base", [](const std::string& v) { param.player.one_coffee_shirt[0].base = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].darkest", [](const std::string& v) { param.player.two_coffee_shirt[1].darkest = Color::fromHex(v); }}, {"player.one_coffee_shirt[0].light", [](const std::string& v) { param.player.one_coffee_shirt[0].light = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].dark", [](const std::string& v) { param.player.two_coffee_shirt[1].dark = Color::fromHex(v); }}, {"player.one_coffee_shirt[1].darkest", [](const std::string& v) { param.player.one_coffee_shirt[1].darkest = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].base", [](const std::string& v) { param.player.two_coffee_shirt[1].base = Color::fromHex(v); }}, {"player.one_coffee_shirt[1].dark", [](const std::string& v) { param.player.one_coffee_shirt[1].dark = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].light", [](const std::string& v) { param.player.two_coffee_shirt[1].light = Color::fromHex(v); }} {"player.one_coffee_shirt[1].base", [](const std::string& v) { param.player.one_coffee_shirt[1].base = Color::fromHex(v); }},
}; {"player.one_coffee_shirt[1].light", [](const std::string& v) { param.player.one_coffee_shirt[1].light = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].darkest", [](const std::string& v) { param.player.two_coffee_shirt[0].darkest = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].dark", [](const std::string& v) { param.player.two_coffee_shirt[0].dark = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].base", [](const std::string& v) { param.player.two_coffee_shirt[0].base = Color::fromHex(v); }},
{"player.two_coffee_shirt[0].light", [](const std::string& v) { param.player.two_coffee_shirt[0].light = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].darkest", [](const std::string& v) { param.player.two_coffee_shirt[1].darkest = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].dark", [](const std::string& v) { param.player.two_coffee_shirt[1].dark = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].base", [](const std::string& v) { param.player.two_coffee_shirt[1].base = Color::fromHex(v); }},
{"player.two_coffee_shirt[1].light", [](const std::string& v) { param.player.two_coffee_shirt[1].light = Color::fromHex(v); }},
{"player.outline_color[0]", [](const std::string& v) { param.player.outline_color[0] = Color::fromHex(v); }},
{"player.outline_color[1]", [](const std::string& v) { param.player.outline_color[1] = Color::fromHex(v); }}};
static const std::unordered_map<std::string, std::function<void(const std::string&)>> BOOL_PARAMS = { static const std::unordered_map<std::string, std::function<void(const std::string&)>> BOOL_PARAMS = {
{"game.hit_stop", [](const std::string& v) { param.game.hit_stop = stringToBool(v); }}, {"game.hit_stop", [](const std::string& v) { param.game.hit_stop = stringToBool(v); }},
{"scoreboard.separator_autocolor", [](const std::string& v) { param.scoreboard.separator_autocolor = stringToBool(v); }}, {"scoreboard.separator_autocolor", [](const std::string& v) { param.scoreboard.separator_autocolor = stringToBool(v); }},
{"scoreboard.text_autocolor", [](const std::string& v) { param.scoreboard.text_autocolor = stringToBool(v); }}, {"scoreboard.text_autocolor", [](const std::string& v) { param.scoreboard.text_autocolor = stringToBool(v); }},
{"balloon.bouncing_sound", [](const std::string& v) { param.balloon.bouncing_sound = stringToBool(v); }}, {"balloon.bouncing_sound", [](const std::string& v) { param.balloon.bouncing_sound = stringToBool(v); }},
{"notification.sound", [](const std::string& v) { param.notification.sound = stringToBool(v); }}, {"notification.sound", [](const std::string& v) { param.notification.sound = stringToBool(v); }},
{"service_menu.drop_shadow", [](const std::string& v) { param.service_menu.drop_shadow = stringToBool(v); }} {"service_menu.drop_shadow", [](const std::string& v) { param.service_menu.drop_shadow = stringToBool(v); }}};
};
static const std::unordered_map<std::string, std::function<void(const std::string&)>> FLOAT_PARAMS = { static const std::unordered_map<std::string, std::function<void(const std::string&)>> FLOAT_PARAMS = {
{"balloon.settings[0].vel", [](const std::string& v) { param.balloon.settings.at(0).vel = std::stof(v); }}, {"balloon.settings[0].vel", [](const std::string& v) { param.balloon.settings.at(0).vel = std::stof(v); }},
{"balloon.settings[0].grav", [](const std::string& v) { param.balloon.settings.at(0).grav = std::stof(v); }}, {"balloon.settings[0].grav", [](const std::string& v) { param.balloon.settings.at(0).grav = std::stof(v); }},
{"balloon.settings[1].vel", [](const std::string& v) { param.balloon.settings.at(1).vel = std::stof(v); }}, {"balloon.settings[1].vel", [](const std::string& v) { param.balloon.settings.at(1).vel = std::stof(v); }},
{"balloon.settings[1].grav", [](const std::string& v) { param.balloon.settings.at(1).grav = std::stof(v); }}, {"balloon.settings[1].grav", [](const std::string& v) { param.balloon.settings.at(1).grav = std::stof(v); }},
{"balloon.settings[2].vel", [](const std::string& v) { param.balloon.settings.at(2).vel = std::stof(v); }}, {"balloon.settings[2].vel", [](const std::string& v) { param.balloon.settings.at(2).vel = std::stof(v); }},
{"balloon.settings[2].grav", [](const std::string& v) { param.balloon.settings.at(2).grav = std::stof(v); }}, {"balloon.settings[2].grav", [](const std::string& v) { param.balloon.settings.at(2).grav = std::stof(v); }},
{"balloon.settings[3].vel", [](const std::string& v) { param.balloon.settings.at(3).vel = std::stof(v); }}, {"balloon.settings[3].vel", [](const std::string& v) { param.balloon.settings.at(3).vel = std::stof(v); }},
{"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); }},
{"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); }},
{"service_menu.window_message.min_width", [](const std::string& v) { param.service_menu.window_message.min_width = std::stof(v); }}, {"service_menu.window_message.min_width", [](const std::string& v) { param.service_menu.window_message.min_width = std::stof(v); }},
{"service_menu.window_message.min_height", [](const std::string& v) { param.service_menu.window_message.min_height = std::stof(v); }}, {"service_menu.window_message.min_height", [](const std::string& v) { param.service_menu.window_message.min_height = std::stof(v); }},
{"service_menu.window_message.max_width_ratio", [](const std::string& v) { param.service_menu.window_message.max_width_ratio = std::stof(v); }}, {"service_menu.window_message.max_width_ratio", [](const std::string& v) { param.service_menu.window_message.max_width_ratio = std::stof(v); }},
{"service_menu.window_message.max_height_ratio", [](const std::string& v) { param.service_menu.window_message.max_height_ratio = std::stof(v); }}, {"service_menu.window_message.max_height_ratio", [](const std::string& v) { param.service_menu.window_message.max_height_ratio = std::stof(v); }},
{"service_menu.window_message.text_safety_margin", [](const std::string& v) { param.service_menu.window_message.text_safety_margin = std::stof(v); }}, {"service_menu.window_message.text_safety_margin", [](const std::string& v) { param.service_menu.window_message.text_safety_margin = std::stof(v); }},
{"service_menu.window_message.animation_duration", [](const std::string& v) { param.service_menu.window_message.animation_duration = std::stof(v); }} {"service_menu.window_message.animation_duration", [](const std::string& v) { param.service_menu.window_message.animation_duration = std::stof(v); }}};
};
static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS_EXTRA = { static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS_EXTRA = {};
};
static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = { // Colores válidos para globos
{"balloon.color[0]", [](const std::string& v) { param.balloon.color.at(0) = v; }}, static const std::unordered_map<std::string, bool> VALID_BALLOON_COLORS = {
{"balloon.color[1]", [](const std::string& v) { param.balloon.color.at(1) = v; }}, {"blue", true}, {"orange", true}, {"red", true}, {"green", true}
{"balloon.color[2]", [](const std::string& v) { param.balloon.color.at(2) = v; }}, };
{"balloon.color[3]", [](const std::string& v) { param.balloon.color.at(3) = v; }}
}; auto validateBalloonColor = [](const std::string& color) -> bool {
return VALID_BALLOON_COLORS.find(color) != VALID_BALLOON_COLORS.end();
};
// Lambda para intentar cada mapa de parámetros static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = {
auto try_map = [&](const auto& param_map) -> bool { {"balloon.color[0]", [validateBalloonColor](const std::string& v) {
auto it = param_map.find(var); if (!validateBalloonColor(v)) {
if (it != param_map.end()) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'blue' por defecto.", v.c_str());
it->second(value); param.balloon.color.at(0) = "blue";
return true;
}
return false;
};
// Intentar con todos los mapas
if (try_map(INT_PARAMS) || try_map(COLOR_PARAMS) || try_map(BOOL_PARAMS) ||
try_map(FLOAT_PARAMS) || try_map(STRING_PARAMS)) {
return true;
}
// Casos especiales que necesitan lógica personalizada
if (var == "notification.pos_h") {
if (value == "LEFT") {
param.notification.pos_h = Notifier::Position::LEFT;
} else if (value == "MIDDLE") {
param.notification.pos_h = Notifier::Position::MIDDLE;
} else { } else {
param.notification.pos_h = Notifier::Position::RIGHT; param.balloon.color.at(0) = v;
} }
}},
{"balloon.color[1]", [validateBalloonColor](const std::string& v) {
if (!validateBalloonColor(v)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'orange' por defecto.", v.c_str());
param.balloon.color.at(1) = "orange";
} else {
param.balloon.color.at(1) = v;
}
}},
{"balloon.color[2]", [validateBalloonColor](const std::string& v) {
if (!validateBalloonColor(v)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'red' por defecto.", v.c_str());
param.balloon.color.at(2) = "red";
} else {
param.balloon.color.at(2) = v;
}
}},
{"balloon.color[3]", [validateBalloonColor](const std::string& v) {
if (!validateBalloonColor(v)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'green' por defecto.", v.c_str());
param.balloon.color.at(3) = "green";
} else {
param.balloon.color.at(3) = v;
}
}}};
// Lambda para intentar cada mapa de parámetros
auto try_map = [&](const auto& param_map) -> bool {
auto it = param_map.find(var);
if (it != param_map.end()) {
it->second(value);
return true; return true;
} }
return false;
};
if (var == "notification.pos_v") { // Intentar con todos los mapas
param.notification.pos_v = value == "TOP" ? Notifier::Position::TOP : Notifier::Position::BOTTOM; if (try_map(INT_PARAMS) || try_map(COLOR_PARAMS) || try_map(BOOL_PARAMS) ||
return true; try_map(FLOAT_PARAMS) || try_map(STRING_PARAMS)) {
} return true;
return false; // Parámetro no encontrado
} }
}
// Casos especiales que necesitan lógica personalizada
if (var == "notification.pos_h") {
if (value == "LEFT") {
param.notification.pos_h = Notifier::Position::LEFT;
} else if (value == "MIDDLE") {
param.notification.pos_h = Notifier::Position::MIDDLE;
} else {
param.notification.pos_h = Notifier::Position::RIGHT;
}
return true;
}
if (var == "notification.pos_v") {
param.notification.pos_v = value == "TOP" ? Notifier::Position::TOP : Notifier::Position::BOTTOM;
return true;
}
return false; // Parámetro no encontrado
}
} // namespace

View File

@@ -22,6 +22,7 @@ struct ParamGame {
Uint32 speed = 15; // Este valor no estaba en el archivo de configuración Uint32 speed = 15; // Este valor no estaba en el archivo de configuración
bool hit_stop = GameDefaults::Game::HIT_STOP; bool hit_stop = GameDefaults::Game::HIT_STOP;
Uint32 hit_stop_ms = GameDefaults::Game::HIT_STOP_MS; Uint32 hit_stop_ms = GameDefaults::Game::HIT_STOP_MS;
Color item_text_outline_color;
}; };
// --- Parámetros del fade --- // --- Parámetros del fade ---
@@ -29,9 +30,8 @@ struct ParamFade {
Color color = Color::fromHex(GameDefaults::Fade::COLOR); Color color = Color::fromHex(GameDefaults::Fade::COLOR);
float num_squares_width = GameDefaults::Fade::NUM_SQUARES_WIDTH; float num_squares_width = GameDefaults::Fade::NUM_SQUARES_WIDTH;
float num_squares_height = GameDefaults::Fade::NUM_SQUARES_HEIGHT; float num_squares_height = GameDefaults::Fade::NUM_SQUARES_HEIGHT;
int random_squares_delay = GameDefaults::Fade::RANDOM_SQUARES_DELAY; int random_squares_duration_ms = GameDefaults::Fade::RANDOM_SQUARES_DURATION_MS;
int random_squares_mult = GameDefaults::Fade::RANDOM_SQUARES_MULT; int post_duration_ms = GameDefaults::Fade::POST_DURATION_MS;
int post_duration = GameDefaults::Fade::POST_DURATION;
float venetian_size = GameDefaults::Fade::VENETIAN_SIZE; float venetian_size = GameDefaults::Fade::VENETIAN_SIZE;
}; };
@@ -56,7 +56,7 @@ struct ParamBalloon {
float vel; float vel;
// Constructor por defecto // Constructor por defecto
constexpr Settings(float grav_val = 0.0f, float vel_val = 0.0f) constexpr Settings(float grav_val = 0.0F, float vel_val = 0.0F)
: grav(grav_val), vel(vel_val) {} : grav(grav_val), vel(vel_val) {}
}; };
@@ -168,26 +168,54 @@ struct ParamPlayer {
}; };
// Inicialización con valores por defecto // Inicialización con valores por defecto
std::array<Shirt, 2> one_coffee_shirt = {{Shirt(Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_DARKEST), const Shirt default_player0_shirt = Shirt(
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_DARK), Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER0_DARKEST),
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_BASE), Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER0_DARK),
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_LIGHT)), Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER0_BASE),
Shirt(Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_DARKEST), Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER0_LIGHT));
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_DARK),
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_BASE),
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_LIGHT))}};
std::array<Shirt, 2> two_coffee_shirt = {{Shirt(Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_DARKEST), const Shirt default_player1_shirt = Shirt(
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_DARK), Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER1_DARKEST),
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_BASE), Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER1_DARK),
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_LIGHT)), Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER1_BASE),
Shirt(Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_DARKEST), Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER1_LIGHT));
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_DARK),
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_BASE), std::array<Shirt, 2> default_shirt = {default_player0_shirt, default_player1_shirt};
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_LIGHT))}};
const Shirt one_coffee_player0_shirt = Shirt(
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_DARKEST),
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_DARK),
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_BASE),
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_LIGHT));
const Shirt one_coffee_player1_shirt = Shirt(
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_DARKEST),
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_DARK),
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_BASE),
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_LIGHT));
std::array<Shirt, 2> one_coffee_shirt = {one_coffee_player0_shirt, one_coffee_player1_shirt};
const Shirt two_coffee_player0_shirt = Shirt(
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_DARKEST),
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_DARK),
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_BASE),
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_LIGHT));
const Shirt two_coffee_player1_shirt = Shirt(
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_DARKEST),
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_DARK),
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_BASE),
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_LIGHT));
std::array<Shirt, 2> two_coffee_shirt = {two_coffee_player0_shirt, two_coffee_player1_shirt};
const Color outline_player0_color = Color::fromHex(GameDefaults::Player::OutlineColor::PLAYER0);
const Color outline_player1_color = Color::fromHex(GameDefaults::Player::OutlineColor::PLAYER1);
std::array<Color, 2> outline_color = {outline_player0_color, outline_player1_color};
}; };
// --- Estructura principal para almacenar todos los parámetros del juego --- // --- Estructura Param: almacena todos los parámetros del juego ---
struct Param { struct Param {
ParamGame game; ParamGame game;
ParamFade fade; ParamFade fade;
@@ -207,10 +235,10 @@ struct Param {
Param() { Param() {
// Inicializar play_area usando los valores por defecto // Inicializar play_area usando los valores por defecto
game.play_area.rect = { game.play_area.rect = {
GameDefaults::Game::PLAY_AREA_X, .x = GameDefaults::Game::PLAY_AREA_X,
GameDefaults::Game::PLAY_AREA_Y, .y = GameDefaults::Game::PLAY_AREA_Y,
GameDefaults::Game::PLAY_AREA_W, .w = GameDefaults::Game::PLAY_AREA_W,
GameDefaults::Game::PLAY_AREA_H}; .h = GameDefaults::Game::PLAY_AREA_H};
// Las zonas calculadas se inicializarán en precalculateZones() // Las zonas calculadas se inicializarán en precalculateZones()
precalculateZones(); precalculateZones();
@@ -220,8 +248,8 @@ struct Param {
void precalculateZones(); void precalculateZones();
}; };
// --- Variable global con los parámetros del juego --- // --- Variables ---
extern Param param; extern Param param; // Variable global con los parámetros del juego
// --- Funciones globales --- // --- Funciones ---
void loadParamsFromFile(const std::string& file_path); void loadParamsFromFile(const std::string& file_path); // Carga parámetros desde archivo

View File

@@ -11,7 +11,7 @@ auto createPath(float start, float end, PathType type, float fixed_pos, int step
for (int i = 0; i < steps; ++i) { for (int i = 0; i < steps; ++i) {
double t = static_cast<double>(i) / (steps - 1); double t = static_cast<double>(i) / (steps - 1);
double value = start + (end - start) * easing_function(t); double value = start + ((end - start) * easing_function(t));
if ((start > 0 && end < 0) || (start < 0 && end > 0)) { if ((start > 0 && end < 0) || (start < 0 && end > 0)) {
value = start + (end > 0 ? 1 : -1) * std::abs(end - start) * easing_function(t); value = start + (end > 0 ? 1 : -1) * std::abs(end - start) * easing_function(t);
@@ -56,7 +56,7 @@ void PathSprite::addPath(Path path, bool centered) {
switch (path_centered) { switch (path_centered) {
case PathCentered::ON_X: { case PathCentered::ON_X: {
const int X = path.spots.back().x - pos_.w / 2; const int X = path.spots.back().x - (pos_.w / 2);
for (auto &spot : path.spots) { for (auto &spot : path.spots) {
spot.x = X; spot.x = X;
} }
@@ -64,7 +64,7 @@ void PathSprite::addPath(Path path, bool centered) {
break; break;
} }
case PathCentered::ON_Y: { case PathCentered::ON_Y: {
const int Y = path.spots.back().y - pos_.h / 2; const int Y = path.spots.back().y - (pos_.h / 2);
for (auto &spot : path.spots) { for (auto &spot : path.spots) {
spot.y = Y; spot.y = Y;
} }
@@ -83,8 +83,8 @@ void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int s
} }
// Añade un recorrido // Añade un recorrido
void PathSprite::addPath(std::vector<SDL_FPoint> spots, int waiting_counter) { void PathSprite::addPath(const std::vector<SDL_FPoint> &spots, int waiting_counter) {
paths_.emplace_back(std::move(spots), waiting_counter); paths_.emplace_back(spots, waiting_counter);
} }
// Habilita el objeto // Habilita el objeto

View File

@@ -4,27 +4,27 @@
#include <functional> // Para std::function #include <functional> // Para std::function
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <vector> // Para vector #include <utility>
#include <vector> // Para vector
#include "sprite.h" // Para Sprite #include "sprite.h" // Para Sprite
class Texture; class Texture;
// --- Tipos de recorrido --- // --- Enums ---
enum class PathType { enum class PathType { // Tipos de recorrido
VERTICAL, VERTICAL,
HORIZONTAL, HORIZONTAL,
}; };
// --- Centrado del recorrido --- enum class PathCentered { // Centrado del recorrido
enum class PathCentered {
ON_X, ON_X,
ON_Y, ON_Y,
NONE, NONE,
}; };
// --- Estructura Path: define un recorrido para el sprite --- // --- Estructuras ---
struct Path { struct Path { // Define un recorrido para el sprite
std::vector<SDL_FPoint> spots; // Puntos por los que se desplazará el sprite std::vector<SDL_FPoint> spots; // Puntos por los que se desplazará el sprite
int waiting_counter; // Tiempo de espera una vez en el destino int waiting_counter; // Tiempo de espera una vez en el destino
bool on_destination = false; // Indica si ha llegado al destino bool on_destination = false; // Indica si ha llegado al destino
@@ -36,15 +36,15 @@ struct Path {
: spots(spots_init), waiting_counter(waiting_counter_init) {} : spots(spots_init), waiting_counter(waiting_counter_init) {}
}; };
// Devuelve un vector con los puntos que conforman la ruta // --- Funciones ---
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>; // Devuelve un vector con los puntos que conforman la ruta
// --- Clase PathSprite: Sprite que sigue uno o varios recorridos --- // --- Clase PathSprite: Sprite que sigue uno o varios recorridos ---
class PathSprite : public Sprite { class PathSprite : public Sprite {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
explicit PathSprite(std::shared_ptr<Texture> texture) explicit PathSprite(std::shared_ptr<Texture> texture)
: Sprite(texture) {} : Sprite(std::move(texture)) {}
~PathSprite() override = default; ~PathSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
@@ -53,7 +53,7 @@ class PathSprite : public Sprite {
// --- 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(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, int waiting_counter = 0); // 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(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
// --- Estado y control --- // --- Estado y control ---

View File

@@ -5,18 +5,18 @@
#include <string> #include <string>
#include <utility> #include <utility>
// Clase dedicada para manejar el sistema de pausa // --- Clase PauseManager: maneja el sistema de pausa del juego ---
class PauseManager { class PauseManager {
public: public:
// Enum encapsulado dentro de la clase // --- Enums ---
enum class Source : uint8_t { enum class Source : uint8_t { // Fuentes de pausa
NONE = 0, NONE = 0,
PLAYER = 1 << 0, // 0001 PLAYER = 1 << 0, // 0001
SERVICE_MENU = 1 << 1, // 0010 SERVICE_MENU = 1 << 1, // 0010
FOCUS_LOST = 1 << 2 // 0100 FOCUS_LOST = 1 << 2 // 0100
}; };
// Operadores para trabajar con las banderas (funciones friend) // --- Operadores friend ---
friend auto operator|(Source a, Source b) -> Source { friend auto operator|(Source a, Source b) -> Source {
return static_cast<Source>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b)); return static_cast<Source>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
} }
@@ -42,27 +42,12 @@ class PauseManager {
return a = a & b; return a = a & b;
} }
private: // --- Constructor y destructor ---
Source flags_ = Source::NONE; explicit PauseManager(std::function<void(bool)> callback = nullptr) // Constructor con callback opcional
std::function<void(bool)> on_pause_changed_callback_;
[[nodiscard]] auto hasFlag(Source flag) const -> bool {
return (flags_ & flag) != Source::NONE;
}
void notifyPauseChanged() {
if (on_pause_changed_callback_) {
on_pause_changed_callback_(isPaused());
}
}
public:
// Constructor con callback opcional
explicit PauseManager(std::function<void(bool)> callback = nullptr)
: on_pause_changed_callback_(std::move(callback)) {} : on_pause_changed_callback_(std::move(callback)) {}
// Establece/quita una fuente de pausa específica // --- Métodos principales ---
void setFlag(Source source, bool enable) { void setFlag(Source source, bool enable) { // Establece/quita una fuente de pausa específica
bool was_paused = isPaused(); bool was_paused = isPaused();
if (enable) { if (enable) {
@@ -76,33 +61,29 @@ class PauseManager {
} }
} }
// Métodos específicos para cada fuente // --- Métodos específicos para cada fuente ---
void setPlayerPause(bool enable) { setFlag(Source::PLAYER, enable); } void setPlayerPause(bool enable) { setFlag(Source::PLAYER, enable); }
void setServiceMenuPause(bool enable) { setFlag(Source::SERVICE_MENU, enable); } void setServiceMenuPause(bool enable) { setFlag(Source::SERVICE_MENU, enable); }
void setFocusLossPause(bool enable) { setFlag(Source::FOCUS_LOST, enable); } void setFocusLossPause(bool enable) { setFlag(Source::FOCUS_LOST, enable); }
// Toggle para el jugador (más común) void togglePlayerPause() { setPlayerPause(!isPlayerPaused()); } // Toggle para el jugador (más común)
void togglePlayerPause() { setPlayerPause(!isPlayerPaused()); }
// Getters // --- Getters ---
[[nodiscard]] auto isPaused() const -> bool { return flags_ != Source::NONE; } [[nodiscard]] auto isPaused() const -> bool { return flags_ != Source::NONE; }
[[nodiscard]] auto isPlayerPaused() const -> bool { return hasFlag(Source::PLAYER); } [[nodiscard]] auto isPlayerPaused() const -> bool { return hasFlag(Source::PLAYER); }
[[nodiscard]] auto isServiceMenuPaused() const -> bool { return hasFlag(Source::SERVICE_MENU); } [[nodiscard]] auto isServiceMenuPaused() const -> bool { return hasFlag(Source::SERVICE_MENU); }
[[nodiscard]] auto isFocusLossPaused() const -> bool { return hasFlag(Source::FOCUS_LOST); } [[nodiscard]] auto isFocusLossPaused() const -> bool { return hasFlag(Source::FOCUS_LOST); }
// Obtiene las banderas actuales [[nodiscard]] auto getFlags() const -> Source { return flags_; } // Obtiene las banderas actuales
[[nodiscard]] auto getFlags() const -> Source { return flags_; }
// Limpia todas las pausas (útil para reset) // --- Métodos de utilidad ---
void clearAll() { void clearAll() { // Limpia todas las pausas (útil para reset)
if (isPaused()) { if (isPaused()) {
flags_ = Source::NONE; flags_ = Source::NONE;
notifyPauseChanged(); notifyPauseChanged();
} }
} }
[[nodiscard]] auto getStatusString() const -> std::string { // Método para debug
// Método para debug
[[nodiscard]] auto getStatusString() const -> std::string {
if (flags_ == Source::NONE) { if (flags_ == Source::NONE) {
return "Active"; return "Active";
} }
@@ -133,9 +114,22 @@ class PauseManager {
return result; return result;
} }
void setCallback(std::function<void(bool)> callback) { // Permite cambiar el callback en runtime
on_pause_changed_callback_ = std::move(callback);
}
// Permite cambiar el callback en runtime private:
void setCallback(std::function<void(bool)> callback) { // --- Variables ---
on_pause_changed_callback_ = callback; Source flags_ = Source::NONE;
std::function<void(bool)> on_pause_changed_callback_;
// --- Métodos internos ---
[[nodiscard]] auto hasFlag(Source flag) const -> bool {
return (flags_ & flag) != Source::NONE;
}
void notifyPauseChanged() {
if (on_pause_changed_callback_) {
on_pause_changed_callback_(isPaused());
}
} }
}; };

View File

@@ -22,17 +22,7 @@
// Constructor // Constructor
Player::Player(const Config &config) Player::Player(const Config &config)
: player_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(0), config.animations.at(0))), : player_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(0), config.animations.at(0))), power_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(4), config.animations.at(1))), enter_name_(std::make_unique<EnterName>()), hi_score_table_(config.hi_score_table), glowing_entry_(config.glowing_entry), stage_info_(config.stage_info), play_area_(*config.play_area), id_(config.id), default_pos_x_(config.x), default_pos_y_(config.y), demo_(config.demo) {
power_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(4), config.animations.at(1))),
enter_name_(std::make_unique<EnterName>()),
hi_score_table_(config.hi_score_table),
glowing_entry_(config.glowing_entry),
stage_info_(config.stage_info),
play_area_(*config.play_area),
id_(config.id),
default_pos_x_(config.x),
default_pos_y_(config.y),
demo_(config.demo) {
// Configura objetos // Configura objetos
player_sprite_->addTexture(config.texture.at(1)); player_sprite_->addTexture(config.texture.at(1));
player_sprite_->addTexture(config.texture.at(2)); player_sprite_->addTexture(config.texture.at(2));
@@ -199,7 +189,7 @@ void Player::handlePlayingMovement() {
} }
void Player::handleRecoverMovement() { void Player::handleRecoverMovement() {
if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_brbrbr.wav"); } if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_recover.wav"); }
if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); } if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); }
} }
@@ -646,7 +636,7 @@ void Player::setPlayingState(State state) {
init(); init();
setInvulnerable(true); setInvulnerable(true);
setScoreboardMode(Scoreboard::Mode::SCORE); setScoreboardMode(Scoreboard::Mode::SCORE);
stage_info_->canCollectPower(); stage_info_->enablePowerCollection();
break; break;
} }
case State::CONTINUE: { case State::CONTINUE: {
@@ -788,15 +778,30 @@ void Player::setInvulnerable(bool value) {
// Monitoriza el estado // Monitoriza el estado
void Player::updateInvulnerable() { void Player::updateInvulnerable() {
if (playing_state_ == State::PLAYING) { if (playing_state_ == State::PLAYING && invulnerable_) {
if (invulnerable_) { if (invulnerable_counter_ > 0) {
if (invulnerable_counter_ > 0) { --invulnerable_counter_;
--invulnerable_counter_;
invulnerable_counter_ % 8 > 3 ? player_sprite_->setActiveTexture(coffees_) : player_sprite_->setActiveTexture(3); // Frecuencia fija de parpadeo (como el original)
} else { constexpr int blink_speed = 8;
setInvulnerable(false);
player_sprite_->setActiveTexture(coffees_); // Calcula proporción decreciente: menos textura blanca hacia el final
// Al inicio: 50-50, hacia el final: 70-30 (menos blanco)
float progress = 1.0f - (static_cast<float>(invulnerable_counter_) / INVULNERABLE_COUNTER);
int white_frames = static_cast<int>((0.5f - progress * 0.2f) * blink_speed);
// Alterna entre texturas con proporción variable
bool should_show_invulnerable = (invulnerable_counter_ % blink_speed) < white_frames;
size_t target_texture = should_show_invulnerable ? INVULNERABLE_TEXTURE : coffees_;
// Solo cambia textura si es diferente (optimización)
if (player_sprite_->getActiveTexture() != target_texture) {
player_sprite_->setActiveTexture(target_texture);
} }
} else {
// Fin de invulnerabilidad
setInvulnerable(false);
player_sprite_->setActiveTexture(coffees_);
} }
} }
} }

View File

@@ -24,15 +24,14 @@ class Player {
static constexpr int WIDTH = 32; // Anchura static constexpr int WIDTH = 32; // Anchura
static constexpr int HEIGHT = 32; // Altura static constexpr int HEIGHT = 32; // Altura
// --- Id para los jugadores --- // --- Enums ---
enum class Id : int { enum class Id : int {
NO_PLAYER = -1, NO_PLAYER = -1, // Sin jugador
BOTH_PLAYERS = 0, BOTH_PLAYERS = 0, // Ambos jugadores
PLAYER1 = 1, PLAYER1 = 1, // Jugador 1
PLAYER2 = 2 PLAYER2 = 2 // Jugador 2
}; };
// --- Estados posibles del jugador ---
enum class State { enum class State {
// Estados de movimiento // Estados de movimiento
WALKING_LEFT, // Caminando hacia la izquierda WALKING_LEFT, // Caminando hacia la izquierda
@@ -76,17 +75,18 @@ class Player {
RESPAWNING, // Tras continuar y dar las gracias, otorga inmunidad y vuelve al juego RESPAWNING, // Tras continuar y dar las gracias, otorga inmunidad y vuelve al juego
}; };
// --- Estructuras ---
struct Config { struct Config {
Id id; Id id; // Identificador del jugador
float x; float x; // Posición X inicial
int y; int y; // Posición Y inicial
bool demo; bool demo; // Modo demo
SDL_FRect *play_area; // Usamos puntero para mantener la referencia SDL_FRect *play_area; // Área de juego (puntero para mantener referencia)
std::vector<std::shared_ptr<Texture>> texture; std::vector<std::shared_ptr<Texture>> texture; // Texturas del jugador
std::vector<std::vector<std::string>> animations; std::vector<std::vector<std::string>> animations; // Animaciones del jugador
Table *hi_score_table; // También como puntero para referencia Table *hi_score_table; // Tabla de puntuaciones (puntero para referencia)
int *glowing_entry; // Puntero para mantener la referencia int *glowing_entry; // Entrada brillante (puntero para mantener referencia)
IStageInfo *stage_info; // Puntero para el gestor de pantallas IStageInfo *stage_info; // Gestor de pantallas (puntero)
}; };
// --- Constructor y destructor --- // --- Constructor y destructor ---
@@ -184,21 +184,22 @@ class Player {
void setScoreBoardPanel(Scoreboard::Id panel) { scoreboard_panel_ = panel; } void setScoreBoardPanel(Scoreboard::Id panel) { scoreboard_panel_ = panel; }
void setScoreMultiplier(float value) { score_multiplier_ = value; } void setScoreMultiplier(float value) { score_multiplier_ = value; }
void setWalkingState(State state) { walking_state_ = state; } void setWalkingState(State state) { walking_state_ = state; }
void addCredit(); void addCredit();
void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = gamepad; } void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = std::move(gamepad); }
[[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; } [[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; }
void setUsesKeyboard(bool value) { uses_keyboard_ = value; } void setUsesKeyboard(bool value) { uses_keyboard_ = value; }
[[nodiscard]] auto getUsesKeyboard() const -> bool { return uses_keyboard_; } [[nodiscard]] auto getUsesKeyboard() const -> bool { return uses_keyboard_; }
private: private:
// --- Constantes --- // --- Constantes ---
static constexpr int POWERUP_COUNTER = 1500; // Duración del estado PowerUp static constexpr int POWERUP_COUNTER = 1500; // Duración del estado PowerUp
static constexpr int INVULNERABLE_COUNTER = 200; // Duración del estado invulnerable static constexpr int INVULNERABLE_COUNTER = 200; // Duración del estado invulnerable
static constexpr float BASE_SPEED = 1.5F; // Velocidad base del jugador static constexpr size_t INVULNERABLE_TEXTURE = 3; // Textura usada durante invulnerabilidad
static constexpr int COOLING_DURATION = 50; static constexpr float BASE_SPEED = 1.5F; // Velocidad base del jugador
static constexpr int COOLING_COMPLETE = 0; static constexpr int COOLING_DURATION = 50; // Duración del enfriamiento tras disparar
static constexpr int WAITING_COUNTER = 1000; static constexpr int COOLING_COMPLETE = 0; // Valor que indica enfriamiento completado
static constexpr int WAITING_COUNTER = 1000; // Tiempo de espera en estado de espera
// --- 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
@@ -208,25 +209,25 @@ class Player {
Table *hi_score_table_ = nullptr; // Tabla de máximas puntuaciones Table *hi_score_table_ = nullptr; // Tabla de máximas puntuaciones
int *glowing_entry_ = nullptr; // Entrada de la tabla de puntuaciones para hacerla brillar int *glowing_entry_ = nullptr; // Entrada de la tabla de puntuaciones para hacerla brillar
IStageInfo *stage_info_; // Informacion de la pantalla actual IStageInfo *stage_info_; // Informacion de la pantalla actual
std::string name_; // Nombre del jugador
std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones
// --- Estructuras y enums --- // --- Variables de estado ---
SDL_FRect play_area_; // Rectángulo con la zona de juego SDL_FRect play_area_; // Rectángulo con la zona de juego
Circle collider_ = Circle(0, 0, 9); // Círculo de colisión del jugador Circle collider_ = Circle(0, 0, 9); // Círculo de colisión del jugador
std::string name_; // Nombre del jugador
std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones
Scoreboard::Id scoreboard_panel_ = Scoreboard::Id::LEFT; // Panel del marcador asociado al jugador Scoreboard::Id scoreboard_panel_ = Scoreboard::Id::LEFT; // Panel del marcador asociado al jugador
Id id_; // Identificador para el jugador Id id_; // Identificador para el jugador
State walking_state_ = State::WALKING_STOP; // Estado del jugador al moverse State walking_state_ = State::WALKING_STOP; // Estado del jugador al moverse
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
// --- Variables float --- Uint32 continue_ticks_ = 0; // Variable para poder cambiar el contador de continue en función del tiempo
float pos_x_ = 0.0F; // Posición en el eje X Uint32 name_entry_ticks_ = 0; // Variable para poder cambiar el contador de poner nombre en función del tiempo
float default_pos_x_; // Posición inicial para el jugador Uint32 showing_name_ticks_ = 0; // Tiempo en el que se entra al estado SHOWING_NAME
float vel_x_ = 0.0F; // Cantidad de píxeles a desplazarse en el eje X float pos_x_ = 0.0F; // Posición en el eje X
float score_multiplier_ = 1.0F; // Multiplicador de puntos 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
// --- Variables int --- float score_multiplier_ = 1.0F; // Multiplicador de puntos
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
@@ -246,20 +247,13 @@ class Player {
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
bool qualifies_for_high_score_ = false; // Indica si tiene una puntuación que le permite entrar en la tabla de records
// --- Variables Uint32 --- bool invulnerable_ = true; // Indica si el jugador es invulnerable
Uint32 continue_ticks_ = 0; // Variable para poder cambiar el contador de continue en función del tiempo bool extra_hit_ = false; // Indica si el jugador tiene un toque extra
Uint32 name_entry_ticks_ = 0; // Variable para poder cambiar el contador de poner nombre en función del tiempo bool power_up_ = false; // Indica si el jugador tiene activo el modo PowerUp
Uint32 showing_name_ticks_ = 0; // Tiempo en el que se entra al estado SHOWING_NAME bool demo_ = false; // Para que el jugador sepa si está en el modo demostración
bool game_completed_ = false; // Indica si ha completado el juego
// --- Flags booleanas --- bool uses_keyboard_ = false; // Indica si usa el teclado como dispositivo de control
bool qualifies_for_high_score_ = false; // Indica si tiene una puntuación que le permite entrar en la tabla de records
bool invulnerable_ = true; // Indica si el jugador es invulnerable
bool extra_hit_ = false; // Indica si el jugador tiene un toque extra
bool power_up_ = false; // Indica si el jugador tiene activo el modo PowerUp
bool demo_ = false; // Para que el jugador sepa si está en el modo demostración
bool game_completed_ = false; // Indica si ha completado el juego
bool uses_keyboard_ = false; // Indica si usa el teclado como dispositivo de control
// --- 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

View File

@@ -80,7 +80,7 @@ void Resource::loadTextFilesQuiet() {
for (const auto &l : list) { for (const auto &l : list) {
auto name = getFileName(l); auto name = getFileName(l);
// Buscar en nuestra lista y cargar directamente // Buscar en nuestra lista y cargar directamente
auto it = std::find_if(text_files_.begin(), text_files_.end(), [&name](const auto &t) { return t.name == name; }); auto it = std::ranges::find_if(text_files_, [&name](const auto &t) { return t.name == name; });
if (it != text_files_.end()) { if (it != text_files_.end()) {
it->text_file = Text::loadFile(l); it->text_file = Text::loadFile(l);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Text file loaded: %s", name.c_str()); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Text file loaded: %s", name.c_str());
@@ -109,9 +109,9 @@ void Resource::loadEssentialTextures() {
for (const auto &file : texture_list) { for (const auto &file : texture_list) {
auto name = getFileName(file); auto name = getFileName(file);
// Solo cargar texturas esenciales // Solo cargar texturas esenciales
if (std::find(ESSENTIAL_TEXTURES.begin(), ESSENTIAL_TEXTURES.end(), name) != ESSENTIAL_TEXTURES.end()) { if (std::ranges::find(ESSENTIAL_TEXTURES, name) != ESSENTIAL_TEXTURES.end()) {
// Buscar en nuestra lista y cargar // Buscar en nuestra lista y cargar
auto it = std::find_if(textures_.begin(), textures_.end(), [&name](const auto &t) { return t.name == name; }); auto it = std::ranges::find_if(textures_, [&name](const auto &t) { return t.name == name; });
if (it != textures_.end()) { if (it != textures_.end()) {
it->texture = std::make_shared<Texture>(Screen::get()->getRenderer(), file); it->texture = std::make_shared<Texture>(Screen::get()->getRenderer(), file);
} }
@@ -186,7 +186,7 @@ void Resource::initResourceLists() {
// Obtiene el sonido a partir de un nombre (con carga perezosa) // Obtiene el sonido a partir de un nombre (con carga perezosa)
auto Resource::getSound(const std::string &name) -> JA_Sound_t * { auto Resource::getSound(const std::string &name) -> JA_Sound_t * {
auto it = std::find_if(sounds_.begin(), sounds_.end(), [&name](const auto &s) { return s.name == name; }); auto it = std::ranges::find_if(sounds_, [&name](const auto &s) { return s.name == name; });
if (it != sounds_.end()) { if (it != sounds_.end()) {
// Si está en modo lazy y no se ha cargado aún, lo carga ahora // Si está en modo lazy y no se ha cargado aún, lo carga ahora
@@ -202,7 +202,7 @@ auto Resource::getSound(const std::string &name) -> JA_Sound_t * {
// Obtiene la música a partir de un nombre (con carga perezosa) // Obtiene la música a partir de un nombre (con carga perezosa)
auto Resource::getMusic(const std::string &name) -> JA_Music_t * { auto Resource::getMusic(const std::string &name) -> JA_Music_t * {
auto it = std::find_if(musics_.begin(), musics_.end(), [&name](const auto &m) { return m.name == name; }); auto it = std::ranges::find_if(musics_, [&name](const auto &m) { return m.name == name; });
if (it != musics_.end()) { if (it != musics_.end()) {
// Si está en modo lazy y no se ha cargado aún, lo carga ahora // Si está en modo lazy y no se ha cargado aún, lo carga ahora
@@ -218,7 +218,7 @@ auto Resource::getMusic(const std::string &name) -> JA_Music_t * {
// Obtiene la textura a partir de un nombre (con carga perezosa) // Obtiene la textura a partir de un nombre (con carga perezosa)
auto Resource::getTexture(const std::string &name) -> std::shared_ptr<Texture> { auto Resource::getTexture(const std::string &name) -> std::shared_ptr<Texture> {
auto it = std::find_if(textures_.begin(), textures_.end(), [&name](const auto &t) { return t.name == name; }); auto it = std::ranges::find_if(textures_, [&name](const auto &t) { return t.name == name; });
if (it != textures_.end()) { if (it != textures_.end()) {
// Si está en modo lazy y no se ha cargado aún, lo carga ahora // Si está en modo lazy y no se ha cargado aún, lo carga ahora
@@ -234,7 +234,7 @@ auto Resource::getTexture(const std::string &name) -> std::shared_ptr<Texture> {
// Obtiene el fichero de texto a partir de un nombre (con carga perezosa) // Obtiene el fichero de texto a partir de un nombre (con carga perezosa)
auto Resource::getTextFile(const std::string &name) -> std::shared_ptr<Text::File> { auto Resource::getTextFile(const std::string &name) -> std::shared_ptr<Text::File> {
auto it = std::find_if(text_files_.begin(), text_files_.end(), [&name](const auto &t) { return t.name == name; }); auto it = std::ranges::find_if(text_files_, [&name](const auto &t) { return t.name == name; });
if (it != text_files_.end()) { if (it != text_files_.end()) {
// Si está en modo lazy y no se ha cargado aún, lo carga ahora // Si está en modo lazy y no se ha cargado aún, lo carga ahora
@@ -250,7 +250,7 @@ auto Resource::getTextFile(const std::string &name) -> std::shared_ptr<Text::Fil
// Obtiene el objeto de texto a partir de un nombre (con carga perezosa) // Obtiene el objeto de texto a partir de un nombre (con carga perezosa)
auto Resource::getText(const std::string &name) -> std::shared_ptr<Text> { auto Resource::getText(const std::string &name) -> std::shared_ptr<Text> {
auto it = std::find_if(texts_.begin(), texts_.end(), [&name](const auto &t) { return t.name == name; }); auto it = std::ranges::find_if(texts_, [&name](const auto &t) { return t.name == name; });
if (it != texts_.end()) { if (it != texts_.end()) {
// Si está en modo lazy y no se ha cargado aún, lo carga ahora // Si está en modo lazy y no se ha cargado aún, lo carga ahora
@@ -266,7 +266,7 @@ auto Resource::getText(const std::string &name) -> std::shared_ptr<Text> {
// Obtiene la animación a partir de un nombre (con carga perezosa) // Obtiene la animación a partir de un nombre (con carga perezosa)
auto Resource::getAnimation(const std::string &name) -> AnimationsFileBuffer & { auto Resource::getAnimation(const std::string &name) -> AnimationsFileBuffer & {
auto it = std::find_if(animations_.begin(), animations_.end(), [&name](const auto &a) { return a.name == name; }); auto it = std::ranges::find_if(animations_, [&name](const auto &a) { return a.name == name; });
if (it != animations_.end()) { if (it != animations_.end()) {
// Si está en modo lazy y no se ha cargado aún (vector vacío), lo carga ahora // Si está en modo lazy y no se ha cargado aún (vector vacío), lo carga ahora
@@ -346,18 +346,18 @@ auto Resource::loadTextLazy(const std::string &name) -> std::shared_ptr<Text> {
}; };
const std::vector<TextMapping> TEXT_MAPPINGS = { const std::vector<TextMapping> TEXT_MAPPINGS = {
{"04b_25", "04b_25.png", "04b_25.txt"}, {.key = "04b_25", .texture_file = "04b_25.png", .text_file = "04b_25.txt"},
{"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"}, {.key = "04b_25_2x", .texture_file = "04b_25_2x.png", .text_file = "04b_25_2x.txt"},
{"04b_25_metal", "04b_25_metal.png", "04b_25.txt"}, {.key = "04b_25_metal", .texture_file = "04b_25_metal.png", .text_file = "04b_25.txt"},
{"04b_25_grey", "04b_25_grey.png", "04b_25.txt"}, {.key = "04b_25_grey", .texture_file = "04b_25_grey.png", .text_file = "04b_25.txt"},
{"04b_25_flat", "04b_25_flat.png", "04b_25.txt"}, {.key = "04b_25_flat", .texture_file = "04b_25_flat.png", .text_file = "04b_25.txt"},
{"04b_25_reversed", "04b_25_reversed.png", "04b_25.txt"}, {.key = "04b_25_reversed", .texture_file = "04b_25_reversed.png", .text_file = "04b_25.txt"},
{"04b_25_flat_2x", "04b_25_flat_2x.png", "04b_25_2x.txt"}, {.key = "04b_25_flat_2x", .texture_file = "04b_25_flat_2x.png", .text_file = "04b_25_2x.txt"},
{"04b_25_reversed_2x", "04b_25_reversed_2x.png", "04b_25_2x.txt"}, {.key = "04b_25_reversed_2x", .texture_file = "04b_25_reversed_2x.png", .text_file = "04b_25_2x.txt"},
{"8bithud", "8bithud.png", "8bithud.txt"}, {.key = "8bithud", .texture_file = "8bithud.png", .text_file = "8bithud.txt"},
{"aseprite", "aseprite.png", "aseprite.txt"}, {.key = "aseprite", .texture_file = "aseprite.png", .text_file = "aseprite.txt"},
{"smb2", "smb2.png", "smb2.txt"}, {.key = "smb2", .texture_file = "smb2.png", .text_file = "smb2.txt"},
{"smb2_grad", "smb2_grad.png", "smb2.txt"}}; {.key = "smb2_grad", .texture_file = "smb2_grad.png", .text_file = "smb2.txt"}};
for (const auto &mapping : TEXT_MAPPINGS) { for (const auto &mapping : TEXT_MAPPINGS) {
if (mapping.key == name) { if (mapping.key == name) {
@@ -531,18 +531,18 @@ void Resource::createPlayerTextures() {
// Configuración de jugadores y sus paletas // Configuración de jugadores y sus paletas
struct PlayerConfig { struct PlayerConfig {
std::string base_texture; std::string base_texture;
std::vector<std::string> palette_files; std::vector<std::string> palette_files;
std::string name_prefix; std::string name_prefix;
}; };
std::vector<PlayerConfig> players = { std::vector<PlayerConfig> players = {
{"player1.gif", {"player1_coffee1.pal", "player1_coffee2.pal", "player1_invencible.pal"}, "player1"}, {.base_texture = "player1.gif", .palette_files = {"player1_coffee1.pal", "player1_coffee2.pal", "player1_invencible.pal"}, .name_prefix = "player1"},
{"player2.gif", {"player2_coffee1.pal", "player2_coffee2.pal", "player2_invencible.pal"}, "player2"}}; {.base_texture = "player2.gif", .palette_files = {"player2_coffee1.pal", "player2_coffee2.pal", "player2_invencible.pal"}, .name_prefix = "player2"}};
// Bucle principal modificado para usar un índice (player_idx) // Bucle principal
for (size_t player_idx = 0; player_idx < players.size(); ++player_idx) { for (size_t player_idx = 0; player_idx < players.size(); ++player_idx) {
const auto &player = players[player_idx]; // Obtenemos el jugador actual const auto &player = players[player_idx]; // Obtenemos el jugador actual
// Encontrar el archivo original de la textura // Encontrar el archivo original de la textura
std::string texture_file_path; std::string texture_file_path;
@@ -554,40 +554,52 @@ void Resource::createPlayerTextures() {
} }
} }
// Crear variante con paleta original (pal0) - usar la textura ya cargada // Crear las 4 texturas con sus respectivas paletas
auto base_texture = getTexture(player.base_texture); for (int palette_idx = 0; palette_idx < 4; ++palette_idx) {
std::string pal0_name = player.name_prefix + "_pal0"; std::shared_ptr<Texture> texture;
textures_.emplace_back(pal0_name, base_texture);
printWithDots("Player Texture : ", pal0_name, "[ DONE ]");
// Crear variantes con paletas adicionales - CADA UNA DESDE EL ARCHIVO if (palette_idx == 0) {
for (size_t i = 0; i < player.palette_files.size(); ++i) { // Textura 0 - usar la ya cargada y modificar solo paleta 0 (default_shirt)
// Crear textura completamente nueva desde el archivo texture = getTexture(player.base_texture);
auto texture_copy = std::make_shared<Texture>(Screen::get()->getRenderer(), texture_file_path); texture->setPaletteColor(0, 16, param.player.default_shirt[player_idx].darkest.TO_UINT32());
texture->setPaletteColor(0, 17, param.player.default_shirt[player_idx].dark.TO_UINT32());
texture->setPaletteColor(0, 18, param.player.default_shirt[player_idx].base.TO_UINT32());
texture->setPaletteColor(0, 19, param.player.default_shirt[player_idx].light.TO_UINT32());
texture->setPaletteColor(0, 56, param.player.outline_color[player_idx].TO_UINT32());
} else {
// Crear textura nueva desde archivo
texture = std::make_shared<Texture>(Screen::get()->getRenderer(), texture_file_path);
// Añadir todas las paletas // Añadir todas las paletas
texture_copy->addPaletteFromPalFile(Asset::get()->get(player.palette_files[0])); texture->addPaletteFromPalFile(Asset::get()->get(player.palette_files[0]));
texture_copy->addPaletteFromPalFile(Asset::get()->get(player.palette_files[1])); texture->addPaletteFromPalFile(Asset::get()->get(player.palette_files[1]));
texture_copy->addPaletteFromPalFile(Asset::get()->get(player.palette_files[2])); texture->addPaletteFromPalFile(Asset::get()->get(player.palette_files[2]));
// Añade los colores establecidos en param.player usando el índice del jugador (player_idx) if (palette_idx == 1) {
texture_copy->setPaletteColor(1, 16, param.player.one_coffee_shirt[player_idx].darkest.toUint32()); // Textura 1 - modificar solo paleta 1 (one_coffee_shirt)
texture_copy->setPaletteColor(1, 17, param.player.one_coffee_shirt[player_idx].dark.toUint32()); texture->setPaletteColor(1, 16, param.player.one_coffee_shirt[player_idx].darkest.TO_UINT32());
texture_copy->setPaletteColor(1, 18, param.player.one_coffee_shirt[player_idx].base.toUint32()); texture->setPaletteColor(1, 17, param.player.one_coffee_shirt[player_idx].dark.TO_UINT32());
texture_copy->setPaletteColor(1, 19, param.player.one_coffee_shirt[player_idx].light.toUint32()); texture->setPaletteColor(1, 18, param.player.one_coffee_shirt[player_idx].base.TO_UINT32());
texture->setPaletteColor(1, 19, param.player.one_coffee_shirt[player_idx].light.TO_UINT32());
texture->setPaletteColor(1, 56, param.player.outline_color[player_idx].TO_UINT32());
} else if (palette_idx == 2) {
// Textura 2 - modificar solo paleta 2 (two_coffee_shirt)
texture->setPaletteColor(2, 16, param.player.two_coffee_shirt[player_idx].darkest.TO_UINT32());
texture->setPaletteColor(2, 17, param.player.two_coffee_shirt[player_idx].dark.TO_UINT32());
texture->setPaletteColor(2, 18, param.player.two_coffee_shirt[player_idx].base.TO_UINT32());
texture->setPaletteColor(2, 19, param.player.two_coffee_shirt[player_idx].light.TO_UINT32());
texture->setPaletteColor(2, 56, param.player.outline_color[player_idx].TO_UINT32());
}
// Textura 3 (palette_idx == 3) - no modificar nada, usar colores originales
}
texture_copy->setPaletteColor(2, 16, param.player.two_coffee_shirt[player_idx].darkest.toUint32()); // Asignar la paleta correspondiente
texture_copy->setPaletteColor(2, 17, param.player.two_coffee_shirt[player_idx].dark.toUint32()); texture->setPalette(palette_idx);
texture_copy->setPaletteColor(2, 18, param.player.two_coffee_shirt[player_idx].base.toUint32());
texture_copy->setPaletteColor(2, 19, param.player.two_coffee_shirt[player_idx].light.toUint32());
// Cambiar a la paleta específica (índice i+1 porque 0 es la original)
texture_copy->setPalette(i + 1);
// Guardar con nombre específico // Guardar con nombre específico
std::string variant_name = player.name_prefix + "_pal" + std::to_string(i + 1); std::string texture_name = player.name_prefix + "_pal" + std::to_string(palette_idx);
textures_.emplace_back(variant_name, texture_copy); textures_.emplace_back(texture_name, texture);
printWithDots("Player Texture : ", variant_name, "[ DONE ]"); printWithDots("Player Texture : ", texture_name, "[ DONE ]");
} }
} }
} }
@@ -604,33 +616,42 @@ void Resource::createTextTextures() {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXTURES"); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXTURES");
// Texturas de tamaño normal // Texturas de tamaño normal con outline
std::vector<NameAndText> strings = { std::vector<NameAndText> strings1 = {
{"game_text_1000_points", "1.000"}, {"game_text_1000_points", "1.000"},
{"game_text_2500_points", "2.500"}, {"game_text_2500_points", "2.500"},
{"game_text_5000_points", "5.000"}, {"game_text_5000_points", "5.000"},
{"game_text_powerup", Lang::getText("[GAME_TEXT] 4")}, {"game_text_powerup", Lang::getText("[GAME_TEXT] 4")},
{"game_text_one_hit", Lang::getText("[GAME_TEXT] 5")}, {"game_text_one_hit", Lang::getText("[GAME_TEXT] 5")},
{"game_text_stop", Lang::getText("[GAME_TEXT] 6")}, {"game_text_stop", Lang::getText("[GAME_TEXT] 6")}};
auto text1 = getText("04b_25_enhanced");
for (const auto &s : strings1) {
textures_.emplace_back(s.name, text1->writeDXToTexture(Text::STROKE, s.text, -2, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
printWithDots("Texture : ", s.name, "[ DONE ]");
}
// Texturas de tamaño normal
std::vector<NameAndText> strings2 = {
{"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}}; {"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}};
auto text = getText("04b_25"); auto text2 = getText("04b_25");
for (const auto &s : strings) { for (const auto &s : strings2) {
textures_.emplace_back(s.name, text->writeToTexture(s.text, 1, -2)); textures_.emplace_back(s.name, text2->writeDXToTexture(Text::STROKE, s.text, -2, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
printWithDots("Texture : ", s.name, "[ DONE ]"); printWithDots("Texture : ", s.name, "[ DONE ]");
} }
// Texturas de tamaño doble // Texturas de tamaño doble
std::vector<NameAndText> strings2_x = { std::vector<NameAndText> strings3 = {
{"game_text_100000_points", "100.000"}, {"game_text_100000_points", "100.000"},
{"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")}, {"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")},
{"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")}, {"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")},
{"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")}, {"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")},
{"game_text_game_over", "Game Over"}}; {"game_text_game_over", "Game Over"}};
auto text2 = getText("04b_25_2x"); auto text3 = getText("04b_25_2x");
for (const auto &s : strings2_x) { for (const auto &s : strings3) {
textures_.emplace_back(s.name, text2->writeToTexture(s.text, 1, -4)); textures_.emplace_back(s.name, text3->writeToTexture(s.text, 1, -4));
printWithDots("Texture : ", s.name, "[ DONE ]"); printWithDots("Texture : ", s.name, "[ DONE ]");
} }
} }
@@ -641,15 +662,18 @@ void Resource::createText() {
std::string key; std::string key;
std::string texture_file; std::string texture_file;
std::string text_file; std::string text_file;
std::string white_texture_file; // Textura blanca opcional
ResourceInfo(std::string k, std::string t_file, std::string txt_file) ResourceInfo(std::string k, std::string t_file, std::string txt_file, std::string w_file = "")
: key(std::move(k)), texture_file(std::move(t_file)), text_file(std::move(txt_file)) {} : key(std::move(k)), texture_file(std::move(t_file)), text_file(std::move(txt_file)), white_texture_file(std::move(w_file)) {}
}; };
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXT OBJECTS"); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXT OBJECTS");
std::vector<ResourceInfo> resources = { std::vector<ResourceInfo> resources = {
{"04b_25", "04b_25.png", "04b_25.txt"}, {"04b_25", "04b_25.png", "04b_25.txt"},
{"04b_25_enhanced", "04b_25.png", "04b_25.txt", "04b_25_white.png"}, // Nueva fuente con textura blanca
{"04b_25_white", "04b_25_white.png", "04b_25.txt"},
{"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"}, {"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"},
{"04b_25_metal", "04b_25_metal.png", "04b_25.txt"}, {"04b_25_metal", "04b_25_metal.png", "04b_25.txt"},
{"04b_25_grey", "04b_25_grey.png", "04b_25.txt"}, {"04b_25_grey", "04b_25_grey.png", "04b_25.txt"},
@@ -663,7 +687,13 @@ void Resource::createText() {
{"smb2_grad", "smb2_grad.png", "smb2.txt"}}; {"smb2_grad", "smb2_grad.png", "smb2.txt"}};
for (const auto &resource : resources) { for (const auto &resource : resources) {
texts_.emplace_back(resource.key, std::make_shared<Text>(getTexture(resource.texture_file), getTextFile(resource.text_file))); if (!resource.white_texture_file.empty()) {
// Crear texto con textura blanca
texts_.emplace_back(resource.key, std::make_shared<Text>(getTexture(resource.texture_file), getTexture(resource.white_texture_file), getTextFile(resource.text_file)));
} else {
// Crear texto normal
texts_.emplace_back(resource.key, std::make_shared<Text>(getTexture(resource.texture_file), getTextFile(resource.text_file)));
}
printWithDots("Text : ", resource.key, "[ DONE ]"); printWithDots("Text : ", resource.key, "[ DONE ]");
} }
} }
@@ -784,15 +814,15 @@ void Resource::initProgressBar() {
const float BAR_Y_POSITION = param.game.height - BAR_HEIGHT - Y_PADDING; const float BAR_Y_POSITION = param.game.height - BAR_HEIGHT - Y_PADDING;
const float WIRED_BAR_WIDTH = param.game.width - (X_PADDING * 2); const float WIRED_BAR_WIDTH = param.game.width - (X_PADDING * 2);
loading_wired_rect_ = {X_PADDING, BAR_Y_POSITION, WIRED_BAR_WIDTH, BAR_HEIGHT}; loading_wired_rect_ = {.x = X_PADDING, .y = BAR_Y_POSITION, .w = WIRED_BAR_WIDTH, .h = BAR_HEIGHT};
const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * loading_count_.getPercentage(); const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * loading_count_.getPercentage();
loading_full_rect_ = {X_PADDING, BAR_Y_POSITION, FULL_BAR_WIDTH, BAR_HEIGHT}; loading_full_rect_ = {.x = X_PADDING, .y = BAR_Y_POSITION, .w = FULL_BAR_WIDTH, .h = BAR_HEIGHT};
} }
// Actualiza el progreso de carga, muestra la barra y procesa eventos // Actualiza el progreso de carga, muestra la barra y procesa eventos
void Resource::updateLoadingProgress(std::string name) { void Resource::updateLoadingProgress(std::string name) {
loading_resource_name_ = name; loading_resource_name_ = std::move(name);
loading_count_.increase(); loading_count_.increase();
updateProgressBar(); updateProgressBar();
renderProgress(); renderProgress();

View File

@@ -140,13 +140,13 @@ class Resource {
void loadEssentialResources(); // Carga recursos esenciales en modo lazy void loadEssentialResources(); // Carga recursos esenciales en modo lazy
void loadEssentialTextures(); // Carga solo las texturas esenciales (fuentes) void loadEssentialTextures(); // Carga solo las texturas esenciales (fuentes)
void loadTextFilesQuiet(); // Carga ficheros de texto sin mostrar progreso (para modo lazy) void loadTextFilesQuiet(); // Carga ficheros de texto sin mostrar progreso (para modo lazy)
void createPlayerTextures(); // Crea las texturas de jugadores con todas sus variantes de paleta void createPlayerTextures(); // Crea las texturas de jugadores con todas sus variantes de paleta
void createTextTextures(); // Crea las texturas a partir de los datos cargados void createTextTextures(); // Crea las texturas a partir de los datos cargados
void createText(); // Crea los objetos de texto void createText(); // Crea los objetos de texto
void clear(); // Vacía todos los vectores de recursos void clear(); // Vacía todos los vectores de recursos
void load(); // Carga todos los recursos void load(); // Carga todos los recursos
void clearSounds(); // Vacía el vector de sonidos void clearSounds(); // Vacía el vector de sonidos
void clearMusics(); // Vacía el vector de músicas void clearMusics(); // Vacía el vector de músicas
// --- Métodos para carga perezosa --- // --- Métodos para carga perezosa ---
void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido

View File

@@ -309,7 +309,7 @@ void Scoreboard::renderShowNameMode(size_t panel_index) {
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_); text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
/* TEXTO CENTRADO */ /* TEXTO CENTRADO */
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, record_name_.at(panel_index), 1, getColorLikeKnightRider(name_colors_, loop_counter_ / 5)); text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, record_name_.at(panel_index), 1, Colors::getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
} }
void Scoreboard::renderGameCompletedMode(size_t panel_index) { void Scoreboard::renderGameCompletedMode(size_t panel_index) {
@@ -373,14 +373,14 @@ void Scoreboard::recalculateAnchors() {
const float COL = PANEL_WIDTH / 2; const float COL = PANEL_WIDTH / 2;
// Slots de 4 // Slots de 4
slot4_1_ = {COL, ROW1}; slot4_1_ = {.x = COL, .y = ROW1};
slot4_2_ = {COL, ROW2}; slot4_2_ = {.x = COL, .y = ROW2};
slot4_3_ = {COL, ROW3}; slot4_3_ = {.x = COL, .y = ROW3};
slot4_4_ = {COL, ROW4}; slot4_4_ = {.x = COL, .y = ROW4};
// Primer cuadrado para poner el nombre de record // Primer cuadrado para poner el nombre de record
const int ENTER_NAME_LENGHT = text_scoreboard_->length(std::string(NAME_SIZE, 'A')); const int ENTER_NAME_LENGTH = text_scoreboard_->length(std::string(NAME_SIZE, 'A'));
enter_name_pos_.x = COL - (ENTER_NAME_LENGHT / 2); enter_name_pos_.x = COL - (ENTER_NAME_LENGTH / 2);
enter_name_pos_.y = ROW4; enter_name_pos_.y = ROW4;
// Recoloca los sprites // Recoloca los sprites

View File

@@ -38,7 +38,7 @@ class Scoreboard {
NUM_MODES, NUM_MODES,
}; };
// --- Structs --- // --- Estructuras ---
struct Panel { struct Panel {
Mode mode; // Modo en el que se encuentra el panel Mode mode; // Modo en el que se encuentra el panel
SDL_FRect pos; // Posición donde dibujar el panel dentro del marcador SDL_FRect pos; // Posición donde dibujar el panel dentro del marcador
@@ -80,22 +80,21 @@ class Scoreboard {
// --- Variables de estado --- // --- Variables de estado ---
std::array<std::string, static_cast<int>(Id::SIZE)> name_ = {}; // Nombre de cada jugador std::array<std::string, static_cast<int>(Id::SIZE)> name_ = {}; // Nombre de cada jugador
std::array<std::string, static_cast<int>(Id::SIZE)> record_name_ = {}; // Nombre introducido para la tabla de records std::array<std::string, static_cast<int>(Id::SIZE)> record_name_ = {}; // Nombre introducido para la tabla de records
std::array<Panel, static_cast<int>(Id::SIZE)> panel_ = {}; // Lista con todos los paneles del marcador
std::vector<Color> name_colors_; // Colores para destacar el nombre una vez introducido
std::string hi_score_name_; // Nombre del jugador con la máxima puntuación
SDL_FRect rect_ = {0, 0, 320, 40}; // Posición y dimensiones del marcador
Color color_; // Color del marcador
std::array<size_t, static_cast<int>(Id::SIZE)> selector_pos_ = {}; // Posición del selector de letra para introducir el nombre std::array<size_t, static_cast<int>(Id::SIZE)> selector_pos_ = {}; // Posición del selector de letra para introducir el nombre
std::array<int, static_cast<int>(Id::SIZE)> score_ = {}; // Puntuación de los jugadores std::array<int, static_cast<int>(Id::SIZE)> score_ = {}; // Puntuación de los jugadores
std::array<float, static_cast<int>(Id::SIZE)> mult_ = {}; // Multiplicador de los jugadores
std::array<int, static_cast<int>(Id::SIZE)> continue_counter_ = {}; // Tiempo para continuar de los jugadores std::array<int, static_cast<int>(Id::SIZE)> continue_counter_ = {}; // Tiempo para continuar de los jugadores
std::array<Panel, static_cast<int>(Id::SIZE)> panel_ = {}; // Lista con todos los paneles del marcador std::array<float, static_cast<int>(Id::SIZE)> mult_ = {}; // Multiplicador de los jugadores
Uint64 ticks_ = SDL_GetTicks(); // Variable donde almacenar el valor de SDL_GetTicks()
int stage_ = 1; // Número de fase actual int stage_ = 1; // Número de fase actual
int hi_score_ = 0; // Máxima puntuación int hi_score_ = 0; // Máxima puntuación
float power_ = 0; // Poder actual de la fase int time_counter_ = 0; // Contador de segundos
std::string hi_score_name_; // Nombre del jugador con la máxima puntuación int loop_counter_ = 0; // Contador de bucle
Color color_; // Color del marcador float power_ = 0; // Poder actual de la fase
SDL_FRect rect_ = {0, 0, 320, 40}; // Posición y dimensiones del marcador
Uint64 ticks_ = SDL_GetTicks(); // Variable donde almacenar el valor de SDL_GetTicks()
int time_counter_ = 0; // Contador de segundos
int loop_counter_ = 0; // Contador de bucle
std::vector<Color> name_colors_; // Colores para destacar el nombre una vez introducido
// --- Variables de aspecto --- // --- Variables de aspecto ---
Color text_color1_, text_color2_; // Colores para los marcadores del texto; Color text_color1_, text_color2_; // Colores para los marcadores del texto;
@@ -126,10 +125,10 @@ class Scoreboard {
void renderShowNameMode(size_t panel_index); void renderShowNameMode(size_t panel_index);
void renderGameCompletedMode(size_t panel_index); void renderGameCompletedMode(size_t panel_index);
// --- Constructor y destructor privados (singleton) --- // --- Constructores y destructor privados (singleton) ---
Scoreboard(); Scoreboard(); // Constructor privado
~Scoreboard(); ~Scoreboard(); // Destructor privado
// --- Singleton --- // --- Instancia singleton ---
static Scoreboard *instance; static Scoreboard *instance; // Instancia única de Scoreboard
}; };

View File

@@ -12,7 +12,7 @@ class Notifier;
class ServiceMenu; class ServiceMenu;
class Text; class Text;
// Clase Screen: gestiona la ventana, el renderizador y los efectos visuales globales // --- Clase Screen: gestiona la ventana, el renderizador y los efectos visuales globales (singleton) ---
class Screen { class Screen {
public: public:
// --- Métodos de singleton --- // --- Métodos de singleton ---
@@ -38,8 +38,8 @@ class Screen {
void initShaders(); // Inicializa los shaders void initShaders(); // Inicializa los shaders
// --- Efectos visuales --- // --- Efectos visuales ---
void shake(int desp = 2, int delay = 3, int lenght = 8) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay, lenght); } // Agita la pantalla void shake(int desp = 2, int delay = 3, int length = 8) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay, length); } // Agita la pantalla
void flash(Color color, int lenght = 10, int delay = 0) { flash_effect_ = FlashEffect(true, lenght, delay, color); } // Pone la pantalla de color void flash(Color color, int length = 10, int delay = 0) { flash_effect_ = FlashEffect(true, length, delay, color); } // Pone la pantalla de color
void toggleShaders(); // Alterna entre activar y desactivar los shaders void toggleShaders(); // Alterna entre activar y desactivar los shaders
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
@@ -62,9 +62,9 @@ class Screen {
private: private:
// --- Constantes --- // --- Constantes ---
static constexpr int WINDOWS_DECORATIONS = 35; static constexpr int WINDOWS_DECORATIONS = 35; // Decoraciones de la ventana
// --- Estructuras internas --- // --- Estructuras privadas ---
struct FPS { struct FPS {
Uint32 ticks{0}; // Tiempo en milisegundos desde que se comenzó a contar. Uint32 ticks{0}; // Tiempo en milisegundos desde que se comenzó a contar.
int frame_count{0}; // Número acumulado de frames en el intervalo. int frame_count{0}; // Número acumulado de frames en el intervalo.
@@ -85,16 +85,16 @@ class Screen {
// Efecto de flash en pantalla: pinta la pantalla de un color durante unos frames // Efecto de flash en pantalla: pinta la pantalla de un color durante unos frames
struct FlashEffect { struct FlashEffect {
bool enabled; // Indica si el efecto está activo bool enabled; // Indica si el efecto está activo
int lenght; // Duración total del efecto en frames int length; // Duración total del efecto en frames
int delay; // Retraso antes de mostrar el flash int delay; // Retraso antes de mostrar el flash
int counter; // Contador de frames restantes int counter; // Contador de frames restantes
Color color; // Color del flash Color color; // Color del flash
explicit FlashEffect(bool enabled = false, int lenght = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF)) explicit FlashEffect(bool enabled = false, int length = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF))
: enabled(enabled), lenght(lenght), delay(delay), counter(lenght), color(color) {} : enabled(enabled), length(length), delay(delay), counter(length), color(color) {}
void update() { (enabled && counter > 0) ? counter-- : static_cast<int>(enabled = false); } void update() { (enabled && counter > 0) ? counter-- : static_cast<int>(enabled = false); }
[[nodiscard]] auto isRendarable() const -> bool { return enabled && counter < lenght - delay; } [[nodiscard]] auto isRendarable() const -> bool { return enabled && counter < length - delay; }
}; };
// Efecto de sacudida/agitación de pantalla: mueve la imagen para simular un temblor // Efecto de sacudida/agitación de pantalla: mueve la imagen para simular un temblor
@@ -102,17 +102,17 @@ class Screen {
int desp; // Desplazamiento máximo de la sacudida (en píxeles) int desp; // Desplazamiento máximo de la sacudida (en píxeles)
int delay; // Frames entre cada movimiento de sacudida int delay; // Frames entre cada movimiento de sacudida
int counter; // Contador de frames para el siguiente movimiento int counter; // Contador de frames para el siguiente movimiento
int lenght; // Duración total del efecto en frames int length; // Duración total del efecto en frames
int remaining; // Frames restantes de sacudida int remaining; // Frames restantes de sacudida
int original_pos; // Posición original de la imagen (x) int original_pos; // Posición original de la imagen (x)
int original_width; // Ancho original de la imagen int original_width; // Ancho original de la imagen
bool enabled; // Indica si el efecto está activo bool enabled; // Indica si el efecto está activo
explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int orig_pos = 0, int orig_width = 800) explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int orig_pos = 0, int orig_width = 800)
: desp(dp), delay(dl), counter(cnt), lenght(len), remaining(rem), original_pos(orig_pos), original_width(orig_width), enabled(en) {} : desp(dp), delay(dl), counter(cnt), length(len), remaining(rem), original_pos(orig_pos), original_width(orig_width), enabled(en) {}
// Activa el efecto de sacudida y guarda la posición y tamaño originales // Activa el efecto de sacudida y guarda la posición y tamaño originales
void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_lenght = -1) { void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_length = -1) {
if (!enabled) { if (!enabled) {
enabled = true; enabled = true;
original_pos = src_rect.x; original_pos = src_rect.x;
@@ -125,14 +125,14 @@ class Screen {
if (new_delay != -1) { if (new_delay != -1) {
delay = new_delay; delay = new_delay;
} }
if (new_lenght != -1) { if (new_length != -1) {
lenght = new_lenght; length = new_length;
} }
src_rect.w -= desp; src_rect.w -= desp;
dst_rect.w = src_rect.w; dst_rect.w = src_rect.w;
} }
remaining = lenght; remaining = length;
counter = delay; counter = delay;
} }
@@ -170,21 +170,19 @@ class Screen {
#endif #endif
// --- Singleton ---
static Screen *instance;
// --- Objetos y punteros --- // --- Objetos y punteros ---
SDL_Window *window_; // Ventana de la aplicación SDL_Window *window_; // Ventana de la aplicación
SDL_Renderer *renderer_; // El renderizador de la ventana SDL_Renderer *renderer_; // El renderizador de la ventana
SDL_Texture *game_canvas_; // Textura donde se dibuja todo antes de volcarse al renderizador SDL_Texture *game_canvas_; // Textura donde se dibuja todo antes de volcarse al renderizador
ServiceMenu *service_menu_; // Objeto para mostrar el menú de servicio ServiceMenu *service_menu_; // Objeto para mostrar el menú de servicio
Notifier *notifier_; // Objeto para mostrar las notificaciones por pantalla Notifier *notifier_; // Objeto para mostrar las notificaciones por pantalla
std::shared_ptr<Text> text_; // Objeto para escribir texto en pantalla
// --- Variables de estado --- // --- Variables de estado ---
SDL_FRect src_rect_; // Coordenadas de origen para dibujar la textura del juego SDL_FRect src_rect_; // Coordenadas de origen para dibujar la textura del juego
SDL_FRect dst_rect_; // Coordenadas destino para dibujar la textura del juego SDL_FRect dst_rect_; // Coordenadas destino para dibujar la textura del juego
FPS fps_; // Gestión de frames por segundo
std::string shader_source_; // Almacena el contenido del archivo GLSL std::string shader_source_; // Almacena el contenido del archivo GLSL
FPS fps_; // Gestión de frames por segundo
FlashEffect flash_effect_; // Efecto de flash en pantalla FlashEffect flash_effect_; // Efecto de flash en pantalla
ShakeEffect shake_effect_; // Efecto de agitar la pantalla ShakeEffect shake_effect_; // Efecto de agitar la pantalla
bool attenuate_effect_ = false; // Indica si la pantalla ha de estar atenuada bool attenuate_effect_ = false; // Indica si la pantalla ha de estar atenuada
@@ -192,9 +190,6 @@ class Screen {
Debug debug_info_; // Información de debug Debug debug_info_; // Información de debug
#endif #endif
// --- Texto ---
std::shared_ptr<Text> text_; // Objeto para escribir texto en pantalla
// --- Métodos internos --- // --- Métodos internos ---
auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana
void renderFlash(); // Dibuja el efecto de flash en la pantalla void renderFlash(); // Dibuja el efecto de flash en la pantalla
@@ -208,7 +203,10 @@ class Screen {
void renderAttenuate(); // Atenúa la pantalla void renderAttenuate(); // Atenúa la pantalla
void createText(); // Crea el objeto de texto void createText(); // Crea el objeto de texto
// --- Constructores y destructor --- // --- Constructores y destructor privados (singleton) ---
Screen(); Screen(); // Constructor privado
~Screen(); ~Screen(); // Destructor privado
// --- Instancia singleton ---
static Screen *instance; // Instancia única de Screen
}; };

View File

@@ -12,7 +12,7 @@
#include "audio.h" // Para Audio #include "audio.h" // Para Audio
#include "balloon_manager.h" // Para BalloonManager #include "balloon_manager.h" // Para BalloonManager
#include "color.h" // Para Zone, SHADOW_TEXT_COLOR, NO_TEXT_COLOR, Color #include "color.h" // Para Zone, Colors::SHADOW_TEXT, Colors::NO_COLOR_MOD, Color
#include "fade.h" // Para Fade, FadeType, FadeMode #include "fade.h" // Para Fade, FadeType, FadeMode
#include "global_events.h" // Para check #include "global_events.h" // Para check
#include "global_inputs.h" // Para check #include "global_inputs.h" // Para check
@@ -35,12 +35,7 @@ constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner";
// Constructor // Constructor
Credits::Credits() Credits::Credits()
: balloon_manager_(std::make_unique<BalloonManager>(nullptr)), : balloon_manager_(std::make_unique<BalloonManager>(nullptr)), tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::DIAGONAL)), fade_in_(std::make_unique<Fade>()), fade_out_(std::make_unique<Fade>()), text_texture_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, static_cast<int>(param.game.width), static_cast<int>(param.game.height))), canvas_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, static_cast<int>(param.game.width), static_cast<int>(param.game.height))) {
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::DIAGONAL)),
fade_in_(std::make_unique<Fade>()),
fade_out_(std::make_unique<Fade>()),
text_texture_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
canvas_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)) {
if (text_texture_ == nullptr) { if (text_texture_ == nullptr) {
throw std::runtime_error("Failed to create SDL texture for text."); throw std::runtime_error("Failed to create SDL texture for text.");
} }
@@ -48,13 +43,13 @@ Credits::Credits()
balloon_manager_->setPlayArea(play_area_); balloon_manager_->setPlayArea(play_area_);
fade_in_->setColor(param.fade.color); fade_in_->setColor(param.fade.color);
fade_in_->setType(FadeType::FULLSCREEN); fade_in_->setType(Fade::Type::FULLSCREEN);
fade_in_->setPostDuration(50); fade_in_->setPostDuration(50);
fade_in_->setMode(FadeMode::IN); fade_in_->setMode(Fade::Mode::IN);
fade_in_->activate(); fade_in_->activate();
fade_out_->setColor(0, 0, 0); fade_out_->setColor(0, 0, 0);
fade_out_->setType(FadeType::FULLSCREEN); fade_out_->setType(Fade::Type::FULLSCREEN);
fade_out_->setPostDuration(400); fade_out_->setPostDuration(400);
updateRedRect(); updateRedRect();
@@ -63,7 +58,7 @@ Credits::Credits()
initPlayers(); initPlayers();
SDL_SetTextureBlendMode(text_texture_, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(text_texture_, SDL_BLENDMODE_BLEND);
fillTextTexture(); fillTextTexture();
steps_ = std::abs((top_black_rect_.h - param.game.game_area.center_y - 1) + ((left_black_rect_.w - param.game.game_area.center_x) / 4)); steps_ = static_cast<int>(std::abs((top_black_rect_.h - param.game.game_area.center_y - 1) + ((left_black_rect_.w - param.game.game_area.center_x) / 4)));
} }
// Destructor // Destructor
@@ -170,58 +165,60 @@ void Credits::fillTextTexture() {
const int SPACE_POST_TITLE = 3 + text->getCharacterSize(); const int SPACE_POST_TITLE = 3 + text->getCharacterSize();
const int SPACE_PRE_TITLE = text->getCharacterSize() * 4; const int SPACE_PRE_TITLE = text->getCharacterSize() * 4;
const int TEXTS_HEIGHT = 1 * text->getCharacterSize() + 8 * SPACE_POST_TITLE + 3 * SPACE_PRE_TITLE; const int TEXTS_HEIGHT = (1 * text->getCharacterSize()) + (8 * SPACE_POST_TITLE) + (3 * SPACE_PRE_TITLE);
credits_rect_dst_.h = credits_rect_src_.h = TEXTS_HEIGHT; const int POS_X = static_cast<int>(param.game.game_area.center_x);
credits_rect_dst_.h = credits_rect_src_.h = static_cast<float>(TEXTS_HEIGHT);
auto text_style = Text::Style(Text::CENTER | Text::SHADOW, Colors::NO_COLOR_MOD, Colors::SHADOW_TEXT);
// PROGRAMMED_AND_DESIGNED_BY // PROGRAMMED_AND_DESIGNED_BY
int y = 0; int y = 0;
text_grad->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(0), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text_grad->writeStyle(POS_X, y, TEXTS.at(0), text_style);
y += SPACE_POST_TITLE; y += SPACE_POST_TITLE;
text->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(4), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text->writeStyle(POS_X, y, TEXTS.at(4), text_style);
// PIXELART_DRAWN_BY // PIXELART_DRAWN_BY
y += SPACE_PRE_TITLE; y += SPACE_PRE_TITLE;
text_grad->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(1), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text_grad->writeStyle(POS_X, y, TEXTS.at(1), text_style);
y += SPACE_POST_TITLE; y += SPACE_POST_TITLE;
text->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(4), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text->writeStyle(POS_X, y, TEXTS.at(4), text_style);
// MUSIC_COMPOSED_BY // MUSIC_COMPOSED_BY
y += SPACE_PRE_TITLE; y += SPACE_PRE_TITLE;
text_grad->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(2), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text_grad->writeStyle(POS_X, y, TEXTS.at(2), text_style);
y += SPACE_POST_TITLE; y += SPACE_POST_TITLE;
text->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(5), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text->writeStyle(POS_X, y, TEXTS.at(5), text_style);
y += SPACE_POST_TITLE; y += SPACE_POST_TITLE;
text->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(6), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text->writeStyle(POS_X, y, TEXTS.at(6), text_style);
// SOUND_EFFECTS // SOUND_EFFECTS
y += SPACE_PRE_TITLE; y += SPACE_PRE_TITLE;
text_grad->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(3), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text_grad->writeStyle(POS_X, y, TEXTS.at(3), text_style);
y += SPACE_POST_TITLE; y += SPACE_POST_TITLE;
text->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(7), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text->writeStyle(POS_X, y, TEXTS.at(7), text_style);
y += SPACE_POST_TITLE; y += SPACE_POST_TITLE;
text->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(8), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text->writeStyle(POS_X, y, TEXTS.at(8), text_style);
y += SPACE_POST_TITLE; y += SPACE_POST_TITLE;
text->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(9), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text->writeStyle(POS_X, y, TEXTS.at(9), text_style);
y += SPACE_POST_TITLE; y += SPACE_POST_TITLE;
text->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, TEXTS.at(10), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text->writeStyle(POS_X, y, TEXTS.at(10), text_style);
// Mini logo // Mini logo
y += SPACE_PRE_TITLE; y += SPACE_PRE_TITLE;
mini_logo_rect_src_.y = y; mini_logo_rect_src_.y = static_cast<float>(y);
auto mini_logo_sprite = std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png")); auto mini_logo_sprite = std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"));
mini_logo_sprite->setPosition(1 + param.game.game_area.center_x - mini_logo_sprite->getWidth() / 2, 1 + y); mini_logo_sprite->setPosition(1 + POS_X - (mini_logo_sprite->getWidth() / 2), 1 + y);
Resource::get()->getTexture("logo_jailgames_mini.png")->setColor(SHADOW_TEXT_COLOR.r, SHADOW_TEXT_COLOR.g, SHADOW_TEXT_COLOR.b); Resource::get()->getTexture("logo_jailgames_mini.png")->setColor(Colors::SHADOW_TEXT.r, Colors::SHADOW_TEXT.g, Colors::SHADOW_TEXT.b);
mini_logo_sprite->render(); mini_logo_sprite->render();
mini_logo_sprite->setPosition(param.game.game_area.center_x - mini_logo_sprite->getWidth() / 2, y); mini_logo_sprite->setPosition(POS_X - (mini_logo_sprite->getWidth() / 2), y);
Resource::get()->getTexture("logo_jailgames_mini.png")->setColor(255, 255, 255); Resource::get()->getTexture("logo_jailgames_mini.png")->setColor(255, 255, 255);
mini_logo_sprite->render(); mini_logo_sprite->render();
// Texto con el copyright // Texto con el copyright
y += mini_logo_sprite->getHeight() + 3; y += mini_logo_sprite->getHeight() + 3;
text->writeDX(Text::CENTER | Text::SHADOW, param.game.game_area.center_x, y, std::string(TEXT_COPYRIGHT), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); text->writeDX(Text::CENTER | Text::SHADOW, POS_X, y, std::string(TEXT_COPYRIGHT), 1, Colors::NO_COLOR_MOD, 1, Colors::SHADOW_TEXT);
// Resetea el renderizador // Resetea el renderizador
SDL_SetRenderTarget(Screen::get()->getRenderer(), nullptr); SDL_SetRenderTarget(Screen::get()->getRenderer(), nullptr);
@@ -384,7 +381,7 @@ void Credits::initPlayers() {
players_.back()->setPlayingState(Player::State::CREDITS); players_.back()->setPlayingState(Player::State::CREDITS);
// Registra los jugadores en Options // Registra los jugadores en Options
for (auto player : players_) { for (const auto &player : players_) {
Options::keyboard.addPlayer(player); Options::keyboard.addPlayer(player);
Options::gamepad_manager.addPlayer(player); Options::gamepad_manager.addPlayer(player);
} }

View File

@@ -16,7 +16,7 @@
#include "balloon.h" // Para Balloon #include "balloon.h" // Para Balloon
#include "balloon_manager.h" // Para BalloonManager #include "balloon_manager.h" // Para BalloonManager
#include "bullet.h" // Para Bullet, BulletType, BulletMoveStatus #include "bullet.h" // Para Bullet, BulletType, BulletMoveStatus
#include "color.h" // Para Color, FLASH_COLOR #include "color.h" // Para Color, Colors::FLASH
#include "difficulty.h" // Para Code #include "difficulty.h" // Para Code
#include "fade.h" // Para Fade, FadeType, FadeMode #include "fade.h" // Para Fade, FadeType, FadeMode
#include "global_events.h" // Para check #include "global_events.h" // Para check
@@ -49,18 +49,7 @@
// Constructor // Constructor
Game::Game(Player::Id player_id, int current_stage, bool demo) Game::Game(Player::Id player_id, int current_stage, bool demo)
: renderer_(Screen::get()->getRenderer()), : renderer_(Screen::get()->getRenderer()), screen_(Screen::get()), input_(Input::get()), canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)), pause_manager_(std::make_unique<PauseManager>([this](bool is_paused) { onPauseStateChanged(is_paused); })), stage_manager_(std::make_unique<StageManager>()), balloon_manager_(std::make_unique<BalloonManager>(stage_manager_.get())), background_(std::make_unique<Background>(stage_manager_->getPowerNeededToReachStage(stage_manager_->getTotalStages() - 1))), fade_in_(std::make_unique<Fade>()), fade_out_(std::make_unique<Fade>()), tabe_(std::make_unique<Tabe>()), hit_(Hit(Resource::get()->getTexture("hit.png"))) {
screen_(Screen::get()),
input_(Input::get()),
canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)),
pause_manager_(std::make_unique<PauseManager>([this](bool is_paused) { onPauseStateChanged(is_paused); })),
stage_manager_(std::make_unique<StageManager>()),
balloon_manager_(std::make_unique<BalloonManager>(stage_manager_.get())),
background_(std::make_unique<Background>(stage_manager_->getTotalPowerNeededToCompleteGame())),
fade_in_(std::make_unique<Fade>()),
fade_out_(std::make_unique<Fade>()),
tabe_(std::make_unique<Tabe>()),
hit_(Hit(Resource::get()->getTexture("hit.png"))) {
// Pasa variables // Pasa variables
demo_.enabled = demo; demo_.enabled = demo;
@@ -79,15 +68,15 @@ 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 ? 80 : 0); fade_in_->setPreDuration(demo_.enabled ? 500 : 0);
fade_in_->setPostDuration(0); fade_in_->setPostDuration(0);
fade_in_->setType(FadeType::RANDOM_SQUARE); fade_in_->setType(Fade::Type::RANDOM_SQUARE2);
fade_in_->setMode(FadeMode::IN); fade_in_->setMode(Fade::Mode::IN);
fade_in_->activate(); fade_in_->activate();
fade_out_->setColor(param.fade.color); fade_out_->setColor(param.fade.color);
fade_out_->setPostDuration(param.fade.post_duration); fade_out_->setPostDuration(param.fade.post_duration_ms);
fade_out_->setType(FadeType::VENETIAN); fade_out_->setType(Fade::Type::VENETIAN);
background_->setPos(param.game.play_area.rect); background_->setPos(param.game.play_area.rect);
@@ -230,7 +219,8 @@ void Game::updatePlayers() {
handlePlayerCollision(player, balloon); handlePlayerCollision(player, balloon);
if (demo_.enabled && allPlayersAreNotPlaying()) { if (demo_.enabled && allPlayersAreNotPlaying()) {
fade_out_->setType(FadeType::RANDOM_SQUARE); fade_out_->setType(Fade::Type::RANDOM_SQUARE2);
fade_out_->setPostDuration(500);
fade_out_->activate(); fade_out_->activate();
} }
} }
@@ -281,7 +271,7 @@ void Game::updateStage() {
// Efectos de cambio de fase // Efectos de cambio de fase
playSound("stage_change.wav"); playSound("stage_change.wav");
balloon_manager_->resetBalloonSpeed(); balloon_manager_->resetBalloonSpeed();
screen_->flash(FLASH_COLOR, 3); screen_->flash(Colors::FLASH, 3);
screen_->shake(); screen_->shake();
// Obtener datos de la nueva fase // Obtener datos de la nueva fase
@@ -466,35 +456,35 @@ void Game::checkPlayerItemCollision(std::shared_ptr<Player> &player) {
switch (item->getType()) { switch (item->getType()) {
case ItemType::DISK: { case ItemType::DISK: {
player->addScore(1000, Options::settings.hi_score_table.back().score); player->addScore(1000, Options::settings.hi_score_table.back().score);
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(0)->getWidth()) / 2; const auto X = item->getPosX() + ((item->getWidth() - game_text_textures_.at(0)->getWidth()) / 2);
createItemText(X, game_text_textures_.at(0)); createItemText(X, game_text_textures_.at(0));
playSound("item_pickup.wav"); playSound("item_pickup.wav");
break; break;
} }
case ItemType::GAVINA: { case ItemType::GAVINA: {
player->addScore(2500, Options::settings.hi_score_table.back().score); player->addScore(2500, Options::settings.hi_score_table.back().score);
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(1)->getWidth()) / 2; const auto X = item->getPosX() + ((item->getWidth() - game_text_textures_.at(1)->getWidth()) / 2);
createItemText(X, game_text_textures_.at(1)); createItemText(X, game_text_textures_.at(1));
playSound("item_pickup.wav"); playSound("item_pickup.wav");
break; break;
} }
case ItemType::PACMAR: { case ItemType::PACMAR: {
player->addScore(5000, Options::settings.hi_score_table.back().score); player->addScore(5000, Options::settings.hi_score_table.back().score);
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2; const auto X = item->getPosX() + ((item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2);
createItemText(X, game_text_textures_.at(2)); createItemText(X, game_text_textures_.at(2));
playSound("item_pickup.wav"); playSound("item_pickup.wav");
break; break;
} }
case ItemType::DEBIAN: { case ItemType::DEBIAN: {
player->addScore(100000, Options::settings.hi_score_table.back().score); player->addScore(100000, Options::settings.hi_score_table.back().score);
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(6)->getWidth()) / 2; const auto X = item->getPosX() + ((item->getWidth() - game_text_textures_.at(6)->getWidth()) / 2);
createItemText(X, game_text_textures_.at(6)); createItemText(X, game_text_textures_.at(6));
playSound("debian_pickup.wav"); playSound("debian_pickup.wav");
break; break;
} }
case ItemType::CLOCK: { case ItemType::CLOCK: {
enableTimeStopItem(); enableTimeStopItem();
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(5)->getWidth()) / 2; const auto X = item->getPosX() + ((item->getWidth() - game_text_textures_.at(5)->getWidth()) / 2);
createItemText(X, game_text_textures_.at(5)); createItemText(X, game_text_textures_.at(5));
playSound("item_pickup.wav"); playSound("item_pickup.wav");
break; break;
@@ -502,11 +492,11 @@ void Game::checkPlayerItemCollision(std::shared_ptr<Player> &player) {
case ItemType::COFFEE: { case ItemType::COFFEE: {
if (player->getCoffees() == 2) { if (player->getCoffees() == 2) {
player->addScore(5000, Options::settings.hi_score_table.back().score); player->addScore(5000, Options::settings.hi_score_table.back().score);
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2; const auto X = item->getPosX() + ((item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2);
createItemText(X, game_text_textures_.at(2)); createItemText(X, game_text_textures_.at(2));
} else { } else {
player->giveExtraHit(); player->giveExtraHit();
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(4)->getWidth()) / 2; const auto X = item->getPosX() + ((item->getWidth() - game_text_textures_.at(4)->getWidth()) / 2);
createItemText(X, game_text_textures_.at(4)); createItemText(X, game_text_textures_.at(4));
} }
playSound("voice_coffee.wav"); playSound("voice_coffee.wav");
@@ -515,7 +505,7 @@ void Game::checkPlayerItemCollision(std::shared_ptr<Player> &player) {
case ItemType::COFFEE_MACHINE: { case ItemType::COFFEE_MACHINE: {
player->setPowerUp(); player->setPowerUp();
coffee_machine_enabled_ = false; coffee_machine_enabled_ = false;
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(3)->getWidth()) / 2; const auto X = item->getPosX() + ((item->getWidth() - game_text_textures_.at(3)->getWidth()) / 2);
createItemText(X, game_text_textures_.at(3)); createItemText(X, game_text_textures_.at(3));
playSound("voice_power_up.wav"); playSound("voice_power_up.wav");
break; break;
@@ -549,7 +539,7 @@ void Game::checkBulletCollision() {
} }
// Maneja la colisión entre bala y Tabe // Maneja la colisión entre bala y Tabe
auto Game::checkBulletTabeCollision(std::shared_ptr<Bullet> bullet) -> bool { auto Game::checkBulletTabeCollision(const std::shared_ptr<Bullet> &bullet) -> bool {
if (!tabe_->isEnabled()) { if (!tabe_->isEnabled()) {
return false; return false;
} }
@@ -581,7 +571,7 @@ void Game::handleTabeHitEffects() {
} }
// Maneja la colisión entre bala y globos // Maneja la colisión entre bala y globos
auto Game::checkBulletBalloonCollision(std::shared_ptr<Bullet> bullet) -> bool { auto Game::checkBulletBalloonCollision(const std::shared_ptr<Bullet> &bullet) -> bool {
for (auto &balloon : balloon_manager_->getBalloons()) { for (auto &balloon : balloon_manager_->getBalloons()) {
if (!balloon->isEnabled() || balloon->isInvulnerable()) { if (!balloon->isEnabled() || balloon->isInvulnerable()) {
continue; continue;
@@ -598,7 +588,7 @@ auto Game::checkBulletBalloonCollision(std::shared_ptr<Bullet> bullet) -> bool {
} }
// Procesa el impacto en un globo // Procesa el impacto en un globo
void Game::processBalloonHit(std::shared_ptr<Bullet> bullet, std::shared_ptr<Balloon> balloon) { void Game::processBalloonHit(const std::shared_ptr<Bullet> &bullet, const std::shared_ptr<Balloon> &balloon) {
auto player = getPlayer(bullet->getOwner()); auto player = getPlayer(bullet->getOwner());
handleItemDrop(balloon, player); handleItemDrop(balloon, player);
@@ -608,7 +598,7 @@ void Game::processBalloonHit(std::shared_ptr<Bullet> bullet, std::shared_ptr<Bal
} }
// Maneja la caída de items cuando se destruye un globo // Maneja la caída de items cuando se destruye un globo
void Game::handleItemDrop(std::shared_ptr<Balloon> balloon, std::shared_ptr<Player> player) { void Game::handleItemDrop(const std::shared_ptr<Balloon> &balloon, const std::shared_ptr<Player> &player) {
const auto DROPPED_ITEM = dropItem(); const auto DROPPED_ITEM = dropItem();
if (DROPPED_ITEM == ItemType::NONE || demo_.recording) { if (DROPPED_ITEM == ItemType::NONE || demo_.recording) {
return; return;
@@ -623,9 +613,9 @@ void Game::handleItemDrop(std::shared_ptr<Balloon> balloon, std::shared_ptr<Play
} }
// Maneja la destrucción del globo y puntuación // Maneja la destrucción del globo y puntuación
void Game::handleBalloonDestruction(std::shared_ptr<Balloon> balloon, std::shared_ptr<Player> player) { void Game::handleBalloonDestruction(std::shared_ptr<Balloon> balloon, const std::shared_ptr<Player> &player) {
if (player->isPlaying()) { if (player->isPlaying()) {
auto const SCORE = balloon_manager_->popBalloon(balloon) * player->getScoreMultiplier() * difficulty_score_multiplier_; auto const SCORE = balloon_manager_->popBalloon(std::move(balloon)) * player->getScoreMultiplier() * difficulty_score_multiplier_;
player->addScore(SCORE, Options::settings.hi_score_table.back().score); player->addScore(SCORE, Options::settings.hi_score_table.back().score);
player->incScoreMultiplier(); player->incScoreMultiplier();
} }
@@ -758,7 +748,7 @@ void Game::freeItems() {
} }
// Crea un objeto PathSprite // Crea un objeto PathSprite
void Game::createItemText(int x, std::shared_ptr<Texture> texture) { void Game::createItemText(int x, const std::shared_ptr<Texture> &texture) {
path_sprites_.emplace_back(std::make_unique<PathSprite>(texture)); path_sprites_.emplace_back(std::make_unique<PathSprite>(texture));
const auto W = texture->getWidth(); const auto W = texture->getWidth();
@@ -781,7 +771,7 @@ void Game::createItemText(int x, std::shared_ptr<Texture> texture) {
} }
// Crea un objeto PathSprite // Crea un objeto PathSprite
void Game::createMessage(const std::vector<Path> &paths, std::shared_ptr<Texture> texture) { void Game::createMessage(const std::vector<Path> &paths, const std::shared_ptr<Texture> &texture) {
path_sprites_.emplace_back(std::make_unique<PathSprite>(texture)); path_sprites_.emplace_back(std::make_unique<PathSprite>(texture));
// Inicializa // Inicializa
@@ -874,7 +864,7 @@ void Game::handlePlayerCollision(std::shared_ptr<Player> &player, std::shared_pt
if (player->hasExtraHit()) { if (player->hasExtraHit()) {
// Lo pierde // Lo pierde
player->removeExtraHit(); player->removeExtraHit();
throwCoffee(player->getPosX() + (player->getWidth() / 2), player->getPosY() + (player->getHeight() / 2)); throwCoffee(player->getPosX() + (Player::WIDTH / 2), player->getPosY() + (Player::HEIGHT / 2));
playSound("coffee_out.wav"); playSound("coffee_out.wav");
screen_->shake(); screen_->shake();
} else { } else {
@@ -986,12 +976,14 @@ void Game::fillCanvas() {
// Dibuja los objetos // Dibuja los objetos
background_->render(); background_->render();
renderPlayers();
renderSmartSprites();
renderItems();
balloon_manager_->render(); balloon_manager_->render();
renderSmartSprites(); // El cafe que sale cuando te golpean
renderItems();
tabe_->render(); tabe_->render();
renderBullets(); renderBullets();
renderPlayers();
renderPathSprites(); renderPathSprites();
// Deja el renderizador apuntando donde estaba // Deja el renderizador apuntando donde estaba
@@ -1031,7 +1023,7 @@ void Game::initPaths() {
const auto &texture = Resource::get()->getTexture("game_text_get_ready"); const auto &texture = Resource::get()->getTexture("game_text_get_ready");
const auto W = texture->getWidth(); const auto W = texture->getWidth();
const int X0 = -W; const int X0 = -W;
const int X1 = param.game.play_area.center_x - W / 2; const int X1 = param.game.play_area.center_x - (W / 2);
const int X2 = param.game.play_area.rect.w; const int X2 = param.game.play_area.rect.w;
const int Y = param.game.play_area.center_y; const int Y = param.game.play_area.center_y;
paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 20); paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 20);
@@ -1043,7 +1035,7 @@ void Game::initPaths() {
const auto &texture = Resource::get()->getTexture("game_text_last_stage"); const auto &texture = Resource::get()->getTexture("game_text_last_stage");
const auto H = texture->getHeight(); const auto H = texture->getHeight();
const int Y0 = param.game.play_area.rect.h - H; const int Y0 = param.game.play_area.rect.h - H;
const int Y1 = param.game.play_area.center_y - H / 2; const int Y1 = param.game.play_area.center_y - (H / 2);
const int Y2 = -H; const int Y2 = -H;
const int X = param.game.play_area.center_x; const int X = param.game.play_area.center_x;
paths_.emplace_back(createPath(Y0, Y1, PathType::VERTICAL, X, 80, easeOutQuint), 20); paths_.emplace_back(createPath(Y0, Y1, PathType::VERTICAL, X, 80, easeOutQuint), 20);
@@ -1056,9 +1048,9 @@ void Game::initPaths() {
const auto W = texture->getWidth(); const auto W = texture->getWidth();
const auto H = texture->getHeight(); const auto H = texture->getHeight();
const int X0 = -W; const int X0 = -W;
const int X1 = param.game.play_area.center_x - W / 2; const int X1 = param.game.play_area.center_x - (W / 2);
const int X2 = param.game.play_area.rect.w; const int X2 = param.game.play_area.rect.w;
const int Y = param.game.play_area.center_y - H / 2 - 20; const int Y = param.game.play_area.center_y - (H / 2) - 20;
paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 400); paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 400);
paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0); paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0);
} }
@@ -1069,9 +1061,9 @@ void Game::initPaths() {
const auto W = texture->getWidth(); const auto W = texture->getWidth();
const auto H = texture->getHeight(); const auto H = texture->getHeight();
const int X0 = param.game.play_area.rect.w; const int X0 = param.game.play_area.rect.w;
const int X1 = param.game.play_area.center_x - W / 2; const int X1 = param.game.play_area.center_x - (W / 2);
const int X2 = -W; const int X2 = -W;
const int Y = param.game.play_area.center_y + H / 2 - 20; const int Y = param.game.play_area.center_y + (H / 2) - 20;
paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 400); paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 400);
paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0); paths_.emplace_back(createPath(X1, X2, PathType::HORIZONTAL, Y, 80, easeInQuint), 0);
} }
@@ -1239,7 +1231,7 @@ void Game::checkInput() {
// Verifica si alguno de los controladores ha solicitado una pausa y actualiza el estado de pausa del juego. // Verifica si alguno de los controladores ha solicitado una pausa y actualiza el estado de pausa del juego.
void Game::checkPauseInput() { void Game::checkPauseInput() {
// Comprueba los mandos // Comprueba los mandos
for (auto gamepad : input_->getGamepads()) { for (const auto &gamepad : input_->getGamepads()) {
if (input_->checkAction(Input::Action::PAUSE, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) { if (input_->checkAction(Input::Action::PAUSE, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
pause_manager_->togglePlayerPause(); pause_manager_->togglePlayerPause();
return; return;
@@ -1302,7 +1294,7 @@ void Game::handleFireInput(const std::shared_ptr<Player> &player, BulletType bul
switch (bullet_type) { switch (bullet_type) {
case BulletType::UP: case BulletType::UP:
player->setInput(Input::Action::FIRE_CENTER); player->setInput(Input::Action::FIRE_CENTER);
bullet.x = 2 + player->getPosX() + (player->getWidth() - Bullet::WIDTH) / 2; bullet.x = 2 + player->getPosX() + (Player::WIDTH - Bullet::WIDTH) / 2;
bullet.y = player->getPosY() - (Bullet::HEIGHT / 2); bullet.y = player->getPosY() - (Bullet::HEIGHT / 2);
break; break;
case BulletType::LEFT: case BulletType::LEFT:
@@ -1312,7 +1304,7 @@ void Game::handleFireInput(const std::shared_ptr<Player> &player, BulletType bul
break; break;
case BulletType::RIGHT: case BulletType::RIGHT:
player->setInput(Input::Action::FIRE_RIGHT); player->setInput(Input::Action::FIRE_RIGHT);
bullet.x = player->getPosX() + player->getWidth() - (Bullet::WIDTH / 2); bullet.x = player->getPosX() + Player::WIDTH - (Bullet::WIDTH / 2);
bullet.y = player->getPosY(); bullet.y = player->getPosY();
break; break;
default: default:
@@ -1489,8 +1481,10 @@ void Game::initDemo(Player::Id player_id) {
// Asigna cafes a los jugadores // Asigna cafes a los jugadores
for (auto &player : players_) { for (auto &player : players_) {
for (int i = 0; i < rand() % 3; ++i) { if (player->isPlaying()) {
player->giveExtraHit(); for (int i = 0; i < rand() % 3; ++i) {
player->giveExtraHit();
}
} }
player->setInvulnerable(true); player->setInvulnerable(true);
@@ -1616,7 +1610,7 @@ void Game::initPlayers(Player::Id player_id) {
} }
// Registra los jugadores en Options // Registra los jugadores en Options
for (auto player : players_) { for (const auto &player : players_) {
Options::keyboard.addPlayer(player); Options::keyboard.addPlayer(player);
Options::gamepad_manager.addPlayer(player); Options::gamepad_manager.addPlayer(player);
} }
@@ -1660,7 +1654,8 @@ void Game::updateDemo() {
// Activa el fundido antes de acabar con los datos de la demo // Activa el fundido antes de acabar con los datos de la demo
if (demo_.counter == TOTAL_DEMO_DATA - 200) { if (demo_.counter == TOTAL_DEMO_DATA - 200) {
fade_out_->setType(FadeType::RANDOM_SQUARE); fade_out_->setType(Fade::Type::RANDOM_SQUARE2);
fade_out_->setPostDuration(param.fade.post_duration_ms);
fade_out_->activate(); fade_out_->activate();
} }
@@ -1777,11 +1772,11 @@ void Game::updateMenace() {
} }
const auto &stage = current_stage.value(); const auto &stage = current_stage.value();
const double fraction = stage_manager_->getCurrentStageProgressFraction(); const double FRACTION = stage_manager_->getCurrentStageProgressFraction();
const int difference = stage.getMaxMenace() - stage.getMinMenace(); const int DIFFERENCE = stage.getMaxMenace() - stage.getMinMenace();
// Aumenta el nivel de amenaza en función del progreso de la fase // Aumenta el nivel de amenaza en función del progreso de la fase
menace_threshold_ = stage.getMinMenace() + (difference * fraction); menace_threshold_ = stage.getMinMenace() + (DIFFERENCE * FRACTION);
if (menace_ < menace_threshold_) { if (menace_ < menace_threshold_) {
balloon_manager_->deployRandomFormation(stage_manager_->getCurrentStageIndex()); balloon_manager_->deployRandomFormation(stage_manager_->getCurrentStageIndex());
@@ -1908,7 +1903,7 @@ void Game::handleDebugEvents(const SDL_Event &event) {
} }
case SDLK_5: // 5.000 case SDLK_5: // 5.000
{ {
const int X = players_.at(0)->getPosX() + (players_.at(0)->getWidth() - game_text_textures_[3]->getWidth()) / 2; const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
createItemText(X, game_text_textures_.at(2)); createItemText(X, game_text_textures_.at(2));
break; break;
} }
@@ -1919,7 +1914,7 @@ void Game::handleDebugEvents(const SDL_Event &event) {
} }
case SDLK_7: // 100.000 case SDLK_7: // 100.000
{ {
const int X = players_.at(0)->getPosX() + (players_.at(0)->getWidth() - game_text_textures_[3]->getWidth()) / 2; const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
createItemText(X, game_text_textures_.at(6)); createItemText(X, game_text_textures_.at(6));
break; break;
} }
@@ -1940,14 +1935,14 @@ void Game::handleDebugEvents(const SDL_Event &event) {
++formation_id_; ++formation_id_;
balloon_manager_->destroyAllBalloons(); balloon_manager_->destroyAllBalloons();
balloon_manager_->deployFormation(formation_id_); balloon_manager_->deployFormation(formation_id_);
std::cout << formation_id_ << std::endl; std::cout << formation_id_ << '\n';
break; break;
} }
case SDLK_KP_MINUS: { case SDLK_KP_MINUS: {
--formation_id_; --formation_id_;
balloon_manager_->destroyAllBalloons(); balloon_manager_->destroyAllBalloons();
balloon_manager_->deployFormation(formation_id_); balloon_manager_->deployFormation(formation_id_);
std::cout << formation_id_ << std::endl; std::cout << formation_id_ << '\n';
break; break;
} }
default: default:

View File

@@ -33,29 +33,29 @@ namespace Difficulty {
enum class Code; enum class Code;
} // namespace Difficulty } // namespace Difficulty
// Clase Game // --- Clase Game: gestor principal del juego ---
class Game { class Game {
public: public:
// --- Constantes --- // --- Constantes ---
static constexpr bool DEMO_OFF = false; static constexpr bool DEMO_OFF = false; // Modo demo desactivado
static constexpr bool DEMO_ON = true; static constexpr bool DEMO_ON = true; // Modo demo activado
// --- Constructor y destructor --- // --- Constructor y destructor ---
Game(Player::Id player_id, int current_stage, bool demo); Game(Player::Id player_id, int current_stage, bool demo); // Constructor principal
~Game(); ~Game(); // Destructor
// --- Bucle principal --- // --- Bucle principal ---
void run(); void run(); // Ejecuta el bucle principal del juego
private: private:
// --- Tipos internos --- // --- Enums ---
enum class State { enum class State {
FADE_IN, FADE_IN, // Transición de entrada
ENTERING_PLAYER, ENTERING_PLAYER, // Jugador entrando
SHOWING_GET_READY_MESSAGE, SHOWING_GET_READY_MESSAGE, // Mostrando mensaje de preparado
PLAYING, PLAYING, // Jugando
COMPLETED, COMPLETED, // Juego completado
GAME_OVER, GAME_OVER, // Fin del juego
}; };
// --- Constantes internas --- // --- Constantes internas ---
@@ -216,9 +216,9 @@ class Game {
void freeBullets(); // Libera memoria del vector de balas void freeBullets(); // Libera memoria del vector de balas
// --- Colisiones específicas de balas --- // --- Colisiones específicas de balas ---
auto checkBulletTabeCollision(std::shared_ptr<Bullet> bullet) -> bool; // Detecta colisión bala-Tabe auto checkBulletTabeCollision(const std::shared_ptr<Bullet> &bullet) -> bool; // Detecta colisión bala-Tabe
auto checkBulletBalloonCollision(std::shared_ptr<Bullet> bullet) -> bool; // Detecta colisión bala-globo auto checkBulletBalloonCollision(const std::shared_ptr<Bullet> &bullet) -> bool; // Detecta colisión bala-globo
void processBalloonHit(std::shared_ptr<Bullet> bullet, 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(); // Actualiza posición y estado de todos los ítems
@@ -235,7 +235,7 @@ class Game {
void throwCoffee(int x, int y); // Crea efecto de café arrojado al ser golpeado 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(std::shared_ptr<Balloon> balloon, 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(); // Actualiza todos los sprites con lógica propia
@@ -249,17 +249,16 @@ class Game {
void initPaths(); // Inicializa rutas predefinidas para animaciones void initPaths(); // Inicializa rutas predefinidas para animaciones
// --- Creación de sprites especiales --- // --- Creación de sprites especiales ---
void createItemText(int x, std::shared_ptr<Texture> texture); // Crea texto animado para ítems void createItemText(int x, const std::shared_ptr<Texture> &texture); // Crea texto animado para ítems
void createMessage(const std::vector<Path> &paths, std::shared_ptr<Texture> texture); // Crea mensaje con animación por ruta void createMessage(const std::vector<Path> &paths, const std::shared_ptr<Texture> &texture); // Crea mensaje con animación por ruta
// --- Sistema de globos y enemigos --- // --- Sistema de globos y enemigos ---
void handleBalloonDestruction(std::shared_ptr<Balloon> balloon, std::shared_ptr<Player> player); // Procesa destrucción de globo void handleBalloonDestruction(std::shared_ptr<Balloon> balloon, const std::shared_ptr<Player> &player); // Procesa destrucción de globo
void handleTabeHitEffects(); // Gestiona efectos al golpear a Tabe void handleTabeHitEffects(); // Gestiona efectos al golpear a Tabe
void checkAndUpdateBalloonSpeed(); // Ajusta velocidad de globos según progreso void checkAndUpdateBalloonSpeed(); // Ajusta velocidad de globos según progreso
// --- Gestión de fases y progresión --- // --- Gestión de fases y progresión ---
void updateStage(); // Verifica y actualiza cambio de fase void updateStage(); // Verifica y actualiza cambio de fase
void setTotalPower(); // Calcula poder total necesario para completar el juego
void initDifficultyVars(); // Inicializa variables de dificultad void initDifficultyVars(); // Inicializa variables de dificultad
// --- Sistema de amenaza --- // --- Sistema de amenaza ---

View File

@@ -9,7 +9,7 @@
#include "audio.h" // Para Audio #include "audio.h" // Para Audio
#include "background.h" // Para Background #include "background.h" // Para Background
#include "color.h" // Para Color, easeOutQuint, NO_TEXT_COLOR #include "color.h" // Para Color, easeOutQuint, Colors::NO_COLOR_MOD
#include "fade.h" // Para Fade, FadeMode, FadeType #include "fade.h" // Para Fade, FadeMode, FadeType
#include "global_events.h" // Para check #include "global_events.h" // Para check
#include "global_inputs.h" // Para check #include "global_inputs.h" // Para check
@@ -29,15 +29,7 @@
// Constructor // Constructor
HiScoreTable::HiScoreTable() HiScoreTable::HiScoreTable()
: renderer_(Screen::get()->getRenderer()), : renderer_(Screen::get()->getRenderer()), backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), fade_(std::make_unique<Fade>()), background_(std::make_unique<Background>()), ticks_(0), view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}), fade_mode_(Fade::Mode::IN), background_fade_color_(Color(0, 0, 0)) {
backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
fade_(std::make_unique<Fade>()),
background_(std::make_unique<Background>()),
ticks_(0),
view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}),
fade_mode_(FadeMode::IN),
background_fade_color_(Color(0, 0, 0)) {
// Inicializa el resto // Inicializa el resto
Section::name = Section::Name::HI_SCORE_TABLE; Section::name = Section::Name::HI_SCORE_TABLE;
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
@@ -133,13 +125,13 @@ void HiScoreTable::run() {
void HiScoreTable::updateFade() { void HiScoreTable::updateFade() {
fade_->update(); fade_->update();
if (fade_->hasEnded() && fade_mode_ == FadeMode::IN) { if (fade_->hasEnded() && fade_mode_ == Fade::Mode::IN) {
fade_->reset(); fade_->reset();
fade_mode_ = FadeMode::OUT; fade_mode_ = Fade::Mode::OUT;
fade_->setMode(fade_mode_); fade_->setMode(fade_mode_);
} }
if (fade_->hasEnded() && fade_mode_ == FadeMode::OUT) { if (fade_->hasEnded() && fade_mode_ == Fade::Mode::OUT) {
Section::name = (Section::options == Section::Options::HI_SCORE_AFTER_PLAYING) Section::name = (Section::options == Section::Options::HI_SCORE_AFTER_PLAYING)
? Section::Name::TITLE ? Section::Name::TITLE
: Section::Name::INSTRUCTIONS; : Section::Name::INSTRUCTIONS;
@@ -178,11 +170,11 @@ void HiScoreTable::createSprites() {
float backbuffer_height; float backbuffer_height;
SDL_GetTextureSize(backbuffer_, &backbuffer_width, &backbuffer_height); SDL_GetTextureSize(backbuffer_, &backbuffer_width, &backbuffer_height);
constexpr int ENTRY_LENGHT = 22; constexpr int ENTRY_LENGTH = 22;
constexpr int MAX_NAMES = 10; constexpr int MAX_NAMES = 10;
const int SPACE_BETWEEN_HEADER = entry_text->getCharacterSize() * 4; const int SPACE_BETWEEN_HEADER = entry_text->getCharacterSize() * 4;
const int SPACE_BETWEEN_LINES = entry_text->getCharacterSize() * 2; const int SPACE_BETWEEN_LINES = entry_text->getCharacterSize() * 2;
const int SIZE = SPACE_BETWEEN_HEADER + SPACE_BETWEEN_LINES * (MAX_NAMES - 1) + entry_text->getCharacterSize(); const int SIZE = SPACE_BETWEEN_HEADER + (SPACE_BETWEEN_LINES * (MAX_NAMES - 1)) + entry_text->getCharacterSize();
const int FIRST_LINE = (param.game.height - SIZE) / 2; const int FIRST_LINE = (param.game.height - SIZE) / 2;
// Crea el sprite para el texto de cabecera // Crea el sprite para el texto de cabecera
@@ -191,13 +183,13 @@ void HiScoreTable::createSprites() {
// Crea los sprites para las entradas en la tabla de puntuaciones // Crea los sprites para las entradas en la tabla de puntuaciones
const int ANIMATION = rand() % 4; const int ANIMATION = rand() % 4;
const std::string SAMPLE_LINE(ENTRY_LENGHT + 3, ' '); const std::string SAMPLE_LINE(ENTRY_LENGTH + 3, ' ');
auto sample_entry = std::make_unique<Sprite>(entry_text->writeDXToTexture(Text::SHADOW, SAMPLE_LINE, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR)); auto sample_entry = std::make_unique<Sprite>(entry_text->writeDXToTexture(Text::SHADOW, SAMPLE_LINE, 1, Colors::NO_COLOR_MOD, 1, Colors::SHADOW_TEXT));
const auto ENTRY_WIDTH = sample_entry->getWidth(); const auto ENTRY_WIDTH = sample_entry->getWidth();
for (int i = 0; i < MAX_NAMES; ++i) { for (int i = 0; i < MAX_NAMES; ++i) {
const auto TABLE_POSITION = format(i + 1) + ". "; const auto TABLE_POSITION = format(i + 1) + ". ";
const auto SCORE = format(Options::settings.hi_score_table.at(i).score); const auto SCORE = format(Options::settings.hi_score_table.at(i).score);
const auto NUM_DOTS = ENTRY_LENGHT - Options::settings.hi_score_table.at(i).name.size() - SCORE.size(); const auto NUM_DOTS = ENTRY_LENGTH - Options::settings.hi_score_table.at(i).name.size() - SCORE.size();
const auto *const ONE_CC = Options::settings.hi_score_table.at(i).one_credit_complete ? " }" : ""; const auto *const ONE_CC = Options::settings.hi_score_table.at(i).one_credit_complete ? " }" : "";
std::string dots; std::string dots;
for (int j = 0; j < (int)NUM_DOTS; ++j) { for (int j = 0; j < (int)NUM_DOTS; ++j) {
@@ -205,7 +197,7 @@ void HiScoreTable::createSprites() {
} }
const auto LINE = TABLE_POSITION + Options::settings.hi_score_table.at(i).name + dots + SCORE + ONE_CC; const auto LINE = TABLE_POSITION + Options::settings.hi_score_table.at(i).name + dots + SCORE + ONE_CC;
entry_names_.emplace_back(std::make_shared<PathSprite>(entry_text->writeDXToTexture(Text::SHADOW, LINE, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR))); entry_names_.emplace_back(std::make_shared<PathSprite>(entry_text->writeDXToTexture(Text::SHADOW, LINE, 1, Colors::NO_COLOR_MOD, 1, Colors::SHADOW_TEXT)));
const int DEFAULT_POS_X = (backbuffer_width - ENTRY_WIDTH) / 2; const int DEFAULT_POS_X = (backbuffer_width - ENTRY_WIDTH) / 2;
const int POS_X = (i < 9) ? DEFAULT_POS_X : DEFAULT_POS_X - entry_text->getCharacterSize(); const int POS_X = (i < 9) ? DEFAULT_POS_X : DEFAULT_POS_X - entry_text->getCharacterSize();
const int POS_Y = (i * SPACE_BETWEEN_LINES) + FIRST_LINE + SPACE_BETWEEN_HEADER; const int POS_Y = (i * SPACE_BETWEEN_LINES) + FIRST_LINE + SPACE_BETWEEN_HEADER;
@@ -272,14 +264,15 @@ void HiScoreTable::updateSprites() {
// Inicializa el fade // Inicializa el fade
void HiScoreTable::initFade() { void HiScoreTable::initFade() {
fade_->setColor(param.fade.color); fade_->setColor(param.fade.color);
fade_->setType(FadeType::RANDOM_SQUARE); fade_->setType(Fade::Type::RANDOM_SQUARE2);
fade_->setPostDuration(param.fade.post_duration); fade_->setPostDuration(param.fade.post_duration_ms);
fade_->setMode(fade_mode_); fade_->setMode(fade_mode_);
fade_->activate(); fade_->activate();
} }
// Inicializa el fondo // Inicializa el fondo
void HiScoreTable::initBackground() { void HiScoreTable::initBackground() {
background_->setManualMode(true);
background_->setPos(param.game.game_area.rect); background_->setPos(param.game.game_area.rect);
background_->setCloudsSpeed(-0.1F); background_->setCloudsSpeed(-0.1F);
@@ -291,7 +284,7 @@ void HiScoreTable::initBackground() {
background_->setTransition(0.0F); background_->setTransition(0.0F);
background_->setSunProgression(1.0F); background_->setSunProgression(1.0F);
background_->setMoonProgression(0.0F); background_->setMoonProgression(0.0F);
background_fade_color_ = GREEN_SKY_COLOR; background_fade_color_ = Colors::GREEN_SKY;
break; break;
} }
@@ -301,7 +294,7 @@ void HiScoreTable::initBackground() {
background_->setTransition(0.0F); background_->setTransition(0.0F);
background_->setSunProgression(0.65F); background_->setSunProgression(0.65F);
background_->setMoonProgression(0.0F); background_->setMoonProgression(0.0F);
background_fade_color_ = PINK_SKY_COLOR; background_fade_color_ = Colors::PINK_SKY;
break; break;
} }
@@ -311,7 +304,7 @@ void HiScoreTable::initBackground() {
background_->setTransition(0.0F); background_->setTransition(0.0F);
background_->setSunProgression(0.0F); background_->setSunProgression(0.0F);
background_->setMoonProgression(0.0F); background_->setMoonProgression(0.0F);
background_fade_color_ = BLUE_SKY_COLOR; background_fade_color_ = Colors::BLUE_SKY;
break; break;
} }
@@ -322,7 +315,7 @@ void HiScoreTable::initBackground() {
// Obtiene un color del vector de colores de entradas // Obtiene un color del vector de colores de entradas
auto HiScoreTable::getEntryColor(int counter) -> Color { auto HiScoreTable::getEntryColor(int counter) -> Color {
int cycle_length = entry_colors_.size() * 2 - 2; int cycle_length = (entry_colors_.size() * 2) - 2;
size_t n = counter % cycle_length; size_t n = counter % cycle_length;
size_t index; size_t index;

View File

@@ -7,24 +7,19 @@
#include <vector> // Para vector #include <vector> // Para vector
#include "color.h" // Para Color #include "color.h" // Para Color
#include "fade.h" // Para Fade
#include "path_sprite.h" // Para Path, PathSprite (ptr only) #include "path_sprite.h" // Para Path, PathSprite (ptr only)
class Background; class Background;
class Fade;
class Sprite; class Sprite;
enum class FadeMode : Uint8;
/* // --- Clase HiScoreTable: muestra la tabla de puntuaciones más altas ---
Esta clase gestiona un estado del programa. Se encarga de mostrar la tabla con las puntuaciones // Esta clase gestiona un estado del programa. Se encarga de mostrar la tabla con las puntuaciones
más altas. Para ello utiliza un objeto que se encarga de pintar el fondo y una textura // más altas. Para ello utiliza un objeto que se encarga de pintar el fondo y una textura
sobre la que escribe las puntuaciones. Esta textura se recorre modificando la ventana de vista // sobre la que escribe las puntuaciones. Esta textura se recorre modificando la ventana de vista
para dar el efecto de que la textura se mueve sobre la pantalla. // para dar el efecto de que la textura se mueve sobre la pantalla.
// Para mejorar la legibilidad de los textos, el objeto que dibuja el fondo es capaz de modificar
Para mejorar la legibilidad de los textos, el objeto que dibuja el fondo es capaz de modificar // su atenuación.
su atenuación.
*/
// Clase HiScoreTable
class HiScoreTable { class HiScoreTable {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
@@ -52,7 +47,7 @@ class HiScoreTable {
Uint16 counter_ = 0; // Contador Uint16 counter_ = 0; // Contador
Uint64 ticks_; // Contador de ticks para ajustar la velocidad del programa Uint64 ticks_; // Contador de ticks para ajustar la velocidad del programa
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
FadeMode 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

View File

@@ -9,7 +9,7 @@
#include <vector> // Para vector #include <vector> // Para vector
#include "audio.h" // Para Audio #include "audio.h" // Para Audio
#include "color.h" // Para Color, SHADOW_TEXT_COLOR, Zone, NO_TEXT_C... #include "color.h" // Para Color, Colors::SHADOW_TEXT, Zone, NO_TEXT_C...
#include "fade.h" // Para Fade, FadeMode, FadeType #include "fade.h" // Para Fade, FadeMode, FadeType
#include "global_events.h" // Para check #include "global_events.h" // Para check
#include "global_inputs.h" // Para check #include "global_inputs.h" // Para check
@@ -26,12 +26,7 @@
// Constructor // Constructor
Instructions::Instructions() Instructions::Instructions()
: renderer_(Screen::get()->getRenderer()), : renderer_(Screen::get()->getRenderer()), texture_(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)), text_(Resource::get()->getText("smb2")), tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::STATIC)), fade_(std::make_unique<Fade>()) {
texture_(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)),
text_(Resource::get()->getText("smb2")),
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::STATIC)),
fade_(std::make_unique<Fade>()) {
// Configura las texturas // Configura las texturas
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND);
@@ -43,9 +38,9 @@ Instructions::Instructions()
// Inicializa objetos // Inicializa objetos
tiled_bg_->setColor(param.title.bg_color); tiled_bg_->setColor(param.title.bg_color);
fade_->setColor(param.fade.color); fade_->setColor(param.fade.color);
fade_->setType(FadeType::FULLSCREEN); fade_->setType(Fade::Type::FULLSCREEN);
fade_->setPostDuration(param.fade.post_duration); fade_->setPostDuration(param.fade.post_duration_ms);
fade_->setMode(FadeMode::IN); fade_->setMode(Fade::Mode::IN);
fade_->activate(); fade_->activate();
// Inicializa las líneas con un retraso progresivo de 50 ms // Inicializa las líneas con un retraso progresivo de 50 ms
@@ -137,7 +132,7 @@ void Instructions::fillTexture() {
const int FIRST_LINE = (param.game.height - SIZE) / 2; const int FIRST_LINE = (param.game.height - SIZE) / 2;
// Calcula cual es el texto más largo de las descripciones de los items // Calcula cual es el texto más largo de las descripciones de los items
int lenght = 0; int length = 0;
const std::array<std::string, 5> ITEM_DESCRIPTIONS = { const std::array<std::string, 5> ITEM_DESCRIPTIONS = {
Lang::getText("[INSTRUCTIONS] 07"), Lang::getText("[INSTRUCTIONS] 07"),
Lang::getText("[INSTRUCTIONS] 08"), Lang::getText("[INSTRUCTIONS] 08"),
@@ -146,32 +141,32 @@ void Instructions::fillTexture() {
Lang::getText("[INSTRUCTIONS] 11")}; Lang::getText("[INSTRUCTIONS] 11")};
for (const auto &desc : ITEM_DESCRIPTIONS) { for (const auto &desc : ITEM_DESCRIPTIONS) {
const int L = text_->length(desc); const int L = text_->length(desc);
lenght = L > lenght ? L : lenght; length = L > length ? L : length;
} }
const int ANCHOR_ITEM = (param.game.width - (lenght + X_OFFSET)) / 2; const int ANCHOR_ITEM = (param.game.width - (length + X_OFFSET)) / 2;
auto caption_style = Text::Style(Text::CENTER | Text::COLOR | Text::SHADOW, ORANGE_TEXT_COLOR, SHADOW_TEXT_COLOR); auto caption_style = Text::Style(Text::CENTER | Text::COLOR | Text::SHADOW, Colors::ORANGE_TEXT, Colors::SHADOW_TEXT);
auto text_style = Text::Style(Text::CENTER | Text::COLOR | Text::SHADOW, NO_TEXT_COLOR, SHADOW_TEXT_COLOR); auto text_style = Text::Style(Text::CENTER | Text::COLOR | Text::SHADOW, Colors::NO_COLOR_MOD, Colors::SHADOW_TEXT);
// Escribe el texto de las instrucciones // Escribe el texto de las instrucciones
text_->writeStyle(param.game.game_area.center_x, FIRST_LINE, Lang::getText("[INSTRUCTIONS] 01"), caption_style); text_->writeStyle(param.game.game_area.center_x, FIRST_LINE, Lang::getText("[INSTRUCTIONS] 01"), caption_style);
const int ANCHOR1 = FIRST_LINE + SPACE_POST_HEADER; const int ANCHOR1 = FIRST_LINE + SPACE_POST_HEADER;
text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + SPACE_BETWEEN_LINES * 0, Lang::getText("[INSTRUCTIONS] 02"), text_style); text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + (SPACE_BETWEEN_LINES * 0), Lang::getText("[INSTRUCTIONS] 02"), text_style);
text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + SPACE_BETWEEN_LINES * 1, Lang::getText("[INSTRUCTIONS] 03"), text_style); text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + (SPACE_BETWEEN_LINES * 1), Lang::getText("[INSTRUCTIONS] 03"), text_style);
text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + SPACE_NEW_PARAGRAPH + SPACE_BETWEEN_LINES * 2, Lang::getText("[INSTRUCTIONS] 04"), text_style); text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + SPACE_NEW_PARAGRAPH + (SPACE_BETWEEN_LINES * 2), Lang::getText("[INSTRUCTIONS] 04"), text_style);
text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + SPACE_NEW_PARAGRAPH + SPACE_BETWEEN_LINES * 3, Lang::getText("[INSTRUCTIONS] 05"), text_style); text_->writeStyle(param.game.game_area.center_x, ANCHOR1 + SPACE_NEW_PARAGRAPH + (SPACE_BETWEEN_LINES * 3), Lang::getText("[INSTRUCTIONS] 05"), text_style);
// Escribe el texto de los objetos y sus puntos // Escribe el texto de los objetos y sus puntos
const int ANCHOR2 = ANCHOR1 + SPACE_PRE_HEADER + SPACE_NEW_PARAGRAPH + SPACE_BETWEEN_LINES * 3; const int ANCHOR2 = ANCHOR1 + SPACE_PRE_HEADER + SPACE_NEW_PARAGRAPH + (SPACE_BETWEEN_LINES * 3);
text_->writeStyle(param.game.game_area.center_x, ANCHOR2, Lang::getText("[INSTRUCTIONS] 06"), caption_style); text_->writeStyle(param.game.game_area.center_x, ANCHOR2, Lang::getText("[INSTRUCTIONS] 06"), caption_style);
const int ANCHOR3 = ANCHOR2 + SPACE_POST_HEADER; const int ANCHOR3 = ANCHOR2 + SPACE_POST_HEADER;
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + SPACE_BETWEEN_ITEM_LINES * 0, Lang::getText("[INSTRUCTIONS] 07"), SHADOW_TEXT_COLOR); text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 0), Lang::getText("[INSTRUCTIONS] 07"), Colors::SHADOW_TEXT);
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + SPACE_BETWEEN_ITEM_LINES * 1, Lang::getText("[INSTRUCTIONS] 08"), SHADOW_TEXT_COLOR); text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 1), Lang::getText("[INSTRUCTIONS] 08"), Colors::SHADOW_TEXT);
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + SPACE_BETWEEN_ITEM_LINES * 2, Lang::getText("[INSTRUCTIONS] 09"), SHADOW_TEXT_COLOR); text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 2), Lang::getText("[INSTRUCTIONS] 09"), Colors::SHADOW_TEXT);
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + SPACE_BETWEEN_ITEM_LINES * 3, Lang::getText("[INSTRUCTIONS] 10"), SHADOW_TEXT_COLOR); text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 3), Lang::getText("[INSTRUCTIONS] 10"), Colors::SHADOW_TEXT);
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + SPACE_BETWEEN_ITEM_LINES * 4, Lang::getText("[INSTRUCTIONS] 11"), SHADOW_TEXT_COLOR); text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 4), Lang::getText("[INSTRUCTIONS] 11"), Colors::SHADOW_TEXT);
// Deja el renderizador como estaba // Deja el renderizador como estaba
SDL_SetRenderTarget(renderer_, temp); SDL_SetRenderTarget(renderer_, temp);

View File

@@ -25,8 +25,8 @@ class TiledBG;
por la pantalla sobre el mosaico de fondo (gestionado por el correspondiente objeto). por la pantalla sobre el mosaico de fondo (gestionado por el correspondiente objeto).
*/ */
// Estructura para almacenar información de línea animada // --- Estructuras ---
struct Line { struct Line { // Almacena información de línea animada
int y; // Coordenada Y de la línea int y; // Coordenada Y de la línea
float x; // Coordenada X inicial (usamos float para mayor precisión en el suavizado) float x; // Coordenada X inicial (usamos float para mayor precisión en el suavizado)
int direction; // Dirección de movimiento: -1 para izquierda, 1 para derecha int direction; // Dirección de movimiento: -1 para izquierda, 1 para derecha

View File

@@ -326,7 +326,7 @@ void Intro::initSprites() {
card_sprites_.push_back(std::move(sprite)); card_sprites_.push_back(std::move(sprite));
} }
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 - 10, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeInOutExpo, 0);

View File

@@ -11,12 +11,9 @@
#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 ---
Esta clase gestiona un estado del programa. Se encarga de mostrar la secuencia // Esta clase gestiona un estado del programa. Se encarga de mostrar la secuencia
de introducción. // de introducción.
*/
// Clase Intro
class Intro { class Intro {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---

View File

@@ -10,15 +10,12 @@
class Texture; class Texture;
/* // --- Clase Logo: dibuja el logo de JAILGAMES con efectos visuales ---
Esta clase gestiona un estado del programa. Se encarga de dibujar por pantalla el // 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 // logo de "JAILGAMES" utilizando un sencillo efecto consistente en generar un sprite por
cada línea del bitmap que forma la palabra "JAILGAMES". Posteriormente realiza una // cada línea del bitmap que forma la palabra "JAILGAMES". Posteriormente realiza una
modulación de color sobre la textura para simular un fade to black al estilo // modulación de color sobre la textura para simular un fade to black al estilo
ZX Spectrum. // ZX Spectrum.
*/
// --- Clase Logo ---
class Logo { class Logo {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---

View File

@@ -7,7 +7,7 @@
#include <vector> // Para vector #include <vector> // Para vector
#include "audio.h" // Para Audio #include "audio.h" // Para Audio
#include "color.h" // Para NO_TEXT_COLOR, TITLE_SHADOW_TEXT_COLOR #include "color.h" // Para Colors::NO_COLOR_MOD, Colors::TITLE_SHADOW_TEXT
#include "fade.h" // Para Fade, FadeType #include "fade.h" // Para Fade, FadeType
#include "game_logo.h" // Para GameLogo #include "game_logo.h" // Para GameLogo
#include "global_events.h" // Para check #include "global_events.h" // Para check
@@ -37,20 +37,14 @@ class Texture;
// Constructor // Constructor
Title::Title() Title::Title()
: text_(Resource::get()->getText("smb2_grad")), : text_(Resource::get()->getText("smb2_grad")), fade_(std::make_unique<Fade>()), 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)), mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))), state_(TitleState::LOGO_ANIMATING), num_controllers_(Input::get()->getNumGamepads()) {
fade_(std::make_unique<Fade>()),
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)),
mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))),
num_controllers_(Input::get()->getNumGamepads()),
state_(TitleState::LOGO_ANIMATING) {
// Configura objetos // Configura objetos
tiled_bg_->setColor(param.title.bg_color); tiled_bg_->setColor(param.title.bg_color);
game_logo_->enable(); game_logo_->enable();
mini_logo_sprite_->setX(param.game.game_area.center_x - mini_logo_sprite_->getWidth() / 2); mini_logo_sprite_->setX(param.game.game_area.center_x - (mini_logo_sprite_->getWidth() / 2));
fade_->setColor(param.fade.color); fade_->setColor(param.fade.color);
fade_->setType(FadeType::RANDOM_SQUARE); fade_->setType(Fade::Type::RANDOM_SQUARE2);
fade_->setPostDuration(param.fade.post_duration); fade_->setPostDuration(param.fade.post_duration_ms);
initPlayers(); initPlayers();
// Asigna valores a otras variables // Asigna valores a otras variables
@@ -201,7 +195,7 @@ void Title::printColorValue(const Color& color) {
<< std::hex << std::setw(2) << std::setfill('0') << (int)color.r << std::hex << std::setw(2) << std::setfill('0') << (int)color.r
<< std::setw(2) << std::setfill('0') << (int)color.g << std::setw(2) << std::setfill('0') << (int)color.g
<< std::setw(2) << std::setfill('0') << (int)color.b << std::setw(2) << std::setfill('0') << (int)color.b
<< std::endl; << '\n';
} }
#endif #endif
@@ -438,9 +432,9 @@ void Title::renderStartPrompt() {
param.title.press_start_position, param.title.press_start_position,
Lang::getText("[TITLE] PRESS_BUTTON_TO_PLAY"), Lang::getText("[TITLE] PRESS_BUTTON_TO_PLAY"),
1, 1,
NO_TEXT_COLOR, Colors::NO_COLOR_MOD,
1, 1,
TITLE_SHADOW_TEXT_COLOR); Colors::TITLE_SHADOW_TEXT);
} }
} }
@@ -455,9 +449,9 @@ void Title::renderCopyright() {
anchor_.copyright_text, anchor_.copyright_text,
std::string(TEXT_COPYRIGHT), std::string(TEXT_COPYRIGHT),
1, 1,
NO_TEXT_COLOR, Colors::NO_COLOR_MOD,
1, 1,
TITLE_SHADOW_TEXT_COLOR); Colors::TITLE_SHADOW_TEXT);
} }
} }
@@ -538,7 +532,7 @@ void Title::initPlayers() {
players_.back()->setPlayingState(Player::State::TITLE_HIDDEN); players_.back()->setPlayingState(Player::State::TITLE_HIDDEN);
// Registra los jugadores en Options // Registra los jugadores en Options
for (auto player : players_) { for (const auto& player : players_) {
Options::keyboard.addPlayer(player); Options::keyboard.addPlayer(player);
Options::gamepad_manager.addPlayer(player); Options::gamepad_manager.addPlayer(player);
} }
@@ -560,7 +554,7 @@ void Title::renderPlayers() {
// Obtiene un jugador a partir de su "id" // Obtiene un jugador a partir de su "id"
auto Title::getPlayer(Player::Id id) -> std::shared_ptr<Player> { auto Title::getPlayer(Player::Id id) -> std::shared_ptr<Player> {
auto it = std::find_if(players_.begin(), players_.end(), [id](const auto& player) { return player->getId() == id; }); auto it = std::ranges::find_if(players_, [id](const auto& player) { return player->getId() == id; });
if (it != players_.end()) { if (it != players_.end()) {
return *it; return *it;

View File

@@ -19,19 +19,11 @@ namespace Options {
struct Gamepad; struct Gamepad;
} // namespace Options } // namespace Options
// Textos // --- Constantes ---
constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner"; 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
// Parámetros // --- Clase Title: gestiona el estado de título/menú principal del juego ---
constexpr bool ALLOW_TITLE_ANIMATION_SKIP = false;
/*
Clase que gestiona el estado de título/menú principal del juego.
Responsable de mostrar el logo, el fondo animado y gestionar la entrada para comenzar la partida.
No permite saltar la animación del título salvo que se cambie el define.
*/
// Clase Title
class Title { class Title {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
@@ -42,17 +34,17 @@ class Title {
void run(); void run();
private: private:
// --- Enumeraciones --- // --- Enums ---
enum class TitleState { enum class TitleState {
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
}; };
// --- Estructura para definir anclas --- // --- Estructuras privadas ---
struct Anchor { struct Anchor {
int mini_logo; int mini_logo; // Ancla del logo mini
int copyright_text; int copyright_text; // Ancla del texto de copyright
}; };
// --- Objetos y punteros --- // --- Objetos y punteros ---
@@ -64,18 +56,16 @@ class Title {
std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores
// --- Variables de estado --- // --- Variables de estado ---
int counter_ = 0; // Temporizador para la pantalla de título Anchor anchor_; // Anclas para definir la posición de los elementos del título
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad
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
int num_controllers_; // Número de mandos conectados
TitleState state_; // Estado actual de la sección TitleState state_; // Estado actual de la sección
bool should_render_start_prompt_ = false; // Indica si se muestra o no el texto de PRESS START BUTTON TO PLAY Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad
bool player1_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 1 int counter_ = 0; // Temporizador para la pantalla de título
bool player2_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 2 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
// -- Variables de diseño --- bool player1_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 1
Anchor anchor_; // Anclas para definir la posición de los elementos del titulo 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(); // Actualiza las variables del objeto

View File

@@ -1,155 +1,157 @@
#include "shutdown.h" #include "shutdown.h"
#include <array>
#include <iostream> #include <iostream>
#include <vector>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#else #else
#include <unistd.h> #include <sys/wait.h>
#include <sys/wait.h> #include <unistd.h>
#include <string>
#include <string>
#endif #endif
namespace SystemShutdown { namespace SystemShutdown {
#ifndef _WIN32 #ifndef _WIN32
// Función auxiliar para sistemas Unix-like // Función auxiliar para sistemas Unix-like
ShutdownResult executeUnixShutdown(const char* command, char* const args[]) { auto executeUnixShutdown(const char* command, const std::vector<char*>& args) -> ShutdownResult {
pid_t pid = fork(); pid_t pid = fork();
if (pid == 0) { if (pid == 0) {
// Proceso hijo // Proceso hijo
execvp(command, args); execvp(command, args.data());
// Si llegamos aquí, execvp falló // Si llegamos aquí, execvp falló
std::cerr << "Error: No se pudo ejecutar " << command << std::endl; std::cerr << "Error: No se pudo ejecutar " << command << '\n';
_exit(1); _exit(1);
} else if (pid > 0) { } else if (pid > 0) {
// Proceso padre // Proceso padre
int status; int status;
waitpid(pid, &status, 0); waitpid(pid, &status, 0);
return (WEXITSTATUS(status) == 0) ? ShutdownResult::SUCCESS : ShutdownResult::ERROR_SYSTEM_CALL; return (WEXITSTATUS(status) == 0) ? ShutdownResult::SUCCESS : ShutdownResult::ERROR_SYSTEM_CALL;
} else { } else {
return ShutdownResult::ERROR_FORK_FAILED; return ShutdownResult::ERROR_FORK_FAILED;
}
} }
}
#endif #endif
// Implementación de las funciones públicas // Implementación de las funciones públicas
ShutdownResult shutdownSystem() { auto shutdownSystem() -> ShutdownResult {
ShutdownConfig config; ShutdownConfig config;
return shutdownSystem(config); return shutdownSystem(config);
} }
ShutdownResult shutdownSystem(int delay_seconds, bool force_apps) { auto shutdownSystem(int delay_seconds, bool force_apps) -> ShutdownResult {
ShutdownConfig config; ShutdownConfig config;
config.delay_seconds = delay_seconds; config.delay_seconds = delay_seconds;
config.force_close_apps = force_apps; config.force_close_apps = force_apps;
return shutdownSystem(config); return shutdownSystem(config);
} }
ShutdownResult shutdownSystem(const ShutdownConfig& config) { auto shutdownSystem(const ShutdownConfig& config) -> ShutdownResult {
#ifdef _WIN32 #ifdef _WIN32
// Windows: Usar CreateProcess // Windows: Usar CreateProcess
STARTUPINFOA si = {0}; STARTUPINFOA si = {0};
PROCESS_INFORMATION pi = {0}; PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si); si.cb = sizeof(si);
// Crear comando con el delay especificado // Crear comando con el delay especificado
std::string command = "shutdown.exe /s /t " + std::to_string(config.delay_seconds); std::string command = "shutdown.exe /s /t " + std::to_string(config.delay_seconds);
if (config.force_close_apps) { if (config.force_close_apps) {
command += " /f"; command += " /f";
} }
// CreateProcess necesita un array de char modificable // CreateProcess necesita un array de char modificable
char* cmd_buffer = new char[command.length() + 1]; char* cmd_buffer = new char[command.length() + 1];
strcpy(cmd_buffer, command.c_str()); strcpy(cmd_buffer, command.c_str());
bool success = CreateProcessA( bool success = CreateProcessA(
NULL, // lpApplicationName NULL, // lpApplicationName
cmd_buffer, // lpCommandLine cmd_buffer, // lpCommandLine
NULL, // lpProcessAttributes NULL, // lpProcessAttributes
NULL, // lpThreadAttributes NULL, // lpThreadAttributes
FALSE, // bInheritHandles FALSE, // bInheritHandles
0, // dwCreationFlags 0, // dwCreationFlags
NULL, // lpEnvironment NULL, // lpEnvironment
NULL, // lpCurrentDirectory NULL, // lpCurrentDirectory
&si, // lpStartupInfo &si, // lpStartupInfo
&pi // lpProcessInformation &pi // lpProcessInformation
); );
delete[] cmd_buffer; delete[] cmd_buffer;
if (success) { if (success) {
CloseHandle(pi.hProcess); CloseHandle(pi.hProcess);
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
return ShutdownResult::SUCCESS; return ShutdownResult::SUCCESS;
} else { } else {
DWORD error = GetLastError(); DWORD error = GetLastError();
if (error == ERROR_ACCESS_DENIED) { if (error == ERROR_ACCESS_DENIED) {
return ShutdownResult::ERROR_PERMISSION; return ShutdownResult::ERROR_PERMISSION;
}
return ShutdownResult::ERROR_SYSTEM_CALL;
} }
return ShutdownResult::ERROR_SYSTEM_CALL;
}
#elif __APPLE__ #elif __APPLE__
// macOS - apagado inmediato // macOS - apagado inmediato
char* args[] = { std::vector<char*> args = {
const_cast<char*>("shutdown"), const_cast<char*>("shutdown"),
const_cast<char*>("-h"), const_cast<char*>("-h"),
const_cast<char*>("now"), const_cast<char*>("now"),
NULL nullptr};
};
return executeUnixShutdown("shutdown", args);
return executeUnixShutdown("shutdown", args);
#elif __linux__ #elif __linux__
// Linux - apagado inmediato // Linux - apagado inmediato
char* args[] = { std::vector<char*> args = {
const_cast<char*>("shutdown"), const_cast<char*>("shutdown"),
const_cast<char*>("-h"), const_cast<char*>("-h"),
const_cast<char*>("now"), const_cast<char*>("now"),
NULL nullptr};
};
return executeUnixShutdown("shutdown", args);
return executeUnixShutdown("shutdown", args);
#else #else
return ShutdownResult::ERROR_UNSUPPORTED; return ShutdownResult::ERROR_UNSUPPORTED;
#endif #endif
} }
const char* resultToString(ShutdownResult result) { auto resultToString(ShutdownResult result) -> const char* {
switch (result) { switch (result) {
case ShutdownResult::SUCCESS: case ShutdownResult::SUCCESS:
return "Apagado iniciado exitosamente"; return "Apagado iniciado exitosamente";
case ShutdownResult::ERROR_PERMISSION: case ShutdownResult::ERROR_PERMISSION:
return "Error: Permisos insuficientes"; return "Error: Permisos insuficientes";
case ShutdownResult::ERROR_SYSTEM_CALL: case ShutdownResult::ERROR_SYSTEM_CALL:
return "Error: Fallo en la llamada al sistema"; return "Error: Fallo en la llamada al sistema";
case ShutdownResult::ERROR_FORK_FAILED: case ShutdownResult::ERROR_FORK_FAILED:
return "Error: No se pudo crear proceso hijo"; return "Error: No se pudo crear proceso hijo";
case ShutdownResult::ERROR_UNSUPPORTED: case ShutdownResult::ERROR_UNSUPPORTED:
return "Error: Sistema operativo no soportado"; return "Error: Sistema operativo no soportado";
default: default:
return "Error desconocido"; return "Error desconocido";
}
} }
}
bool isShutdownSupported() { auto isShutdownSupported() -> bool {
#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__) #if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
return true; return true;
#else #else
return false; return false;
#endif #endif
} }
const char* getRequiredPermissions() { auto getRequiredPermissions() -> const char* {
#ifdef _WIN32 #ifdef _WIN32
return "Requiere permisos de Administrador en Windows"; return "Requiere permisos de Administrador en Windows";
#elif defined(__APPLE__) || defined(__linux__) #elif defined(__APPLE__) || defined(__linux__)
return "Requiere permisos de root/sudo en Unix"; return "Requiere permisos de root/sudo en Unix";
#else #else
return "Sistema no soportado"; return "Sistema no soportado";
#endif #endif
} }
} // namespace SystemShutdown } // namespace SystemShutdown

View File

@@ -1,47 +1,33 @@
#pragma once #pragma once
// Utilidad multiplataforma para apagar el sistema de forma segura // --- Namespace SystemShutdown: utilidad multiplataforma para apagar el sistema de forma segura ---
namespace SystemShutdown { namespace SystemShutdown {
// Códigos de resultado para las operaciones de apagado // --- Enums ---
enum class ShutdownResult { enum class ShutdownResult {
SUCCESS = 0, SUCCESS = 0, // Éxito
ERROR_PERMISSION, // Error de permisos insuficientes ERROR_PERMISSION, // Error de permisos insuficientes
ERROR_SYSTEM_CALL, // Error en la llamada al sistema ERROR_SYSTEM_CALL, // Error en la llamada al sistema
ERROR_FORK_FAILED, // Error al crear proceso hijo (Unix) ERROR_FORK_FAILED, // Error al crear proceso hijo (Unix)
ERROR_UNSUPPORTED // Sistema operativo no soportado ERROR_UNSUPPORTED // Sistema operativo no soportado
}; };
// --- Estructuras ---
struct ShutdownConfig {
int delay_seconds{5}; // Segundos de retraso antes del apagado
bool force_close_apps{true}; // Forzar cierre de aplicaciones
const char* shutdown_message{"El sistema se apagará..."}; // Mensaje mostrado durante el apagado
// Configuración para el apagado del sistema
struct ShutdownConfig {
int delay_seconds;
bool force_close_apps;
const char* shutdown_message;
// Constructor con valores por defecto // Constructor con valores por defecto
ShutdownConfig() ShutdownConfig() = default;
: delay_seconds(5) };
, force_close_apps(true)
, shutdown_message("El sistema se apagará...")
{}
};
// Apaga el sistema con configuración por defecto // --- Funciones ---
ShutdownResult shutdownSystem(); auto shutdownSystem() -> ShutdownResult; // Apaga el sistema con configuración por defecto
auto shutdownSystem(const ShutdownConfig& config) -> ShutdownResult; // Apaga el sistema con configuración personalizada
auto shutdownSystem(int delay_seconds, bool force_apps = true) -> ShutdownResult; // Apaga el sistema con parámetros simples
auto resultToString(ShutdownResult result) -> const char*; // Convierte un código de resultado a string descriptivo
auto isShutdownSupported() -> bool; // Verifica si el sistema actual soporta apagado programático
auto getRequiredPermissions() -> const char*; // Obtiene información sobre los permisos necesarios
// Apaga el sistema con configuración personalizada } // namespace SystemShutdown
ShutdownResult shutdownSystem(const ShutdownConfig& config);
// Apaga el sistema con parámetros simples
ShutdownResult shutdownSystem(int delay_seconds, bool force_apps = true);
// Convierte un código de resultado a string descriptivo
const char* resultToString(ShutdownResult result);
// Verifica si el sistema actual soporta apagado programático
bool isShutdownSupported();
// Obtiene información sobre los permisos necesarios en la plataforma actual
const char* getRequiredPermissions();
} // namespace SystemShutdown

View File

@@ -1,17 +1,18 @@
#pragma once #pragma once
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <utility>
#include "animated_sprite.h" // Para AnimatedSprite #include "animated_sprite.h" // Para AnimatedSprite
class Texture; class Texture;
// Clase SmartSprite: Sprite animado que se mueve hacia un destino y puede deshabilitarse automáticamente // --- Clase SmartSprite: sprite animado que se mueve hacia un destino y puede deshabilitarse automáticamente ---
class SmartSprite : public AnimatedSprite { class SmartSprite : public AnimatedSprite {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
explicit SmartSprite(std::shared_ptr<Texture> texture) explicit SmartSprite(std::shared_ptr<Texture> texture)
: AnimatedSprite(texture) {} : AnimatedSprite(std::move(texture)) {}
~SmartSprite() override = default; ~SmartSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
@@ -31,11 +32,11 @@ class SmartSprite : public AnimatedSprite {
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 internas --- // --- Variables de estado ---
bool on_destination_ = false; // Indica si está en el destino
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 int finished_counter_ = 0; // Contador para deshabilitarlo
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

View File

@@ -1,22 +1,24 @@
#include "sprite.h" #include "sprite.h"
#include <utility>
#include <vector> // Para vector #include <vector> // Para vector
#include "texture.h" // Para Texture #include "texture.h" // Para Texture
// Constructor // Constructor
Sprite::Sprite(std::shared_ptr<Texture> texture, float pos_x, float pos_y, float width, float height) Sprite::Sprite(std::shared_ptr<Texture> texture, float pos_x, float pos_y, float width, float height)
: textures_{texture}, : textures_{std::move(texture)},
pos_((SDL_FRect){pos_x, pos_y, width, height}), pos_((SDL_FRect){pos_x, pos_y, width, height}),
sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {} sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {}
Sprite::Sprite(std::shared_ptr<Texture> texture, SDL_FRect rect) Sprite::Sprite(std::shared_ptr<Texture> texture, SDL_FRect rect)
: textures_{texture}, : textures_{std::move(texture)},
pos_(rect), pos_(rect),
sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {} sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {}
Sprite::Sprite(std::shared_ptr<Texture> texture) Sprite::Sprite(std::shared_ptr<Texture> texture)
: textures_{texture}, : textures_{std::move(texture)},
pos_(SDL_FRect{0, 0, static_cast<float>(textures_.at(texture_index_)->getWidth()), static_cast<float>(textures_.at(texture_index_)->getHeight())}), pos_(SDL_FRect{0, 0, static_cast<float>(textures_.at(texture_index_)->getWidth()), static_cast<float>(textures_.at(texture_index_)->getHeight())}),
sprite_clip_(pos_) {} sprite_clip_(pos_) {}
@@ -39,8 +41,8 @@ void Sprite::setPosition(SDL_FPoint point) {
// Reinicia las variables a cero // Reinicia las variables a cero
void Sprite::clear() { void Sprite::clear() {
pos_ = {0, 0, 0, 0}; pos_ = {.x = 0, .y = 0, .w = 0, .h = 0};
sprite_clip_ = {0, 0, 0, 0}; sprite_clip_ = {.x = 0, .y = 0, .w = 0, .h = 0};
} }
// Cambia la textura activa por índice // Cambia la textura activa por índice

View File

@@ -4,11 +4,12 @@
#include <cstddef> // Para size_t #include <cstddef> // Para size_t
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <vector> // Para vector #include <utility>
#include <vector> // Para vector
class Texture; class Texture;
// Clase Sprite: representa un objeto gráfico básico con posición, tamaño y textura // --- Clase Sprite: representa un objeto gráfico básico con posición, tamaño y textura ---
class Sprite { class Sprite {
public: public:
// --- Constructores y destructor --- // --- Constructores y destructor ---
@@ -52,20 +53,19 @@ class Sprite {
// --- Textura --- // --- Textura ---
[[nodiscard]] auto getTexture() const -> std::shared_ptr<Texture> { return textures_.at(texture_index_); } [[nodiscard]] auto getTexture() const -> std::shared_ptr<Texture> { return textures_.at(texture_index_); }
void setTexture(std::shared_ptr<Texture> texture) { textures_.at(texture_index_) = texture; } void setTexture(std::shared_ptr<Texture> texture) { textures_.at(texture_index_) = std::move(texture); }
void addTexture(std::shared_ptr<Texture> texture) { textures_.push_back(texture); } void addTexture(const std::shared_ptr<Texture>& texture) { textures_.push_back(texture); }
auto setActiveTexture(size_t index) -> bool; // Cambia la textura activa por índice auto setActiveTexture(size_t index) -> bool; // Cambia la textura activa por índice
[[nodiscard]] auto getActiveTextureIndex() const -> size_t { return texture_index_; } // Obtiene el índice de la textura activa [[nodiscard]] auto getActiveTexture() const -> size_t { return texture_index_; } // Alias para getActiveTextureIndex
[[nodiscard]] auto getTextureCount() const -> size_t { return textures_.size(); } // Obtiene el número total de texturas [[nodiscard]] auto getTextureCount() const -> size_t { return textures_.size(); } // Obtiene el número total de texturas
protected: protected:
auto getTextureRef() -> std::shared_ptr<Texture>& { // --- Métodos internos ---
return textures_.at(texture_index_); auto getTextureRef() -> std::shared_ptr<Texture>& { return textures_.at(texture_index_); } // Obtiene referencia a la textura activa
}
// --- Variables internas --- // --- Variables internas ---
size_t texture_index_ = 0;
std::vector<std::shared_ptr<Texture>> textures_; // Lista de texturas std::vector<std::shared_ptr<Texture>> textures_; // Lista de texturas
size_t texture_index_ = 0; // Índice de la textura activa
SDL_FRect pos_; // Posición y tamaño donde dibujar el sprite SDL_FRect pos_; // Posición y tamaño donde dibujar el sprite
SDL_FRect sprite_clip_; // Rectángulo de origen de la textura que se dibujará en pantalla SDL_FRect sprite_clip_; // Rectángulo de origen de la textura que se dibujará en pantalla
double zoom_ = 1.0F; // Zoom aplicado a la textura double zoom_ = 1.0F; // Zoom aplicado a la textura

View File

@@ -1,20 +1,25 @@
#include "stage.h" #include "stage.h"
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <utility>
// Implementación de StageData // Implementación de StageData
StageData::StageData(int power_to_complete, int min_menace, int max_menace, const std::string& name) StageData::StageData(int power_to_complete, int min_menace, int max_menace, std::string name)
: power_to_complete_(power_to_complete), min_menace_(min_menace), : status_(StageStatus::LOCKED),
max_menace_(max_menace), name_(name), status_(StageStatus::LOCKED) {} name_(std::move(name)),
power_to_complete_(power_to_complete),
min_menace_(min_menace),
max_menace_(max_menace) {}
// Implementación de StageManager // Implementación de StageManager
StageManager::StageManager() StageManager::StageManager()
: current_power_(0), total_power_(0), current_stage_index_(0), : power_change_callback_(nullptr),
power_collection_state_(PowerCollectionState::ENABLED), power_collection_state_(PowerCollectionState::ENABLED),
power_change_callback_(nullptr) { current_stage_index_(0),
initialize(); current_power_(0),
} total_power_(0) { initialize(); }
void StageManager::initialize() { void StageManager::initialize() {
stages_.clear(); stages_.clear();
@@ -24,12 +29,12 @@ void StageManager::initialize() {
void StageManager::initialize(const std::string& stages_file) { void StageManager::initialize(const std::string& stages_file) {
stages_.clear(); stages_.clear();
// Intentar cargar desde archivo, si falla usar valores predeterminados // Intentar cargar desde archivo, si falla usar valores predeterminados
if (!loadStagesFromFile(stages_file)) { if (!loadStagesFromFile(stages_file)) {
createDefaultStages(); createDefaultStages();
} }
reset(); reset();
} }
@@ -55,25 +60,25 @@ void StageManager::createDefaultStages() {
stages_.emplace_back(950, 7 + (4 * 7), 7 + (4 * 10), "Maestría"); stages_.emplace_back(950, 7 + (4 * 7), 7 + (4 * 10), "Maestría");
} }
bool StageManager::loadStagesFromFile(const std::string& filename) { auto StageManager::loadStagesFromFile(const std::string& filename) -> bool {
std::ifstream file(filename); std::ifstream file(filename);
if (!file.is_open()) { if (!file.is_open()) {
return false; // No se pudo abrir el archivo return false; // No se pudo abrir el archivo
} }
std::string line; std::string line;
while (std::getline(file, line)) { while (std::getline(file, line)) {
// Ignorar líneas vacías y comentarios (líneas que empiezan con #) // Ignorar líneas vacías y comentarios (líneas que empiezan con #)
if (line.empty() || line[0] == '#') { if (line.empty() || line[0] == '#') {
continue; continue;
} }
// Parsear línea: power_to_complete,min_menace,max_menace,name // Parsear línea: power_to_complete,min_menace,max_menace,name
std::stringstream ss(line); std::stringstream ss(line);
std::string token; std::string token;
std::vector<std::string> tokens; std::vector<std::string> tokens;
// Dividir por comas // Dividir por comas
while (std::getline(ss, token, ',')) { while (std::getline(ss, token, ',')) {
// Eliminar espacios en blanco al inicio y final // Eliminar espacios en blanco al inicio y final
@@ -81,76 +86,76 @@ bool StageManager::loadStagesFromFile(const std::string& filename) {
token.erase(token.find_last_not_of(" \t") + 1); token.erase(token.find_last_not_of(" \t") + 1);
tokens.push_back(token); tokens.push_back(token);
} }
// Verificar que tenemos exactamente 4 campos // Verificar que tenemos exactamente 4 campos
if (tokens.size() != 4) { if (tokens.size() != 4) {
// Error de formato, continuar con la siguiente línea // Error de formato, continuar con la siguiente línea
continue; continue;
} }
try { try {
// Convertir a enteros los primeros tres campos // Convertir a enteros los primeros tres campos
int power_to_complete = std::stoi(tokens[0]); int power_to_complete = std::stoi(tokens[0]);
int min_menace = std::stoi(tokens[1]); int min_menace = std::stoi(tokens[1]);
int max_menace = std::stoi(tokens[2]); int max_menace = std::stoi(tokens[2]);
std::string name = tokens[3]; std::string name = tokens[3];
// Validar valores // Validar valores
if (power_to_complete <= 0 || min_menace < 0 || max_menace < min_menace) { if (power_to_complete <= 0 || min_menace < 0 || max_menace < min_menace) {
continue; // Valores inválidos, saltar línea continue; // Valores inválidos, saltar línea
} }
// Crear y añadir la fase // Crear y añadir la fase
stages_.emplace_back(power_to_complete, min_menace, max_menace, name); stages_.emplace_back(power_to_complete, min_menace, max_menace, name);
} catch (const std::exception&) { } catch (const std::exception&) {
// Error de conversión, continuar con la siguiente línea // Error de conversión, continuar con la siguiente línea
continue; continue;
} }
} }
file.close(); file.close();
// Verificar que se cargó al menos una fase // Verificar que se cargó al menos una fase
return !stages_.empty(); return !stages_.empty();
} }
bool StageManager::advanceToNextStage() { auto StageManager::advanceToNextStage() -> bool {
if (!isCurrentStageCompleted() || current_stage_index_ >= stages_.size() - 1) { if (!isCurrentStageCompleted() || current_stage_index_ >= stages_.size() - 1) {
return false; return false;
} }
current_stage_index_++; current_stage_index_++;
current_power_ = 0; // Reiniciar poder para la nueva fase current_power_ = 0; // Reiniciar poder para la nueva fase
updateStageStatuses(); updateStageStatuses();
return true; return true;
} }
bool StageManager::jumpToStage(size_t target_stage_index) { auto StageManager::jumpToStage(size_t target_stage_index) -> bool {
if (!validateStageIndex(target_stage_index)) { if (!validateStageIndex(target_stage_index)) {
return false; return false;
} }
// Calcular el poder acumulado hasta la fase objetivo // Calcular el poder acumulado hasta la fase objetivo
int accumulated_power = 0; int accumulated_power = 0;
for (size_t i = 0; i < target_stage_index; ++i) { for (size_t i = 0; i < target_stage_index; ++i) {
accumulated_power += stages_[i].getPowerToComplete(); accumulated_power += stages_[i].getPowerToComplete();
} }
// Actualizar estado // Actualizar estado
current_stage_index_ = target_stage_index; current_stage_index_ = target_stage_index;
current_power_ = 0; // Comenzar la fase objetivo sin poder current_power_ = 0; // Comenzar la fase objetivo sin poder
total_power_ = accumulated_power; // Poder total como si se hubieran completado las anteriores total_power_ = accumulated_power; // Poder total como si se hubieran completado las anteriores
updateStageStatuses(); updateStageStatuses();
return true; return true;
} }
bool StageManager::subtractPower(int amount) { auto StageManager::subtractPower(int amount) -> bool {
if (amount <= 0 || current_power_ < amount) { if (amount <= 0 || current_power_ < amount) {
return false; return false;
} }
current_power_ -= amount; current_power_ -= amount;
updateStageStatuses(); updateStageStatuses();
return true; return true;
@@ -164,69 +169,73 @@ void StageManager::disablePowerCollection() {
power_collection_state_ = PowerCollectionState::DISABLED; power_collection_state_ = PowerCollectionState::DISABLED;
} }
std::optional<StageData> StageManager::getCurrentStage() const { auto StageManager::getCurrentStage() const -> std::optional<StageData> {
return getStage(current_stage_index_); return getStage(current_stage_index_);
} }
std::optional<StageData> StageManager::getStage(size_t index) const { auto StageManager::getStage(size_t index) const -> std::optional<StageData> {
if (!validateStageIndex(index)) { if (!validateStageIndex(index)) {
return std::nullopt; return std::nullopt;
} }
return stages_[index]; return stages_[index];
} }
bool StageManager::isCurrentStageCompleted() const { auto StageManager::isCurrentStageCompleted() const -> bool {
auto current_stage = getCurrentStage(); auto current_stage = getCurrentStage();
if (!current_stage.has_value()) { if (!current_stage.has_value()) {
return false; return false;
} }
return current_power_ >= current_stage->getPowerToComplete(); return current_power_ >= current_stage->getPowerToComplete();
} }
bool StageManager::isGameCompleted() const { auto StageManager::isGameCompleted() const -> bool {
return current_stage_index_ >= stages_.size() - 1 && isCurrentStageCompleted(); return current_stage_index_ >= stages_.size() - 1 && isCurrentStageCompleted();
} }
double StageManager::getProgressPercentage() const { auto StageManager::getProgressPercentage() const -> double {
if (stages_.empty()) return 0.0; if (stages_.empty()) {
return 0.0;
}
int total_power_needed = getTotalPowerNeededToCompleteGame(); int total_power_needed = getTotalPowerNeededToCompleteGame();
if (total_power_needed == 0) return 100.0; if (total_power_needed == 0) {
return 100.0;
}
return (static_cast<double>(total_power_) / total_power_needed) * 100.0; return (static_cast<double>(total_power_) / total_power_needed) * 100.0;
} }
double StageManager::getCurrentStageProgressPercentage() const { auto StageManager::getCurrentStageProgressPercentage() const -> double {
return getCurrentStageProgressFraction() * 100.0; return getCurrentStageProgressFraction() * 100.0;
} }
double StageManager::getCurrentStageProgressFraction() const { auto StageManager::getCurrentStageProgressFraction() const -> double {
auto current_stage = getCurrentStage(); auto current_stage = getCurrentStage();
if (!current_stage.has_value()) { if (!current_stage.has_value()) {
return 0.0; return 0.0;
} }
int power_needed = current_stage->getPowerToComplete(); int power_needed = current_stage->getPowerToComplete();
if (power_needed == 0) { if (power_needed == 0) {
return 1.0; return 1.0;
} }
// Devuelve una fracción entre 0.0 y 1.0 // Devuelve una fracción entre 0.0 y 1.0
double fraction = static_cast<double>(current_power_) / power_needed; double fraction = static_cast<double>(current_power_) / power_needed;
return std::min(fraction, 1.0); return std::min(fraction, 1.0);
} }
int StageManager::getPowerNeededForCurrentStage() const { auto StageManager::getPowerNeededForCurrentStage() const -> int {
auto current_stage = getCurrentStage(); auto current_stage = getCurrentStage();
if (!current_stage.has_value()) { if (!current_stage.has_value()) {
return 0; return 0;
} }
return std::max(0, current_stage->getPowerToComplete() - current_power_); return std::max(0, current_stage->getPowerToComplete() - current_power_);
} }
int StageManager::getTotalPowerNeededToCompleteGame() const { auto StageManager::getTotalPowerNeededToCompleteGame() const -> int {
int total_power_needed = 0; int total_power_needed = 0;
for (const auto& stage : stages_) { for (const auto& stage : stages_) {
total_power_needed += stage.getPowerToComplete(); total_power_needed += stage.getPowerToComplete();
@@ -234,8 +243,20 @@ int StageManager::getTotalPowerNeededToCompleteGame() const {
return total_power_needed; return total_power_needed;
} }
auto StageManager::getPowerNeededToReachStage(size_t target_stage_index) const -> int {
if (!validateStageIndex(target_stage_index)) {
return 0;
}
int power_needed = 0;
for (size_t i = 0; i < target_stage_index; ++i) {
power_needed += stages_[i].getPowerToComplete();
}
return power_needed;
}
// Implementación de la interfaz IStageInfo // Implementación de la interfaz IStageInfo
bool StageManager::canCollectPower() const { auto StageManager::canCollectPower() const -> bool {
return power_collection_state_ == PowerCollectionState::ENABLED; return power_collection_state_ == PowerCollectionState::ENABLED;
} }
@@ -243,7 +264,7 @@ void StageManager::addPower(int amount) {
if (amount <= 0 || !canCollectPower()) { if (amount <= 0 || !canCollectPower()) {
return; return;
} }
current_power_ += amount; current_power_ += amount;
total_power_ += amount; total_power_ += amount;
@@ -251,7 +272,7 @@ void StageManager::addPower(int amount) {
if (power_change_callback_) { if (power_change_callback_) {
power_change_callback_(amount); power_change_callback_(amount);
} }
// Verificar si se completó la fase actual // Verificar si se completó la fase actual
if (isCurrentStageCompleted()) { if (isCurrentStageCompleted()) {
auto current_stage = getCurrentStage(); auto current_stage = getCurrentStage();
@@ -259,22 +280,22 @@ void StageManager::addPower(int amount) {
stages_[current_stage_index_].setStatus(StageStatus::COMPLETED); stages_[current_stage_index_].setStatus(StageStatus::COMPLETED);
} }
} }
updateStageStatuses(); updateStageStatuses();
} }
int StageManager::getCurrentMenaceLevel() const { auto StageManager::getCurrentMenaceLevel() const -> int {
auto current_stage = getCurrentStage(); auto current_stage = getCurrentStage();
if (!current_stage.has_value()) { if (!current_stage.has_value()) {
return 0; return 0;
} }
return current_stage->getMinMenace(); return current_stage->getMinMenace();
} }
// Gestión de callbacks // Gestión de callbacks
void StageManager::setPowerChangeCallback(PowerChangeCallback callback) { void StageManager::setPowerChangeCallback(PowerChangeCallback callback) {
power_change_callback_ = callback; power_change_callback_ = std::move(callback);
} }
void StageManager::removePowerChangeCallback() { void StageManager::removePowerChangeCallback() {
@@ -282,7 +303,7 @@ void StageManager::removePowerChangeCallback() {
} }
// Métodos privados // Métodos privados
bool StageManager::validateStageIndex(size_t index) const { auto StageManager::validateStageIndex(size_t index) const -> bool {
return index < stages_.size(); return index < stages_.size();
} }

View File

@@ -1,111 +1,112 @@
#pragma once #pragma once
#include "stage_interface.h" #include <functional> // Para function
#include <vector> #include <optional> // Para optional
#include <optional> #include <string> // Para string
#include <string> #include <vector> // Para vector
#include <functional>
// --- Estados posibles para la recolección de poder --- #include "stage_interface.h" // Para IStageInfo
// --- Enums ---
enum class PowerCollectionState { enum class PowerCollectionState {
ENABLED, // Recolección habilitada ENABLED, // Recolección habilitada
DISABLED // Recolección deshabilitada DISABLED // Recolección deshabilitada
}; };
// --- Estados posibles para una fase del juego ---
enum class StageStatus { enum class StageStatus {
LOCKED, // Fase bloqueada LOCKED, // Fase bloqueada
IN_PROGRESS, // Fase en progreso IN_PROGRESS, // Fase en progreso
COMPLETED // Fase completada COMPLETED // Fase completada
}; };
// --- Representa los datos de una fase del juego --- // --- Clase StageData: representa los datos de una fase del juego ---
class StageData { class StageData {
private: public:
int power_to_complete_; // Poder necesario para completar la fase // --- Constructor ---
int min_menace_; // Nivel mínimo de amenaza StageData(int power_to_complete, int min_menace, int max_menace, std::string name = ""); // Constructor de una fase
int max_menace_; // Nivel máximo de amenaza
std::string name_; // Nombre de la fase
StageStatus status_; // Estado actual de la fase
public: // --- Getters ---
// Constructor de una fase [[nodiscard]] auto getPowerToComplete() const -> int { return power_to_complete_; } // Obtiene el poder necesario para completar
StageData(int power_to_complete, int min_menace, int max_menace, const std::string& name = ""); [[nodiscard]] auto getMinMenace() const -> int { return min_menace_; } // Obtiene el nivel mínimo de amenaza
[[nodiscard]] auto getMaxMenace() const -> int { return max_menace_; } // Obtiene el nivel máximo de amenaza
[[nodiscard]] auto getName() const -> const std::string& { return name_; } // Obtiene el nombre de la fase
[[nodiscard]] auto getStatus() const -> StageStatus { return status_; } // Obtiene el estado actual
[[nodiscard]] auto isCompleted() const -> bool { return status_ == StageStatus::COMPLETED; } // Verifica si está completada
// --- Getters --- // --- Setters ---
int getPowerToComplete() const { return power_to_complete_; } void setStatus(StageStatus status) { status_ = status; } // Establece el estado de la fase
int getMinMenace() const { return min_menace_; }
int getMaxMenace() const { return max_menace_; }
const std::string& getName() const { return name_; }
StageStatus getStatus() const { return status_; }
// --- Setters --- private:
void setStatus(StageStatus status) { status_ = status; } // --- Variables de estado ---
StageStatus status_; // Estado actual de la fase
// --- Utilidades --- std::string name_; // Nombre de la fase
bool isCompleted() const { return status_ == StageStatus::COMPLETED; } int power_to_complete_; // Poder necesario para completar la fase
int min_menace_; // Nivel mínimo de amenaza
int max_menace_; // Nivel máximo de amenaza
}; };
// --- Gestor principal del sistema de fases del juego --- // --- Clase StageManager: gestor principal del sistema de fases del juego ---
class StageManager : public IStageInfo { class StageManager : public IStageInfo {
private: public:
std::vector<StageData> stages_; // Lista de todas las fases // --- Tipos ---
int current_power_; // Poder actual en la fase activa using PowerChangeCallback = std::function<void(int)>; // Callback para cambios de poder
int total_power_; // Poder total acumulado en todo el juego
size_t current_stage_index_; // Índice de la fase actual
PowerCollectionState power_collection_state_; // Estado de recolección de poder
public: // --- Constructor ---
using PowerChangeCallback = std::function<void(int)>; StageManager(); // Constructor principal
StageManager(); // --- Métodos principales del juego ---
void initialize(); // Inicializa el gestor de fases
// --- Métodos principales del juego --- void initialize(const std::string& stages_file); // Inicializa con archivo personalizado
void initialize(); // Inicializa el gestor de fases void reset(); // Reinicia el progreso del juego
void initialize(const std::string& stages_file); // Inicializa con archivo personalizado auto advanceToNextStage() -> bool; // Avanza a la siguiente fase
void reset(); // Reinicia el progreso del juego
bool advanceToNextStage(); // Avanza a la siguiente fase
// --- Gestión de poder ---
bool subtractPower(int amount); // Resta poder de la fase actual
void enablePowerCollection(); // Habilita la recolección de poder
void disablePowerCollection(); // Deshabilita la recolección de poder
// --- Navegación ---
bool jumpToStage(size_t target_stage_index); // Salta a una fase específica
// --- Consultas de estado ---
std::optional<StageData> getCurrentStage() const; // Obtiene la fase actual
std::optional<StageData> getStage(size_t index) const; // Obtiene una fase específica
size_t getCurrentStageIndex() const { return current_stage_index_; }
int getCurrentPower() const { return current_power_; }
int getTotalPower() const { return total_power_; }
int getTotalPowerNeededToCompleteGame() const; // Poder total necesario para completar el juego
size_t getTotalStages() const { return stages_.size(); }
// --- Seguimiento de progreso ---
bool isCurrentStageCompleted() const; // Verifica si la fase actual está completada
bool isGameCompleted() const; // Verifica si el juego está completado
double getProgressPercentage() const; // Progreso total del juego (0-100%)
double getCurrentStageProgressPercentage() const; // Progreso de la fase actual (0-100%)
double getCurrentStageProgressFraction() const; // Progreso de la fase actual (0.0-1.0)
int getPowerNeededForCurrentStage() const; // Poder restante para completar la fase actual
// --- Gestión de callbacks ---
void setPowerChangeCallback(PowerChangeCallback callback); // Establece callback para cambios de poder
void removePowerChangeCallback(); // Elimina callback de cambios de poder
// --- Implementación de la interfaz IStageInfo --- // --- Gestión de poder ---
bool canCollectPower() const override; auto subtractPower(int amount) -> bool; // Resta poder de la fase actual
void addPower(int amount) override; void enablePowerCollection() override; // Habilita la recolección de poder
int getCurrentMenaceLevel() const override; void disablePowerCollection(); // Deshabilita la recolección de poder
private: // --- Navegación ---
PowerChangeCallback power_change_callback_; // Callback para notificar cambios de poder auto jumpToStage(size_t target_stage_index) -> bool; // Salta a una fase específica
// --- Métodos privados --- // --- Consultas de estado ---
void createDefaultStages(); // Crea las fases predeterminadas del juego [[nodiscard]] auto getCurrentStage() const -> std::optional<StageData>; // Obtiene la fase actual
bool loadStagesFromFile(const std::string& filename); // Carga fases desde archivo [[nodiscard]] auto getStage(size_t index) const -> std::optional<StageData>; // Obtiene una fase específica
bool validateStageIndex(size_t index) const; // Valida que un índice de fase sea válido [[nodiscard]] auto getCurrentStageIndex() const -> size_t { return current_stage_index_; } // Obtiene el índice de la fase actual
void updateStageStatuses(); // Actualiza los estados de todas las fases [[nodiscard]] auto getCurrentPower() const -> int { return current_power_; } // Obtiene el poder actual
[[nodiscard]] auto getTotalPower() const -> int { return total_power_; } // Obtiene el poder total acumulado
[[nodiscard]] auto getTotalPowerNeededToCompleteGame() const -> int; // Poder total necesario para completar el juego
[[nodiscard]] auto getPowerNeededToReachStage(size_t target_stage_index) const -> int; // Poder necesario para llegar a la fase X
[[nodiscard]] auto getTotalStages() const -> size_t { return stages_.size(); } // Obtiene el número total de fases
// --- Seguimiento de progreso ---
[[nodiscard]] auto isCurrentStageCompleted() const -> bool; // Verifica si la fase actual está completada
[[nodiscard]] auto isGameCompleted() const -> bool; // Verifica si el juego está completado
[[nodiscard]] auto getProgressPercentage() const -> double; // Progreso total del juego (0-100%)
[[nodiscard]] auto getCurrentStageProgressPercentage() const -> double; // Progreso de la fase actual (0-100%)
[[nodiscard]] auto getCurrentStageProgressFraction() const -> double; // Progreso de la fase actual (0.0-1.0)
[[nodiscard]] auto getPowerNeededForCurrentStage() const -> int; // Poder restante para completar la fase actual
// --- Gestión de callbacks ---
void setPowerChangeCallback(PowerChangeCallback callback); // Establece callback para cambios de poder
void removePowerChangeCallback(); // Elimina callback de cambios de poder
// --- Implementación de la interfaz IStageInfo ---
[[nodiscard]] auto canCollectPower() const -> bool override; // Verifica si se puede recolectar poder
void addPower(int amount) override; // Añade poder a la fase actual
[[nodiscard]] auto getCurrentMenaceLevel() const -> int override; // Obtiene el nivel de amenaza actual
private:
// --- Variables de estado ---
std::vector<StageData> stages_; // Lista de todas las fases
PowerChangeCallback power_change_callback_; // Callback para notificar cambios de poder
PowerCollectionState power_collection_state_; // Estado de recolección de poder
size_t current_stage_index_; // Índice de la fase actual
int current_power_; // Poder actual en la fase activa
int total_power_; // Poder total acumulado en todo el juego
// --- Métodos internos ---
void createDefaultStages(); // Crea las fases predeterminadas del juego
auto loadStagesFromFile(const std::string& filename) -> bool; // Carga fases desde archivo
[[nodiscard]] auto validateStageIndex(size_t index) const -> bool; // Valida que un índice de fase sea válido
void updateStageStatuses(); // Actualiza los estados de todas las fases
}; };

View File

@@ -6,13 +6,14 @@
* sin requerir acceso a toda la funcionalidad de StageManager. * sin requerir acceso a toda la funcionalidad de StageManager.
*/ */
class IStageInfo { class IStageInfo {
public: public:
virtual ~IStageInfo() = default; virtual ~IStageInfo() = default;
// Interfaz de recolección de poder // Interfaz de recolección de poder
virtual bool canCollectPower() const = 0; [[nodiscard]] virtual auto canCollectPower() const -> bool = 0;
virtual void addPower(int amount) = 0; virtual void enablePowerCollection() = 0;
virtual void addPower(int amount) = 0;
// Ajuste de comportamiento del gameplay
virtual int getCurrentMenaceLevel() const = 0; // Ajuste de comportamiento del gameplay
[[nodiscard]] virtual auto getCurrentMenaceLevel() const -> int = 0;
}; };

View File

@@ -1,190 +1,192 @@
#include "system_utils.h" #include "system_utils.h"
#include <iostream>
#include <sys/stat.h> #include <sys/stat.h>
#include <cerrno> #include <cerrno>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <direct.h>
#include <shlobj.h> #include <shlobj.h>
#include <direct.h> #include <windows.h>
// Evitar conflictos con macros de Windows // Evitar conflictos con macros de Windows
#ifdef ERROR_ALREADY_EXISTS #ifdef ERROR_ALREADY_EXISTS
#undef ERROR_ALREADY_EXISTS #undef ERROR_ALREADY_EXISTS
#endif #endif
#else #else
#include <pwd.h> #include <pwd.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
namespace SystemUtils { namespace SystemUtils {
// Función auxiliar para crear una carpeta individual // Función auxiliar para crear una carpeta individual
Result createSingleFolder(const std::string& path, int permissions) { auto createSingleFolder(const std::string& path, int permissions) -> Result {
struct stat st = {0}; struct stat st = {.st_dev = 0};
// Verificar si ya existe
if (stat(path.c_str(), &st) == 0) {
return Result::SUCCESS; // Ya existe, no es error por defecto
}
// Intentar crear la carpeta // Verificar si ya existe
int result; if (stat(path.c_str(), &st) == 0) {
return Result::SUCCESS; // Ya existe, no es error por defecto
}
// Intentar crear la carpeta
int result;
#ifdef _WIN32 #ifdef _WIN32
result = _mkdir(path.c_str()); result = _mkdir(path.c_str());
#else #else
result = mkdir(path.c_str(), permissions); result = mkdir(path.c_str(), permissions);
#endif #endif
if (result == -1) { if (result == -1) {
switch (errno) { switch (errno) {
case EACCES: case EACCES:
return Result::PERMISSION_DENIED; return Result::PERMISSION_DENIED;
case EEXIST: case EEXIST:
return Result::ALREADY_EXISTS; return Result::ALREADY_EXISTS;
case ENAMETOOLONG: case ENAMETOOLONG:
return Result::PATH_TOO_LONG; return Result::PATH_TOO_LONG;
default: default:
return Result::UNKNOWN_ERROR; return Result::UNKNOWN_ERROR;
}
}
return Result::SUCCESS;
}
// Función auxiliar para crear carpetas padre recursivamente
auto createParentFolders(const std::string& path, int permissions) -> Result {
size_t pos = 0;
while ((pos = path.find('/', pos + 1)) != std::string::npos) {
std::string parent = path.substr(0, pos);
if (!parent.empty() && !folderExists(parent)) {
Result result = createSingleFolder(parent, permissions);
if (result != Result::SUCCESS && result != Result::ALREADY_EXISTS) {
return result;
} }
} }
return Result::SUCCESS;
} }
// Función auxiliar para crear carpetas padre recursivamente return Result::SUCCESS;
Result createParentFolders(const std::string& path, int permissions) { }
size_t pos = 0;
auto createApplicationFolder(const std::string& app_name, std::string& out_path) -> Result {
while ((pos = path.find('/', pos + 1)) != std::string::npos) { FolderConfig config;
std::string parent = path.substr(0, pos); return createApplicationFolder(app_name, out_path, config);
if (!parent.empty() && !folderExists(parent)) { }
Result result = createSingleFolder(parent, permissions);
if (result != Result::SUCCESS && result != Result::ALREADY_EXISTS) { auto createApplicationFolder(const std::string& app_name, std::string& out_path, const FolderConfig& config) -> Result {
return result; out_path = getApplicationDataPath(app_name);
} return createFolder(out_path, config);
} }
auto createFolder(const std::string& path) -> Result {
FolderConfig config;
return createFolder(path, config);
}
auto createFolder(const std::string& path, const FolderConfig& config) -> Result {
if (path.empty()) {
return Result::INVALID_PATH;
}
// Verificar si ya existe y si eso es un error
if (folderExists(path) && config.fail_if_exists) {
return Result::ALREADY_EXISTS;
}
// Crear carpetas padre si es necesario
if (config.create_parents) {
Result parent_result = createParentFolders(path, config.permissions);
if (parent_result != Result::SUCCESS) {
return parent_result;
} }
return Result::SUCCESS;
} }
Result createApplicationFolder(const std::string& app_name, std::string& out_path) { // Crear la carpeta final
FolderConfig config; return createSingleFolder(path, config.permissions);
return createApplicationFolder(app_name, out_path, config); }
}
Result createApplicationFolder(const std::string& app_name, std::string& out_path, const FolderConfig& config) { auto getApplicationDataPath(const std::string& app_name) -> std::string {
out_path = getApplicationDataPath(app_name);
return createFolder(out_path, config);
}
Result createFolder(const std::string& path) {
FolderConfig config;
return createFolder(path, config);
}
Result createFolder(const std::string& path, const FolderConfig& config) {
if (path.empty()) {
return Result::INVALID_PATH;
}
// Verificar si ya existe y si eso es un error
if (folderExists(path) && config.fail_if_exists) {
return Result::ALREADY_EXISTS;
}
// Crear carpetas padre si es necesario
if (config.create_parents) {
Result parent_result = createParentFolders(path, config.permissions);
if (parent_result != Result::SUCCESS) {
return parent_result;
}
}
// Crear la carpeta final
return createSingleFolder(path, config.permissions);
}
std::string getApplicationDataPath(const std::string& app_name) {
#ifdef _WIN32 #ifdef _WIN32
char* appdata = getenv("APPDATA"); char* appdata = getenv("APPDATA");
if (appdata) { if (appdata) {
return std::string(appdata) + "/" + app_name; return std::string(appdata) + "/" + app_name;
} }
return "C:/Users/Default/AppData/Roaming/" + app_name; return "C:/Users/Default/AppData/Roaming/" + app_name;
#elif __APPLE__ #elif __APPLE__
std::string home = getHomeDirectory(); std::string home = getHomeDirectory();
return home + "/Library/Application Support/" + app_name; return home + "/Library/Application Support/" + app_name;
#elif __linux__ #elif __linux__
std::string home = getHomeDirectory(); std::string home = getHomeDirectory();
return home + "/.config/" + app_name; return home + "/.config/" + app_name;
#else #else
// Fallback genérico // Fallback genérico
std::string home = getHomeDirectory(); std::string home = getHomeDirectory();
return home + "/." + app_name; return home + "/." + app_name;
#endif #endif
} }
bool folderExists(const std::string& path) { auto folderExists(const std::string& path) -> bool {
struct stat st = {0}; struct stat st = {.st_dev = 0};
return (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)); return (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
} }
const char* resultToString(Result result) { auto resultToString(Result result) -> const char* {
switch (result) { switch (result) {
case Result::SUCCESS: case Result::SUCCESS:
return "Operación exitosa"; return "Operación exitosa";
case Result::PERMISSION_DENIED: case Result::PERMISSION_DENIED:
return "Error: Permisos insuficientes"; return "Error: Permisos insuficientes";
case Result::PATH_TOO_LONG: case Result::PATH_TOO_LONG:
return "Error: Ruta demasiado larga"; return "Error: Ruta demasiado larga";
case Result::ALREADY_EXISTS: case Result::ALREADY_EXISTS:
return "Error: La carpeta ya existe"; return "Error: La carpeta ya existe";
case Result::INVALID_PATH: case Result::INVALID_PATH:
return "Error: Ruta inválida"; return "Error: Ruta inválida";
case Result::UNKNOWN_ERROR: case Result::UNKNOWN_ERROR:
return "Error desconocido"; return "Error desconocido";
default: default:
return "Error no identificado"; return "Error no identificado";
}
} }
}
std::string getHomeDirectory() { auto getHomeDirectory() -> std::string {
#ifdef _WIN32 #ifdef _WIN32
char* userprofile = getenv("USERPROFILE"); char* userprofile = getenv("USERPROFILE");
if (userprofile) { if (userprofile) {
return std::string(userprofile); return std::string(userprofile);
} }
return "C:/Users/Default"; return "C:/Users/Default";
#else #else
struct passwd *pw = getpwuid(getuid()); struct passwd* pw = getpwuid(getuid());
if (pw && pw->pw_dir) { if ((pw != nullptr) && (pw->pw_dir != nullptr)) {
return std::string(pw->pw_dir); return {pw->pw_dir};
}
// Fallback
char* home = getenv("HOME");
if (home) {
return std::string(home);
}
return "/tmp";
#endif
} }
std::string getTempDirectory() { // Fallback
char* home = getenv("HOME");
if (home != nullptr) {
return {home};
}
return "/tmp";
#endif
}
auto getTempDirectory() -> std::string {
#ifdef _WIN32 #ifdef _WIN32
char* temp = getenv("TEMP"); char* temp = getenv("TEMP");
if (temp) { if (temp) {
return std::string(temp); return std::string(temp);
}
return "C:/Windows/Temp";
#else
return "/tmp";
#endif
} }
return "C:/Windows/Temp";
#else
return "/tmp";
#endif
}
} // namespace SystemUtils } // namespace SystemUtils

View File

@@ -2,58 +2,37 @@
#include <string> #include <string>
// Utilidades multiplataforma para operaciones del sistema // --- Namespace SystemUtils: utilidades multiplataforma para operaciones del sistema ---
namespace SystemUtils { namespace SystemUtils {
// --- Enums ---
enum class Result { // Códigos de resultado para operaciones del sistema
SUCCESS = 0,
PERMISSION_DENIED, // Sin permisos para crear la carpeta
PATH_TOO_LONG, // Ruta demasiado larga
ALREADY_EXISTS, // Ya existe (solo si se considera error)
INVALID_PATH, // Ruta inválida
UNKNOWN_ERROR // Error desconocido
};
// Códigos de resultado para operaciones del sistema // --- Estructuras ---
enum class Result { struct FolderConfig { // Configuración para creación de carpetas
SUCCESS = 0, bool create_parents{true}; // Crear carpetas padre si no existen
PERMISSION_DENIED, // Sin permisos para crear la carpeta bool fail_if_exists{false}; // Fallar si la carpeta ya existe
PATH_TOO_LONG, // Ruta demasiado larga int permissions{0755}; // Permisos Unix (ignorado en Windows)
ALREADY_EXISTS, // Ya existe (solo si se considera error)
INVALID_PATH, // Ruta inválida
UNKNOWN_ERROR // Error desconocido
};
// Configuración para creación de carpetas
struct FolderConfig {
bool create_parents; // Crear carpetas padre si no existen
bool fail_if_exists; // Fallar si la carpeta ya existe
int permissions; // Permisos Unix (ignorado en Windows)
// Constructor con valores por defecto // Constructor con valores por defecto
FolderConfig() FolderConfig() = default;
: create_parents(true) };
, fail_if_exists(false)
, permissions(0755) // rwxr-xr-x
{}
};
// Crea la carpeta del sistema donde guardar datos de la aplicación // --- Funciones ---
Result createApplicationFolder(const std::string& app_name, std::string& out_path); auto createApplicationFolder(const std::string& app_name, std::string& out_path) -> Result; // Crea la carpeta del sistema donde guardar datos de la aplicación
auto createApplicationFolder(const std::string& app_name, std::string& out_path, const FolderConfig& config) -> Result; // Crea la carpeta del sistema con configuración personalizada
auto createFolder(const std::string& path) -> Result; // Crea una carpeta en la ruta especificada
auto createFolder(const std::string& path, const FolderConfig& config) -> Result; // Crea una carpeta con configuración personalizada
auto getApplicationDataPath(const std::string& app_name) -> std::string; // Obtiene la ruta de datos de la aplicación (sin crearla)
auto folderExists(const std::string& path) -> bool; // Verifica si una carpeta existe
auto resultToString(Result result) -> const char*; // Convierte un código de resultado a string descriptivo
auto getHomeDirectory() -> std::string; // Obtiene el directorio home del usuario
auto getTempDirectory() -> std::string; // Obtiene el directorio temporal del sistema
// Crea la carpeta del sistema con configuración personalizada } // namespace SystemUtils
Result createApplicationFolder(const std::string& app_name, std::string& out_path, const FolderConfig& config);
// Crea una carpeta en la ruta especificada
Result createFolder(const std::string& path);
// Crea una carpeta con configuración personalizada
Result createFolder(const std::string& path, const FolderConfig& config);
// Obtiene la ruta de datos de la aplicación (sin crearla)
std::string getApplicationDataPath(const std::string& app_name);
// Verifica si una carpeta existe
bool folderExists(const std::string& path);
// Convierte un código de resultado a string descriptivo
const char* resultToString(Result result);
// Obtiene el directorio home del usuario
std::string getHomeDirectory();
// Obtiene el directorio temporal del sistema
std::string getTempDirectory();
} // namespace SystemUtils

View File

@@ -96,7 +96,7 @@ void Tabe::move() {
? LEFT[rand() % CHOICES] ? LEFT[rand() % CHOICES]
: RIGHT[rand() % CHOICES]; : RIGHT[rand() % CHOICES];
setRandomFlyPath(DIRECTION, 20 + rand() % 40); setRandomFlyPath(DIRECTION, 20 + (rand() % 40));
} }
} }
@@ -125,9 +125,9 @@ void Tabe::enable() {
} }
// Establece un vuelo aleatorio // Establece un vuelo aleatorio
void Tabe::setRandomFlyPath(Direction direction, int lenght) { void Tabe::setRandomFlyPath(Direction direction, int length) {
direction_ = direction; direction_ = direction;
fly_distance_ = lenght; fly_distance_ = length;
waiting_counter_ = 5 + rand() % 15; waiting_counter_ = 5 + rand() % 15;
Audio::get()->playSound("tabe.wav"); Audio::get()->playSound("tabe.wav");

View File

@@ -129,7 +129,7 @@ class Tabe {
// --- Métodos internos --- // --- Métodos internos ---
void move(); // Mueve el objeto void move(); // Mueve el objeto
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 lenght); // Establece un vuelo aleatorio void setRandomFlyPath(Direction direction, int length); // Establece un vuelo aleatorio
void updateState(); // Actualiza el estado void updateState(); // Actualiza el estado
void updateTimer(); // Actualiza el temporizador void updateTimer(); // Actualiza el temporizador
void disable(); // Deshabilita el objeto void disable(); // Deshabilita el objeto

Some files were not shown because too many files have changed in this diff Show More