62 Commits

Author SHA1 Message Date
301e0145fb la consola ja no pausa al jugador 2026-03-29 18:24:47 +02:00
3b233f0e12 eliminat Options::console 2026-03-29 18:14:51 +02:00
fd9be2066d reorganitzats els comandos de consola 2026-03-29 18:08:33 +02:00
7551115912 renderInfo amb animacio 2026-03-29 17:47:48 +02:00
b986778bb4 fix: dos logs de consola amb el format incorrecte 2026-03-29 17:34:42 +02:00
fd2e2f1014 eliminat el soport per a arguments 2026-03-29 17:32:06 +02:00
77b844065e canvi d'skin en la consola 2026-03-29 17:21:05 +02:00
2fe79de1d8 arreglos en Console i SS 2026-03-29 14:24:20 +02:00
d6ecadfd3a fix: vsync off no anava en Wayland 2026-03-29 14:13:02 +02:00
ad467847b9 corregit compile_spirv.cmake y la system_folder per a shaders 2026-03-29 13:45:29 +02:00
1bb2142d19 fix: corregida logica per obrir i entrar a la jail. ja no mira el nom de la habitacio sino el numero 2026-03-29 12:58:32 +02:00
f5a82229fe afegits cheats a la consola 2026-03-29 12:42:51 +02:00
145bab037f - eliminats accents en titols d'habitacions
- corregits fitxers .fnt (falta aseprite)
- corregint font_gent.py
- revisades algunes traduccions
2026-03-29 09:48:58 +02:00
754ad2de49 comandos i tecles per a manejar el nou disseny de shaders 2026-03-29 09:09:22 +02:00
a9b7c3f025 treballant en poder incloure diferents shaders 2026-03-29 08:23:42 +02:00
4db92d423c cos: afegida duracio final el logo 2026-03-29 07:45:28 +02:00
6717c1e307 fix: bug mileanri que deixava al jugador atascat en algunes rampes en certes condicions 2026-03-28 22:22:32 +01:00
9282d661aa millorada una mica la classe Debug en quant a mostrar info 2026-03-28 21:58:54 +01:00
a21f530dd4 corregit el caracter de coret que s'havia perdut 2026-03-28 20:53:33 +01:00
7483bf63c8 establir la posicio i habitacio inicial de debug desde la consola 2026-03-28 20:36:56 +01:00
268763f162 clase Debug ara carrega la posicio i habitacio inicial desde un fitxer 2026-03-28 20:15:44 +01:00
71c7b8e553 mes comandos per a Console 2026-03-28 14:14:33 +01:00
854a5f04b2 les tecles de funcio ja tornena a funcionar amb la consola oberta 2026-03-28 14:03:41 +01:00
ed21a47f92 augmentat el limit de caracters en la consola 2026-03-28 13:53:07 +01:00
637df23bc7 mes comandos en Console 2026-03-28 13:37:46 +01:00
ea421b4e17 fix: en TITLE, la consola no bloquejava la pulsacio del 1 al 4 i entrava a les opcions 2026-03-28 13:31:20 +01:00
3a5ff06dab - afegits comandos de GameControl per activar trucos o canviar de habitacio
- modificat el comando debug, ja no activa el overlay sino el mode debug
- nou alias show info i hide info
2026-03-28 13:27:33 +01:00
dfb0d2134f ajustat "al gust" el hud (consola, render info i notifier) 2026-03-28 13:04:03 +01:00
b459e2106f clang format 2026-03-28 12:50:00 +01:00
065f66d40e nova clase renderInfo
afegit control de offset a les notificacions
2026-03-28 12:49:38 +01:00
f15658a767 afegit soport per a configurar el audio (en config i en console) 2026-03-28 11:08:01 +01:00
21c8d1e8ca eliminat renderInfo de ResourceCache::init 2026-03-28 10:49:39 +01:00
a06eb8c8e9 fix: #include <iomanip> en Screen per a std::setprecision() 2026-03-28 10:08:08 +01:00
6b73a76d31 canvis en renderInfo
acabant de pulir el calcul actual del zoom en non integer scale
2026-03-28 02:01:51 +01:00
348a76090b permet escollir driver de gpu o no escollir-ne cap 2026-03-28 01:14:41 +01:00
02c1bf647e modificats defaults per a kiosk mode 2026-03-28 00:42:27 +01:00
a7f0a18e6d canvis de upscale i downscale en consola 2026-03-28 00:37:52 +01:00
8355c266a6 afegits comandos y restriccions en la consola per al modo kiosko 2026-03-28 00:26:29 +01:00
7bad27d686 canvi i reinici d'escene en la consola 2026-03-28 00:02:14 +01:00
d39622c7e2 afegits els comandos de les tecles de funció 2026-03-27 23:32:44 +01:00
4910d201f9 primer comando implementat en la consola 2026-03-27 23:19:47 +01:00
e85800c5ed ja es pot escriure en la consola 2026-03-27 22:54:47 +01:00
f25ee18329 treballant en la consola 2026-03-27 22:24:55 +01:00
3712f0c8d9 implementat lanzcos en el supersampling 2026-03-27 21:59:14 +01:00
c063488e8e optimitzat textureToRenderer() 2026-03-27 20:34:45 +01:00
deb0a8677f mostra el render device en info_debug 2026-03-27 10:18:41 +01:00
c5a7c9e70d optimitzant textureToRenderer() 2026-03-27 09:46:25 +01:00
92453a6104 llevats colorins del debug 2026-03-26 08:49:49 +01:00
7aff3e2109 afegit overlay de debug 2026-03-26 08:47:24 +01:00
8d213e7b3e afinant els shaders 2026-03-26 07:46:11 +01:00
c6d409c303 corregides scanlines per a treballar amb subpixels per proporció 2026-03-25 22:31:34 +01:00
6914f7df93 supersampling ara aplica al tamany de la finestra, no a la textura base 2026-03-25 22:00:10 +01:00
1dbfff2c17 fix: el supersampling es feia per cpu en lloc de per gpu 2026-03-25 21:33:06 +01:00
3493636954 correccions en les traduccions 2026-03-25 20:51:52 +01:00
8ff1073e4a corregides les scanlines per a paletes amb fondo blanc 2026-03-25 18:31:36 +01:00
6497e26202 reordenades i renombrades les classes sprite 2026-03-25 18:01:33 +01:00
e0e37204d7 eliminada la classe Texture 2026-03-25 17:54:23 +01:00
6595b28790 clang-format
clang-tidy (macos)
2026-03-23 07:26:21 +01:00
0ddb6c85e1 afegit un poc de chroma al preset crt 2026-03-22 23:00:39 +01:00
f84007902e afegit flicker a postfx 2026-03-22 22:38:18 +01:00
49ae2ae41f per defecte el joc eixirà ara en valencià 2026-03-22 22:07:12 +01:00
c701421a8f corregits offsets en smb2.fnt 2026-03-22 22:04:23 +01:00
134 changed files with 23831 additions and 3907 deletions

View File

@@ -41,14 +41,14 @@ set(APP_SOURCES
# Core - Rendering # Core - Rendering
source/core/rendering/gif.cpp source/core/rendering/gif.cpp
source/core/rendering/pixel_reveal.cpp source/core/rendering/pixel_reveal.cpp
source/core/rendering/render_info.cpp
source/core/rendering/screen.cpp source/core/rendering/screen.cpp
source/core/rendering/surface.cpp source/core/rendering/surface.cpp
source/core/rendering/surface_animated_sprite.cpp source/core/rendering/sprite/animated_sprite.cpp
source/core/rendering/surface_dissolve_sprite.cpp source/core/rendering/sprite/dissolve_sprite.cpp
source/core/rendering/surface_moving_sprite.cpp source/core/rendering/sprite/moving_sprite.cpp
source/core/rendering/surface_sprite.cpp source/core/rendering/sprite/sprite.cpp
source/core/rendering/text.cpp source/core/rendering/text.cpp
source/core/rendering/texture.cpp
# Core - Locale # Core - Locale
source/core/locale/locale.cpp source/core/locale/locale.cpp
@@ -96,6 +96,7 @@ set(APP_SOURCES
source/game/scenes/title.cpp source/game/scenes/title.cpp
# Game - UI # Game - UI
source/game/ui/console.cpp
source/game/ui/notifier.cpp source/game/ui/notifier.cpp
# Utils # Utils
@@ -124,32 +125,62 @@ message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}")
if(NOT APPLE) if(NOT APPLE)
find_program(GLSLC_EXE NAMES glslc) find_program(GLSLC_EXE NAMES glslc)
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert") set(SHADERS_DIR "${CMAKE_SOURCE_DIR}/data/shaders")
set(SHADER_FRAG_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.frag") set(HEADERS_DIR "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu")
set(SHADER_VERT_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/postfx_vert_spv.h")
set(SHADER_FRAG_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/postfx_frag_spv.h") set(SHADER_POSTFX_VERT_SRC "${SHADERS_DIR}/postfx.vert")
set(SHADER_POSTFX_FRAG_SRC "${SHADERS_DIR}/postfx.frag")
set(SHADER_UPSCALE_FRAG_SRC "${SHADERS_DIR}/upscale.frag")
set(SHADER_DOWNSCALE_FRAG_SRC "${SHADERS_DIR}/downscale.frag")
set(SHADER_CRTPI_FRAG_SRC "${SHADERS_DIR}/crtpi_frag.glsl")
set(SHADER_POSTFX_VERT_H "${HEADERS_DIR}/postfx_vert_spv.h")
set(SHADER_POSTFX_FRAG_H "${HEADERS_DIR}/postfx_frag_spv.h")
set(SHADER_UPSCALE_FRAG_H "${HEADERS_DIR}/upscale_frag_spv.h")
set(SHADER_DOWNSCALE_FRAG_H "${HEADERS_DIR}/downscale_frag_spv.h")
set(SHADER_CRTPI_FRAG_H "${HEADERS_DIR}/crtpi_frag_spv.h")
set(ALL_SHADER_HEADERS
"${SHADER_POSTFX_VERT_H}"
"${SHADER_POSTFX_FRAG_H}"
"${SHADER_UPSCALE_FRAG_H}"
"${SHADER_DOWNSCALE_FRAG_H}"
"${SHADER_CRTPI_FRAG_H}"
)
set(ALL_SHADER_SOURCES
"${SHADER_POSTFX_VERT_SRC}"
"${SHADER_POSTFX_FRAG_SRC}"
"${SHADER_UPSCALE_FRAG_SRC}"
"${SHADER_DOWNSCALE_FRAG_SRC}"
"${SHADER_CRTPI_FRAG_SRC}"
)
if(GLSLC_EXE) if(GLSLC_EXE)
add_custom_command( add_custom_command(
OUTPUT "${SHADER_VERT_H}" "${SHADER_FRAG_H}" OUTPUT ${ALL_SHADER_HEADERS}
COMMAND "${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.sh" COMMAND ${CMAKE_COMMAND}
DEPENDS "${SHADER_VERT_SRC}" "${SHADER_FRAG_SRC}" -D GLSLC=${GLSLC_EXE}
-D SHADERS_DIR=${SHADERS_DIR}
-D HEADERS_DIR=${HEADERS_DIR}
-P ${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.cmake
DEPENDS ${ALL_SHADER_SOURCES}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Compilando shaders SPIR-V..." COMMENT "Compilando shaders SPIR-V..."
) )
add_custom_target(shaders DEPENDS "${SHADER_VERT_H}" "${SHADER_FRAG_H}") add_custom_target(shaders DEPENDS ${ALL_SHADER_HEADERS})
message(STATUS "glslc encontrado: shaders se compilarán automáticamente") message(STATUS "glslc encontrado: shaders se compilarán automáticamente")
else() else()
if(NOT EXISTS "${SHADER_VERT_H}" OR NOT EXISTS "${SHADER_FRAG_H}") foreach(HDR ${ALL_SHADER_HEADERS})
if(NOT EXISTS "${HDR}")
message(FATAL_ERROR message(FATAL_ERROR
"glslc no encontrado y headers SPIR-V no existen.\n" "glslc no encontrado y header SPIR-V no existe: ${HDR}\n"
" Instala glslc: sudo apt install glslang-tools (Linux)\n" " Instala glslc: sudo apt install glslang-tools (Linux)\n"
" choco install vulkan-sdk (Windows)\n" " choco install vulkan-sdk (Windows)\n"
" O genera los headers manualmente: tools/shaders/compile_spirv.sh" " O genera los headers manualmente: tools/shaders/compile_spirv.sh"
) )
else()
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados")
endif() endif()
endforeach()
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados")
endif() endif()
else() else()
message(STATUS "macOS: shaders SPIR-V omitidos (usa Metal)") message(STATUS "macOS: shaders SPIR-V omitidos (usa Metal)")

View File

@@ -74,6 +74,10 @@ assets:
path: ${SYSTEM_FOLDER}/config.yaml path: ${SYSTEM_FOLDER}/config.yaml
required: false required: false
absolute: true absolute: true
- type: DATA
path: ${SYSTEM_FOLDER}/debug.yaml
required: false
absolute: true
- type: DATA - type: DATA
path: ${SYSTEM_FOLDER}/stats_buffer.csv path: ${SYSTEM_FOLDER}/stats_buffer.csv
required: false required: false
@@ -87,7 +91,11 @@ assets:
required: false required: false
absolute: true absolute: true
- type: DATA - type: DATA
path: ${SYSTEM_FOLDER}/postfx.yaml path: ${SYSTEM_FOLDER}/shaders/postfx.yaml
required: false
absolute: true
- type: DATA
path: ${SYSTEM_FOLDER}/shaders/crtpi.yaml
required: false required: false
absolute: true absolute: true

View File

@@ -105,24 +105,24 @@ columns 15
193 6 # Á 193 6 # Á
200 6 # È 200 6 # È
201 6 # É 201 6 # É
204 6 # Ì
205 6 # Í 205 6 # Í
207 6 # Ï
210 6 # Ò 210 6 # Ò
211 6 # Ó 211 6 # Ó
219 6 # Ù
218 6 # Ú 218 6 # Ú
220 6 # Ü
209 6 # Ñ 209 6 # Ñ
199 6 # Ç 199 6 # Ç
224 5 # à 224 5 # à
225 5 # á 225 5 # á
232 5 # è 232 5 # è
233 5 # é 233 5 # é
236 4 # ì
237 4 # í 237 4 # í
239 4 # ï
242 5 # ò 242 5 # ò
243 5 # ó 243 5 # ó
249 5 # ù
250 5 # ú 250 5 # ú
252 5 # ü
241 5 # ñ 241 5 # ñ
231 5 # ç 231 5 # ç
161 2 # ¡ 161 2 # ¡

View File

@@ -101,24 +101,24 @@ columns 15
193 6 # Á 193 6 # Á
200 7 # È 200 7 # È
201 7 # É 201 7 # É
204 6 # Ì
205 6 # Í 205 6 # Í
207 6 # Ï
210 7 # Ò 210 7 # Ò
211 7 # Ó 211 7 # Ó
217 6 # Ù
218 6 # Ú 218 6 # Ú
220 6 # Ü
209 7 # Ñ 209 7 # Ñ
199 7 # Ç 199 7 # Ç
224 6 # à 224 6 # à
225 6 # á 225 6 # á
232 7 # è 232 7 # è
233 7 # é 233 7 # é
236 6 # ì
237 6 # í 237 6 # í
239 6 # ï
242 7 # ò 242 7 # ò
243 7 # ó 243 7 # ó
249 6 # ù
250 6 # ú 250 6 # ú
252 6 # ü
241 7 # ñ 241 7 # ñ
231 7 # ç 231 7 # ç
161 2 # ¡ 161 2 # ¡

View File

@@ -6,24 +6,24 @@ box_height 8
columns 15 columns 15
# codepoint_decimal ancho_visual # codepoint_decimal ancho_visual
32 8 # U+0020 32 7 # U+0020
33 5 # ! 33 7 # !
34 6 # " 34 7 # "
35 7 # # 35 7 # #
36 7 # $ 36 7 # $
37 7 # % 37 7 # %
38 7 # & 38 7 # &
39 4 # ' 39 7 # '
40 6 # ( 40 7 # (
41 5 # ) 41 7 # )
42 7 # * 42 7 # *
43 7 # + 43 7 # +
44 4 # , 44 7 # ,
45 7 # - 45 7 # -
46 4 # . 46 7 # .
47 7 # / 47 7 # /
48 7 # 0 48 7 # 0
49 6 # 1 49 7 # 1
50 7 # 2 50 7 # 2
51 7 # 3 51 7 # 3
52 7 # 4 52 7 # 4
@@ -32,11 +32,11 @@ columns 15
55 7 # 7 55 7 # 7
56 7 # 8 56 7 # 8
57 7 # 9 57 7 # 9
58 4 # : 58 7 # :
59 4 # ; 59 7 # ;
60 6 # < 60 7 # <
61 6 # = 61 7 # =
62 6 # > 62 7 # >
63 7 # ? 63 7 # ?
64 7 # @ 64 7 # @
65 7 # A 65 7 # A
@@ -47,7 +47,7 @@ columns 15
70 7 # F 70 7 # F
71 7 # G 71 7 # G
72 7 # H 72 7 # H
73 6 # I 73 7 # I
74 7 # J 74 7 # J
75 7 # K 75 7 # K
76 7 # L 76 7 # L
@@ -65,12 +65,12 @@ columns 15
88 7 # X 88 7 # X
89 7 # Y 89 7 # Y
90 7 # Z 90 7 # Z
91 6 # [ 91 7 # [
92 7 # \ 92 7 # \
93 5 # ] 93 7 # ]
94 6 # ^ 94 7 # ^
95 7 # _ 95 7 # _
96 4 # ` 96 7 # `
97 7 # a 97 7 # a
98 7 # b 98 7 # b
99 7 # c 99 7 # c
@@ -79,7 +79,7 @@ columns 15
102 7 # f 102 7 # f
103 7 # g 103 7 # g
104 7 # h 104 7 # h
105 6 # i 105 7 # i
106 7 # j 106 7 # j
107 7 # k 107 7 # k
108 7 # l 108 7 # l
@@ -97,36 +97,37 @@ columns 15
120 7 # x 120 7 # x
121 7 # y 121 7 # y
122 7 # z 122 7 # z
123 6 # { 123 7 # {
124 5 # | 124 7 # |
125 5 # } 125 7 # }
126 7 # ~ 126 7 # ~
192 7 # À 192 7 # À
193 7 # Á 193 7 # Á
200 7 # È 200 7 # È
201 7 # É 201 7 # É
205 6 # Í 204 7 # Ì
207 6 # Ï 205 7 # Í
210 7 # Ò 210 7 # Ò
211 7 # Ó 211 7 # Ó
217 7 # Ù
218 7 # Ú 218 7 # Ú
220 7 # Ü
209 7 # Ñ 209 7 # Ñ
199 7 # Ç 199 7 # Ç
224 7 # à 224 7 # à
225 7 # á 225 7 # á
232 7 # è 232 7 # è
233 7 # é 233 7 # é
237 6 # í 236 7 # ì
239 6 # ï 237 7 # í
242 7 # ò 242 7 # ò
243 7 # ó 243 7 # ó
249 7 # ù
250 7 # ú 250 7 # ú
252 7 # ü
241 7 # ñ 241 7 # ñ
231 7 # ç 231 7 # ç
161 5 # ¡ 161 7 # ¡
191 7 # ¿ 191 7 # ¿
171 7 # « 171 7 # «
187 7 # » 187 7 # »
183 4 # · 183 7 # ·
228 7 # ä (corazón)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 968 B

After

Width:  |  Height:  |  Size: 1001 B

View File

@@ -105,24 +105,24 @@ columns 15
193 5 # Á 193 5 # Á
200 4 # È 200 4 # È
201 4 # É 201 4 # É
204 1 # Ì
205 1 # Í 205 1 # Í
207 1 # Ï
210 5 # Ò 210 5 # Ò
211 5 # Ó 211 5 # Ó
217 5 # Ù
218 5 # Ú 218 5 # Ú
220 5 # Ü
209 5 # Ñ 209 5 # Ñ
199 5 # Ç 199 5 # Ç
224 4 # à 224 4 # à
225 4 # á 225 4 # á
232 4 # è 232 4 # è
233 4 # é 233 4 # é
236 1 # ì
237 1 # í 237 1 # í
239 1 # ï
242 4 # ò 242 4 # ò
243 4 # ó 243 4 # ó
249 4 # ù
250 4 # ú 250 4 # ú
252 4 # ü
241 4 # ñ 241 4 # ñ
231 3 # ç 231 3 # ç
161 1 # ¡ 161 1 # ¡

View File

@@ -2,10 +2,10 @@
# lang: ca # lang: ca
title: title:
marquee: "EH JAILEROS!! ESTEM EN 2022 I ENCARA HO PETEM COM EN 1998!! QUE, HO HEU SENTIT O NO? ELS JAILGAMES HAN TORNAT!! SÍ, COLLONS, HAN TORNAT!! MÉS DE 10 TÍTOLS QUE EL JAILDOC TÉ A FOC LENT!! AIXÒ ÉS UNA BARBARITAT, PERÒ... QUIN EIXIRÀ PRIMER? I ATENCIÓ, QUE HI HA UN APARELLET NOU QUE VOS FARÀ VOLAR EL CAP: EL P.A.C.O.! PERÒ UN MOMENT... QUÈ ÉS AQUELL ENCANTET QUE VE ALLÀ? OOOH, AQUELLA MINIASCII ÉS AMOR DEL BO!! LI PEGARIA UNA MOSSEGADA A CADA BYTE! OSTRES! I NO VOS OBLIDEU DE PUJAR AQUELLS JAILGAMES VELLS I PANXUTS DE MS-DOS A GITHUB, QUE SI NO ES PERDRAN!! QUIN SERÀ EL PROPER PROJECTE DEL JAILDOC? QUÈ PRENDRA VIDA? AI MARE... NI IDEA, PERÒ ACÍ PODEU SABER-HO SI RESOLGUEU EL DILEMA DEL JAILDOCTOR... VOS ATREVIU O QUÈ?" marquee: "EI JAILERS!! ESTEM EN 2022 I ENCARA HO PETEM COM EN 1998!! QUÉ, HO HEU SENTIT O NO? ELS JAILGAMES HAN TORNAT!! SÍ, COLLONS, HAN TORNAT!! MÉS DE 10 TÍTOLS QUE EL JAILDOC TÉ EN LA CUINA A FOC LENT!! MOLT LENT!! AIXÒ ÉS UNA BARBARITAT, PERÒ... QUIN EIXIRÀ PRIMER? I ATENCIÓ, QUE HI HA UN APARELLET NOU QUE VOS FARÀ VOLAR EL CAP: EL P.A.C.O.! PERÒ UN MOMENT... QUÈ ÉS AQUELLA COSETA QUE VE PER ALLÀ? OOOH, AQUELLA MINIASCII ÉS AMOR DEL BO!! LI PEGARIA UNA LLEPAETA A CADA BYTE! OSTRES! I NO VOS OBLIDEU DE PUJAR AQUELLS JAILGAMES VELLS I PANXUTS DE MS-DOS A GITHUB, QUE SI NO ES PERDRAN!! QUIN SERÀ EL PRÒXIM PROJECTE DE JAILDOC? SERÀ UN PROJECTE DE MERDA? AI MARE... NI IDEA, PERÒ ACÍ PODEU SABER-HO SI RESOLEU EL DILEMA DEL JAILDOCTOR... VOS ATREVIU O QUÈ? VAAAAA!!!"
menu: menu:
play: "1. JUGAR" play: "1. JUGAR"
keyboard: "2. REDEFINIR TECLAT" keyboard: "2. REDEFINIR TECLES"
joystick: "3. REDEFINIR MANDO" joystick: "3. REDEFINIR MANDO"
projects: "4. PROJECTES" projects: "4. PROJECTES"
keys: keys:
@@ -42,11 +42,11 @@ ending:
t6: "FOREN ALLIBERATS" t6: "FOREN ALLIBERATS"
t7: "HI HAVIA FINS I TOT BARRULLS" t7: "HI HAVIA FINS I TOT BARRULLS"
t8: "I BEGGINERS ENTRE LA GENT" t8: "I BEGGINERS ENTRE LA GENT"
t9: "BRY ESTAVA FENT LLAGRIMETA..." t9: "BRY ESTAVA PLORANT..."
t10: "PERÒ DE SOBTE ALGUNA COSA" t10: "PERÒ DE SOBTE ALGUNA COSA"
t11: "LI VA CRIDAR L'ATENCIÓ" t11: "LI VA CRIDAR L'ATENCIÓ"
t12: "UN MUNT DE FERRALLA!" t12: "UN MUNT DE FERRALLA!"
t13: "PLE D'ANDROMINES QUE NI ANAVEN!!" t13: "PLE DE TRASTOS QUE NI ANAVEN!!"
t14: "I ALESHORES," t14: "I ALESHORES,"
t15: "QUARANTA PROJECTES NOUS" t15: "QUARANTA PROJECTES NOUS"
t16: "VAN NÀIXER..." t16: "VAN NÀIXER..."
@@ -71,7 +71,7 @@ credits:
f9: "F9 VORA DE LA PANTALLA" f9: "F9 VORA DE LA PANTALLA"
author: "UN JOC DE JAILDESIGNER" author: "UN JOC DE JAILDESIGNER"
date: "FET A L'ESTIU/TARDOR DEL 2022" date: "FET A L'ESTIU/TARDOR DEL 2022"
love: "M'ENCANTEN ELS JAILGAMES!" love: "I LOVE JAILGAMES! "
achievements: achievements:
header: "ASSOLIMENT DESBLOQUEJAT!" header: "ASSOLIMENT DESBLOQUEJAT!"
@@ -108,9 +108,11 @@ ui:
fullscreen_enabled: "PANTALLA COMPLETA ACTIVADA" fullscreen_enabled: "PANTALLA COMPLETA ACTIVADA"
fullscreen_disabled: "PANTALLA COMPLETA DESACTIVADA" fullscreen_disabled: "PANTALLA COMPLETA DESACTIVADA"
window_zoom: "ZOOM FINESTRA x" window_zoom: "ZOOM FINESTRA x"
postfx_enabled: "POSTFX ACTIVAT" shaders_enabled: "SHADERS ACTIVATS"
postfx_disabled: "POSTFX DESACTIVAT" shaders_disabled: "SHADERS DESACTIVATS"
shader: "SHADER"
postfx: "POSTFX" postfx: "POSTFX"
crtpi: "CRTPI"
supersampling_enabled: "SUPERMOSTREIG ACTIVAT" supersampling_enabled: "SUPERMOSTREIG ACTIVAT"
supersampling_disabled: "SUPERMOSTREIG DESACTIVAT" supersampling_disabled: "SUPERMOSTREIG DESACTIVAT"
palette: "PALETA" palette: "PALETA"

View File

@@ -108,9 +108,11 @@ ui:
fullscreen_enabled: "FULLSCREEN ENABLED" fullscreen_enabled: "FULLSCREEN ENABLED"
fullscreen_disabled: "FULLSCREEN DISABLED" fullscreen_disabled: "FULLSCREEN DISABLED"
window_zoom: "WINDOW ZOOM x" window_zoom: "WINDOW ZOOM x"
postfx_enabled: "POSTFX ENABLED" shaders_enabled: "SHADERS ON"
postfx_disabled: "POSTFX DISABLED" shaders_disabled: "SHADERS OFF"
shader: "SHADER"
postfx: "POSTFX" postfx: "POSTFX"
crtpi: "CRTPI"
supersampling_enabled: "SUPERSAMPLING ON" supersampling_enabled: "SUPERSAMPLING ON"
supersampling_disabled: "SUPERSAMPLING OFF" supersampling_disabled: "SUPERSAMPLING OFF"
palette: "PALETTE" palette: "PALETTE"

View File

@@ -1,7 +1,7 @@
# BIG JUMP # BIG JUMP
room: room:
name_en: "BIG JUMP" name_en: "BIG JUMP"
name_ca: "GRAN SALT" name_ca: "EL GRAN BOT"
bgColor: black bgColor: black
border: red border: red
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# WELCOME TO MY ABBEY # WELCOME TO MY ABBEY
room: room:
name_en: "WELCOME TO MY ABBEY" name_en: "WELCOME TO MY ABBEY"
name_ca: "BENVINGUT A LA MEVA ABADIA" name_ca: "BENVINGUT A LA MEUA ABADIA"
bgColor: blue bgColor: blue
border: yellow border: yellow
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# TREE TOP # TREE TOP
room: room:
name_en: "TREE TOP" name_en: "TREE TOP"
name_ca: "AMUNT DE L'ARBRE" name_ca: "DALT DE L'ARBRE"
bgColor: bright_black bgColor: bright_black
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# LAZY ROOM # LAZY ROOM
room: room:
name_en: "LAZY ROOM" name_en: "LAZY ROOM"
name_ca: "SALA DE LA PEREA" name_ca: "LA SALA GOSSA"
bgColor: black bgColor: black
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# KILLING SPREE # KILLING SPREE
room: room:
name_en: "KILLING SPREE" name_en: "KILLING SPREE"
name_ca: "MATANCA INDISCRIMINADA" name_ca: "MATANÇA INDISCRIMINADA"
bgColor: black bgColor: black
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# NOW THIS IS THE BATCAVE! # NOW THIS IS THE BATCAVE!
room: room:
name_en: "NOW THIS IS THE BATCAVE!" name_en: "NOW THIS IS THE BATCAVE!"
name_ca: "AQUESTA SI QUE ES LA BATCOVA!" name_ca: "ESTA SI QUE ES LA BATCOVA!"
bgColor: black bgColor: black
border: black border: black
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# THE FRIDGE # THE FRIDGE
room: room:
name_en: "THE FRIDGE" name_en: "THE FRIDGE"
name_ca: "LA NEVERA" name_ca: "EL FRIGO"
bgColor: blue bgColor: blue
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# I DID NOT COPY THIS ONE # I DID NOT COPY THIS ONE
room: room:
name_en: "I DID NOT COPY THIS ONE" name_en: "I DID NOT COPY THIS ONE"
name_ca: "ESTA NO LA HE COPIADA, NO" name_ca: "ESTA NO LA HE COPIADA"
bgColor: black bgColor: black
border: magenta border: magenta
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# THIS CAN'T BE THE BATCAVE # THIS CAN'T BE THE BATCAVE
room: room:
name_en: "THIS CAN'T BE THE BATCAVE" name_en: "THIS CAN'T BE THE BATCAVE"
name_ca: "AQUESTA NO POT SER LA BATCOVA" name_ca: "ESTA NO POT SER LA BATCOVA"
bgColor: black bgColor: black
border: cyan border: cyan
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# ENTER PAKU SIMBEL # ENTER PAKU SIMBEL
room: room:
name_en: "ENTER PAKU SIMBEL" name_en: "ENTER PAKU SIMBEL"
name_ca: "ENTRANT A PAKU SIMBEL" name_ca: "ACCEDINT A PAKU SIMBEL"
bgColor: bright_black bgColor: bright_black
border: yellow border: yellow
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# } WE ALL LOVE JAILGAMES } # } WE ALL LOVE JAILGAMES }
room: room:
name_en: "} WE ALL LOVE JAILGAMES }" name_en: "ä WE ALL LOVE JAILGAMES ä"
name_ca: "} AMOR PELS JAILGAMES }" name_ca: "ä AMOR PELS JAILGAMES ä"
bgColor: black bgColor: black
border: bright_black border: bright_black
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# PREVENT THE CRISIS # PREVENT THE CRISIS
room: room:
name_en: "PREVENT THE CRISIS" name_en: "PREVENT THE CRISIS"
name_ca: "PREVEU LA CRISI" name_ca: "EVITA LA CRISI"
bgColor: black bgColor: black
border: bright_magenta border: bright_magenta
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# AROUND WITH ME # AROUND WITH ME
room: room:
name_en: "AROUND WITH ME" name_en: "AROUND WITH ME"
name_ca: "VOLTA AMB MI" name_ca: "AROUNDA AMB MI"
bgColor: black bgColor: black
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# SANDWITCH AND COUNTER # SANDWITCH AND COUNTER
room: room:
name_en: "SANDWITCH AND COUNTER" name_en: "SANDWITCH AND COUNTER"
name_ca: "SANDVITX I COUNTER S." name_ca: "SANDVITX I COUNTER STRIKE"
bgColor: black bgColor: black
border: cyan border: cyan
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# FEEL THE HEAT # FEEL THE HEAT
room: room:
name_en: "FEEL THE HEAT" name_en: "FEEL THE HEAT"
name_ca: "NOTA LA CALOR" name_ca: "NOTA EL CALORET"
bgColor: bright_black bgColor: bright_black
border: bright_yellow border: bright_yellow
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# WE NEED A ROBOT # WE NEED A ROBOT
room: room:
name_en: "WE NEED A ROBOT" name_en: "WE NEED A JAILROBOT"
name_ca: "NECESSITEM UN ROBOT" name_ca: "NECESSITEM UN JAILROBOT"
bgColor: black bgColor: black
border: red border: red
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# STORED JAILGAMES # STORED JAILGAMES
room: room:
name_en: "STORED JAILGAMES" name_en: "STORED JAILGAMES"
name_ca: "JAILGAMES EMMAGATZEMATS" name_ca: "EL MAGATZEM DE JAILGAMES"
bgColor: black bgColor: black
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# P.A.C.O. WORKSHOP # P.A.C.O. WORKSHOP
room: room:
name_en: "P.A.C.O. WORKSHOP" name_en: "P.A.C.O. WORKSHOP"
name_ca: "TALLER DE P.A.C.O." name_ca: "EL TALLER DE P.A.C.O."
bgColor: black bgColor: black
border: yellow border: yellow
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# CHIRPING # CHIRPING
room: room:
name_en: "CHIRPING DEVELOPMENT" name_en: "CHIRPING"
name_ca: "DESENVOLUPANT CHIRPING" name_ca: "CHIRPING"
bgColor: black bgColor: black
border: magenta border: magenta
tileSetFile: standard.gif tileSetFile: standard.gif

157
data/shaders/crtpi.glsl Normal file
View File

@@ -0,0 +1,157 @@
#version 330 core
// Configuración
#define SCANLINES
#define MULTISAMPLE
#define GAMMA
//#define FAKE_GAMMA
//#define CURVATURE
//#define SHARPER
#define MASK_TYPE 2
#define CURVATURE_X 0.05
#define CURVATURE_Y 0.1
#define MASK_BRIGHTNESS 0.80
#define SCANLINE_WEIGHT 6.0
#define SCANLINE_GAP_BRIGHTNESS 0.12
#define BLOOM_FACTOR 3.5
#define INPUT_GAMMA 2.4
#define OUTPUT_GAMMA 2.2
// Inputs desde vertex shader
in vec2 vTexCoord;
in float vFilterWidth;
#if defined(CURVATURE)
in vec2 vScreenScale;
#endif
// Output
out vec4 FragColor;
// Uniforms
uniform sampler2D Texture;
uniform vec2 TextureSize;
#if defined(CURVATURE)
vec2 Distort(vec2 coord)
{
vec2 CURVATURE_DISTORTION = vec2(CURVATURE_X, CURVATURE_Y);
vec2 barrelScale = 1.0 - (0.23 * CURVATURE_DISTORTION);
coord *= vScreenScale;
coord -= vec2(0.5);
float rsq = coord.x * coord.x + coord.y * coord.y;
coord += coord * (CURVATURE_DISTORTION * rsq);
coord *= barrelScale;
if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5)
coord = vec2(-1.0);
else
{
coord += vec2(0.5);
coord /= vScreenScale;
}
return coord;
}
#endif
float CalcScanLineWeight(float dist)
{
return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS);
}
float CalcScanLine(float dy)
{
float scanLineWeight = CalcScanLineWeight(dy);
#if defined(MULTISAMPLE)
scanLineWeight += CalcScanLineWeight(dy - vFilterWidth);
scanLineWeight += CalcScanLineWeight(dy + vFilterWidth);
scanLineWeight *= 0.3333333;
#endif
return scanLineWeight;
}
void main()
{
#if defined(CURVATURE)
vec2 texcoord = Distort(vTexCoord);
if (texcoord.x < 0.0) {
FragColor = vec4(0.0);
return;
}
#else
vec2 texcoord = vTexCoord;
#endif
vec2 texcoordInPixels = texcoord * TextureSize;
#if defined(SHARPER)
vec2 tempCoord = floor(texcoordInPixels) + 0.5;
vec2 coord = tempCoord / TextureSize;
vec2 deltas = texcoordInPixels - tempCoord;
float scanLineWeight = CalcScanLine(deltas.y);
vec2 signs = sign(deltas);
deltas.x *= 2.0;
deltas = deltas * deltas;
deltas.y = deltas.y * deltas.y;
deltas.x *= 0.5;
deltas.y *= 8.0;
deltas /= TextureSize;
deltas *= signs;
vec2 tc = coord + deltas;
#else
float tempY = floor(texcoordInPixels.y) + 0.5;
float yCoord = tempY / TextureSize.y;
float dy = texcoordInPixels.y - tempY;
float scanLineWeight = CalcScanLine(dy);
float signY = sign(dy);
dy = dy * dy;
dy = dy * dy;
dy *= 8.0;
dy /= TextureSize.y;
dy *= signY;
vec2 tc = vec2(texcoord.x, yCoord + dy);
#endif
vec3 colour = texture(Texture, tc).rgb;
#if defined(SCANLINES)
#if defined(GAMMA)
#if defined(FAKE_GAMMA)
colour = colour * colour;
#else
colour = pow(colour, vec3(INPUT_GAMMA));
#endif
#endif
scanLineWeight *= BLOOM_FACTOR;
colour *= scanLineWeight;
#if defined(GAMMA)
#if defined(FAKE_GAMMA)
colour = sqrt(colour);
#else
colour = pow(colour, vec3(1.0 / OUTPUT_GAMMA));
#endif
#endif
#endif
#if MASK_TYPE == 0
FragColor = vec4(colour, 1.0);
#elif MASK_TYPE == 1
float whichMask = fract(gl_FragCoord.x * 0.5);
vec3 mask;
if (whichMask < 0.5)
mask = vec3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS);
else
mask = vec3(1.0, MASK_BRIGHTNESS, 1.0);
FragColor = vec4(colour * mask, 1.0);
#elif MASK_TYPE == 2
float whichMask = fract(gl_FragCoord.x * 0.3333333);
vec3 mask = vec3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS);
if (whichMask < 0.3333333)
mask.x = 1.0;
else if (whichMask < 0.6666666)
mask.y = 1.0;
else
mask.z = 1.0;
FragColor = vec4(colour * mask, 1.0);
#endif
}

48
data/shaders/crtpi.vert Normal file
View File

@@ -0,0 +1,48 @@
#version 330 core
// Configuración
#define SCANLINES
#define MULTISAMPLE
#define GAMMA
//#define FAKE_GAMMA
//#define CURVATURE
//#define SHARPER
#define MASK_TYPE 2
#define CURVATURE_X 0.05
#define CURVATURE_Y 0.1
#define MASK_BRIGHTNESS 0.80
#define SCANLINE_WEIGHT 6.0
#define SCANLINE_GAP_BRIGHTNESS 0.12
#define BLOOM_FACTOR 3.5
#define INPUT_GAMMA 2.4
#define OUTPUT_GAMMA 2.2
// Inputs (desde VAO)
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
// Outputs al fragment shader
out vec2 vTexCoord;
out float vFilterWidth;
#if defined(CURVATURE)
out vec2 vScreenScale;
#endif
// Uniforms
uniform vec2 TextureSize;
void main()
{
#if defined(CURVATURE)
vScreenScale = vec2(1.0, 1.0);
#endif
// Calcula filterWidth dinámicamente basándose en la altura de la textura
vFilterWidth = (768.0 / TextureSize.y) / 3.0;
// Pasar coordenadas de textura (invertir Y para SDL)
vTexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y) * 1.0001;
// Posición del vértice (ya en espacio de clip [-1, 1])
gl_Position = vec4(aPosition, 0.0, 1.0);
}

View File

@@ -0,0 +1,152 @@
#version 450
// Vulkan GLSL fragment shader — CRT-Pi PostFX
// Algoritmo de scanlines continuas con pesos gaussianos, bloom y máscara de fósforo.
// Basado en el shader CRT-Pi original (GLSL 3.3), portado a GLSL 4.50 con parámetros uniformes.
//
// Compile: glslc -fshader-stage=frag --target-env=vulkan1.0 crtpi_frag.glsl -o crtpi_frag.spv
// xxd -i crtpi_frag.spv > ../../source/core/rendering/sdl3gpu/crtpi_frag_spv.h
layout(location = 0) in vec2 v_uv;
layout(location = 0) out vec4 out_color;
layout(set = 2, binding = 0) uniform sampler2D Texture;
layout(set = 3, binding = 0) uniform CrtPiBlock {
// vec4 #0
float scanline_weight; // Ajuste gaussiano de scanlines (default 6.0)
float scanline_gap_brightness; // Brillo mínimo entre scanlines (default 0.12)
float bloom_factor; // Factor de brillo en zonas iluminadas (default 3.5)
float input_gamma; // Gamma de entrada — linealización (default 2.4)
// vec4 #1
float output_gamma; // Gamma de salida — codificación (default 2.2)
float mask_brightness; // Brillo sub-píxeles de la máscara (default 0.80)
float curvature_x; // Distorsión barrel eje X (default 0.05)
float curvature_y; // Distorsión barrel eje Y (default 0.10)
// vec4 #2
int mask_type; // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
int enable_scanlines; // 0 = off, 1 = on
int enable_multisample; // 0 = off, 1 = on (antialiasing analítico de scanlines)
int enable_gamma; // 0 = off, 1 = on
// vec4 #3
int enable_curvature; // 0 = off, 1 = on
int enable_sharper; // 0 = off, 1 = on
float texture_width; // Ancho del canvas lógico en píxeles
float texture_height; // Alto del canvas lógico en píxeles
} u;
// Distorsión barrel CRT
vec2 distort(vec2 coord, vec2 screen_scale) {
vec2 curvature = vec2(u.curvature_x, u.curvature_y);
vec2 barrel_scale = 1.0 - (0.23 * curvature);
coord *= screen_scale;
coord -= vec2(0.5);
float rsq = coord.x * coord.x + coord.y * coord.y;
coord += coord * (curvature * rsq);
coord *= barrel_scale;
if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5) {
return vec2(-1.0); // fuera de pantalla
}
coord += vec2(0.5);
coord /= screen_scale;
return coord;
}
float calcScanLineWeight(float dist) {
return max(1.0 - dist * dist * u.scanline_weight, u.scanline_gap_brightness);
}
float calcScanLine(float dy, float filter_width) {
float weight = calcScanLineWeight(dy);
if (u.enable_multisample != 0) {
weight += calcScanLineWeight(dy - filter_width);
weight += calcScanLineWeight(dy + filter_width);
weight *= 0.3333333;
}
return weight;
}
void main() {
vec2 tex_size = vec2(u.texture_width, u.texture_height);
// filterWidth: equivalente al original (768.0 / TextureSize.y) / 3.0
float filter_width = (768.0 / u.texture_height) / 3.0;
vec2 texcoord = v_uv;
// Curvatura barrel opcional
if (u.enable_curvature != 0) {
texcoord = distort(texcoord, vec2(1.0, 1.0));
if (texcoord.x < 0.0) {
out_color = vec4(0.0, 0.0, 0.0, 1.0);
return;
}
}
vec2 texcoord_in_pixels = texcoord * tex_size;
vec2 tc;
float scan_line_weight;
if (u.enable_sharper != 0) {
// Modo SHARPER: filtrado bicúbico-like con subpixel sharpen
vec2 temp_coord = floor(texcoord_in_pixels) + 0.5;
tc = temp_coord / tex_size;
vec2 deltas = texcoord_in_pixels - temp_coord;
scan_line_weight = calcScanLine(deltas.y, filter_width);
vec2 signs = sign(deltas);
deltas.x *= 2.0;
deltas = deltas * deltas;
deltas.y = deltas.y * deltas.y;
deltas.x *= 0.5;
deltas.y *= 8.0;
deltas /= tex_size;
deltas *= signs;
tc = tc + deltas;
} else {
// Modo estándar
float temp_y = floor(texcoord_in_pixels.y) + 0.5;
float y_coord = temp_y / tex_size.y;
float dy = texcoord_in_pixels.y - temp_y;
scan_line_weight = calcScanLine(dy, filter_width);
float sign_y = sign(dy);
dy = dy * dy;
dy = dy * dy;
dy *= 8.0;
dy /= tex_size.y;
dy *= sign_y;
tc = vec2(texcoord.x, y_coord + dy);
}
vec3 colour = texture(Texture, tc).rgb;
if (u.enable_scanlines != 0) {
if (u.enable_gamma != 0) {
colour = pow(colour, vec3(u.input_gamma));
}
colour *= scan_line_weight * u.bloom_factor;
if (u.enable_gamma != 0) {
colour = pow(colour, vec3(1.0 / u.output_gamma));
}
}
// Máscara de fósforo
if (u.mask_type == 1) {
float which_mask = fract(gl_FragCoord.x * 0.5);
vec3 mask = (which_mask < 0.5)
? vec3(u.mask_brightness, 1.0, u.mask_brightness)
: vec3(1.0, u.mask_brightness, 1.0);
colour *= mask;
} else if (u.mask_type == 2) {
float which_mask = fract(gl_FragCoord.x * 0.3333333);
vec3 mask = vec3(u.mask_brightness);
if (which_mask < 0.3333333)
mask.x = 1.0;
else if (which_mask < 0.6666666)
mask.y = 1.0;
else
mask.z = 1.0;
colour *= mask;
}
out_color = vec4(colour, 1.0);
}

BIN
data/shaders/crtpi_frag.spv Normal file

Binary file not shown.

View File

@@ -0,0 +1,48 @@
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 0) out vec4 out_color;
layout(set = 2, binding = 0) uniform sampler2D source;
layout(set = 3, binding = 0) uniform DownscaleUniforms {
int algorithm; // 0 = Lanczos2 (ventana 2, ±2 taps), 1 = Lanczos3 (ventana 3, ±3 taps)
float pad0;
float pad1;
float pad2;
} u;
// Kernel Lanczos normalizado: sinc(t) * sinc(t/a) para |t| < a, 0 fuera.
float lanczos(float t, float a) {
t = abs(t);
if (t < 0.0001) { return 1.0; }
if (t >= a) { return 0.0; }
const float PI = 3.14159265358979;
float pt = PI * t;
return (a * sin(pt) * sin(pt / a)) / (pt * pt);
}
void main() {
vec2 src_size = vec2(textureSize(source, 0));
// Posición en coordenadas de texel (centros de texel en N+0.5)
vec2 p = v_uv * src_size;
vec2 p_floor = floor(p);
float a = (u.algorithm == 0) ? 2.0 : 3.0;
int win = int(a);
vec4 color = vec4(0.0);
float weight_sum = 0.0;
for (int j = -win; j <= win; j++) {
for (int i = -win; i <= win; i++) {
// Centro del texel (i,j) relativo a p_floor
vec2 tap_center = p_floor + vec2(float(i), float(j)) + 0.5;
vec2 offset = tap_center - p;
float w = lanczos(offset.x, a) * lanczos(offset.y, a);
color += texture(source, tap_center / src_size) * w;
weight_sum += w;
}
}
out_color = (weight_sum > 0.0) ? (color / weight_sum) : vec4(0.0, 0.0, 0.0, 1.0);
}

View File

@@ -23,9 +23,9 @@ layout(set = 3, binding = 0) uniform PostFXUniforms {
float curvature; float curvature;
float bleeding; float bleeding;
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_) float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
float time; // seconds since SDL init (for future animated effects) float time; // seconds since SDL init
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS) float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
float pad1; // padding — 48 bytes total (3 × 16) float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — 48 bytes total (3 × 16)
} u; } u;
// YCbCr helpers for NTSC bleeding // YCbCr helpers for NTSC bleeding
@@ -85,8 +85,8 @@ void main() {
colour = base; colour = base;
} }
// Aberración cromática // Aberración cromática (drift animado con time para efecto NTSC real)
float ca = u.chroma_strength * 0.005; float ca = u.chroma_strength * 0.005 * (1.0 + 0.15 * sin(u.time * 7.3));
colour.r = texture(scene, uv + vec2(ca, 0.0)).r; colour.r = texture(scene, uv + vec2(ca, 0.0)).r;
colour.b = texture(scene, uv - vec2(ca, 0.0)).b; colour.b = texture(scene, uv - vec2(ca, 0.0)).b;
@@ -96,17 +96,22 @@ void main() {
colour = mix(colour, lin, u.gamma_strength); colour = mix(colour, lin, u.gamma_strength);
} }
// Scanlines — 1 pixel físico oscuro por fila lógica. // Scanlines — proporción 2/3 brillantes + 1/3 oscuras por fila lógica.
// Usa uv.y (independiente del offset de letterbox) con pixel_scale para // Casos especiales: 1 subfila → sin efecto; 2 subfilas → 1+1 (50/50).
// calcular la posición dentro de la fila en coordenadas físicas. // Constantes ajustables:
// 3x: 1 dark + 2 bright. 4x: 1 dark + 3 bright. const float SCAN_DARK_RATIO = 0.333; // fracción de subfilas oscuras (ps >= 3)
// bright=3.5×, dark floor=0.42 (mantiene aspecto CRT original). const float SCAN_DARK_FLOOR = 0.42; // multiplicador de brillo de subfilas oscuras
if (u.scanline_strength > 0.0) { if (u.scanline_strength > 0.0) {
float ps = max(1.0, round(u.pixel_scale)); float ps = max(1.0, round(u.pixel_scale));
float frac_in_row = fract(uv.y * u.screen_height); float frac_in_row = fract(uv.y * u.screen_height);
float row_pos = floor(frac_in_row * ps); float row_pos = floor(frac_in_row * ps);
float is_dark = step(ps - 1.0, row_pos); // bright_rows: cuántas subfilas son brillantes
float scan = mix(3.5, 0.42, is_dark); // ps==1 → ps (todo brillante → is_dark nunca se activa)
// ps==2 → 1 brillante + 1 oscura
// ps>=3 → floor(ps * (1 - DARK_RATIO)) brillantes
float bright_rows = (ps < 2.0) ? ps : ((ps < 3.0) ? 1.0 : floor(ps * (1.0 - SCAN_DARK_RATIO)));
float is_dark = step(bright_rows, row_pos);
float scan = mix(1.0, SCAN_DARK_FLOOR, is_dark);
colour *= mix(1.0, scan, u.scanline_strength); colour *= mix(1.0, scan, u.scanline_strength);
} }
@@ -134,5 +139,11 @@ void main() {
colour = mix(colour, colour * mask, u.mask_strength); colour = mix(colour, colour * mask, u.mask_strength);
} }
// Parpadeo de fósforo CRT (~50 Hz)
if (u.flicker > 0.0) {
float flicker_wave = sin(u.time * 100.0) * 0.5 + 0.5;
colour *= 1.0 - u.flicker * 0.04 * flicker_wave;
}
out_color = vec4(colour, 1.0); out_color = vec4(colour, 1.0);
} }

Binary file not shown.

15
data/shaders/upscale.frag Normal file
View File

@@ -0,0 +1,15 @@
#version 450
// Vulkan GLSL fragment shader — Nearest-neighbour upscale pass
// Used as the first render pass when supersampling is active.
// Compile: glslc upscale.frag -o upscale.frag.spv
// xxd -i upscale.frag.spv > ../../source/core/rendering/sdl3gpu/upscale_frag_spv.h
layout(location = 0) in vec2 v_uv;
layout(location = 0) out vec4 out_color;
layout(set = 2, binding = 0) uniform sampler2D scene;
void main() {
out_color = texture(scene, v_uv);
}

Binary file not shown.

View File

@@ -41,7 +41,7 @@ void Audio::update() {
} }
// Reproduce la música // Reproduce la música
void Audio::playMusic(const std::string& name, const int loop) { void Audio::playMusic(const std::string& name, const int loop) { // NOLINT(readability-convert-member-functions-to-static)
bool new_loop = (loop != 0); bool new_loop = (loop != 0);
// Si ya está sonando exactamente la misma pista y mismo modo loop, no hacemos nada // Si ya está sonando exactamente la misma pista y mismo modo loop, no hacemos nada
@@ -71,7 +71,7 @@ void Audio::playMusic(const std::string& name, const int loop) {
} }
// Pausa la música // Pausa la música
void Audio::pauseMusic() { void Audio::pauseMusic() { // NOLINT(readability-convert-member-functions-to-static)
if (music_enabled_ && music_.state == MusicState::PLAYING) { if (music_enabled_ && music_.state == MusicState::PLAYING) {
JA_PauseMusic(); JA_PauseMusic();
music_.state = MusicState::PAUSED; music_.state = MusicState::PAUSED;
@@ -79,7 +79,7 @@ void Audio::pauseMusic() {
} }
// Continua la música pausada // Continua la música pausada
void Audio::resumeMusic() { void Audio::resumeMusic() { // NOLINT(readability-convert-member-functions-to-static)
if (music_enabled_ && music_.state == MusicState::PAUSED) { if (music_enabled_ && music_.state == MusicState::PAUSED) {
JA_ResumeMusic(); JA_ResumeMusic();
music_.state = MusicState::PLAYING; music_.state = MusicState::PLAYING;
@@ -87,7 +87,7 @@ void Audio::resumeMusic() {
} }
// Detiene la música // Detiene la música
void Audio::stopMusic() { void Audio::stopMusic() { // NOLINT(readability-make-member-function-const)
if (music_enabled_) { if (music_enabled_) {
JA_StopMusic(); JA_StopMusic();
music_.state = MusicState::STOPPED; music_.state = MusicState::STOPPED;
@@ -177,6 +177,18 @@ void Audio::initSDLAudio() {
JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2); JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2);
enable(Options::audio.enabled); enable(Options::audio.enabled);
// Aplicar estado de música y sonido guardado en las opciones.
// enable() ya aplica los volúmenes, pero no toca music_enabled_/sound_enabled_.
// Si alguno está desactivado, hay que forzar el volumen a 0 en el backend.
if (!Options::audio.music.enabled) {
setMusicVolume(0.0F); // music_enabled_=true aún → llega a JA
enableMusic(false);
}
if (!Options::audio.sound.enabled) {
setSoundVolume(0.0F); // sound_enabled_=true aún → llega a JA
enableSound(false);
}
std::cout << "\n** AUDIO SYSTEM **\n"; std::cout << "\n** AUDIO SYSTEM **\n";
std::cout << "Audio system initialized successfully\n"; std::cout << "Audio system initialized successfully\n";
} }

View File

@@ -7,9 +7,11 @@
#include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT #include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT
#include "core/locale/locale.hpp" // Para Locale #include "core/locale/locale.hpp" // Para Locale
#include "core/rendering/render_info.hpp" // Para RenderInfo
#include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/screen.hpp" // Para Screen
#include "game/options.hpp" // Para Options, options, OptionsVideo, Section #include "game/options.hpp" // Para Options, options, OptionsVideo, Section
#include "game/scene_manager.hpp" // Para SceneManager #include "game/scene_manager.hpp" // Para SceneManager
#include "game/ui/console.hpp" // Para Console
#include "game/ui/notifier.hpp" // Para Notifier, NotificationText #include "game/ui/notifier.hpp" // Para Notifier, NotificationText
#include "utils/utils.hpp" // Para stringInVector #include "utils/utils.hpp" // Para stringInVector
@@ -24,7 +26,7 @@ namespace GlobalInputs {
if (stringInVector(Notifier::get()->getCodes(), CODE)) { if (stringInVector(Notifier::get()->getCodes(), CODE)) {
SceneManager::current = SceneManager::Scene::TITLE; SceneManager::current = SceneManager::Scene::TITLE;
} else { } else {
Notifier::get()->show({Locale::get()->get("ui.press_again_menu")}, Notifier::Style::DEFAULT, -1, true, CODE); Notifier::get()->show({Locale::get()->get("ui.press_again_menu")}, Notifier::Style::DEFAULT, -1, true, CODE); // NOLINT(readability-static-accessed-through-instance)
} }
return; return;
} }
@@ -44,7 +46,7 @@ namespace GlobalInputs {
if (stringInVector(Notifier::get()->getCodes(), CODE)) { if (stringInVector(Notifier::get()->getCodes(), CODE)) {
SceneManager::current = SceneManager::Scene::QUIT; SceneManager::current = SceneManager::Scene::QUIT;
} else { } else {
Notifier::get()->show({Locale::get()->get("ui.press_again_exit")}, Notifier::Style::DEFAULT, -1, true, CODE); Notifier::get()->show({Locale::get()->get("ui.press_again_exit")}, Notifier::Style::DEFAULT, -1, true, CODE); // NOLINT(readability-static-accessed-through-instance)
} }
} }
@@ -68,66 +70,74 @@ namespace GlobalInputs {
void handleToggleBorder() { void handleToggleBorder() {
Screen::get()->toggleBorder(); Screen::get()->toggleBorder();
Notifier::get()->show({Locale::get()->get(Options::video.border.enabled ? "ui.border_enabled" : "ui.border_disabled")}); Notifier::get()->show({Locale::get()->get(Options::video.border.enabled ? "ui.border_enabled" : "ui.border_disabled")}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleToggleVideoMode() { void handleToggleVideoMode() {
Screen::get()->toggleVideoMode(); Screen::get()->toggleVideoMode();
Notifier::get()->show({Locale::get()->get(static_cast<int>(Options::video.fullscreen) == 0 ? "ui.fullscreen_disabled" : "ui.fullscreen_enabled")}); Notifier::get()->show({Locale::get()->get(static_cast<int>(Options::video.fullscreen) == 0 ? "ui.fullscreen_disabled" : "ui.fullscreen_enabled")}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleDecWindowZoom() { void handleDecWindowZoom() {
if (Screen::get()->decWindowZoom()) { if (Screen::get()->decWindowZoom()) {
Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)}); Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)}); // NOLINT(readability-static-accessed-through-instance)
} }
} }
void handleIncWindowZoom() { void handleIncWindowZoom() {
if (Screen::get()->incWindowZoom()) { if (Screen::get()->incWindowZoom()) {
Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)}); Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)}); // NOLINT(readability-static-accessed-through-instance)
} }
} }
void handleTogglePostFX() { void handleToggleShaders() {
Screen::get()->togglePostFX(); Screen::get()->toggleShaders();
Notifier::get()->show({Locale::get()->get(Options::video.postfx ? "ui.postfx_enabled" : "ui.postfx_disabled")}); Notifier::get()->show({Locale::get()->get(Options::video.postfx ? "ui.shaders_enabled" : "ui.shaders_disabled")}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleToggleSupersampling() { void handleNextShaderPreset() {
Screen::get()->toggleSupersampling(); if (Options::current_shader == Rendering::ShaderType::CRTPI) {
Notifier::get()->show({Locale::get()->get(Options::video.supersampling ? "ui.supersampling_enabled" : "ui.supersampling_disabled")}); if (!Options::crtpi_presets.empty()) {
Options::current_crtpi_preset = (Options::current_crtpi_preset + 1) % static_cast<int>(Options::crtpi_presets.size());
Screen::get()->reloadCrtPi();
Notifier::get()->show({Locale::get()->get("ui.crtpi") + " " + Options::crtpi_presets[static_cast<size_t>(Options::current_crtpi_preset)].name}); // NOLINT(readability-static-accessed-through-instance)
} }
} else {
void handleNextPostFXPreset() {
if (!Options::postfx_presets.empty()) { if (!Options::postfx_presets.empty()) {
Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size()); Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
Screen::get()->reloadPostFX(); Screen::get()->reloadPostFX();
Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)].name}); Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)].name}); // NOLINT(readability-static-accessed-through-instance)
} }
} }
}
void handleNextShader() {
Screen::get()->nextShader();
Notifier::get()->show({Locale::get()->get("ui.shader") + " " + // NOLINT(readability-static-accessed-through-instance)
(Options::current_shader == Rendering::ShaderType::CRTPI ? "CRTPI" : "POSTFX")});
}
void handleNextPalette() { void handleNextPalette() {
Screen::get()->nextPalette(); Screen::get()->nextPalette();
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + Options::video.palette}); Notifier::get()->show({Locale::get()->get("ui.palette") + " " + Options::video.palette}); // NOLINT(readability-static-accessed-through-instance)
} }
void handlePreviousPalette() { void handlePreviousPalette() {
Screen::get()->previousPalette(); Screen::get()->previousPalette();
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + Options::video.palette}); Notifier::get()->show({Locale::get()->get("ui.palette") + " " + Options::video.palette}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleToggleIntegerScale() { void handleToggleIntegerScale() {
Screen::get()->toggleIntegerScale(); Screen::get()->toggleIntegerScale();
Screen::get()->setVideoMode(Options::video.fullscreen); Screen::get()->setVideoMode(Options::video.fullscreen);
Notifier::get()->show({Locale::get()->get(Options::video.integer_scale ? "ui.integer_scale_enabled" : "ui.integer_scale_disabled")}); Notifier::get()->show({Locale::get()->get(Options::video.integer_scale ? "ui.integer_scale_enabled" : "ui.integer_scale_disabled")}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleToggleVSync() { void handleToggleVSync() {
Screen::get()->toggleVSync(); Screen::get()->toggleVSync();
Notifier::get()->show({Locale::get()->get(Options::video.vertical_sync ? "ui.vsync_enabled" : "ui.vsync_disabled")}); Notifier::get()->show({Locale::get()->get(Options::video.vertical_sync ? "ui.vsync_enabled" : "ui.vsync_disabled")}); // NOLINT(readability-static-accessed-through-instance)
} }
// Detecta qué acción global ha sido presionada (si alguna) // Detecta qué acción global ha sido presionada (si alguna)
auto getPressedAction() -> InputAction { auto getPressedAction() -> InputAction {
if (Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) {
@@ -177,6 +187,9 @@ namespace GlobalInputs {
if (Input::get()->checkAction(InputAction::SHOW_DEBUG_INFO, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::SHOW_DEBUG_INFO, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::SHOW_DEBUG_INFO; return InputAction::SHOW_DEBUG_INFO;
} }
if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_CONSOLE;
}
return InputAction::NONE; return InputAction::NONE;
} }
@@ -186,6 +199,16 @@ namespace GlobalInputs {
// Comprueba los inputs que se pueden introducir en cualquier sección del juego // Comprueba los inputs que se pueden introducir en cualquier sección del juego
void handle() { void handle() {
const bool CONSOLE_ACTIVE = Console::get() != nullptr && Console::get()->isActive();
if (CONSOLE_ACTIVE) {
// TAB/ESC cierran la consola en lugar de ejecutar sus acciones normales
if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT) ||
Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) {
Console::get()->toggle();
return;
}
} else {
// Salida de administrador en modo kiosko (Ctrl+Shift+Alt+Q) // Salida de administrador en modo kiosko (Ctrl+Shift+Alt+Q)
if (Options::kiosk.enabled) { if (Options::kiosk.enabled) {
SDL_Keymod mod = SDL_GetModState(); SDL_Keymod mod = SDL_GetModState();
@@ -195,10 +218,16 @@ namespace GlobalInputs {
return; return;
} }
} }
}
// Detectar qué acción global está siendo presionada // Detectar qué acción global está siendo presionada
InputAction action = getPressedAction(); InputAction action = getPressedAction();
// Con consola activa, ACCEPT (saltar sección) y EXIT están bloqueados
if (CONSOLE_ACTIVE && (action == InputAction::ACCEPT || action == InputAction::EXIT)) {
return;
}
// Ejecutar el handler correspondiente usando switch statement // Ejecutar el handler correspondiente usando switch statement
switch (action) { switch (action) {
case InputAction::EXIT: case InputAction::EXIT:
@@ -226,15 +255,15 @@ namespace GlobalInputs {
break; break;
case InputAction::TOGGLE_POSTFX: case InputAction::TOGGLE_POSTFX:
handleTogglePostFX(); handleToggleShaders();
break; break;
case InputAction::NEXT_POSTFX_PRESET: case InputAction::NEXT_POSTFX_PRESET:
handleNextPostFXPreset(); handleNextShaderPreset();
break; break;
case InputAction::TOGGLE_SUPERSAMPLING: case InputAction::TOGGLE_SUPERSAMPLING:
handleToggleSupersampling(); handleNextShader();
break; break;
case InputAction::NEXT_PALETTE: case InputAction::NEXT_PALETTE:
@@ -253,9 +282,13 @@ namespace GlobalInputs {
handleToggleVSync(); handleToggleVSync();
break; break;
case InputAction::TOGGLE_CONSOLE:
if (Console::get() != nullptr) { Console::get()->toggle(); }
break;
#ifdef _DEBUG #ifdef _DEBUG
case InputAction::TOGGLE_DEBUG: case InputAction::TOGGLE_DEBUG:
Screen::get()->toggleFPS(); if (RenderInfo::get() != nullptr) { RenderInfo::get()->toggle(); }
break; break;
case InputAction::SHOW_DEBUG_INFO: case InputAction::SHOW_DEBUG_INFO:

View File

@@ -14,7 +14,7 @@
Input* Input::instance = nullptr; Input* Input::instance = nullptr;
// Inicializa la instancia única del singleton // Inicializa la instancia única del singleton
void Input::init(const std::string& game_controller_db_path) { void Input::init(const std::string& game_controller_db_path) { // NOLINT(readability-convert-member-functions-to-static)
Input::instance = new Input(game_controller_db_path); Input::instance = new Input(game_controller_db_path);
} }
@@ -51,7 +51,8 @@ Input::Input(std::string game_controller_db_path)
{Action::TOGGLE_BORDER, KeyState{.scancode = SDL_SCANCODE_F9}}, {Action::TOGGLE_BORDER, KeyState{.scancode = SDL_SCANCODE_F9}},
{Action::TOGGLE_VSYNC, KeyState{.scancode = SDL_SCANCODE_F10}}, {Action::TOGGLE_VSYNC, KeyState{.scancode = SDL_SCANCODE_F10}},
{Action::PAUSE, KeyState{.scancode = SDL_SCANCODE_F11}}, {Action::PAUSE, KeyState{.scancode = SDL_SCANCODE_F11}},
{Action::TOGGLE_DEBUG, KeyState{.scancode = SDL_SCANCODE_F12}}}; {Action::TOGGLE_DEBUG, KeyState{.scancode = SDL_SCANCODE_F12}},
{Action::TOGGLE_CONSOLE, KeyState{.scancode = SDL_SCANCODE_TAB}}};
initSDLGamePad(); // Inicializa el subsistema SDL_INIT_GAMEPAD initSDLGamePad(); // Inicializa el subsistema SDL_INIT_GAMEPAD
} }
@@ -69,7 +70,7 @@ void Input::applyKeyboardBindingsFromOptions() {
} }
// Aplica configuración de botones del gamepad desde Options al primer gamepad conectado // Aplica configuración de botones del gamepad desde Options al primer gamepad conectado
void Input::applyGamepadBindingsFromOptions() { void Input::applyGamepadBindingsFromOptions() { // NOLINT(readability-convert-member-functions-to-static)
// Si no hay gamepads conectados, no hay nada que hacer // Si no hay gamepads conectados, no hay nada que hacer
if (gamepads_.empty()) { if (gamepads_.empty()) {
return; return;
@@ -90,21 +91,21 @@ void Input::applyGamepadBindingsFromOptions() {
} }
// Asigna inputs a botones del mando // Asigna inputs a botones del mando
void Input::bindGameControllerButton(const std::shared_ptr<Gamepad>& gamepad, Action action, SDL_GamepadButton button) { void Input::bindGameControllerButton(const std::shared_ptr<Gamepad>& gamepad, Action action, SDL_GamepadButton button) { // NOLINT(readability-convert-member-functions-to-static)
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(const 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) { // NOLINT(readability-convert-member-functions-to-static)
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, const std::shared_ptr<Gamepad>& gamepad) -> bool { auto Input::checkAction(Action action, bool repeat, bool check_keyboard, const std::shared_ptr<Gamepad>& gamepad) -> bool { // NOLINT(readability-convert-member-functions-to-static)
bool success_keyboard = false; bool success_keyboard = false;
bool success_controller = false; bool success_controller = false;
@@ -142,7 +143,7 @@ auto Input::checkAction(Action action, bool repeat, bool check_keyboard, const s
} }
// Comprueba si hay almenos una acción activa // Comprueba si hay almenos una acción activa
auto Input::checkAnyInput(bool check_keyboard, const std::shared_ptr<Gamepad>& gamepad) -> bool { auto Input::checkAnyInput(bool check_keyboard, const std::shared_ptr<Gamepad>& gamepad) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// 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 ---
@@ -179,7 +180,7 @@ auto Input::checkAnyInput(bool check_keyboard, const std::shared_ptr<Gamepad>& g
} }
// Comprueba si hay algún botón pulsado // Comprueba si hay algún botón pulsado
auto Input::checkAnyButton(bool repeat) -> bool { auto Input::checkAnyButton(bool repeat) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Solo comprueba los botones definidos previamente // Solo comprueba los botones definidos previamente
for (auto bi : BUTTON_INPUTS) { for (auto bi : BUTTON_INPUTS) {
// Comprueba el teclado // Comprueba el teclado
@@ -219,7 +220,7 @@ auto Input::getControllerNames() const -> std::vector<std::string> {
auto Input::getNumGamepads() const -> int { return gamepads_.size(); } auto Input::getNumGamepads() const -> int { return gamepads_.size(); }
// Obtiene el gamepad a partir de un event.id // Obtiene el gamepad a partir de un event.id
auto Input::getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Input::Gamepad> { auto Input::getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Input::Gamepad> { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& gamepad : gamepads_) { for (const auto& gamepad : gamepads_) {
if (gamepad->instance_id == id) { if (gamepad->instance_id == id) {
return gamepad; return gamepad;
@@ -228,7 +229,7 @@ auto Input::getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Input::Gamepa
return nullptr; return nullptr;
} }
auto Input::getGamepadByName(const std::string& name) const -> std::shared_ptr<Input::Gamepad> { auto Input::getGamepadByName(const std::string& name) const -> std::shared_ptr<Input::Gamepad> { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& gamepad : gamepads_) { for (const auto& gamepad : gamepads_) {
if (gamepad && gamepad->name == name) { if (gamepad && gamepad->name == name) {
return gamepad; return gamepad;
@@ -238,12 +239,12 @@ 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(const std::shared_ptr<Gamepad>& gamepad, Action action) -> SDL_GamepadButton { auto Input::getControllerBinding(const std::shared_ptr<Gamepad>& gamepad, Action action) -> SDL_GamepadButton { // NOLINT(readability-convert-member-functions-to-static)
return static_cast<SDL_GamepadButton>(gamepad->bindings[action].button); return static_cast<SDL_GamepadButton>(gamepad->bindings[action].button);
} }
// Comprueba el eje del mando // Comprueba el eje del mando
auto Input::checkAxisInput(Action action, const std::shared_ptr<Gamepad>& gamepad, bool repeat) -> bool { auto Input::checkAxisInput(Action action, const std::shared_ptr<Gamepad>& gamepad, bool repeat) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Obtener el binding configurado para esta acción // Obtener el binding configurado para esta acción
auto& binding = gamepad->bindings[action]; auto& binding = gamepad->bindings[action];
@@ -286,7 +287,7 @@ auto Input::checkAxisInput(Action action, const std::shared_ptr<Gamepad>& gamepa
} }
// Comprueba los triggers del mando como botones digitales // Comprueba los triggers del mando como botones digitales
auto Input::checkTriggerInput(Action action, const std::shared_ptr<Gamepad>& gamepad, bool repeat) -> bool { auto Input::checkTriggerInput(Action action, const std::shared_ptr<Gamepad>& gamepad, bool repeat) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Solo manejamos botones específicos que pueden ser triggers // Solo manejamos botones específicos que pueden ser triggers
if (gamepad->bindings[action].button != static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID)) { if (gamepad->bindings[action].button != static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID)) {
// Solo procesamos L2 y R2 como triggers // Solo procesamos L2 y R2 como triggers
@@ -333,13 +334,13 @@ auto Input::checkTriggerInput(Action action, const std::shared_ptr<Gamepad>& gam
return false; return false;
} }
void Input::addGamepadMappingsFromFile() { void Input::addGamepadMappingsFromFile() { // NOLINT(readability-convert-member-functions-to-static)
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() << '\n'; std::cout << "Error, could not load " << gamepad_mappings_file_.c_str() << " file: " << SDL_GetError() << '\n';
} }
} }
void Input::discoverGamepads() { void Input::discoverGamepads() { // NOLINT(readability-convert-member-functions-to-static)
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
handleEvent(event); // Comprueba mandos conectados handleEvent(event); // Comprueba mandos conectados
@@ -375,7 +376,7 @@ void Input::resetInputStates() {
} }
} }
void Input::update() { void Input::update() { // NOLINT(readability-convert-member-functions-to-static)
// --- TECLADO --- // --- TECLADO ---
const bool* key_states = SDL_GetKeyboardState(nullptr); const bool* key_states = SDL_GetKeyboardState(nullptr);
@@ -399,7 +400,7 @@ void Input::update() {
} }
} }
auto Input::handleEvent(const SDL_Event& event) -> std::string { auto Input::handleEvent(const SDL_Event& event) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
switch (event.type) { switch (event.type) {
case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_ADDED:
return addGamepad(event.gdevice.which); return addGamepad(event.gdevice.which);
@@ -409,7 +410,7 @@ auto Input::handleEvent(const SDL_Event& event) -> std::string {
return {}; return {};
} }
auto Input::addGamepad(int device_index) -> std::string { auto Input::addGamepad(int device_index) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
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() << '\n'; std::cerr << "Error al abrir el gamepad: " << SDL_GetError() << '\n';
@@ -423,8 +424,8 @@ auto Input::addGamepad(int device_index) -> std::string {
return name + " CONNECTED"; return name + " CONNECTED";
} }
auto Input::removeGamepad(SDL_JoystickID id) -> std::string { auto Input::removeGamepad(SDL_JoystickID id) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
auto it = std::ranges::find_if(gamepads_, [id](const std::shared_ptr<Gamepad>& gamepad) { auto it = std::ranges::find_if(gamepads_, [id](const std::shared_ptr<Gamepad>& gamepad) -> bool {
return gamepad->instance_id == id; return gamepad->instance_id == id;
}); });
@@ -438,7 +439,7 @@ auto Input::removeGamepad(SDL_JoystickID id) -> std::string {
return {}; return {};
} }
void Input::printConnectedGamepads() const { void Input::printConnectedGamepads() const { // NOLINT(readability-convert-member-functions-to-static)
if (gamepads_.empty()) { if (gamepads_.empty()) {
std::cout << "No hay gamepads conectados." << '\n'; std::cout << "No hay gamepads conectados." << '\n';
return; return;
@@ -452,7 +453,7 @@ void Input::printConnectedGamepads() const {
} }
} }
auto Input::findAvailableGamepadByName(const std::string& gamepad_name) -> std::shared_ptr<Input::Gamepad> { auto Input::findAvailableGamepadByName(const std::string& gamepad_name) -> std::shared_ptr<Input::Gamepad> { // NOLINT(readability-convert-member-functions-to-static)
// Si no hay gamepads disponibles, devolver gamepad por defecto // Si no hay gamepads disponibles, devolver gamepad por defecto
if (gamepads_.empty()) { if (gamepads_.empty()) {
return nullptr; return nullptr;

View File

@@ -101,12 +101,12 @@ class Input {
// --- Gestión de gamepads --- // --- Gestión de gamepads ---
[[nodiscard]] auto gameControllerFound() const -> bool; [[nodiscard]] auto gameControllerFound() const -> bool;
[[nodiscard]] auto getNumGamepads() const -> int; [[nodiscard]] auto getNumGamepads() const -> int;
auto getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Gamepad>; [[nodiscard]] auto getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Gamepad>;
auto getGamepadByName(const std::string& name) const -> std::shared_ptr<Input::Gamepad>; [[nodiscard]] auto getGamepadByName(const std::string& name) const -> std::shared_ptr<Input::Gamepad>;
auto getGamepads() const -> const Gamepads& { return gamepads_; } [[nodiscard]] auto getGamepads() const -> const Gamepads& { return gamepads_; }
auto findAvailableGamepadByName(const std::string& gamepad_name) -> std::shared_ptr<Gamepad>; auto findAvailableGamepadByName(const std::string& gamepad_name) -> std::shared_ptr<Gamepad>;
static auto getControllerName(const 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>; [[nodiscard]] auto getControllerNames() const -> std::vector<std::string>;
[[nodiscard]] static auto getControllerBinding(const std::shared_ptr<Gamepad>& gamepad, Action action) -> SDL_GamepadButton; [[nodiscard]] static auto getControllerBinding(const std::shared_ptr<Gamepad>& gamepad, Action action) -> SDL_GamepadButton;
void printConnectedGamepads() const; void printConnectedGamepads() const;

View File

@@ -33,6 +33,7 @@ enum class InputAction : int { // Acciones de entrada posibles en el juego
PREVIOUS_PALETTE, PREVIOUS_PALETTE,
SHOW_DEBUG_INFO, SHOW_DEBUG_INFO,
TOGGLE_DEBUG, TOGGLE_DEBUG,
TOGGLE_CONSOLE,
// Input obligatorio // Input obligatorio
NONE, NONE,

View File

@@ -5,13 +5,12 @@
#include <string> #include <string>
#include "external/fkyaml_node.hpp" // Para fkyaml::node #include "external/fkyaml_node.hpp" // Para fkyaml::node
#include "game/options.hpp" // Para Options::console
// [SINGLETON] // [SINGLETON]
Locale* Locale::instance = nullptr; Locale* Locale::instance = nullptr;
// [SINGLETON] Crea el objeto con esta función estática // [SINGLETON] Crea el objeto con esta función estática
void Locale::init(const std::string& file_path) { void Locale::init(const std::string& file_path) { // NOLINT(readability-convert-member-functions-to-static)
Locale::instance = new Locale(); Locale::instance = new Locale();
Locale::instance->loadFromFile(file_path); Locale::instance->loadFromFile(file_path);
} }
@@ -28,20 +27,18 @@ auto Locale::get() -> Locale* {
} }
// Devuelve la traducción de la clave o la clave como fallback // Devuelve la traducción de la clave o la clave como fallback
auto Locale::get(const std::string& key) const -> std::string { auto Locale::get(const std::string& key) const -> std::string { // NOLINT(readability-convert-member-functions-to-static)
auto it = strings_.find(key); auto it = strings_.find(key);
if (it != strings_.end()) { if (it != strings_.end()) {
return it->second; return it->second;
} }
if (Options::console) {
std::cerr << "Locale: clave no encontrada: " << key << '\n'; std::cerr << "Locale: clave no encontrada: " << key << '\n';
}
return key; return key;
} }
// Aplana un nodo YAML de forma recursiva: {a: {b: "val"}} -> {"a.b" -> "val"} // Aplana un nodo YAML de forma recursiva: {a: {b: "val"}} -> {"a.b" -> "val"}
void Locale::flatten(const void* node_ptr, const std::string& prefix) { void Locale::flatten(const void* node_ptr, const std::string& prefix) { // NOLINT(readability-convert-member-functions-to-static)
const auto& node = *static_cast<const fkyaml::node*>(node_ptr); const auto& node = *static_cast<const fkyaml::node*>(node_ptr);
for (auto itr = node.begin(); itr != node.end(); ++itr) { for (auto itr = node.begin(); itr != node.end(); ++itr) {
@@ -59,19 +56,15 @@ void Locale::flatten(const void* node_ptr, const std::string& prefix) {
} }
// Carga las traducciones desde el fichero YAML indicado // Carga las traducciones desde el fichero YAML indicado
void Locale::loadFromFile(const std::string& file_path) { void Locale::loadFromFile(const std::string& file_path) { // NOLINT(readability-convert-member-functions-to-static)
if (file_path.empty()) { if (file_path.empty()) {
if (Options::console) {
std::cerr << "Locale: ruta de fichero vacía, sin traducciones cargadas\n"; std::cerr << "Locale: ruta de fichero vacía, sin traducciones cargadas\n";
}
return; return;
} }
std::ifstream file(file_path); std::ifstream file(file_path);
if (!file.is_open()) { if (!file.is_open()) {
if (Options::console) {
std::cerr << "Locale: no se puede abrir " << file_path << '\n'; std::cerr << "Locale: no se puede abrir " << file_path << '\n';
}
return; return;
} }
@@ -79,12 +72,8 @@ void Locale::loadFromFile(const std::string& file_path) {
auto yaml = fkyaml::node::deserialize(file); auto yaml = fkyaml::node::deserialize(file);
flatten(&yaml, ""); flatten(&yaml, "");
if (Options::console) {
std::cout << "Locale: " << strings_.size() << " traducciones cargadas desde " << file_path << '\n'; std::cout << "Locale: " << strings_.size() << " traducciones cargadas desde " << file_path << '\n';
}
} catch (const fkyaml::exception& e) { } catch (const fkyaml::exception& e) {
if (Options::console) {
std::cerr << "Locale: error al parsear YAML: " << e.what() << '\n'; std::cerr << "Locale: error al parsear YAML: " << e.what() << '\n';
} }
}
} }

View File

@@ -15,7 +15,7 @@ namespace GIF {
} }
// Inicializa el diccionario LZW con los valores iniciales // Inicializa el diccionario LZW con los valores iniciales
inline void initializeDictionary(std::vector<DictionaryEntry>& dictionary, int code_length, int& dictionary_ind) { inline void initializeDictionary(std::vector<DictionaryEntry>& dictionary, int code_length, int& dictionary_ind) { // NOLINT(readability-identifier-naming)
int size = 1 << code_length; int size = 1 << code_length;
dictionary.resize(1 << (code_length + 1)); dictionary.resize(1 << (code_length + 1));
for (dictionary_ind = 0; dictionary_ind < size; dictionary_ind++) { for (dictionary_ind = 0; dictionary_ind < size; dictionary_ind++) {
@@ -55,7 +55,7 @@ namespace GIF {
} }
// Agrega una nueva entrada al diccionario // Agrega una nueva entrada al diccionario
inline void addDictionaryEntry(std::vector<DictionaryEntry>& dictionary, int& dictionary_ind, int& code_length, int prev, int code) { inline void addDictionaryEntry(std::vector<DictionaryEntry>& dictionary, int& dictionary_ind, int& code_length, int prev, int code) { // NOLINT(readability-identifier-naming)
uint8_t first_byte; uint8_t first_byte;
if (code == dictionary_ind) { if (code == dictionary_ind) {
first_byte = findFirstByte(dictionary, prev); first_byte = findFirstByte(dictionary, prev);
@@ -90,7 +90,7 @@ namespace GIF {
return match_len; return match_len;
} }
void Gif::decompress(int code_length, const uint8_t* input, int input_length, uint8_t* out) { void Gif::decompress(int code_length, const uint8_t* input, int input_length, uint8_t* out) { // NOLINT(readability-convert-member-functions-to-static)
// Verifica que el code_length tenga un rango razonable. // Verifica que el code_length tenga un rango razonable.
if (code_length < 2 || code_length > 12) { if (code_length < 2 || code_length > 12) {
throw std::runtime_error("Invalid LZW code length"); throw std::runtime_error("Invalid LZW code length");
@@ -146,7 +146,7 @@ namespace GIF {
} }
} }
auto Gif::readSubBlocks(const uint8_t*& buffer) -> std::vector<uint8_t> { auto Gif::readSubBlocks(const uint8_t*& buffer) -> std::vector<uint8_t> { // NOLINT(readability-convert-member-functions-to-static)
std::vector<uint8_t> data; std::vector<uint8_t> data;
uint8_t block_size = *buffer; uint8_t block_size = *buffer;
buffer++; buffer++;
@@ -159,7 +159,7 @@ namespace GIF {
return data; return data;
} }
auto Gif::processImageDescriptor(const uint8_t*& buffer, const std::vector<RGB>& gct, int resolution_bits) -> std::vector<uint8_t> { auto Gif::processImageDescriptor(const uint8_t*& buffer, const std::vector<RGB>& gct, int resolution_bits) -> std::vector<uint8_t> { // NOLINT(readability-convert-member-functions-to-static)
ImageDescriptor image_descriptor; ImageDescriptor image_descriptor;
// Lee 9 bytes para el image descriptor. // Lee 9 bytes para el image descriptor.
readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor)); readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor));
@@ -175,7 +175,7 @@ namespace GIF {
return uncompressed_data; return uncompressed_data;
} }
auto Gif::loadPalette(const uint8_t* buffer) -> std::vector<uint32_t> { auto Gif::loadPalette(const uint8_t* buffer) -> std::vector<uint32_t> { // NOLINT(readability-convert-member-functions-to-static)
uint8_t header[6]; uint8_t header[6];
std::memcpy(header, buffer, 6); std::memcpy(header, buffer, 6);
buffer += 6; buffer += 6;
@@ -186,7 +186,7 @@ namespace GIF {
std::vector<uint32_t> global_color_table; std::vector<uint32_t> global_color_table;
if ((screen_descriptor.fields & 0x80) != 0) { if ((screen_descriptor.fields & 0x80) != 0) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1)); int global_color_table_size = 1 << ((screen_descriptor.fields & 0x07) + 1);
global_color_table.resize(global_color_table_size); global_color_table.resize(global_color_table_size);
for (int i = 0; i < global_color_table_size; ++i) { for (int i = 0; i < global_color_table_size; ++i) {
uint8_t r = buffer[0]; uint8_t r = buffer[0];
@@ -199,7 +199,7 @@ namespace GIF {
return global_color_table; return global_color_table;
} }
auto Gif::processGifStream(const uint8_t* buffer, uint16_t& w, uint16_t& h) -> std::vector<uint8_t> { auto Gif::processGifStream(const uint8_t* buffer, uint16_t& w, uint16_t& h) -> std::vector<uint8_t> { // NOLINT(readability-convert-member-functions-to-static)
// Leer la cabecera de 6 bytes ("GIF87a" o "GIF89a") // Leer la cabecera de 6 bytes ("GIF87a" o "GIF89a")
uint8_t header[6]; uint8_t header[6];
std::memcpy(header, buffer, 6); std::memcpy(header, buffer, 6);
@@ -222,7 +222,7 @@ namespace GIF {
int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1; int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
std::vector<RGB> global_color_table; std::vector<RGB> global_color_table;
if ((screen_descriptor.fields & 0x80) != 0) { if ((screen_descriptor.fields & 0x80) != 0) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1)); int global_color_table_size = 1 << ((screen_descriptor.fields & 0x07) + 1);
global_color_table.resize(global_color_table_size); global_color_table.resize(global_color_table_size);
std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size); std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size);
buffer += 3 * global_color_table_size; buffer += 3 * global_color_table_size;

View File

@@ -65,11 +65,8 @@ PixelReveal::PixelReveal(int width, int height, float pixels_per_second, float s
} }
} }
// Destructor
PixelReveal::~PixelReveal() = default;
// Actualiza el estado del revelado // Actualiza el estado del revelado
void PixelReveal::update(float time_active) { void PixelReveal::update(float time_active) { // NOLINT(readability-make-member-function-const)
// En modo normal revela (pone transparente); en modo inverso cubre (pone negro) // En modo normal revela (pone transparente); en modo inverso cubre (pone negro)
const auto PIXEL_COLOR = reverse_ ? static_cast<Uint8>(PaletteColor::BLACK) : static_cast<Uint8>(PaletteColor::TRANSPARENT); const auto PIXEL_COLOR = reverse_ ? static_cast<Uint8>(PaletteColor::BLACK) : static_cast<Uint8>(PaletteColor::TRANSPARENT);
@@ -106,5 +103,5 @@ void PixelReveal::render(int dst_x, int dst_y) const {
// Indica si el revelado ha completado todas las filas // Indica si el revelado ha completado todas las filas
auto PixelReveal::isComplete() const -> bool { auto PixelReveal::isComplete() const -> bool {
return std::ranges::all_of(row_step_, [this](int s) { return s >= num_steps_; }); return std::ranges::all_of(row_step_, [this](int s) -> bool { return s >= num_steps_; });
} }

View File

@@ -16,8 +16,7 @@ class PixelReveal {
// Constructor // Constructor
PixelReveal(int width, int height, float pixels_per_second, float step_duration, int num_steps = 4, bool reverse = false, RevealMode mode = RevealMode::RANDOM); PixelReveal(int width, int height, float pixels_per_second, float step_duration, int num_steps = 4, bool reverse = false, RevealMode mode = RevealMode::RANDOM);
// Destructor definido en el .cpp para que unique_ptr<Surface> funcione con forward declaration ~PixelReveal() = default;
~PixelReveal();
// Actualiza el estado del revelado según el tiempo transcurrido // Actualiza el estado del revelado según el tiempo transcurrido
void update(float time_active); void update(float time_active);

View File

@@ -0,0 +1,158 @@
#include "core/rendering/render_info.hpp"
#include <SDL3/SDL.h>
#include <algorithm> // Para transform
#include <cmath> // Para round, floor
#include <iomanip> // Para setprecision
#include <sstream> // Para ostringstream
#include <string> // Para string
#include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/surface.hpp" // Para Surface
#include "core/rendering/text.hpp" // Para Text
#include "game/options.hpp" // Para Options
#include "game/ui/console.hpp" // Para Console
#include "game/ui/notifier.hpp" // Para Notifier
// [SINGLETON]
RenderInfo* RenderInfo::render_info = nullptr;
// [SINGLETON] Crearemos el objeto con esta función estática
void RenderInfo::init() {
RenderInfo::render_info = new RenderInfo();
}
// [SINGLETON] Destruiremos el objeto con esta función estática
void RenderInfo::destroy() {
delete RenderInfo::render_info;
RenderInfo::render_info = nullptr;
}
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
auto RenderInfo::get() -> RenderInfo* {
return RenderInfo::render_info;
}
// Constructor: en DEBUG se activa inmediatamente (notifica a Notifier del offset)
RenderInfo::RenderInfo() {
#ifdef _DEBUG
toggle();
#endif
}
// Actualiza la animación de entrada/salida del overlay
void RenderInfo::update(float delta_time) {
switch (status_) {
case Status::RISING:
y_ += SLIDE_SPEED * delta_time;
if (y_ >= 0.0F) {
y_ = 0.0F;
status_ = Status::ACTIVE;
}
break;
case Status::VANISHING:
y_ -= SLIDE_SPEED * delta_time;
if (y_ <= static_cast<float>(-HEIGHT)) {
y_ = static_cast<float>(-HEIGHT);
status_ = Status::HIDDEN;
}
break;
default:
break;
}
}
// Renderiza el overlay de información por pantalla
void RenderInfo::render() const {
if (status_ == Status::HIDDEN) { return; }
// FPS
std::string line = std::to_string(Screen::get()->getLastFPS()) + " fps";
// Driver GPU
const auto& driver = Screen::get()->getGPUDriver();
line += " | " + (driver.empty() ? std::string("sdl") : driver);
// Zoom calculado (alto físico / alto lógico), con coma decimal y sin ceros innecesarios
const float ROUNDED = std::round(Screen::get()->getZoomFactor() * 100.0F) / 100.0F;
std::string zoom_str;
if (ROUNDED == std::floor(ROUNDED)) {
zoom_str = std::to_string(static_cast<int>(ROUNDED));
} else {
std::ostringstream oss;
oss << std::fixed << std::setprecision(2) << ROUNDED;
zoom_str = oss.str();
if (zoom_str.back() == '0') { zoom_str.pop_back(); }
std::replace(zoom_str.begin(), zoom_str.end(), '.', ',');
}
line += " | " + zoom_str + "x";
// PostFX: muestra shader + preset y supersampling, o nada si está desactivado
if (Options::video.postfx) {
const bool IS_CRTPI = (Options::current_shader == Rendering::ShaderType::CRTPI);
const std::string SHADER_NAME = IS_CRTPI ? "crtpi" : "postfx";
std::string preset_name = "-";
if (IS_CRTPI) {
if (!Options::crtpi_presets.empty()) {
preset_name = Options::crtpi_presets[static_cast<size_t>(Options::current_crtpi_preset)].name;
}
} else {
if (!Options::postfx_presets.empty()) {
preset_name = Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)].name;
}
}
const bool SHOW_SS = Options::video.supersampling && !IS_CRTPI;
line += " | " + SHADER_NAME + " " + preset_name + (SHOW_SS ? " (ss)" : "");
}
// Todo en lowercase
std::transform(line.begin(), line.end(), line.begin(), [](unsigned char c) { return std::tolower(c); });
// Constantes visuales (igual que Console)
static constexpr Uint8 BG_COLOR = 0; // PaletteColor::BLACK
static constexpr Uint8 MSG_COLOR = 9; // PaletteColor::BRIGHT_GREEN
static constexpr int TEXT_SIZE = 6;
static constexpr int PADDING_V = TEXT_SIZE / 2 - 1;
// Fuente: preferir la de la consola si está disponible
auto text_obj = (Console::get() != nullptr) ? Console::get()->getText() : Screen::get()->getText();
// Posición Y: debajo de la consola + offset animado propio
const int CONSOLE_Y = (Console::get() != nullptr) ? Console::get()->getVisibleHeight() : 0;
const int Y = CONSOLE_Y + static_cast<int>(y_);
// Rectángulo de fondo: ancho completo, alto ajustado al texto
const SDL_FRect RECT = {
.x = 0.0F,
.y = static_cast<float>(Y),
.w = Options::game.width,
.h = static_cast<float>(TEXT_SIZE + (PADDING_V * 2))};
auto game_surface = Screen::get()->getGameSurface();
game_surface->fillRect(&RECT, BG_COLOR);
// game_surface->drawRectBorder(&RECT, BORDER_COLOR);
text_obj->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG,
static_cast<int>(Options::game.width / 2),
Y + PADDING_V,
line,
1,
MSG_COLOR);
}
// Activa o desactiva el overlay y notifica a Notifier del cambio de offset
void RenderInfo::toggle() {
switch (status_) {
case Status::HIDDEN:
status_ = Status::RISING;
Screen::get()->updateZoomFactor();
if (Notifier::get() != nullptr) { Notifier::get()->addYOffset(HEIGHT); }
break;
case Status::ACTIVE:
status_ = Status::VANISHING;
if (Notifier::get() != nullptr) { Notifier::get()->removeYOffset(HEIGHT); }
break;
default:
break;
}
}

View File

@@ -0,0 +1,34 @@
#pragma once
class RenderInfo {
public:
// Singleton
static void init();
static void destroy();
static auto get() -> RenderInfo*;
// Métodos principales
void update(float delta_time);
void render() const;
void toggle();
// Consultas
[[nodiscard]] auto isActive() const -> bool { return status_ != Status::HIDDEN; }
// Altura fija del overlay (TEXT_SIZE(6) + PADDING_V(2) * 2)
static constexpr int HEIGHT = 10;
static constexpr float SLIDE_SPEED = 120.0F;
private:
enum class Status { HIDDEN, RISING, ACTIVE, VANISHING };
// Singleton
static RenderInfo* render_info;
// Constructor y destructor privados [SINGLETON]
RenderInfo();
~RenderInfo() = default;
Status status_{Status::HIDDEN};
float y_{static_cast<float>(-HEIGHT)};
};

View File

@@ -4,12 +4,15 @@
#include <algorithm> // Para max, min, transform #include <algorithm> // Para max, min, transform
#include <cctype> // Para toupper #include <cctype> // Para toupper
#include <cmath> // Para round, floor
#include <cstring> // Para memcpy
#include <fstream> // Para basic_ostream, operator<<, endl, basic_... #include <fstream> // Para basic_ostream, operator<<, endl, basic_...
#include <iostream> // Para cerr #include <iostream> // Para cerr
#include <iterator> // Para istreambuf_iterator, operator== #include <iterator> // Para istreambuf_iterator, operator==
#include <string> // Para char_traits, string, operator+, operator== #include <string> // Para char_traits, string, operator+, operator==
#include "core/input/mouse.hpp" // Para updateCursorVisibility #include "core/input/mouse.hpp" // Para updateCursorVisibility
#include "core/rendering/render_info.hpp" // Para RenderInfo
#include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader #include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
#include "core/rendering/surface.hpp" // Para Surface, readPalFile #include "core/rendering/surface.hpp" // Para Surface, readPalFile
#include "core/rendering/text.hpp" // Para Text #include "core/rendering/text.hpp" // Para Text
@@ -17,6 +20,7 @@
#include "core/resources/resource_helper.hpp" // Para ResourceHelper #include "core/resources/resource_helper.hpp" // Para ResourceHelper
#include "core/resources/resource_list.hpp" // Para Asset, AssetType #include "core/resources/resource_list.hpp" // Para Asset, AssetType
#include "game/options.hpp" // Para Options, options, OptionsVideo, Border #include "game/options.hpp" // Para Options, options, OptionsVideo, Border
#include "game/ui/console.hpp" // Para Console
#include "game/ui/notifier.hpp" // Para Notifier #include "game/ui/notifier.hpp" // Para Notifier
// [SINGLETON] // [SINGLETON]
@@ -42,13 +46,15 @@ Screen::Screen()
: palettes_(Resource::List::get()->getListByType(Resource::List::Type::PALETTE)) { : palettes_(Resource::List::get()->getListByType(Resource::List::Type::PALETTE)) {
// Arranca SDL VIDEO, crea la ventana y el renderizador // Arranca SDL VIDEO, crea la ventana y el renderizador
initSDLVideo(); initSDLVideo();
if (Options::video.fullscreen) { if (Options::video.fullscreen) { SDL_HideCursor(); }
SDL_HideCursor();
} // Calcular tamaños y hacer .resize() de los buffers de píxeles
adjustWindowSize();
adjustRenderLogicalSize();
updateZoomFactor();
// Ajusta los tamaños // Ajusta los tamaños
game_surface_dstrect_ = {.x = Options::video.border.width, .y = Options::video.border.height, .w = Options::game.width, .h = Options::game.height}; game_surface_dstrect_ = {.x = Options::video.border.width, .y = Options::video.border.height, .w = Options::game.width, .h = Options::game.height};
// adjustWindowSize();
current_palette_ = findPalette(Options::video.palette); current_palette_ = findPalette(Options::video.palette);
// Define el color del borde para el modo de pantalla completa // Define el color del borde para el modo de pantalla completa
@@ -58,20 +64,16 @@ Screen::Screen()
game_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Options::game.width, Options::game.height); game_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Options::game.width, Options::game.height);
if (game_texture_ == nullptr) { if (game_texture_ == nullptr) {
// Registrar el error si está habilitado // Registrar el error si está habilitado
if (Options::console) {
std::cerr << "Error: game_texture_ could not be created!\nSDL Error: " << SDL_GetError() << '\n'; std::cerr << "Error: game_texture_ could not be created!\nSDL Error: " << SDL_GetError() << '\n';
} }
}
SDL_SetTextureScaleMode(game_texture_, SDL_SCALEMODE_NEAREST); SDL_SetTextureScaleMode(game_texture_, SDL_SCALEMODE_NEAREST);
// Crea la textura donde se dibuja el borde que rodea el area de juego // Crea la textura donde se dibuja el borde que rodea el area de juego
border_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Options::game.width + (Options::video.border.width * 2), Options::game.height + (Options::video.border.height * 2)); border_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Options::game.width + (Options::video.border.width * 2), Options::game.height + (Options::video.border.height * 2));
if (border_texture_ == nullptr) { if (border_texture_ == nullptr) {
// Registrar el error si está habilitado // Registrar el error si está habilitado
if (Options::console) {
std::cerr << "Error: border_texture_ could not be created!\nSDL Error: " << SDL_GetError() << '\n'; std::cerr << "Error: border_texture_ could not be created!\nSDL Error: " << SDL_GetError() << '\n';
} }
}
SDL_SetTextureScaleMode(border_texture_, SDL_SCALEMODE_NEAREST); SDL_SetTextureScaleMode(border_texture_, SDL_SCALEMODE_NEAREST);
// Cargar la paleta una sola vez // Cargar la paleta una sola vez
@@ -87,6 +89,10 @@ Screen::Screen()
border_surface_->setPalette(initial_palette); border_surface_->setPalette(initial_palette);
border_surface_->clear(border_color_); border_surface_->clear(border_color_);
// Cachear el color ARGB inicial del borde (borde sólido por defecto)
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
border_argb_color_ = border_pixel_buffer_[0];
// Establece la surface que actuará como renderer para recibir las llamadas a render() // Establece la surface que actuará como renderer para recibir las llamadas a render()
renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_); renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_);
@@ -144,8 +150,10 @@ void Screen::setVideoMode(bool mode) {
// Configura el modo de pantalla y ajusta la ventana // Configura el modo de pantalla y ajusta la ventana
SDL_SetWindowFullscreen(window_, Options::video.fullscreen); SDL_SetWindowFullscreen(window_, Options::video.fullscreen);
SDL_SyncWindow(window_);
adjustWindowSize(); adjustWindowSize();
adjustRenderLogicalSize(); adjustRenderLogicalSize();
updateZoomFactor();
} }
// Camibia entre pantalla completa y ventana // Camibia entre pantalla completa y ventana
@@ -190,6 +198,11 @@ auto Screen::incWindowZoom() -> bool {
void Screen::setBorderColor(Uint8 color) { void Screen::setBorderColor(Uint8 color) {
border_color_ = color; border_color_ = color;
border_surface_->clear(border_color_); border_surface_->clear(border_color_);
// Actualizar caché ARGB del borde sólido (ocurre una vez por habitación, no cada frame)
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
border_argb_color_ = border_pixel_buffer_[0];
border_is_solid_ = true;
} }
// Cambia entre borde visible y no visible // Cambia entre borde visible y no visible
@@ -206,14 +219,21 @@ void Screen::renderNotifications() const {
} }
} }
// Cambia el estado del PostFX // Activa/desactiva todos los shaders respetando el shader actualmente seleccionado
void Screen::togglePostFX() { void Screen::toggleShaders() {
Options::video.postfx = !Options::video.postfx; Options::video.postfx = !Options::video.postfx;
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) { if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
if (Options::video.postfx) { if (Options::video.postfx) {
applyCurrentPostFXPreset(); // Activar: usar el shader actualmente seleccionado
if (Options::current_shader == Rendering::ShaderType::CRTPI) {
shader_backend_->setActiveShader(Rendering::ShaderType::CRTPI);
applyCurrentCrtPiPreset();
} else { } else {
// Pass-through: efectos a 0, el shader copia la textura sin modificar applyCurrentPostFXPreset();
}
} else {
// Desactivar: pass-through con POSTFX (pipeline sin efecto)
shader_backend_->setActiveShader(Rendering::ShaderType::POSTFX);
shader_backend_->setPostFXParams(Rendering::PostFXParams{}); shader_backend_->setPostFXParams(Rendering::PostFXParams{});
} }
} else { } else {
@@ -232,10 +252,20 @@ void Screen::reloadPostFX() {
} }
} }
// Recarga el shader CrtPi del preset actual sin toggle
void Screen::reloadCrtPi() {
if (!shader_backend_) { return; }
applyCurrentCrtPiPreset();
}
// Actualiza la lógica de la clase (versión nueva con delta_time para escenas migradas) // Actualiza la lógica de la clase (versión nueva con delta_time para escenas migradas)
void Screen::update(float delta_time) { void Screen::update(float delta_time) {
fps_.calculate(SDL_GetTicks()); fps_.calculate(SDL_GetTicks());
Notifier::get()->update(delta_time); Notifier::get()->update(delta_time);
if (Console::get() != nullptr) {
Console::get()->update(delta_time);
}
if (RenderInfo::get() != nullptr) { RenderInfo::get()->update(delta_time); }
Mouse::updateCursorVisibility(); Mouse::updateCursorVisibility();
} }
@@ -244,21 +274,41 @@ void Screen::adjustWindowSize() {
window_width_ = Options::game.width + (Options::video.border.enabled ? Options::video.border.width * 2 : 0); window_width_ = Options::game.width + (Options::video.border.enabled ? Options::video.border.width * 2 : 0);
window_height_ = Options::game.height + (Options::video.border.enabled ? Options::video.border.height * 2 : 0); window_height_ = Options::game.height + (Options::video.border.enabled ? Options::video.border.height * 2 : 0);
// Establece el nuevo tamaño // Reservamos memoria una sola vez.
// Si el buffer es más pequeño que la superficie, crash asegurado.
border_pixel_buffer_.resize(static_cast<size_t>(window_width_ * window_height_));
game_pixel_buffer_.resize(static_cast<size_t>(Options::game.width * Options::game.height));
// border_pixel_buffer_ es el buffer que se sube a la GPU (tamaño total ventana).
if (Options::video.border.enabled) {
border_pixel_buffer_.resize(static_cast<size_t>(window_width_ * window_height_));
}
// Lógica de centrado y redimensionado de ventana SDL
if (static_cast<int>(Options::video.fullscreen) == 0) { if (static_cast<int>(Options::video.fullscreen) == 0) {
int old_width; int old_w, old_h;
int old_height; SDL_GetWindowSize(window_, &old_w, &old_h);
SDL_GetWindowSize(window_, &old_width, &old_height); int old_x, old_y;
SDL_GetWindowPosition(window_, &old_x, &old_y);
int old_pos_x; const int new_w = window_width_ * Options::window.zoom;
int old_pos_y; const int new_h = window_height_ * Options::window.zoom;
SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y); const int NEW_X = old_x + ((old_w - new_w) / 2);
const int NEW_Y = old_y + ((old_h - new_h) / 2);
const int NEW_POS_X = old_pos_x + ((old_width - (window_width_ * Options::window.zoom)) / 2); SDL_SetWindowSize(window_, new_w, new_h);
const int NEW_POS_Y = old_pos_y + ((old_height - (window_height_ * Options::window.zoom)) / 2);
SDL_SetWindowSize(window_, window_width_ * Options::window.zoom, window_height_ * Options::window.zoom); // En Wayland, SDL_SetWindowPosition es ignorado por el compositor (limitación de
SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS), std::max(NEW_POS_Y, 0)); // protocolo: el compositor controla la posición de ventanas toplevel). Solo se
// aplica en X11/Windows/macOS donde el posicionado funciona correctamente.
// SDL_SyncWindow garantiza que el resize esté completado antes de reposicionar
// (evita el race condition en X11).
SDL_SyncWindow(window_);
const char* driver = SDL_GetCurrentVideoDriver();
const bool is_wayland = (driver != nullptr && SDL_strcmp(driver, "wayland") == 0);
if (!is_wayland) {
SDL_SetWindowPosition(window_, std::max(NEW_X, WINDOWS_DECORATIONS), std::max(NEW_Y, 0));
}
} }
} }
@@ -267,6 +317,25 @@ void Screen::adjustRenderLogicalSize() {
SDL_SetRenderLogicalPresentation(renderer_, window_width_, window_height_, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX); SDL_SetRenderLogicalPresentation(renderer_, window_width_, window_height_, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
} }
// Recalcula y almacena el factor de zoom. Llamar solo cuando SDL ya ha estabilizado el estado de la ventana.
// En ventana: Options::window.zoom (siempre entero).
// En fullscreen: mínimo de las escalas en ambos ejes; floor si integer scale está activo.
void Screen::updateZoomFactor() {
if (!Options::video.fullscreen) {
zoom_factor_ = static_cast<float>(Options::window.zoom);
return;
}
if (window_width_ == 0 || window_height_ == 0) {
zoom_factor_ = 1.0F;
return;
}
int pw{0}, ph{0};
SDL_GetRenderOutputSize(renderer_, &pw, &ph);
const float SCALE = std::min(static_cast<float>(pw) / static_cast<float>(window_width_),
static_cast<float>(ph) / static_cast<float>(window_height_));
zoom_factor_ = Options::video.integer_scale ? std::floor(SCALE) : SCALE;
}
// Establece el renderizador para las surfaces // Establece el renderizador para las surfaces
void Screen::setRendererSurface(const std::shared_ptr<Surface>& surface) { void Screen::setRendererSurface(const std::shared_ptr<Surface>& surface) {
(surface) ? renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(surface) : renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_); (surface) ? renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(surface) : renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_);
@@ -294,7 +363,7 @@ void Screen::previousPalette() {
} }
// Establece la paleta // Establece la paleta
void Screen::setPalete() { void Screen::setPalete() { // NOLINT(readability-convert-member-functions-to-static)
game_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_palette_))); game_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_palette_)));
border_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_palette_))); border_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_palette_)));
@@ -308,6 +377,12 @@ void Screen::setPalete() {
// Convertir a mayúsculas // Convertir a mayúsculas
std::ranges::transform(Options::video.palette, Options::video.palette.begin(), ::toupper); std::ranges::transform(Options::video.palette, Options::video.palette.begin(), ::toupper);
// Actualizar caché si el borde es sólido (la paleta cambia el valor ARGB del color)
if (border_is_solid_) {
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
border_argb_color_ = border_pixel_buffer_[0];
}
} }
// Extrae los nombres de las paletas // Extrae los nombres de las paletas
@@ -318,7 +393,7 @@ void Screen::processPaletteList() {
} }
// Copia la surface a la textura // Copia la surface a la textura
void Screen::surfaceToTexture() { void Screen::surfaceToTexture() { // NOLINT(readability-convert-member-functions-to-static)
if (Options::video.border.enabled) { if (Options::video.border.enabled) {
border_surface_->copyToTexture(renderer_, border_texture_); border_surface_->copyToTexture(renderer_, border_texture_);
game_surface_->copyToTexture(renderer_, border_texture_, nullptr, &game_surface_dstrect_); game_surface_->copyToTexture(renderer_, border_texture_, nullptr, &game_surface_dstrect_);
@@ -329,58 +404,74 @@ void Screen::surfaceToTexture() {
// Copia la textura al renderizador (o hace el present GPU) // Copia la textura al renderizador (o hace el present GPU)
void Screen::textureToRenderer() { void Screen::textureToRenderer() {
SDL_Texture* texture_to_render = Options::video.border.enabled ? border_texture_ : game_texture_;
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) { if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
// ---- SDL3 GPU path: convertir Surface → ARGB → upload → PostFX/pass-through → present ---- const int GAME_W = Options::game.width;
if (Options::video.border.enabled) { const int GAME_H = Options::game.height;
// El border_surface_ solo tiene el color de borde; hay que componer encima el game_surface_
const int BORDER_W = static_cast<int>(border_surface_->getWidth());
const int BORDER_H = static_cast<int>(border_surface_->getHeight());
pixel_buffer_.resize(static_cast<size_t>(BORDER_W * BORDER_H));
border_surface_->toARGBBuffer(pixel_buffer_.data());
// Compositar game_surface_ en la posición correcta dentro del buffer if (Options::video.border.enabled) {
const int GAME_W = static_cast<int>(game_surface_->getWidth()); const int BORDER_W = window_width_;
const int GAME_H = static_cast<int>(game_surface_->getHeight()); const int BORDER_H = window_height_;
const int OFF_X = static_cast<int>(game_surface_dstrect_.x); const int OFF_X = static_cast<int>(game_surface_dstrect_.x);
const int OFF_Y = static_cast<int>(game_surface_dstrect_.y); const int OFF_Y = static_cast<int>(game_surface_dstrect_.y);
std::vector<Uint32> game_pixels(static_cast<size_t>(GAME_W * GAME_H));
game_surface_->toARGBBuffer(game_pixels.data()); if (border_is_solid_) {
for (int y = 0; y < GAME_H; ++y) { // Path A: borde sólido (gameplay normal)
for (int x = 0; x < GAME_W; ++x) { // Rellena solo el marco con el color cacheado — sin lookups de paleta.
pixel_buffer_[static_cast<size_t>(((OFF_Y + y) * BORDER_W) + (OFF_X + x))] = game_pixels[static_cast<size_t>((y * GAME_W) + x)]; // El área central (juego) se deja sin tocar; el overlay la sobreescribe igualmente.
// Franjas superior e inferior (ancho completo)
std::fill_n(border_pixel_buffer_.data(), OFF_Y * BORDER_W, border_argb_color_);
std::fill_n(&border_pixel_buffer_[(OFF_Y + GAME_H) * BORDER_W],
(BORDER_H - OFF_Y - GAME_H) * BORDER_W,
border_argb_color_);
// Columnas laterales en las filas del área de juego
for (int y = OFF_Y; y < OFF_Y + GAME_H; ++y) {
std::fill_n(&border_pixel_buffer_[y * BORDER_W], OFF_X, border_argb_color_);
std::fill_n(&border_pixel_buffer_[y * BORDER_W + OFF_X + GAME_W],
BORDER_W - OFF_X - GAME_W,
border_argb_color_);
} }
}
shader_backend_->uploadPixels(pixel_buffer_.data(), BORDER_W, BORDER_H);
} else { } else {
const int GAME_W = static_cast<int>(game_surface_->getWidth()); // Path B: borde dinámico (escena de carga — bandas de colores animadas)
const int GAME_H = static_cast<int>(game_surface_->getHeight()); // Conversión completa: la escena modifica border_surface_ cada frame
pixel_buffer_.resize(static_cast<size_t>(GAME_W * GAME_H)); border_surface_->toARGBBuffer(border_pixel_buffer_.data());
game_surface_->toARGBBuffer(pixel_buffer_.data());
shader_backend_->uploadPixels(pixel_buffer_.data(), GAME_W, GAME_H);
} }
// Overlay del juego sobre el centro del buffer (ambos paths)
game_surface_->toARGBBuffer(game_pixel_buffer_.data());
for (int y = 0; y < GAME_H; ++y) {
const Uint32* src = &game_pixel_buffer_[y * GAME_W];
Uint32* dst = &border_pixel_buffer_[(OFF_Y + y) * BORDER_W + OFF_X];
std::memcpy(dst, src, GAME_W * sizeof(Uint32));
}
shader_backend_->uploadPixels(border_pixel_buffer_.data(), BORDER_W, BORDER_H);
} else {
// Caso sin borde: subida directa simplificada
game_surface_->toARGBBuffer(game_pixel_buffer_.data());
shader_backend_->uploadPixels(game_pixel_buffer_.data(), GAME_W, GAME_H);
}
shader_backend_->render(); shader_backend_->render();
} else { } else {
// ---- SDL_Renderer path (fallback / no-shader) ---- // Fallback SDL_Renderer (mantiene tu lógica de texturas SDL)
SDL_Texture* tex = Options::video.border.enabled ? border_texture_ : game_texture_;
SDL_SetRenderTarget(renderer_, nullptr); SDL_SetRenderTarget(renderer_, nullptr);
SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(renderer_); SDL_RenderClear(renderer_);
SDL_RenderTexture(renderer_, texture_to_render, nullptr, nullptr); SDL_RenderTexture(renderer_, tex, nullptr, nullptr);
SDL_RenderPresent(renderer_); SDL_RenderPresent(renderer_);
} }
} }
// Renderiza todos los overlays // Renderiza todos los overlays (orden: último dibujado queda encima)
void Screen::renderOverlays() { void Screen::renderOverlays() {
renderNotifications(); renderNotifications(); // Notifier (abajo)
#ifdef _DEBUG if (RenderInfo::get() != nullptr) { RenderInfo::get()->render(); } // RenderInfo (medio)
renderInfo(); if (Console::get() != nullptr) { Console::get()->render(); } // Console (encima)
#endif
} }
// Localiza la paleta dentro del vector de paletas // Localiza la paleta dentro del vector de paletas
auto Screen::findPalette(const std::string& name) -> size_t { auto Screen::findPalette(const std::string& name) -> size_t { // NOLINT(readability-convert-member-functions-to-static)
std::string upper_name = toUpper(name + ".pal"); std::string upper_name = toUpper(name + ".pal");
for (size_t i = 0; i < palettes_.size(); ++i) { for (size_t i = 0; i < palettes_.size(); ++i) {
@@ -391,22 +482,6 @@ auto Screen::findPalette(const std::string& name) -> size_t {
return static_cast<size_t>(0); return static_cast<size_t>(0);
} }
// Muestra información por pantalla
void Screen::renderInfo() const {
if (show_fps_ && (Resource::Cache::get() != nullptr)) {
auto text = Resource::Cache::get()->getText("smb2");
auto color = static_cast<Uint8>(PaletteColor::YELLOW);
auto shadow = static_cast<Uint8>(PaletteColor::BLACK);
// FPS con sombra
const std::string FPS_TEXT = std::to_string(fps_.last_value) + " FPS";
const int FPS_X = Options::game.width - text->length(FPS_TEXT) - 1;
text->writeColored(FPS_X + 1, 1, FPS_TEXT, shadow);
text->writeColored(FPS_X, 0, FPS_TEXT, color);
}
}
// Limpia la game_surface_ // Limpia la game_surface_
void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); } void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); }
@@ -428,9 +503,6 @@ void Screen::hide() { SDL_HideWindow(window_); }
// Establece la visibilidad de las notificaciones // Establece la visibilidad de las notificaciones
void Screen::setNotificationsEnabled(bool value) { notifications_enabled_ = value; } void Screen::setNotificationsEnabled(bool value) { notifications_enabled_ = value; }
// Activa / desactiva el contador de FPS
void Screen::toggleFPS() { show_fps_ = !show_fps_; }
// Alterna entre activar y desactivar el escalado entero // Alterna entre activar y desactivar el escalado entero
void Screen::toggleIntegerScale() { void Screen::toggleIntegerScale() {
Options::video.integer_scale = !Options::video.integer_scale; Options::video.integer_scale = !Options::video.integer_scale;
@@ -438,6 +510,7 @@ void Screen::toggleIntegerScale() {
if (shader_backend_) { if (shader_backend_) {
shader_backend_->setScaleMode(Options::video.integer_scale); shader_backend_->setScaleMode(Options::video.integer_scale);
} }
updateZoomFactor();
} }
// Alterna entre activar y desactivar el V-Sync // Alterna entre activar y desactivar el V-Sync
@@ -452,13 +525,36 @@ void Screen::toggleVSync() {
// Getters // Getters
auto Screen::getRenderer() -> SDL_Renderer* { return renderer_; } auto Screen::getRenderer() -> SDL_Renderer* { return renderer_; }
auto Screen::getRendererSurface() -> std::shared_ptr<Surface> { return (*renderer_surface_); } auto Screen::getRendererSurface() -> std::shared_ptr<Surface> { return (*renderer_surface_); }
auto Screen::getBorderSurface() -> std::shared_ptr<Surface> { return border_surface_; } auto Screen::getGameSurface() -> std::shared_ptr<Surface> { return game_surface_; }
auto Screen::getBorderSurface() -> std::shared_ptr<Surface> {
border_is_solid_ = false; // Modificación externa → modo borde dinámico
return border_surface_;
}
auto loadData(const std::string& filepath) -> std::vector<uint8_t> { auto loadData(const std::string& filepath) -> std::vector<uint8_t> {
// Load using ResourceHelper (supports both filesystem and pack) // Load using ResourceHelper (supports both filesystem and pack)
return Resource::Helper::loadFile(filepath); return Resource::Helper::loadFile(filepath);
} }
void Screen::setLinearUpscale(bool linear) {
Options::video.linear_upscale = linear;
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
shader_backend_->setLinearUpscale(linear);
}
}
void Screen::setDownscaleAlgo(int algo) {
Options::video.downscale_algo = algo;
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
shader_backend_->setDownscaleAlgo(algo);
}
}
auto Screen::getSsTextureSize() const -> std::pair<int, int> {
if (!shader_backend_) { return {0, 0}; }
return shader_backend_->getSsTextureSize();
}
// Activa/desactiva el supersampling global (Ctrl+F4) // Activa/desactiva el supersampling global (Ctrl+F4)
void Screen::toggleSupersampling() { void Screen::toggleSupersampling() {
Options::video.supersampling = !Options::video.supersampling; Options::video.supersampling = !Options::video.supersampling;
@@ -468,18 +564,68 @@ void Screen::toggleSupersampling() {
} }
// Aplica los parámetros del preset actual al backend de shaders // Aplica los parámetros del preset actual al backend de shaders
void Screen::applyCurrentPostFXPreset() { void Screen::applyCurrentPostFXPreset() { // NOLINT(readability-convert-member-functions-to-static)
if (shader_backend_ && !Options::postfx_presets.empty()) { if (shader_backend_ && !Options::postfx_presets.empty()) {
const auto& p = Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)]; const auto& p = Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)];
// Supersampling es un toggle global (Options::video.supersampling), no por preset. // Supersampling es un toggle global (Options::video.supersampling), no por preset.
// setOversample primero: puede recrear texturas antes de que setPostFXParams // setOversample primero: puede recrear texturas antes de que setPostFXParams
// decida si hornear scanlines en CPU o aplicarlas en GPU. // decida si hornear scanlines en CPU o aplicarlas en GPU.
shader_backend_->setOversample(Options::video.supersampling ? 3 : 1); shader_backend_->setOversample(Options::video.supersampling ? 3 : 1);
Rendering::PostFXParams params{.vignette = p.vignette, .scanlines = p.scanlines, .chroma = p.chroma, .mask = p.mask, .gamma = p.gamma, .curvature = p.curvature, .bleeding = p.bleeding}; Rendering::PostFXParams params{.vignette = p.vignette, .scanlines = p.scanlines, .chroma = p.chroma, .mask = p.mask, .gamma = p.gamma, .curvature = p.curvature, .bleeding = p.bleeding, .flicker = p.flicker};
shader_backend_->setPostFXParams(params); shader_backend_->setPostFXParams(params);
} }
} }
// Aplica los parámetros del preset CrtPi actual al backend de shaders
void Screen::applyCurrentCrtPiPreset() { // NOLINT(readability-convert-member-functions-to-static)
if (shader_backend_ && !Options::crtpi_presets.empty()) {
const auto& p = Options::crtpi_presets[static_cast<size_t>(Options::current_crtpi_preset)];
Rendering::CrtPiParams params{
.scanline_weight = p.scanline_weight,
.scanline_gap_brightness = p.scanline_gap_brightness,
.bloom_factor = p.bloom_factor,
.input_gamma = p.input_gamma,
.output_gamma = p.output_gamma,
.mask_brightness = p.mask_brightness,
.curvature_x = p.curvature_x,
.curvature_y = p.curvature_y,
.mask_type = p.mask_type,
.enable_scanlines = p.enable_scanlines,
.enable_multisample = p.enable_multisample,
.enable_gamma = p.enable_gamma,
.enable_curvature = p.enable_curvature,
.enable_sharper = p.enable_sharper,
};
shader_backend_->setCrtPiParams(params);
}
}
// Cambia el shader de post-procesado activo y aplica el preset correspondiente
void Screen::setActiveShader(Rendering::ShaderType type) {
Options::current_shader = type;
if (!shader_backend_) { return; }
if (!Options::video.postfx) {
// Shaders desactivados: guardar preferencia pero mantener pass-through
shader_backend_->setActiveShader(Rendering::ShaderType::POSTFX);
shader_backend_->setPostFXParams(Rendering::PostFXParams{});
return;
}
shader_backend_->setActiveShader(type);
if (type == Rendering::ShaderType::CRTPI) {
applyCurrentCrtPiPreset();
} else {
applyCurrentPostFXPreset();
}
}
// Cicla al siguiente shader disponible (preparado para futura UI)
void Screen::nextShader() {
const Rendering::ShaderType NEXT = (Options::current_shader == Rendering::ShaderType::POSTFX)
? Rendering::ShaderType::CRTPI
: Rendering::ShaderType::POSTFX;
setActiveShader(NEXT);
}
// Inicializa los shaders // Inicializa los shaders
// El device GPU se crea siempre (independientemente de postfx) para evitar // El device GPU se crea siempre (independientemente de postfx) para evitar
// conflictos SDL_Renderer/SDL_GPU al hacer toggle F4 en Windows/Vulkan. // conflictos SDL_Renderer/SDL_GPU al hacer toggle F4 en Windows/Vulkan.
@@ -488,12 +634,16 @@ void Screen::initShaders() {
if (!shader_backend_) { if (!shader_backend_) {
shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>(); shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
shader_backend_->setPreferredDriver(Options::video.gpu_preferred_driver);
} }
shader_backend_->init(window_, tex, "", ""); shader_backend_->init(window_, tex, "", "");
gpu_driver_ = shader_backend_->getDriverName();
// Propagar flags de vsync e integer scale al backend GPU // Propagar flags de vsync, integer scale, upscale y downscale al backend GPU
shader_backend_->setVSync(Options::video.vertical_sync); shader_backend_->setVSync(Options::video.vertical_sync);
shader_backend_->setScaleMode(Options::video.integer_scale); shader_backend_->setScaleMode(Options::video.integer_scale);
shader_backend_->setLinearUpscale(Options::video.linear_upscale);
shader_backend_->setDownscaleAlgo(Options::video.downscale_algo);
if (Options::video.postfx) { if (Options::video.postfx) {
applyCurrentPostFXPreset(); applyCurrentPostFXPreset();
@@ -501,10 +651,16 @@ void Screen::initShaders() {
// Pass-through: todos los efectos a 0, el shader solo copia la textura // Pass-through: todos los efectos a 0, el shader solo copia la textura
shader_backend_->setPostFXParams(Rendering::PostFXParams{}); shader_backend_->setPostFXParams(Rendering::PostFXParams{});
} }
// Restaurar el shader activo guardado en config (y sus parámetros CrtPi si aplica)
shader_backend_->setActiveShader(Options::current_shader);
if (Options::current_shader == Rendering::ShaderType::CRTPI) {
applyCurrentCrtPiPreset();
}
} }
// Obtiene información sobre la pantalla // Obtiene información sobre la pantalla
void Screen::getDisplayInfo() { void Screen::getDisplayInfo() { // NOLINT(readability-convert-member-functions-to-static)
std::cout << "\n** VIDEO SYSTEM **\n"; std::cout << "\n** VIDEO SYSTEM **\n";
int num_displays = 0; int num_displays = 0;
@@ -609,7 +765,7 @@ auto Screen::initSDLVideo() -> bool {
} }
// Crea el objeto de texto // Crea el objeto de texto
void Screen::createText() { void Screen::createText() { // NOLINT(readability-convert-member-functions-to-static)
// Carga la surface de la fuente directamente del archivo // Carga la surface de la fuente directamente del archivo
auto surface = std::make_shared<Surface>(Resource::List::get()->get("aseprite.gif")); auto surface = std::make_shared<Surface>(Resource::List::get()->get("aseprite.gif"));

View File

@@ -6,14 +6,13 @@
#include <cstddef> // Para size_t #include <cstddef> // Para size_t
#include <memory> // Para shared_ptr, __shared_ptr_access #include <memory> // Para shared_ptr, __shared_ptr_access
#include <string> // Para string #include <string> // Para string
#include <utility> // Para std::pair
#include <vector> // Para vector #include <vector> // Para vector
#include "core/rendering/shader_backend.hpp" // Para Rendering::ShaderType, ShaderBackend
#include "utils/utils.hpp" // Para Color #include "utils/utils.hpp" // Para Color
class Surface; class Surface;
class Text; class Text;
namespace Rendering {
class ShaderBackend;
}
class Screen { class Screen {
public: public:
@@ -56,21 +55,31 @@ class Screen {
void nextPalette(); // Cambia a la siguiente paleta void nextPalette(); // Cambia a la siguiente paleta
void previousPalette(); // Cambia a la paleta anterior void previousPalette(); // Cambia a la paleta anterior
void setPalete(); // Establece la paleta actual void setPalete(); // Establece la paleta actual
void togglePostFX(); // Cambia el estado del PostFX void toggleShaders(); // Activa/desactiva todos los shaders respetando current_shader
void toggleSupersampling(); // Activa/desactiva el supersampling global void toggleSupersampling(); // Activa/desactiva el supersampling global
void reloadPostFX(); // Recarga el shader del preset actual sin toggle void reloadPostFX(); // Recarga el shader del preset actual sin toggle
void reloadCrtPi(); // Recarga el shader CrtPi del preset actual sin toggle
void setLinearUpscale(bool linear); // Upscale NEAREST (false) o LINEAR (true) en el paso SS
void setDownscaleAlgo(int algo); // 0=bilinear legacy, 1=Lanczos2, 2=Lanczos3
void setActiveShader(Rendering::ShaderType type); // Cambia el shader de post-procesado activo
void nextShader(); // Cicla al siguiente shader disponible (para futura UI)
// Surfaces y notificaciones // Surfaces y notificaciones
void setRendererSurface(const std::shared_ptr<Surface>& surface = nullptr); // Establece el renderizador para las surfaces void setRendererSurface(const std::shared_ptr<Surface>& surface = nullptr); // Establece el renderizador para las surfaces
void setNotificationsEnabled(bool value); // Establece la visibilidad de las notificaciones void setNotificationsEnabled(bool value); // Establece la visibilidad de las notificaciones
void toggleFPS(); // Activa o desactiva el contador de FPS void updateZoomFactor(); // Recalcula y almacena el factor de zoom real
// Getters // Getters
auto getRenderer() -> SDL_Renderer*; auto getRenderer() -> SDL_Renderer*;
auto getRendererSurface() -> std::shared_ptr<Surface>; auto getRendererSurface() -> std::shared_ptr<Surface>;
auto getBorderSurface() -> std::shared_ptr<Surface>; auto getBorderSurface() -> std::shared_ptr<Surface>;
auto getGameSurface() -> std::shared_ptr<Surface>;
[[nodiscard]] auto getText() const -> std::shared_ptr<Text> { return text_; } [[nodiscard]] auto getText() const -> std::shared_ptr<Text> { return text_; }
[[nodiscard]] auto getGameSurfaceDstRect() const -> SDL_FRect { return game_surface_dstrect_; } [[nodiscard]] auto getGameSurfaceDstRect() const -> SDL_FRect { return game_surface_dstrect_; }
[[nodiscard]] auto getGPUDriver() const -> const std::string& { return gpu_driver_; }
[[nodiscard]] auto getLastFPS() const -> int { return fps_.last_value; }
[[nodiscard]] auto getZoomFactor() const -> float { return zoom_factor_; }
[[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int>;
private: private:
// Estructuras // Estructuras
@@ -116,8 +125,8 @@ class Screen {
void renderOverlays(); // Renderiza todos los overlays void renderOverlays(); // Renderiza todos los overlays
auto findPalette(const std::string& name) -> size_t; // Localiza la paleta dentro del vector de paletas auto findPalette(const std::string& name) -> size_t; // Localiza la paleta dentro del vector de paletas
void initShaders(); // Inicializa los shaders void initShaders(); // Inicializa los shaders
void applyCurrentPostFXPreset(); // Aplica los parámetros del preset actual al backend void applyCurrentPostFXPreset(); // Aplica los parámetros del preset PostFX actual al backend
void renderInfo() const; // Muestra información por pantalla void applyCurrentCrtPiPreset(); // Aplica los parámetros del preset CrtPi actual al backend
void getDisplayInfo(); // Obtiene información sobre la pantalla void getDisplayInfo(); // Obtiene información sobre la pantalla
auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana
void createText(); // Crea el objeto de texto void createText(); // Crea el objeto de texto
@@ -139,9 +148,18 @@ class Screen {
std::unique_ptr<Rendering::ShaderBackend> shader_backend_; // Backend de shaders (OpenGL/Metal/Vulkan) std::unique_ptr<Rendering::ShaderBackend> shader_backend_; // Backend de shaders (OpenGL/Metal/Vulkan)
std::shared_ptr<Text> text_; // Objeto para escribir texto std::shared_ptr<Text> text_; // Objeto para escribir texto
// Buffers persistentes para evitar .resize() cada frame
std::vector<Uint32> game_pixel_buffer_; // Textura de juego
std::vector<Uint32> border_pixel_buffer_; // Textura de borde (composición final borde+juego)
// Caché del borde sólido (gameplay normal)
bool border_is_solid_{true}; // true = borde de color sólido; false = borde dinámico (carga)
Uint32 border_argb_color_{0}; // Color ARGB pre-convertido del borde sólido
// Configuración de ventana y pantalla // Configuración de ventana y pantalla
int window_width_{0}; // Ancho de la pantalla o ventana int window_width_{0}; // Ancho de la pantalla o ventana
int window_height_{0}; // Alto de la pantalla o ventana int window_height_{0}; // Alto de la pantalla o ventana
float zoom_factor_{1.0f}; // Factor de zoom calculado (alto físico / alto lógico)
SDL_FRect game_surface_dstrect_; // Coordenadas donde se dibuja la textura del juego SDL_FRect game_surface_dstrect_; // Coordenadas donde se dibuja la textura del juego
// Paletas y colores // Paletas y colores
@@ -156,11 +174,5 @@ class Screen {
// Shaders // Shaders
std::string info_resolution_; // Texto con la información de la pantalla std::string info_resolution_; // Texto con la información de la pantalla
std::vector<Uint32> pixel_buffer_; // Buffer intermedio para SDL3GPU path (surface → ARGB) std::string gpu_driver_; // Nombre del driver GPU (SDL3GPU), capturado en initShaders()
#ifdef _DEBUG
bool show_fps_{true}; // Indica si ha de mostrar el contador de FPS
#else
bool show_fps_{false}; // Indica si ha de mostrar el contador de FPS
#endif
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1445,5 +1445,6 @@ static const uint8_t kpostfx_vert_spv[] = {
0x38, 0x38,
0x00, 0x00,
0x01, 0x01,
0x00}; 0x00,
};
static const size_t kpostfx_vert_spv_size = 1444; static const size_t kpostfx_vert_spv_size = 1444;

File diff suppressed because it is too large Load Diff

View File

@@ -20,7 +20,42 @@ struct PostFXUniforms {
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_) float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
float time; // seconds since SDL init (SDL_GetTicks() / 1000.0f) float time; // seconds since SDL init (SDL_GetTicks() / 1000.0f)
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS) float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
float pad1; // padding — keep struct at 48 bytes (3 × 16) float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — keep struct at 48 bytes (3 × 16)
};
// CrtPi uniforms pushed to fragment stage each frame.
// Must match the MSL struct and GLSL uniform block layout.
// 14 fields (8 floats + 6 ints) + 2 floats (texture size) = 16 fields = 64 bytes — 4 × 16-byte alignment.
struct CrtPiUniforms {
// vec4 #0
float scanline_weight; // Ajuste gaussiano (default 6.0)
float scanline_gap_brightness; // Brillo mínimo entre scanlines (default 0.12)
float bloom_factor; // Factor brillo zonas iluminadas (default 3.5)
float input_gamma; // Gamma de entrada (default 2.4)
// vec4 #1
float output_gamma; // Gamma de salida (default 2.2)
float mask_brightness; // Brillo sub-píxeles máscara (default 0.80)
float curvature_x; // Distorsión barrel X (default 0.05)
float curvature_y; // Distorsión barrel Y (default 0.10)
// vec4 #2
int mask_type; // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
int enable_scanlines; // 0 = off, 1 = on
int enable_multisample; // 0 = off, 1 = on (antialiasing analítico)
int enable_gamma; // 0 = off, 1 = on
// vec4 #3
int enable_curvature; // 0 = off, 1 = on
int enable_sharper; // 0 = off, 1 = on
float texture_width; // Ancho del canvas en píxeles (inyectado en render)
float texture_height; // Alto del canvas en píxeles (inyectado en render)
};
// Downscale uniforms pushed to the Lanczos downscale fragment stage.
// 1 int + 3 floats = 16 bytes — meets Metal/Vulkan alignment.
struct DownscaleUniforms {
int algorithm; // 0 = Lanczos2 (ventana 2), 1 = Lanczos3 (ventana 3)
float pad0;
float pad1;
float pad2;
}; };
namespace Rendering { namespace Rendering {
@@ -47,6 +82,10 @@ namespace Rendering {
void cleanup() final; // Libera pipeline/texturas pero mantiene el device vivo void cleanup() final; // Libera pipeline/texturas pero mantiene el device vivo
void destroy(); // Limpieza completa (device + swapchain); llamar solo al cerrar void destroy(); // Limpieza completa (device + swapchain); llamar solo al cerrar
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; } [[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
[[nodiscard]] auto getDriverName() const -> std::string override { return driver_name_; }
// Establece el driver GPU preferido (vacío = auto). Debe llamarse antes de init().
void setPreferredDriver(const std::string& driver) override { preferred_driver_ = driver; }
// Sube píxeles ARGB8888 desde CPU; llamado antes de render() // Sube píxeles ARGB8888 desde CPU; llamado antes de render()
void uploadPixels(const Uint32* pixels, int width, int height) override; void uploadPixels(const Uint32* pixels, int width, int height) override;
@@ -63,6 +102,24 @@ namespace Rendering {
// Establece factor de supersampling (1 = off, 3 = 3×SS) // Establece factor de supersampling (1 = off, 3 = 3×SS)
void setOversample(int factor) override; void setOversample(int factor) override;
// Activa/desactiva interpolación LINEAR en el upscale (false = NEAREST)
void setLinearUpscale(bool linear) override;
// Selecciona algoritmo de downscale: 0=bilinear legacy, 1=Lanczos2, 2=Lanczos3
void setDownscaleAlgo(int algo) override;
// Devuelve las dimensiones de la textura de supersampling (0,0 si SS desactivado)
[[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int> override;
// Selecciona el shader de post-procesado activo (POSTFX o CRTPI)
void setActiveShader(ShaderType type) override;
// Actualiza los parámetros del shader CRT-Pi
void setCrtPiParams(const CrtPiParams& p) override;
// Devuelve el shader activo
[[nodiscard]] auto getActiveShader() const -> ShaderType override { return active_shader_; }
private: private:
static auto createShaderMSL(SDL_GPUDevice* device, static auto createShaderMSL(SDL_GPUDevice* device,
const char* msl_source, const char* msl_source,
@@ -80,27 +137,42 @@ namespace Rendering {
Uint32 num_uniform_buffers) -> SDL_GPUShader*; Uint32 num_uniform_buffers) -> SDL_GPUShader*;
auto createPipeline() -> bool; auto createPipeline() -> bool;
auto reinitTexturesAndBuffer() -> bool; // Recrea textura y buffer con oversample actual auto createCrtPiPipeline() -> bool; // Pipeline dedicado para el shader CrtPi
auto reinitTexturesAndBuffer() -> bool; // Recrea scene_texture_ y upload_buffer_
auto recreateScaledTexture(int factor) -> bool; // Recrea scaled_texture_ para factor dado
static auto calcSsFactor(float zoom) -> int; // Primer múltiplo de 3 >= zoom (mín 3)
// Devuelve el mejor present mode disponible: IMMEDIATE > MAILBOX > VSYNC
[[nodiscard]] auto bestPresentMode(bool vsync) const -> SDL_GPUPresentMode;
SDL_Window* window_ = nullptr; SDL_Window* window_ = nullptr;
SDL_GPUDevice* device_ = nullptr; SDL_GPUDevice* device_ = nullptr;
SDL_GPUGraphicsPipeline* pipeline_ = nullptr; SDL_GPUGraphicsPipeline* pipeline_ = nullptr; // PostFX pass (→ swapchain o → postfx_texture_)
SDL_GPUTexture* scene_texture_ = nullptr; SDL_GPUGraphicsPipeline* crtpi_pipeline_ = nullptr; // CrtPi pass (→ swapchain directo, sin SS)
SDL_GPUGraphicsPipeline* postfx_offscreen_pipeline_ = nullptr; // PostFX → postfx_texture_ (B8G8R8A8, solo con Lanczos)
SDL_GPUGraphicsPipeline* upscale_pipeline_ = nullptr; // Upscale pass (solo con SS)
SDL_GPUGraphicsPipeline* downscale_pipeline_ = nullptr; // Lanczos downscale (solo con SS + algo > 0)
SDL_GPUTexture* scene_texture_ = nullptr; // Canvas del juego (game_width_ × game_height_)
SDL_GPUTexture* scaled_texture_ = nullptr; // Upscale target (game×factor), solo con SS
SDL_GPUTexture* postfx_texture_ = nullptr; // PostFX output a resolución escalada, solo con Lanczos
SDL_GPUTransferBuffer* upload_buffer_ = nullptr; SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
SDL_GPUSampler* sampler_ = nullptr; // NEAREST — para path sin supersampling SDL_GPUSampler* sampler_ = nullptr; // NEAREST
SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR — para path con supersampling SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR
PostFXUniforms uniforms_{.vignette_strength = 0.6F, .chroma_strength = 0.15F, .scanline_strength = 0.7F, .screen_height = 192.0F, .pixel_scale = 1.0F, .oversample = 1.0F}; PostFXUniforms uniforms_{.vignette_strength = 0.6F, .chroma_strength = 0.15F, .scanline_strength = 0.7F, .screen_height = 192.0F, .pixel_scale = 1.0F, .oversample = 1.0F};
CrtPiUniforms crtpi_uniforms_{.scanline_weight = 6.0F, .scanline_gap_brightness = 0.12F, .bloom_factor = 3.5F, .input_gamma = 2.4F, .output_gamma = 2.2F, .mask_brightness = 0.80F, .curvature_x = 0.05F, .curvature_y = 0.10F, .mask_type = 2, .enable_scanlines = 1, .enable_multisample = 1, .enable_gamma = 1};
ShaderType active_shader_ = ShaderType::POSTFX; // Shader de post-procesado activo
int game_width_ = 0; // Dimensiones originales del canvas (sin SS) int game_width_ = 0; // Dimensiones originales del canvas
int game_height_ = 0; int game_height_ = 0;
int tex_width_ = 0; // Dimensiones de la textura GPU (game × oversample_) int ss_factor_ = 0; // Factor SS activo (3, 6, 9...) o 0 si SS desactivado
int tex_height_ = 0; int oversample_ = 1; // SS on/off (1 = off, >1 = on)
int oversample_ = 1; // Factor SS actual (1 o 3) int downscale_algo_ = 1; // 0 = bilinear legacy, 1 = Lanczos2, 2 = Lanczos3
float baked_scanline_strength_ = 0.0F; // Guardado para hornear en CPU std::string driver_name_;
std::string preferred_driver_; // Driver preferido; vacío = auto (SDL elige)
bool is_initialized_ = false; bool is_initialized_ = false;
bool vsync_ = true; bool vsync_ = true;
bool integer_scale_ = false; bool integer_scale_ = false;
bool linear_upscale_ = false; // Upscale NEAREST (false) o LINEAR (true)
}; };
} // namespace Rendering } // namespace Rendering

View File

@@ -0,0 +1,634 @@
#pragma once
#include <cstddef>
#include <cstdint>
static const uint8_t kupscale_frag_spv[] = {
0x03,
0x02,
0x23,
0x07,
0x00,
0x00,
0x01,
0x00,
0x0b,
0x00,
0x0d,
0x00,
0x14,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x11,
0x00,
0x02,
0x00,
0x01,
0x00,
0x00,
0x00,
0x0b,
0x00,
0x06,
0x00,
0x01,
0x00,
0x00,
0x00,
0x47,
0x4c,
0x53,
0x4c,
0x2e,
0x73,
0x74,
0x64,
0x2e,
0x34,
0x35,
0x30,
0x00,
0x00,
0x00,
0x00,
0x0e,
0x00,
0x03,
0x00,
0x00,
0x00,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x0f,
0x00,
0x07,
0x00,
0x04,
0x00,
0x00,
0x00,
0x04,
0x00,
0x00,
0x00,
0x6d,
0x61,
0x69,
0x6e,
0x00,
0x00,
0x00,
0x00,
0x09,
0x00,
0x00,
0x00,
0x11,
0x00,
0x00,
0x00,
0x10,
0x00,
0x03,
0x00,
0x04,
0x00,
0x00,
0x00,
0x07,
0x00,
0x00,
0x00,
0x03,
0x00,
0x03,
0x00,
0x02,
0x00,
0x00,
0x00,
0xc2,
0x01,
0x00,
0x00,
0x04,
0x00,
0x0a,
0x00,
0x47,
0x4c,
0x5f,
0x47,
0x4f,
0x4f,
0x47,
0x4c,
0x45,
0x5f,
0x63,
0x70,
0x70,
0x5f,
0x73,
0x74,
0x79,
0x6c,
0x65,
0x5f,
0x6c,
0x69,
0x6e,
0x65,
0x5f,
0x64,
0x69,
0x72,
0x65,
0x63,
0x74,
0x69,
0x76,
0x65,
0x00,
0x00,
0x04,
0x00,
0x08,
0x00,
0x47,
0x4c,
0x5f,
0x47,
0x4f,
0x4f,
0x47,
0x4c,
0x45,
0x5f,
0x69,
0x6e,
0x63,
0x6c,
0x75,
0x64,
0x65,
0x5f,
0x64,
0x69,
0x72,
0x65,
0x63,
0x74,
0x69,
0x76,
0x65,
0x00,
0x05,
0x00,
0x04,
0x00,
0x04,
0x00,
0x00,
0x00,
0x6d,
0x61,
0x69,
0x6e,
0x00,
0x00,
0x00,
0x00,
0x05,
0x00,
0x05,
0x00,
0x09,
0x00,
0x00,
0x00,
0x6f,
0x75,
0x74,
0x5f,
0x63,
0x6f,
0x6c,
0x6f,
0x72,
0x00,
0x00,
0x00,
0x05,
0x00,
0x04,
0x00,
0x0d,
0x00,
0x00,
0x00,
0x73,
0x63,
0x65,
0x6e,
0x65,
0x00,
0x00,
0x00,
0x05,
0x00,
0x04,
0x00,
0x11,
0x00,
0x00,
0x00,
0x76,
0x5f,
0x75,
0x76,
0x00,
0x00,
0x00,
0x00,
0x47,
0x00,
0x04,
0x00,
0x09,
0x00,
0x00,
0x00,
0x1e,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x47,
0x00,
0x04,
0x00,
0x0d,
0x00,
0x00,
0x00,
0x21,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x47,
0x00,
0x04,
0x00,
0x0d,
0x00,
0x00,
0x00,
0x22,
0x00,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x47,
0x00,
0x04,
0x00,
0x11,
0x00,
0x00,
0x00,
0x1e,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x13,
0x00,
0x02,
0x00,
0x02,
0x00,
0x00,
0x00,
0x21,
0x00,
0x03,
0x00,
0x03,
0x00,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x16,
0x00,
0x03,
0x00,
0x06,
0x00,
0x00,
0x00,
0x20,
0x00,
0x00,
0x00,
0x17,
0x00,
0x04,
0x00,
0x07,
0x00,
0x00,
0x00,
0x06,
0x00,
0x00,
0x00,
0x04,
0x00,
0x00,
0x00,
0x20,
0x00,
0x04,
0x00,
0x08,
0x00,
0x00,
0x00,
0x03,
0x00,
0x00,
0x00,
0x07,
0x00,
0x00,
0x00,
0x3b,
0x00,
0x04,
0x00,
0x08,
0x00,
0x00,
0x00,
0x09,
0x00,
0x00,
0x00,
0x03,
0x00,
0x00,
0x00,
0x19,
0x00,
0x09,
0x00,
0x0a,
0x00,
0x00,
0x00,
0x06,
0x00,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x1b,
0x00,
0x03,
0x00,
0x0b,
0x00,
0x00,
0x00,
0x0a,
0x00,
0x00,
0x00,
0x20,
0x00,
0x04,
0x00,
0x0c,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x0b,
0x00,
0x00,
0x00,
0x3b,
0x00,
0x04,
0x00,
0x0c,
0x00,
0x00,
0x00,
0x0d,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x17,
0x00,
0x04,
0x00,
0x0f,
0x00,
0x00,
0x00,
0x06,
0x00,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x20,
0x00,
0x04,
0x00,
0x10,
0x00,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x0f,
0x00,
0x00,
0x00,
0x3b,
0x00,
0x04,
0x00,
0x10,
0x00,
0x00,
0x00,
0x11,
0x00,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x36,
0x00,
0x05,
0x00,
0x02,
0x00,
0x00,
0x00,
0x04,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x03,
0x00,
0x00,
0x00,
0xf8,
0x00,
0x02,
0x00,
0x05,
0x00,
0x00,
0x00,
0x3d,
0x00,
0x04,
0x00,
0x0b,
0x00,
0x00,
0x00,
0x0e,
0x00,
0x00,
0x00,
0x0d,
0x00,
0x00,
0x00,
0x3d,
0x00,
0x04,
0x00,
0x0f,
0x00,
0x00,
0x00,
0x12,
0x00,
0x00,
0x00,
0x11,
0x00,
0x00,
0x00,
0x57,
0x00,
0x05,
0x00,
0x07,
0x00,
0x00,
0x00,
0x13,
0x00,
0x00,
0x00,
0x0e,
0x00,
0x00,
0x00,
0x12,
0x00,
0x00,
0x00,
0x3e,
0x00,
0x03,
0x00,
0x09,
0x00,
0x00,
0x00,
0x13,
0x00,
0x00,
0x00,
0xfd,
0x00,
0x01,
0x00,
0x38,
0x00,
0x01,
0x00,
};
static const size_t kupscale_frag_spv_size = 628;

View File

@@ -3,9 +3,14 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <string> #include <string>
#include <utility>
namespace Rendering { namespace Rendering {
/** @brief Identificador del shader de post-procesado activo */
enum class ShaderType { POSTFX,
CRTPI };
/** /**
* @brief Parámetros de intensidad de los efectos PostFX * @brief Parámetros de intensidad de los efectos PostFX
* Definido a nivel de namespace para facilitar el uso desde subclases y screen.cpp * Definido a nivel de namespace para facilitar el uso desde subclases y screen.cpp
@@ -18,6 +23,28 @@ namespace Rendering {
float gamma = 0.0F; // Corrección gamma (blend 0=off, 1=full) float gamma = 0.0F; // Corrección gamma (blend 0=off, 1=full)
float curvature = 0.0F; // Curvatura barrel CRT float curvature = 0.0F; // Curvatura barrel CRT
float bleeding = 0.0F; // Sangrado de color NTSC float bleeding = 0.0F; // Sangrado de color NTSC
float flicker = 0.0F; // Parpadeo de fósforo CRT ~50 Hz
};
/**
* @brief Parámetros del shader CRT-Pi (algoritmo de scanlines continuas)
* Diferente al PostFX: usa pesos gaussianos por distancia subpixel y bloom.
*/
struct CrtPiParams {
float scanline_weight{6.0F}; // Ajuste gaussiano (mayor = scanlines más estrechas)
float scanline_gap_brightness{0.12F}; // Brillo mínimo en las ranuras entre scanlines
float bloom_factor{3.5F}; // Factor de brillo para zonas iluminadas
float input_gamma{2.4F}; // Gamma de entrada (linealización)
float output_gamma{2.2F}; // Gamma de salida (codificación)
float mask_brightness{0.80F}; // Sub-píxeles tenues en la máscara de fósforo
float curvature_x{0.05F}; // Distorsión barrel eje X
float curvature_y{0.10F}; // Distorsión barrel eje Y
int mask_type{2}; // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
bool enable_scanlines{true}; // Activar efecto de scanlines
bool enable_multisample{true}; // Antialiasing analítico de scanlines
bool enable_gamma{true}; // Corrección gamma
bool enable_curvature{false}; // Distorsión barrel CRT
bool enable_sharper{false}; // Submuestreo más nítido (modo SHARPER)
}; };
/** /**
@@ -89,11 +116,60 @@ namespace Rendering {
*/ */
virtual void setOversample(int /*factor*/) {} virtual void setOversample(int /*factor*/) {}
/**
* @brief Activa/desactiva interpolación LINEAR en el paso de upscale (SS).
* Por defecto NEAREST (false). Solo tiene efecto con supersampling activo.
*/
virtual void setLinearUpscale(bool /*linear*/) {}
[[nodiscard]] virtual auto isLinearUpscale() const -> bool { return false; }
/**
* @brief Selecciona el algoritmo de downscale tras el PostFX (SS activo).
* 0 = bilinear legacy (comportamiento actual, sin textura intermedia),
* 1 = Lanczos2 (ventana 2, ~25 muestras), 2 = Lanczos3 (ventana 3, ~49 muestras).
*/
virtual void setDownscaleAlgo(int /*algo*/) {}
[[nodiscard]] virtual auto getDownscaleAlgo() const -> int { return 0; }
/**
* @brief Devuelve las dimensiones de la textura de supersampling.
* @return Par (ancho, alto) en píxeles; (0, 0) si SS está desactivado.
*/
[[nodiscard]] virtual auto getSsTextureSize() const -> std::pair<int, int> { return {0, 0}; }
/** /**
* @brief Verifica si el backend está usando aceleración por hardware * @brief Verifica si el backend está usando aceleración por hardware
* @return true si usa aceleración (OpenGL/Metal/Vulkan) * @return true si usa aceleración (OpenGL/Metal/Vulkan)
*/ */
[[nodiscard]] virtual auto isHardwareAccelerated() const -> bool = 0; [[nodiscard]] virtual auto isHardwareAccelerated() const -> bool = 0;
/**
* @brief Nombre del driver GPU activo (p.ej. "vulkan", "metal", "direct3d12")
* @return Cadena vacía si no disponible
*/
[[nodiscard]] virtual auto getDriverName() const -> std::string { return {}; }
/**
* @brief Establece el driver GPU preferido antes de init().
* Vacío = selección automática de SDL. Implementado en SDL3GPUShader.
*/
virtual void setPreferredDriver(const std::string& /*driver*/) {}
/**
* @brief Selecciona el shader de post-procesado activo (POSTFX o CRTPI).
* Debe llamarse antes de render(). No recrea pipelines.
*/
virtual void setActiveShader(ShaderType /*type*/) {}
/**
* @brief Establece los parámetros del shader CRT-Pi.
*/
virtual void setCrtPiParams(const CrtPiParams& /*p*/) {}
/**
* @brief Devuelve el shader de post-procesado activo.
*/
[[nodiscard]] virtual auto getActiveShader() const -> ShaderType { return ShaderType::POSTFX; }
}; };
} // namespace Rendering } // namespace Rendering

View File

@@ -1,4 +1,4 @@
#include "core/rendering/surface_animated_sprite.hpp" #include "core/rendering/sprite/animated_sprite.hpp"
#include <cstddef> // Para size_t #include <cstddef> // Para size_t
#include <fstream> // Para basic_ostream, basic_istream, operator<<, basic... #include <fstream> // Para basic_ostream, basic_istream, operator<<, basic...
@@ -16,7 +16,7 @@
// Helper: Convierte un nodo YAML de frames (array) a vector de SDL_FRect // Helper: Convierte un nodo YAML de frames (array) a vector de SDL_FRect
auto convertYAMLFramesToRects(const fkyaml::node& frames_node, float frame_width, float frame_height, int frames_per_row, int max_tiles) -> std::vector<SDL_FRect> { auto convertYAMLFramesToRects(const fkyaml::node& frames_node, float frame_width, float frame_height, int frames_per_row, int max_tiles) -> std::vector<SDL_FRect> {
std::vector<SDL_FRect> frames; std::vector<SDL_FRect> frames;
SDL_FRect rect = {0.0F, 0.0F, frame_width, frame_height}; SDL_FRect rect = {.x = 0.0F, .y = 0.0F, .w = frame_width, .h = frame_height};
for (const auto& frame_index_node : frames_node) { for (const auto& frame_index_node : frames_node) {
const int NUM_TILE = frame_index_node.get_value<int>(); const int NUM_TILE = frame_index_node.get_value<int>();
@@ -31,7 +31,7 @@ auto convertYAMLFramesToRects(const fkyaml::node& frames_node, float frame_width
} }
// Carga las animaciones desde un fichero YAML // Carga las animaciones desde un fichero YAML
auto SurfaceAnimatedSprite::loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr<Surface>& surface, float& frame_width, float& frame_height) -> std::vector<AnimationData> { auto AnimatedSprite::loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr<Surface>& surface, float& frame_width, float& frame_height) -> std::vector<AnimationData> { // NOLINT(readability-convert-member-functions-to-static)
std::vector<AnimationData> animations; std::vector<AnimationData> animations;
// Extract filename for logging // Extract filename for logging
@@ -124,7 +124,7 @@ auto SurfaceAnimatedSprite::loadAnimationsFromYAML(const std::string& file_path,
} }
// Constructor con bytes YAML del cache (parsing lazy) // Constructor con bytes YAML del cache (parsing lazy)
SurfaceAnimatedSprite::SurfaceAnimatedSprite(const AnimationResource& cached_data) { AnimatedSprite::AnimatedSprite(const AnimationResource& cached_data) {
// Parsear YAML desde los bytes cargados en cache // Parsear YAML desde los bytes cargados en cache
std::string yaml_content(cached_data.yaml_data.begin(), cached_data.yaml_data.end()); std::string yaml_content(cached_data.yaml_data.begin(), cached_data.yaml_data.end());
@@ -215,8 +215,8 @@ SurfaceAnimatedSprite::SurfaceAnimatedSprite(const AnimationResource& cached_dat
} }
// Constructor per a subclasses amb surface directa (sense YAML) // Constructor per a subclasses amb surface directa (sense YAML)
SurfaceAnimatedSprite::SurfaceAnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos) AnimatedSprite::AnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
: SurfaceMovingSprite(std::move(surface), pos) { : MovingSprite(std::move(surface), pos) {
// animations_ queda buit (protegit per el guard de animate()) // animations_ queda buit (protegit per el guard de animate())
if (surface_) { if (surface_) {
clip_ = {.x = 0, .y = 0, .w = surface_->getWidth(), .h = surface_->getHeight()}; clip_ = {.x = 0, .y = 0, .w = surface_->getWidth(), .h = surface_->getHeight()};
@@ -224,7 +224,7 @@ SurfaceAnimatedSprite::SurfaceAnimatedSprite(std::shared_ptr<Surface> surface, S
} }
// Obtiene el indice de la animación a partir del nombre // Obtiene el indice de la animación a partir del nombre
auto SurfaceAnimatedSprite::getIndex(const std::string& name) -> int { auto AnimatedSprite::getIndex(const std::string& name) -> int { // NOLINT(readability-convert-member-functions-to-static)
auto index = -1; auto index = -1;
for (const auto& a : animations_) { for (const auto& a : animations_) {
@@ -238,7 +238,7 @@ auto SurfaceAnimatedSprite::getIndex(const std::string& name) -> int {
} }
// Calcula el frame correspondiente a la animación (time-based) // Calcula el frame correspondiente a la animación (time-based)
void SurfaceAnimatedSprite::animate(float delta_time) { void AnimatedSprite::animate(float delta_time) { // NOLINT(readability-convert-member-functions-to-static)
if (animations_.empty()) { return; } if (animations_.empty()) { return; }
if (animations_[current_animation_].speed <= 0.0F) { if (animations_[current_animation_].speed <= 0.0F) {
return; return;
@@ -288,12 +288,12 @@ void SurfaceAnimatedSprite::animate(float delta_time) {
} }
// Comprueba si ha terminado la animación // Comprueba si ha terminado la animación
auto SurfaceAnimatedSprite::animationIsCompleted() -> bool { auto AnimatedSprite::animationIsCompleted() -> bool {
return animations_[current_animation_].completed; return animations_[current_animation_].completed;
} }
// Establece la animacion actual // Establece la animacion actual
void SurfaceAnimatedSprite::setCurrentAnimation(const std::string& name) { void AnimatedSprite::setCurrentAnimation(const std::string& name) {
const auto NEW_ANIMATION = getIndex(name); const auto NEW_ANIMATION = getIndex(name);
if (current_animation_ != NEW_ANIMATION) { if (current_animation_ != NEW_ANIMATION) {
current_animation_ = NEW_ANIMATION; current_animation_ = NEW_ANIMATION;
@@ -305,7 +305,7 @@ void SurfaceAnimatedSprite::setCurrentAnimation(const std::string& name) {
} }
// Establece la animacion actual // Establece la animacion actual
void SurfaceAnimatedSprite::setCurrentAnimation(int index) { void AnimatedSprite::setCurrentAnimation(int index) {
const auto NEW_ANIMATION = index; const auto NEW_ANIMATION = index;
if (current_animation_ != NEW_ANIMATION) { if (current_animation_ != NEW_ANIMATION) {
current_animation_ = NEW_ANIMATION; current_animation_ = NEW_ANIMATION;
@@ -317,20 +317,20 @@ void SurfaceAnimatedSprite::setCurrentAnimation(int index) {
} }
// Actualiza las variables del objeto (time-based) // Actualiza las variables del objeto (time-based)
void SurfaceAnimatedSprite::update(float delta_time) { void AnimatedSprite::update(float delta_time) {
animate(delta_time); animate(delta_time);
SurfaceMovingSprite::update(delta_time); MovingSprite::update(delta_time);
} }
// Reinicia la animación // Reinicia la animación
void SurfaceAnimatedSprite::resetAnimation() { void AnimatedSprite::resetAnimation() {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].accumulated_time = 0.0F; animations_[current_animation_].accumulated_time = 0.0F;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
} }
// Establece el frame actual de la animación // Establece el frame actual de la animación
void SurfaceAnimatedSprite::setCurrentAnimationFrame(int num) { void AnimatedSprite::setCurrentAnimationFrame(int num) {
// Descarta valores fuera de rango // Descarta valores fuera de rango
if (num < 0 || num >= static_cast<int>(animations_[current_animation_].frames.size())) { if (num < 0 || num >= static_cast<int>(animations_[current_animation_].frames.size())) {
num = 0; num = 0;

View File

@@ -7,12 +7,12 @@
#include <utility> #include <utility>
#include <vector> // Para vector #include <vector> // Para vector
#include "core/rendering/surface_moving_sprite.hpp" // Para SMovingSprite #include "core/rendering/sprite/moving_sprite.hpp" // Para SMovingSprite
#include "core/resources/resource_types.hpp" // Para AnimationResource #include "core/resources/resource_types.hpp" // Para AnimationResource
class Surface; class Surface;
class SurfaceAnimatedSprite : public SurfaceMovingSprite { class AnimatedSprite : public MovingSprite {
public: public:
using Animations = std::vector<std::string>; // Tipo para lista de animaciones using Animations = std::vector<std::string>; // Tipo para lista de animaciones
@@ -31,9 +31,9 @@ class SurfaceAnimatedSprite : public SurfaceMovingSprite {
static auto loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr<Surface>& surface, float& frame_width, float& frame_height) -> std::vector<AnimationData>; // Carga las animaciones desde fichero YAML static auto loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr<Surface>& surface, float& frame_width, float& frame_height) -> std::vector<AnimationData>; // Carga las animaciones desde fichero YAML
// Constructores // Constructores
explicit SurfaceAnimatedSprite(const AnimationResource& cached_data); // Constructor con datos pre-cargados del cache explicit AnimatedSprite(const AnimationResource& cached_data); // Constructor con datos pre-cargados del cache
~SurfaceAnimatedSprite() override = default; // Destructor ~AnimatedSprite() override = default; // Destructor
void update(float delta_time) override; // Actualiza las variables del objeto (time-based) void update(float delta_time) override; // Actualiza las variables del objeto (time-based)
@@ -50,7 +50,7 @@ class SurfaceAnimatedSprite : public SurfaceMovingSprite {
protected: protected:
// Constructor per a ús de subclasses que gestionen la surface directament (sense YAML) // Constructor per a ús de subclasses que gestionen la surface directament (sense YAML)
SurfaceAnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos); AnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos);
// Métodos protegidos // Métodos protegidos
void animate(float delta_time); // Calcula el frame correspondiente a la animación actual (time-based) void animate(float delta_time); // Calcula el frame correspondiente a la animación actual (time-based)

View File

@@ -1,4 +1,4 @@
#include "core/rendering/surface_dissolve_sprite.hpp" #include "core/rendering/sprite/dissolve_sprite.hpp"
#include <algorithm> // Para min #include <algorithm> // Para min
#include <cstdint> // Para uint32_t #include <cstdint> // Para uint32_t
@@ -15,7 +15,7 @@ static auto pixelRank(int col, int row) -> float {
} }
// Rang per a un píxel tenint en compte direcció (70% direccional + 30% aleatori) // Rang per a un píxel tenint en compte direcció (70% direccional + 30% aleatori)
auto SurfaceDissolveSprite::computePixelRank(int col, int row, int frame_h, DissolveDirection dir) -> float { auto DissolveSprite::computePixelRank(int col, int row, int frame_h, DissolveDirection dir) -> float {
const float RANDOM = pixelRank(col, row); const float RANDOM = pixelRank(col, row);
if (dir == DissolveDirection::NONE || frame_h <= 0) { if (dir == DissolveDirection::NONE || frame_h <= 0) {
return RANDOM; return RANDOM;
@@ -32,8 +32,8 @@ auto SurfaceDissolveSprite::computePixelRank(int col, int row, int frame_h, Diss
} }
// Constructor per a surface directa (sense AnimationResource) // Constructor per a surface directa (sense AnimationResource)
SurfaceDissolveSprite::SurfaceDissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos) DissolveSprite::DissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
: SurfaceAnimatedSprite(std::move(surface), pos) { : AnimatedSprite(std::move(surface), pos) {
if (surface_) { if (surface_) {
const int W = static_cast<int>(surface_->getWidth()); const int W = static_cast<int>(surface_->getWidth());
const int H = static_cast<int>(surface_->getHeight()); const int H = static_cast<int>(surface_->getHeight());
@@ -44,8 +44,8 @@ SurfaceDissolveSprite::SurfaceDissolveSprite(std::shared_ptr<Surface> surface, S
} }
// Constructor // Constructor
SurfaceDissolveSprite::SurfaceDissolveSprite(const AnimationResource& data) DissolveSprite::DissolveSprite(const AnimationResource& data)
: SurfaceAnimatedSprite(data) { : AnimatedSprite(data) {
if (surface_) { if (surface_) {
const int W = static_cast<int>(surface_->getWidth()); const int W = static_cast<int>(surface_->getWidth());
const int H = static_cast<int>(surface_->getHeight()); const int H = static_cast<int>(surface_->getHeight());
@@ -57,7 +57,7 @@ SurfaceDissolveSprite::SurfaceDissolveSprite(const AnimationResource& data)
} }
// Reconstrueix la surface_display_ filtrant píxels per progress_ // Reconstrueix la surface_display_ filtrant píxels per progress_
void SurfaceDissolveSprite::rebuildDisplaySurface() { void DissolveSprite::rebuildDisplaySurface() {
if (!surface_ || !surface_display_) { if (!surface_ || !surface_display_) {
return; return;
} }
@@ -109,9 +109,9 @@ void SurfaceDissolveSprite::rebuildDisplaySurface() {
} }
// Actualitza animació, moviment i transició temporal // Actualitza animació, moviment i transició temporal
void SurfaceDissolveSprite::update(float delta_time) { void DissolveSprite::update(float delta_time) {
const SDL_FRect OLD_CLIP = clip_; const SDL_FRect OLD_CLIP = clip_;
SurfaceAnimatedSprite::update(delta_time); AnimatedSprite::update(delta_time);
// Detecta canvi de frame d'animació // Detecta canvi de frame d'animació
if (clip_.x != OLD_CLIP.x || clip_.y != OLD_CLIP.y || if (clip_.x != OLD_CLIP.x || clip_.y != OLD_CLIP.y ||
@@ -136,22 +136,22 @@ void SurfaceDissolveSprite::update(float delta_time) {
} }
// Renderitza: usa surface_display_ (amb color replace) si disponible // Renderitza: usa surface_display_ (amb color replace) si disponible
void SurfaceDissolveSprite::render() { void DissolveSprite::render() {
if (!surface_display_) { if (!surface_display_) {
SurfaceAnimatedSprite::render(); AnimatedSprite::render();
return; return;
} }
surface_display_->render(static_cast<int>(pos_.x), static_cast<int>(pos_.y), &clip_, flip_); surface_display_->render(static_cast<int>(pos_.x), static_cast<int>(pos_.y), &clip_, flip_);
} }
// Estableix el progrés manualment // Estableix el progrés manualment
void SurfaceDissolveSprite::setProgress(float progress) { void DissolveSprite::setProgress(float progress) {
progress_ = std::min(std::max(progress, 0.0F), 1.0F); progress_ = std::min(std::max(progress, 0.0F), 1.0F);
needs_rebuild_ = true; needs_rebuild_ = true;
} }
// Inicia dissolució temporal (visible → invisible) // Inicia dissolució temporal (visible → invisible)
void SurfaceDissolveSprite::startDissolve(float duration_ms, DissolveDirection dir) { void DissolveSprite::startDissolve(float duration_ms, DissolveDirection dir) {
direction_ = dir; direction_ = dir;
transition_mode_ = TransitionMode::DISSOLVING; transition_mode_ = TransitionMode::DISSOLVING;
transition_duration_ = duration_ms; transition_duration_ = duration_ms;
@@ -161,7 +161,7 @@ void SurfaceDissolveSprite::startDissolve(float duration_ms, DissolveDirection d
} }
// Inicia generació temporal (invisible → visible) // Inicia generació temporal (invisible → visible)
void SurfaceDissolveSprite::startGenerate(float duration_ms, DissolveDirection dir) { void DissolveSprite::startGenerate(float duration_ms, DissolveDirection dir) {
direction_ = dir; direction_ = dir;
transition_mode_ = TransitionMode::GENERATING; transition_mode_ = TransitionMode::GENERATING;
transition_duration_ = duration_ms; transition_duration_ = duration_ms;
@@ -171,17 +171,17 @@ void SurfaceDissolveSprite::startGenerate(float duration_ms, DissolveDirection d
} }
// Atura la transició temporal // Atura la transició temporal
void SurfaceDissolveSprite::stopTransition() { void DissolveSprite::stopTransition() {
transition_mode_ = TransitionMode::NONE; transition_mode_ = TransitionMode::NONE;
} }
// Retorna si la transició ha acabat // Retorna si la transició ha acabat
auto SurfaceDissolveSprite::isTransitionDone() const -> bool { auto DissolveSprite::isTransitionDone() const -> bool {
return transition_mode_ == TransitionMode::NONE; return transition_mode_ == TransitionMode::NONE;
} }
// Configura substitució de color per a la reconstrucció // Configura substitució de color per a la reconstrucció
void SurfaceDissolveSprite::setColorReplace(Uint8 source, Uint8 target) { void DissolveSprite::setColorReplace(Uint8 source, Uint8 target) {
source_color_ = source; source_color_ = source;
target_color_ = target; target_color_ = target;
needs_rebuild_ = true; needs_rebuild_ = true;

View File

@@ -4,7 +4,7 @@
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include "core/rendering/surface_animated_sprite.hpp" // Para SurfaceAnimatedSprite #include "core/rendering/sprite/animated_sprite.hpp" // Para SurfaceAnimatedSprite
class Surface; class Surface;
@@ -15,11 +15,11 @@ enum class DissolveDirection { NONE,
// Sprite que pot dissoldre's o generar-se de forma aleatòria en X mil·lisegons. // Sprite que pot dissoldre's o generar-se de forma aleatòria en X mil·lisegons.
// progress_ va de 0.0 (totalment visible) a 1.0 (totalment invisible). // progress_ va de 0.0 (totalment visible) a 1.0 (totalment invisible).
class SurfaceDissolveSprite : public SurfaceAnimatedSprite { class DissolveSprite : public AnimatedSprite {
public: public:
explicit SurfaceDissolveSprite(const AnimationResource& data); explicit DissolveSprite(const AnimationResource& data);
SurfaceDissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos); DissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos);
~SurfaceDissolveSprite() override = default; ~DissolveSprite() override = default;
void update(float delta_time) override; void update(float delta_time) override;
void render() override; void render() override;
@@ -52,7 +52,7 @@ class SurfaceDissolveSprite : public SurfaceAnimatedSprite {
TransitionMode transition_mode_{TransitionMode::NONE}; TransitionMode transition_mode_{TransitionMode::NONE};
float transition_duration_{0.0F}; float transition_duration_{0.0F};
float transition_elapsed_{0.0F}; float transition_elapsed_{0.0F};
SDL_FRect prev_clip_{0, 0, 0, 0}; SDL_FRect prev_clip_{.x = 0, .y = 0, .w = 0, .h = 0};
bool needs_rebuild_{false}; bool needs_rebuild_{false};
Uint8 source_color_{255}; // 255 = transparent = sense replace per defecte Uint8 source_color_{255}; // 255 = transparent = sense replace per defecte
Uint8 target_color_{0}; Uint8 target_color_{0};

View File

@@ -1,28 +1,28 @@
#include "core/rendering/surface_moving_sprite.hpp" #include "core/rendering/sprite/moving_sprite.hpp"
#include <utility> #include <utility>
#include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface.hpp" // Para Surface
// Constructor // Constructor
SurfaceMovingSprite::SurfaceMovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos, SDL_FlipMode flip) MovingSprite::MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos, SDL_FlipMode flip)
: SurfaceSprite(std::move(surface), pos), : Sprite(std::move(surface), pos),
x_(pos.x), x_(pos.x),
y_(pos.y), y_(pos.y),
flip_(flip) { SurfaceSprite::pos_ = pos; } flip_(flip) { Sprite::pos_ = pos; }
SurfaceMovingSprite::SurfaceMovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos) MovingSprite::MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
: SurfaceSprite(std::move(surface), pos), : Sprite(std::move(surface), pos),
x_(pos.x), x_(pos.x),
y_(pos.y) { SurfaceSprite::pos_ = pos; } y_(pos.y) { Sprite::pos_ = pos; }
SurfaceMovingSprite::SurfaceMovingSprite() { SurfaceSprite::clear(); } MovingSprite::MovingSprite() { Sprite::clear(); }
SurfaceMovingSprite::SurfaceMovingSprite(std::shared_ptr<Surface> surface) MovingSprite::MovingSprite(std::shared_ptr<Surface> surface)
: SurfaceSprite(std::move(surface)) { SurfaceSprite::clear(); } : Sprite(std::move(surface)) { Sprite::clear(); }
// Reinicia todas las variables // Reinicia todas las variables
void SurfaceMovingSprite::clear() { void MovingSprite::clear() {
// Resetea posición // Resetea posición
x_ = 0.0F; x_ = 0.0F;
y_ = 0.0F; y_ = 0.0F;
@@ -38,13 +38,13 @@ void SurfaceMovingSprite::clear() {
// Resetea flip // Resetea flip
flip_ = SDL_FLIP_NONE; flip_ = SDL_FLIP_NONE;
SurfaceSprite::clear(); Sprite::clear();
} }
// Mueve el sprite (time-based) // Mueve el sprite (time-based)
// Nota: vx_, vy_ ahora se interpretan como pixels/segundo // Nota: vx_, vy_ ahora se interpretan como pixels/segundo
// Nota: ax_, ay_ ahora se interpretan como pixels/segundo² // Nota: ax_, ay_ ahora se interpretan como pixels/segundo²
void SurfaceMovingSprite::move(float delta_time) { void MovingSprite::move(float delta_time) {
// Aplica aceleración a velocidad (time-based) // Aplica aceleración a velocidad (time-based)
vx_ += ax_ * delta_time; vx_ += ax_ * delta_time;
vy_ += ay_ * delta_time; vy_ += ay_ * delta_time;
@@ -59,22 +59,22 @@ void SurfaceMovingSprite::move(float delta_time) {
} }
// Actualiza las variables internas del objeto (time-based) // Actualiza las variables internas del objeto (time-based)
void SurfaceMovingSprite::update(float delta_time) { void MovingSprite::update(float delta_time) {
move(delta_time); move(delta_time);
} }
// Muestra el sprite por pantalla // Muestra el sprite por pantalla
void SurfaceMovingSprite::render() { void MovingSprite::render() {
surface_->render(pos_.x, pos_.y, &clip_, flip_); surface_->render(pos_.x, pos_.y, &clip_, flip_);
} }
// Muestra el sprite por pantalla // Muestra el sprite por pantalla
void SurfaceMovingSprite::render(Uint8 source_color, Uint8 target_color) { void MovingSprite::render(Uint8 source_color, Uint8 target_color) {
surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_, flip_); surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_, flip_);
} }
// Establece la posición y_ el tamaño del objeto // Establece la posición y_ el tamaño del objeto
void SurfaceMovingSprite::setPos(SDL_FRect rect) { void MovingSprite::setPos(SDL_FRect rect) {
x_ = rect.x; x_ = rect.x;
y_ = rect.y; y_ = rect.y;
@@ -82,7 +82,7 @@ void SurfaceMovingSprite::setPos(SDL_FRect rect) {
} }
// Establece el valor de las variables // Establece el valor de las variables
void SurfaceMovingSprite::setPos(float x, float y) { void MovingSprite::setPos(float x, float y) {
x_ = x; x_ = x;
y_ = y; y_ = y;
@@ -91,13 +91,13 @@ void SurfaceMovingSprite::setPos(float x, float y) {
} }
// Establece el valor de la variable // Establece el valor de la variable
void SurfaceMovingSprite::setPosX(float value) { void MovingSprite::setPosX(float value) {
x_ = value; x_ = value;
pos_.x = static_cast<int>(x_); pos_.x = static_cast<int>(x_);
} }
// Establece el valor de la variable // Establece el valor de la variable
void SurfaceMovingSprite::setPosY(float value) { void MovingSprite::setPosY(float value) {
y_ = value; y_ = value;
pos_.y = static_cast<int>(y_); pos_.y = static_cast<int>(y_);
} }

View File

@@ -4,18 +4,18 @@
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include "core/rendering/surface_sprite.hpp" // Para SSprite #include "core/rendering/sprite/sprite.hpp" // Para SSprite
class Surface; // lines 8-8 class Surface; // lines 8-8
// Clase SMovingSprite. Añade movimiento y flip al sprite // Clase SMovingSprite. Añade movimiento y flip al sprite
class SurfaceMovingSprite : public SurfaceSprite { class MovingSprite : public Sprite {
public: public:
// Constructores // Constructores
SurfaceMovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos, SDL_FlipMode flip); MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos, SDL_FlipMode flip);
SurfaceMovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos); MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos);
explicit SurfaceMovingSprite(); explicit MovingSprite();
explicit SurfaceMovingSprite(std::shared_ptr<Surface> surface); explicit MovingSprite(std::shared_ptr<Surface> surface);
~SurfaceMovingSprite() override = default; ~MovingSprite() override = default;
// Actualización y renderizado // Actualización y renderizado
void update(float delta_time) override; // Actualiza variables internas (time-based) void update(float delta_time) override; // Actualiza variables internas (time-based)

View File

@@ -1,37 +1,37 @@
#include "core/rendering/surface_sprite.hpp" #include "core/rendering/sprite/sprite.hpp"
#include <utility> #include <utility>
#include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface.hpp" // Para Surface
// Constructor // Constructor
SurfaceSprite::SurfaceSprite(std::shared_ptr<Surface> surface, float x, float y, float w, float h) Sprite::Sprite(std::shared_ptr<Surface> surface, float x, float y, float w, float h)
: surface_(std::move(surface)), : surface_(std::move(surface)),
pos_{x, y, w, h}, pos_{.x = x, .y = y, .w = w, .h = h},
clip_{0.0F, 0.0F, pos_.w, pos_.h} {} clip_{.x = 0.0F, .y = 0.0F, .w = pos_.w, .h = pos_.h} {}
SurfaceSprite::SurfaceSprite(std::shared_ptr<Surface> surface, SDL_FRect rect) Sprite::Sprite(std::shared_ptr<Surface> surface, SDL_FRect rect)
: surface_(std::move(surface)), : surface_(std::move(surface)),
pos_(rect), pos_(rect),
clip_{0.0F, 0.0F, pos_.w, pos_.h} {} clip_{.x = 0.0F, .y = 0.0F, .w = pos_.w, .h = pos_.h} {}
SurfaceSprite::SurfaceSprite() = default; Sprite::Sprite() = default;
SurfaceSprite::SurfaceSprite(std::shared_ptr<Surface> surface) Sprite::Sprite(std::shared_ptr<Surface> surface)
: surface_(std::move(surface)), : surface_(std::move(surface)),
pos_{0.0F, 0.0F, surface_->getWidth(), surface_->getHeight()}, pos_{0.0F, 0.0F, surface_->getWidth(), surface_->getHeight()},
clip_(pos_) {} clip_(pos_) {}
// Muestra el sprite por pantalla // Muestra el sprite por pantalla
void SurfaceSprite::render() { void Sprite::render() {
surface_->render(pos_.x, pos_.y, &clip_); surface_->render(pos_.x, pos_.y, &clip_);
} }
void SurfaceSprite::render(Uint8 source_color, Uint8 target_color) { void Sprite::render(Uint8 source_color, Uint8 target_color) {
surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_); surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_);
} }
void SurfaceSprite::renderWithVerticalFade(int fade_h, int canvas_height) { void Sprite::renderWithVerticalFade(int fade_h, int canvas_height) {
surface_->renderWithVerticalFade( surface_->renderWithVerticalFade(
static_cast<int>(pos_.x), static_cast<int>(pos_.x),
static_cast<int>(pos_.y), static_cast<int>(pos_.y),
@@ -40,7 +40,7 @@ void SurfaceSprite::renderWithVerticalFade(int fade_h, int canvas_height) {
&clip_); &clip_);
} }
void SurfaceSprite::renderWithVerticalFade(int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color) { void Sprite::renderWithVerticalFade(int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color) {
surface_->renderWithVerticalFade( surface_->renderWithVerticalFade(
static_cast<int>(pos_.x), static_cast<int>(pos_.x),
static_cast<int>(pos_.y), static_cast<int>(pos_.y),
@@ -52,25 +52,25 @@ void SurfaceSprite::renderWithVerticalFade(int fade_h, int canvas_height, Uint8
} }
// Establece la posición del objeto // Establece la posición del objeto
void SurfaceSprite::setPosition(float x, float y) { void Sprite::setPosition(float x, float y) {
pos_.x = x; pos_.x = x;
pos_.y = y; pos_.y = y;
} }
// Establece la posición del objeto // Establece la posición del objeto
void SurfaceSprite::setPosition(SDL_FPoint p) { void Sprite::setPosition(SDL_FPoint p) {
pos_.x = p.x; pos_.x = p.x;
pos_.y = p.y; pos_.y = p.y;
} }
// Reinicia las variables a cero // Reinicia las variables a cero
void SurfaceSprite::clear() { void Sprite::clear() {
pos_ = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; pos_ = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
clip_ = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; clip_ = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
} }
// Actualiza el estado del sprite (time-based) // Actualiza el estado del sprite (time-based)
void SurfaceSprite::update(float delta_time) { void Sprite::update(float delta_time) {
// Base implementation does nothing (static sprites) // Base implementation does nothing (static sprites)
(void)delta_time; // Evita warning de parámetro no usado (void)delta_time; // Evita warning de parámetro no usado
} }

View File

@@ -7,16 +7,16 @@
class Surface; // lines 5-5 class Surface; // lines 5-5
// Clase SurfaceSprite // Clase SurfaceSprite
class SurfaceSprite { class Sprite {
public: public:
// Constructores // Constructores
SurfaceSprite(std::shared_ptr<Surface>, float x, float y, float w, float h); Sprite(std::shared_ptr<Surface>, float x, float y, float w, float h);
SurfaceSprite(std::shared_ptr<Surface>, SDL_FRect rect); Sprite(std::shared_ptr<Surface>, SDL_FRect rect);
SurfaceSprite(); Sprite();
explicit SurfaceSprite(std::shared_ptr<Surface>); explicit Sprite(std::shared_ptr<Surface>);
// Destructor // Destructor
virtual ~SurfaceSprite() = default; virtual ~Sprite() = default;
// Actualización y renderizado // Actualización y renderizado
virtual void update(float delta_time); // Actualiza el estado del sprite (time-based) virtual void update(float delta_time); // Actualiza el estado del sprite (time-based)
@@ -51,12 +51,12 @@ class SurfaceSprite {
// Modificación de clip y surface // Modificación de clip y surface
void setClip(SDL_FRect rect) { clip_ = rect; } void setClip(SDL_FRect rect) { clip_ = rect; }
void setClip(float x, float y, float w, float h) { clip_ = SDL_FRect{x, y, w, h}; } void setClip(float x, float y, float w, float h) { clip_ = SDL_FRect{.x = x, .y = y, .w = w, .h = h}; }
void setSurface(std::shared_ptr<Surface> surface) { surface_ = std::move(surface); } void setSurface(std::shared_ptr<Surface> surface) { surface_ = std::move(surface); }
protected: protected:
// Variables miembro // Variables miembro
std::shared_ptr<Surface> surface_{nullptr}; // Surface donde estan todos los dibujos del sprite std::shared_ptr<Surface> surface_{nullptr}; // Surface donde estan todos los dibujos del sprite
SDL_FRect pos_{0.0F, 0.0F, 0.0F, 0.0F}; // Posición y tamaño donde dibujar el sprite SDL_FRect pos_{.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; // Posición y tamaño donde dibujar el sprite
SDL_FRect clip_{0.0F, 0.0F, 0.0F, 0.0F}; // Rectangulo de origen de la surface que se dibujará en pantalla SDL_FRect clip_{.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; // Rectangulo de origen de la surface que se dibujará en pantalla
}; };

View File

@@ -104,7 +104,7 @@ Surface::Surface(const std::string& file_path)
} }
// Carga una superficie desde un archivo // Carga una superficie desde un archivo
auto Surface::loadSurface(const std::string& file_path) -> SurfaceData { auto Surface::loadSurface(const std::string& file_path) -> SurfaceData { // NOLINT(readability-convert-member-functions-to-static)
// Load file using ResourceHelper (supports both filesystem and pack) // Load file using ResourceHelper (supports both filesystem and pack)
std::vector<Uint8> buffer = Resource::Helper::loadFile(file_path); std::vector<Uint8> buffer = Resource::Helper::loadFile(file_path);
if (buffer.empty()) { if (buffer.empty()) {
@@ -148,14 +148,14 @@ void Surface::setColor(int index, Uint32 color) {
} }
// Rellena la superficie con un color // Rellena la superficie con un color
void Surface::clear(Uint8 color) { void Surface::clear(Uint8 color) { // NOLINT(readability-convert-member-functions-to-static)
const size_t TOTAL_PIXELS = surface_data_->width * surface_data_->height; const size_t TOTAL_PIXELS = surface_data_->width * surface_data_->height;
Uint8* data_ptr = surface_data_->data.get(); Uint8* data_ptr = surface_data_->data.get();
std::fill(data_ptr, data_ptr + TOTAL_PIXELS, color); std::fill(data_ptr, data_ptr + TOTAL_PIXELS, color);
} }
// Pone un pixel en la SurfaceData // Pone un pixel en la SurfaceData
void Surface::putPixel(int x, int y, Uint8 color) { void Surface::putPixel(int x, int y, Uint8 color) { // NOLINT(readability-convert-member-functions-to-static)
if (x < 0 || y < 0 || x >= surface_data_->width || y >= surface_data_->height) { if (x < 0 || y < 0 || x >= surface_data_->width || y >= surface_data_->height) {
return; // Coordenadas fuera de rango return; // Coordenadas fuera de rango
} }
@@ -168,7 +168,7 @@ void Surface::putPixel(int x, int y, Uint8 color) {
auto Surface::getPixel(int x, int y) -> Uint8 { return surface_data_->data.get()[x + (y * static_cast<int>(surface_data_->width))]; } auto Surface::getPixel(int x, int y) -> Uint8 { return surface_data_->data.get()[x + (y * static_cast<int>(surface_data_->width))]; }
// Dibuja un rectangulo relleno // Dibuja un rectangulo relleno
void Surface::fillRect(const SDL_FRect* rect, Uint8 color) { void Surface::fillRect(const SDL_FRect* rect, Uint8 color) { // NOLINT(readability-convert-member-functions-to-static)
// Limitar los valores del rectángulo al tamaño de la superficie // Limitar los valores del rectángulo al tamaño de la superficie
float x_start = std::max(0.0F, rect->x); float x_start = std::max(0.0F, rect->x);
float y_start = std::max(0.0F, rect->y); float y_start = std::max(0.0F, rect->y);
@@ -185,7 +185,7 @@ void Surface::fillRect(const SDL_FRect* rect, Uint8 color) {
} }
// Dibuja el borde de un rectangulo // Dibuja el borde de un rectangulo
void Surface::drawRectBorder(const SDL_FRect* rect, Uint8 color) { void Surface::drawRectBorder(const SDL_FRect* rect, Uint8 color) { // NOLINT(readability-convert-member-functions-to-static)
// Limitar los valores del rectángulo al tamaño de la superficie // Limitar los valores del rectángulo al tamaño de la superficie
float x_start = std::max(0.0F, rect->x); float x_start = std::max(0.0F, rect->x);
float y_start = std::max(0.0F, rect->y); float y_start = std::max(0.0F, rect->y);
@@ -216,7 +216,7 @@ void Surface::drawRectBorder(const SDL_FRect* rect, Uint8 color) {
} }
// Dibuja una linea // Dibuja una linea
void Surface::drawLine(float x1, float y1, float x2, float y2, Uint8 color) { void Surface::drawLine(float x1, float y1, float x2, float y2, Uint8 color) { // NOLINT(readability-convert-member-functions-to-static)
// Calcula las diferencias // Calcula las diferencias
float dx = std::abs(x2 - x1); float dx = std::abs(x2 - x1);
float dy = std::abs(y2 - y1); float dy = std::abs(y2 - y1);
@@ -250,7 +250,7 @@ void Surface::drawLine(float x1, float y1, float x2, float y2, Uint8 color) {
} }
} }
void Surface::render(float dx, float dy, float sx, float sy, float w, float h) { void Surface::render(float dx, float dy, float sx, float sy, float w, float h) { // NOLINT(readability-make-member-function-const)
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData(); auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
// Limitar la región para evitar accesos fuera de rango en origen // Limitar la región para evitar accesos fuera de rango en origen
@@ -270,7 +270,7 @@ void Surface::render(float dx, float dy, float sx, float sy, float w, float h) {
int src_y = sy + iy; int src_y = sy + iy;
Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))]; Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
if (color != transparent_color_) { if (color != static_cast<Uint8>(transparent_color_)) {
surface_data->data.get()[static_cast<size_t>(dest_x + (dest_y * surface_data->width))] = sub_palette_[color]; surface_data->data.get()[static_cast<size_t>(dest_x + (dest_y * surface_data->width))] = sub_palette_[color];
} }
} }
@@ -279,14 +279,14 @@ void Surface::render(float dx, float dy, float sx, float sy, float w, float h) {
} }
} }
void Surface::render(int x, int y, SDL_FRect* src_rect, SDL_FlipMode flip) { void Surface::render(int x, int y, SDL_FRect* src_rect, SDL_FlipMode flip) { // NOLINT(readability-make-member-function-const)
auto surface_data_dest = Screen::get()->getRendererSurface()->getSurfaceData(); auto surface_data_dest = Screen::get()->getRendererSurface()->getSurfaceData();
// Determina la región de origen (clip) a renderizar // Determina la región de origen (clip) a renderizar
float sx = ((src_rect) != nullptr) ? src_rect->x : 0; float sx = (src_rect != nullptr) ? src_rect->x : 0;
float sy = ((src_rect) != nullptr) ? src_rect->y : 0; float sy = (src_rect != nullptr) ? src_rect->y : 0;
float w = ((src_rect) != nullptr) ? src_rect->w : surface_data_->width; float w = (src_rect != nullptr) ? src_rect->w : surface_data_->width;
float h = ((src_rect) != nullptr) ? src_rect->h : surface_data_->height; float h = (src_rect != nullptr) ? src_rect->h : surface_data_->height;
// Limitar la región para evitar accesos fuera de rango en origen // Limitar la región para evitar accesos fuera de rango en origen
w = std::min(w, surface_data_->width - sx); w = std::min(w, surface_data_->width - sx);
@@ -313,7 +313,7 @@ void Surface::render(int x, int y, SDL_FRect* src_rect, SDL_FlipMode flip) {
if (dest_x >= 0 && dest_x < surface_data_dest->width && dest_y >= 0 && dest_y < surface_data_dest->height) { if (dest_x >= 0 && dest_x < surface_data_dest->width && dest_y >= 0 && dest_y < surface_data_dest->height) {
// Copia el píxel si no es transparente // Copia el píxel si no es transparente
Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))]; Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
if (color != transparent_color_) { if (color != static_cast<Uint8>(transparent_color_)) {
surface_data_dest->data[dest_x + (dest_y * surface_data_dest->width)] = sub_palette_[color]; surface_data_dest->data[dest_x + (dest_y * surface_data_dest->width)] = sub_palette_[color];
} }
} }
@@ -334,7 +334,7 @@ void Surface::copyPixelIfNotTransparent(Uint8* dest_data, int dest_x, int dest_y
} }
Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))]; Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
if (color != transparent_color_) { if (color != static_cast<Uint8>(transparent_color_)) {
dest_data[dest_x + (dest_y * dest_width)] = sub_palette_[color]; dest_data[dest_x + (dest_y * dest_width)] = sub_palette_[color];
} }
} }
@@ -344,16 +344,16 @@ void Surface::render(SDL_FRect* src_rect, SDL_FRect* dst_rect, SDL_FlipMode flip
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData(); auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
// Si srcRect es nullptr, tomar toda la superficie fuente // Si srcRect es nullptr, tomar toda la superficie fuente
float sx = ((src_rect) != nullptr) ? src_rect->x : 0; float sx = (src_rect != nullptr) ? src_rect->x : 0;
float sy = ((src_rect) != nullptr) ? src_rect->y : 0; float sy = (src_rect != nullptr) ? src_rect->y : 0;
float sw = ((src_rect) != nullptr) ? src_rect->w : surface_data_->width; float sw = (src_rect != nullptr) ? src_rect->w : surface_data_->width;
float sh = ((src_rect) != nullptr) ? src_rect->h : surface_data_->height; float sh = (src_rect != nullptr) ? src_rect->h : surface_data_->height;
// Si dstRect es nullptr, asignar las mismas dimensiones que srcRect // Si dstRect es nullptr, asignar las mismas dimensiones que srcRect
float dx = ((dst_rect) != nullptr) ? dst_rect->x : 0; float dx = (dst_rect != nullptr) ? dst_rect->x : 0;
float dy = ((dst_rect) != nullptr) ? dst_rect->y : 0; float dy = (dst_rect != nullptr) ? dst_rect->y : 0;
float dw = ((dst_rect) != nullptr) ? dst_rect->w : sw; float dw = (dst_rect != nullptr) ? dst_rect->w : sw;
float dh = ((dst_rect) != nullptr) ? dst_rect->h : sh; float dh = (dst_rect != nullptr) ? dst_rect->h : sh;
// Asegurarse de que srcRect y dstRect tienen las mismas dimensiones // Asegurarse de que srcRect y dstRect tienen las mismas dimensiones
if (sw != dw || sh != dh) { if (sw != dw || sh != dh) {
@@ -389,14 +389,14 @@ void Surface::render(SDL_FRect* src_rect, SDL_FRect* dst_rect, SDL_FlipMode flip
} }
// Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro // Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro
void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 target_color, SDL_FRect* src_rect, SDL_FlipMode flip) { void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 target_color, SDL_FRect* src_rect, SDL_FlipMode flip) const {
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData(); auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
// Determina la región de origen (clip) a renderizar // Determina la región de origen (clip) a renderizar
float sx = ((src_rect) != nullptr) ? src_rect->x : 0; float sx = (src_rect != nullptr) ? src_rect->x : 0;
float sy = ((src_rect) != nullptr) ? src_rect->y : 0; float sy = (src_rect != nullptr) ? src_rect->y : 0;
float w = ((src_rect) != nullptr) ? src_rect->w : surface_data_->width; float w = (src_rect != nullptr) ? src_rect->w : surface_data_->width;
float h = ((src_rect) != nullptr) ? src_rect->h : surface_data_->height; float h = (src_rect != nullptr) ? src_rect->h : surface_data_->height;
// Limitar la región para evitar accesos fuera de rango // Limitar la región para evitar accesos fuera de rango
w = std::min(w, surface_data_->width - sx); w = std::min(w, surface_data_->width - sx);
@@ -420,7 +420,7 @@ void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 tar
// Copia el píxel si no es transparente // Copia el píxel si no es transparente
Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))]; Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
if (color != transparent_color_) { if (color != static_cast<Uint8>(transparent_color_)) {
surface_data->data[dest_x + (dest_y * surface_data->width)] = surface_data->data[dest_x + (dest_y * surface_data->width)] =
(color == source_color) ? target_color : color; (color == source_color) ? target_color : color;
} }
@@ -449,7 +449,7 @@ static auto computeFadeDensity(int screen_y, int fade_h, int canvas_height) -> f
} }
// Render amb dissolució als cantons superior/inferior (hash 2D, sense parpelleig) // Render amb dissolució als cantons superior/inferior (hash 2D, sense parpelleig)
void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, SDL_FRect* src_rect) { void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, SDL_FRect* src_rect) const {
const int SX = (src_rect != nullptr) ? static_cast<int>(src_rect->x) : 0; const int SX = (src_rect != nullptr) ? static_cast<int>(src_rect->x) : 0;
const int SY = (src_rect != nullptr) ? static_cast<int>(src_rect->y) : 0; const int SY = (src_rect != nullptr) ? static_cast<int>(src_rect->y) : 0;
const int SW = (src_rect != nullptr) ? static_cast<int>(src_rect->w) : static_cast<int>(surface_data_->width); const int SW = (src_rect != nullptr) ? static_cast<int>(src_rect->w) : static_cast<int>(surface_data_->width);
@@ -472,7 +472,7 @@ void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height
} }
const Uint8 COLOR = surface_data_->data[((SY + row) * static_cast<int>(surface_data_->width)) + (SX + col)]; const Uint8 COLOR = surface_data_->data[((SY + row) * static_cast<int>(surface_data_->width)) + (SX + col)];
if (static_cast<int>(COLOR) == transparent_color_) { if (COLOR == static_cast<Uint8>(transparent_color_)) {
continue; continue;
} }
@@ -486,7 +486,7 @@ void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height
} }
// Idem però reemplaçant un color índex // Idem però reemplaçant un color índex
void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color, SDL_FRect* src_rect) { void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color, SDL_FRect* src_rect) const {
const int SX = (src_rect != nullptr) ? static_cast<int>(src_rect->x) : 0; const int SX = (src_rect != nullptr) ? static_cast<int>(src_rect->x) : 0;
const int SY = (src_rect != nullptr) ? static_cast<int>(src_rect->y) : 0; const int SY = (src_rect != nullptr) ? static_cast<int>(src_rect->y) : 0;
const int SW = (src_rect != nullptr) ? static_cast<int>(src_rect->w) : static_cast<int>(surface_data_->width); const int SW = (src_rect != nullptr) ? static_cast<int>(src_rect->w) : static_cast<int>(surface_data_->width);
@@ -509,7 +509,7 @@ void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height
} }
const Uint8 COLOR = surface_data_->data[((SY + row) * static_cast<int>(surface_data_->width)) + (SX + col)]; const Uint8 COLOR = surface_data_->data[((SY + row) * static_cast<int>(surface_data_->width)) + (SX + col)];
if (static_cast<int>(COLOR) == transparent_color_) { if (COLOR == static_cast<Uint8>(transparent_color_)) {
continue; continue;
} }
@@ -525,19 +525,29 @@ void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height
// Vuelca los píxeles como ARGB8888 a un buffer externo (sin SDL_Texture ni SDL_Renderer) // Vuelca los píxeles como ARGB8888 a un buffer externo (sin SDL_Texture ni SDL_Renderer)
void Surface::toARGBBuffer(Uint32* buffer) const { void Surface::toARGBBuffer(Uint32* buffer) const {
if (!surface_data_ || (surface_data_->data == nullptr)) { return; } if (!surface_data_ || !surface_data_->data || !buffer) { return; }
const int WIDTH = static_cast<int>(surface_data_->width); const int WIDTH = static_cast<int>(surface_data_->width);
const int HEIGHT = static_cast<int>(surface_data_->height); const int HEIGHT = static_cast<int>(surface_data_->height);
const Uint8* src = surface_data_->data.get(); const Uint8* src = surface_data_->data.get();
for (int y = 0; y < HEIGHT; ++y) {
for (int x = 0; x < WIDTH; ++x) { // Obtenemos el tamaño de la paleta para evitar accesos fuera de rango
buffer[(y * WIDTH) + x] = palette_[src[(y * WIDTH) + x]]; const size_t PAL_SIZE = palette_.size();
for (int i = 0; i < WIDTH * HEIGHT; ++i) {
Uint8 color_index = src[i];
// Verificación de seguridad: ¿El índice existe en la paleta?
if (color_index < PAL_SIZE) {
buffer[i] = palette_[color_index];
} else {
buffer[i] = 0xFF000000; // Negro opaco si el índice es erróneo
} }
} }
} }
// Vuelca la superficie a una textura // Vuelca la superficie a una textura
void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture) { void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture) { // NOLINT(readability-convert-member-functions-to-static)
if ((renderer == nullptr) || (texture == nullptr) || !surface_data_) { if ((renderer == nullptr) || (texture == nullptr) || !surface_data_) {
throw std::runtime_error("Renderer or texture is null."); throw std::runtime_error("Renderer or texture is null.");
} }
@@ -576,7 +586,7 @@ void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture) {
} }
// Vuelca la superficie a una textura // Vuelca la superficie a una textura
void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_FRect* src_rect, SDL_FRect* dest_rect) { void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_FRect* src_rect, SDL_FRect* dest_rect) { // NOLINT(readability-convert-member-functions-to-static)
if ((renderer == nullptr) || (texture == nullptr) || !surface_data_) { if ((renderer == nullptr) || (texture == nullptr) || !surface_data_) {
throw std::runtime_error("Renderer or texture is null."); throw std::runtime_error("Renderer or texture is null.");
} }
@@ -621,7 +631,7 @@ void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_FR
} }
// Realiza un efecto de fundido en la paleta principal // Realiza un efecto de fundido en la paleta principal
auto Surface::fadePalette() -> bool { auto Surface::fadePalette() -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Verificar que el tamaño mínimo de palette_ sea adecuado // Verificar que el tamaño mínimo de palette_ sea adecuado
static constexpr int PALETTE_SIZE = 19; static constexpr int PALETTE_SIZE = 19;
if (sizeof(palette_) / sizeof(palette_[0]) < PALETTE_SIZE) { if (sizeof(palette_) / sizeof(palette_[0]) < PALETTE_SIZE) {
@@ -641,7 +651,7 @@ auto Surface::fadePalette() -> bool {
} }
// Realiza un efecto de fundido en la paleta secundaria // Realiza un efecto de fundido en la paleta secundaria
auto Surface::fadeSubPalette(Uint32 delay) -> bool { auto Surface::fadeSubPalette(Uint32 delay) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Variable estática para almacenar el último tick // Variable estática para almacenar el último tick
static Uint32 last_tick_ = 0; static Uint32 last_tick_ = 0;
@@ -675,4 +685,4 @@ auto Surface::fadeSubPalette(Uint32 delay) -> bool {
} }
// Restaura la sub paleta a su estado original // Restaura la sub paleta a su estado original
void Surface::resetSubPalette() { initializeSubPalette(sub_palette_); } void Surface::resetSubPalette() { initializeSubPalette(sub_palette_); } // NOLINT(readability-convert-member-functions-to-static)

View File

@@ -82,13 +82,13 @@ class Surface {
void render(SDL_FRect* src_rect = nullptr, SDL_FRect* dst_rect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE); void render(SDL_FRect* src_rect = nullptr, SDL_FRect* dst_rect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE);
// Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro // Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro
void renderWithColorReplace(int x, int y, Uint8 source_color = 0, Uint8 target_color = 0, SDL_FRect* src_rect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE); void renderWithColorReplace(int x, int y, Uint8 source_color = 0, Uint8 target_color = 0, SDL_FRect* src_rect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE) const;
// Render amb dissolució als cantons superior/inferior (hash 2D, sense parpelleig) // Render amb dissolució als cantons superior/inferior (hash 2D, sense parpelleig)
void renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, SDL_FRect* src_rect = nullptr); void renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, SDL_FRect* src_rect = nullptr) const;
// Idem però reemplaçant un color índex (per a sprites sobre fons del mateix color) // Idem però reemplaçant un color índex (per a sprites sobre fons del mateix color)
void renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color, SDL_FRect* src_rect = nullptr); void renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color, SDL_FRect* src_rect = nullptr) const;
// Establece un color en la paleta // Establece un color en la paleta
void setColor(int index, Uint32 color); void setColor(int index, Uint32 color);

View File

@@ -8,23 +8,37 @@
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/sprite/sprite.hpp" // Para SSprite
#include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface.hpp" // Para Surface
#include "core/rendering/surface_sprite.hpp" // Para SSprite
#include "core/resources/resource_helper.hpp" // Para ResourceHelper #include "core/resources/resource_helper.hpp" // Para ResourceHelper
#include "utils/utils.hpp" // Para getFileName, stringToColor, printWithDots #include "utils/utils.hpp" // Para getFileName, stringToColor, printWithDots
// Extrae el siguiente codepoint UTF-8 de la cadena, avanzando 'pos' al byte siguiente // Extrae el siguiente codepoint UTF-8 de la cadena, avanzando 'pos' al byte siguiente
auto Text::nextCodepoint(const std::string& s, size_t& pos) -> uint32_t { auto Text::nextCodepoint(const std::string& s, size_t& pos) -> uint32_t { // NOLINT(readability-convert-member-functions-to-static)
auto c = static_cast<unsigned char>(s[pos]); auto c = static_cast<unsigned char>(s[pos]);
uint32_t cp = 0; uint32_t cp = 0;
size_t extra = 0; size_t extra = 0;
if (c < 0x80) { cp = c; extra = 0; } if (c < 0x80) {
else if (c < 0xC0) { pos++; return 0xFFFD; } // byte de continuación suelto cp = c;
else if (c < 0xE0) { cp = c & 0x1F; extra = 1; } extra = 0;
else if (c < 0xF0) { cp = c & 0x0F; extra = 2; } } else if (c < 0xC0) {
else if (c < 0xF8) { cp = c & 0x07; extra = 3; } pos++;
else { pos++; return 0xFFFD; } return 0xFFFD;
} // byte de continuación suelto
else if (c < 0xE0) {
cp = c & 0x1F;
extra = 1;
} else if (c < 0xF0) {
cp = c & 0x0F;
extra = 2;
} else if (c < 0xF8) {
cp = c & 0x07;
extra = 3;
} else {
pos++;
return 0xFFFD;
}
pos++; pos++;
for (size_t i = 0; i < extra && pos < s.size(); ++i, ++pos) { for (size_t i = 0; i < extra && pos < s.size(); ++i, ++pos) {
@@ -36,7 +50,7 @@ auto Text::nextCodepoint(const std::string& s, size_t& pos) -> uint32_t {
} }
// Convierte un codepoint Unicode a una cadena UTF-8 // Convierte un codepoint Unicode a una cadena UTF-8
auto Text::codepointToUtf8(uint32_t cp) -> std::string { auto Text::codepointToUtf8(uint32_t cp) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
std::string result; std::string result;
if (cp < 0x80) { if (cp < 0x80) {
result += static_cast<char>(cp); result += static_cast<char>(cp);
@@ -58,7 +72,7 @@ auto Text::codepointToUtf8(uint32_t cp) -> std::string {
// Carga un fichero de definición de fuente .fnt // Carga un fichero de definición de fuente .fnt
// Formato: líneas "clave valor", comentarios con #, gliphos como "codepoint ancho" // Formato: líneas "clave valor", comentarios con #, gliphos como "codepoint ancho"
auto Text::loadTextFile(const std::string& file_path) -> std::shared_ptr<File> { auto Text::loadTextFile(const std::string& file_path) -> std::shared_ptr<File> { // NOLINT(readability-convert-member-functions-to-static)
auto tf = std::make_shared<File>(); auto tf = std::make_shared<File>();
auto file_data = Resource::Helper::loadFile(file_path); auto file_data = Resource::Helper::loadFile(file_path);
@@ -101,9 +115,9 @@ auto Text::loadTextFile(const std::string& file_path) -> std::shared_ptr<File> {
continue; // línea mal formateada, ignorar continue; // línea mal formateada, ignorar
} }
Offset off{}; Offset off{};
const int row_sp = tf->row_spacing > 0 ? tf->row_spacing : tf->cell_spacing; const int ROW_SP = tf->row_spacing > 0 ? tf->row_spacing : tf->cell_spacing;
off.x = (glyph_index % tf->columns) * (tf->box_width + tf->cell_spacing) + tf->cell_spacing; off.x = ((glyph_index % tf->columns) * (tf->box_width + tf->cell_spacing)) + tf->cell_spacing;
off.y = (glyph_index / tf->columns) * (tf->box_height + row_sp) + tf->cell_spacing; off.y = ((glyph_index / tf->columns) * (tf->box_height + ROW_SP)) + tf->cell_spacing;
off.w = width; off.w = width;
tf->offset[codepoint] = off; tf->offset[codepoint] = off;
++glyph_index; ++glyph_index;
@@ -122,19 +136,19 @@ Text::Text(const std::shared_ptr<Surface>& surface, const std::string& text_file
box_width_ = tf->box_width; box_width_ = tf->box_width;
offset_ = tf->offset; offset_ = tf->offset;
sprite_ = std::make_unique<SurfaceSprite>(surface, (SDL_FRect){0.0F, 0.0F, static_cast<float>(box_width_), static_cast<float>(box_height_)}); sprite_ = std::make_unique<Sprite>(surface, SDL_FRect{.x = 0.0F, .y = 0.0F, .w = static_cast<float>(box_width_), .h = static_cast<float>(box_height_)});
} }
// Constructor desde estructura precargada // Constructor desde estructura precargada
Text::Text(const std::shared_ptr<Surface>& surface, const std::shared_ptr<File>& text_file) Text::Text(const std::shared_ptr<Surface>& surface, const std::shared_ptr<File>& text_file)
: sprite_(std::make_unique<SurfaceSprite>(surface, (SDL_FRect){0.0F, 0.0F, static_cast<float>(text_file->box_width), static_cast<float>(text_file->box_height)})), : sprite_(std::make_unique<Sprite>(surface, SDL_FRect{.x = 0.0F, .y = 0.0F, .w = static_cast<float>(text_file->box_width), .h = static_cast<float>(text_file->box_height)})),
box_width_(text_file->box_width), box_width_(text_file->box_width),
box_height_(text_file->box_height), box_height_(text_file->box_height),
offset_(text_file->offset) { offset_(text_file->offset) {
} }
// Escribe texto en pantalla // Escribe texto en pantalla
void Text::write(int x, int y, const std::string& text, int kerning, int lenght) { void Text::write(int x, int y, const std::string& text, int kerning, int lenght) { // NOLINT(readability-convert-member-functions-to-static)
int shift = 0; int shift = 0;
int glyphs_done = 0; int glyphs_done = 0;
size_t pos = 0; size_t pos = 0;
@@ -156,7 +170,7 @@ void Text::write(int x, int y, const std::string& text, int kerning, int lenght)
} }
// Escribe el texto en una surface // Escribe el texto en una surface
auto Text::writeToSurface(const std::string& text, int zoom, int kerning) -> std::shared_ptr<Surface> { auto Text::writeToSurface(const std::string& text, int zoom, int kerning) -> std::shared_ptr<Surface> { // NOLINT(readability-make-member-function-const)
auto width = length(text, kerning) * zoom; auto width = length(text, kerning) * zoom;
auto height = box_height_ * zoom; auto height = box_height_ * zoom;
auto surface = std::make_shared<Surface>(width, height); auto surface = std::make_shared<Surface>(width, height);
@@ -170,7 +184,7 @@ auto Text::writeToSurface(const std::string& text, int zoom, int kerning) -> std
} }
// Escribe el texto con extras en una surface // Escribe el texto con extras en una surface
auto Text::writeDXToSurface(Uint8 flags, const std::string& text, int kerning, Uint8 text_color, Uint8 shadow_distance, Uint8 shadow_color, int lenght) -> std::shared_ptr<Surface> { auto Text::writeDXToSurface(Uint8 flags, const std::string& text, int kerning, Uint8 text_color, Uint8 shadow_distance, Uint8 shadow_color, int lenght) -> std::shared_ptr<Surface> { // NOLINT(readability-make-member-function-const)
auto width = Text::length(text, kerning) + shadow_distance; auto width = Text::length(text, kerning) + shadow_distance;
auto height = box_height_ + shadow_distance; auto height = box_height_ + shadow_distance;
auto surface = std::make_shared<Surface>(width, height); auto surface = std::make_shared<Surface>(width, height);
@@ -184,7 +198,7 @@ auto Text::writeDXToSurface(Uint8 flags, const std::string& text, int kerning, U
} }
// Escribe el texto con colores // Escribe el texto con colores
void Text::writeColored(int x, int y, const std::string& text, Uint8 color, int kerning, int lenght) { void Text::writeColored(int x, int y, const std::string& text, Uint8 color, int kerning, int lenght) { // NOLINT(readability-convert-member-functions-to-static)
int shift = 0; int shift = 0;
int glyphs_done = 0; int glyphs_done = 0;
size_t pos = 0; size_t pos = 0;
@@ -218,7 +232,7 @@ void Text::writeCentered(int x, int y, const std::string& text, int kerning, int
} }
// Escribe texto con extras // Escribe texto con extras
void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning, Uint8 text_color, Uint8 shadow_distance, Uint8 shadow_color, int lenght) { void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning, Uint8 text_color, Uint8 shadow_distance, Uint8 shadow_color, int lenght) { // NOLINT(readability-convert-member-functions-to-static)
const auto CENTERED = ((flags & CENTER_FLAG) == CENTER_FLAG); const auto CENTERED = ((flags & CENTER_FLAG) == CENTER_FLAG);
const auto SHADOWED = ((flags & SHADOW_FLAG) == SHADOW_FLAG); const auto SHADOWED = ((flags & SHADOW_FLAG) == SHADOW_FLAG);
const auto COLORED = ((flags & COLOR_FLAG) == COLOR_FLAG); const auto COLORED = ((flags & COLOR_FLAG) == COLOR_FLAG);
@@ -233,7 +247,8 @@ void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerni
} }
if (STROKED) { if (STROKED) {
for (int dist = 1; dist <= shadow_distance; ++dist) { const int MAX_DIST = static_cast<int>(shadow_distance);
for (int dist = 1; dist <= MAX_DIST; ++dist) {
for (int dy = -dist; dy <= dist; ++dy) { for (int dy = -dist; dy <= dist; ++dy) {
for (int dx = -dist; dx <= dist; ++dx) { for (int dx = -dist; dx <= dist; ++dx) {
writeColored(x + dx, y + dy, text, shadow_color, kerning, lenght); writeColored(x + dx, y + dy, text, shadow_color, kerning, lenght);
@@ -250,7 +265,7 @@ void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerni
} }
// Obtiene la longitud en pixels de una cadena UTF-8 // Obtiene la longitud en pixels de una cadena UTF-8
auto Text::length(const std::string& text, int kerning) const -> int { auto Text::length(const std::string& text, int kerning) const -> int { // NOLINT(readability-convert-member-functions-to-static)
int shift = 0; int shift = 0;
size_t pos = 0; size_t pos = 0;
@@ -267,7 +282,7 @@ auto Text::length(const std::string& text, int kerning) const -> int {
} }
// Devuelve el ancho en pixels de un glifo dado su codepoint Unicode // Devuelve el ancho en pixels de un glifo dado su codepoint Unicode
auto Text::glyphWidth(uint32_t codepoint, int kerning) const -> int { auto Text::glyphWidth(uint32_t codepoint, int kerning) const -> int { // NOLINT(readability-convert-member-functions-to-static)
auto it = offset_.find(codepoint); auto it = offset_.find(codepoint);
if (it == offset_.end()) { it = offset_.find('?'); } if (it == offset_.end()) { it = offset_.find('?'); }
if (it != offset_.end()) { return it->second.w + kerning; } if (it != offset_.end()) { return it->second.w + kerning; }

View File

@@ -6,7 +6,7 @@
#include <string> // Para string #include <string> // Para string
#include <unordered_map> // Para unordered_map #include <unordered_map> // Para unordered_map
#include "core/rendering/surface_sprite.hpp" // Para SSprite #include "core/rendering/sprite/sprite.hpp" // Para SSprite
class Surface; // Forward declaration class Surface; // Forward declaration
// Clase texto. Pinta texto en pantalla a partir de un bitmap con soporte UTF-8 // Clase texto. Pinta texto en pantalla a partir de un bitmap con soporte UTF-8
@@ -52,7 +52,7 @@ class Text {
[[nodiscard]] auto getCharacterSize() const -> int; // Devuelve el tamaño del caracter [[nodiscard]] auto getCharacterSize() const -> int; // Devuelve el tamaño del caracter
[[nodiscard]] auto glyphWidth(uint32_t codepoint, int kerning = 0) const -> int; // Devuelve el ancho en pixels de un glifo [[nodiscard]] auto glyphWidth(uint32_t codepoint, int kerning = 0) const -> int; // Devuelve el ancho en pixels de un glifo
[[nodiscard]] auto getGlyphClip(uint32_t codepoint) const -> SDL_FRect; // Devuelve el clip rect del glifo [[nodiscard]] auto getGlyphClip(uint32_t codepoint) const -> SDL_FRect; // Devuelve el clip rect del glifo
[[nodiscard]] auto getSprite() const -> SurfaceSprite* { return sprite_.get(); } // Acceso al sprite interno [[nodiscard]] auto getSprite() const -> Sprite* { return sprite_.get(); } // Acceso al sprite interno
void setFixedWidth(bool value); // Establece si se usa un tamaño fijo de letra void setFixedWidth(bool value); // Establece si se usa un tamaño fijo de letra
@@ -62,7 +62,7 @@ class Text {
private: private:
// Objetos y punteros // Objetos y punteros
std::unique_ptr<SurfaceSprite> sprite_ = nullptr; // Objeto con los graficos para el texto std::unique_ptr<Sprite> sprite_ = nullptr; // Objeto con los graficos para el texto
// Variables // Variables
int box_width_ = 0; // Anchura de la caja de cada caracter en el png int box_width_ = 0; // Anchura de la caja de cada caracter en el png

View File

@@ -1,163 +0,0 @@
#include "core/rendering/texture.hpp"
#include <SDL3/SDL.h>
#include <iostream> // Para basic_ostream, operator<<, endl, cout
#include <stdexcept> // Para runtime_error
#include <string> // Para char_traits, operator<<, string, opera...
#include <utility>
#include <vector> // Para vector
#include "utils/utils.hpp" // Para getFileName, Color, printWithDots
#define STB_IMAGE_IMPLEMENTATION
#include "external/stb_image.h" // para stbi_failure_reason, stbi_image_free
// Constructor
Texture::Texture(SDL_Renderer* renderer, std::string path)
: renderer_(renderer),
path_(std::move(path)) {
// Carga el fichero en la textura
if (!path_.empty()) {
// Obtiene la extensión
const std::string EXTENSION = path_.substr(path_.find_last_of('.') + 1);
// .png
if (EXTENSION == "png") {
loadFromFile(path_);
}
}
}
// Destructor
Texture::~Texture() {
unloadTexture();
palettes_.clear();
}
// Carga una imagen desde un fichero
auto Texture::loadFromFile(const std::string& file_path) -> bool {
if (file_path.empty()) {
return false;
}
int req_format = STBI_rgb_alpha;
int width;
int height;
int orig_format;
unsigned char* data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
if (data == nullptr) {
std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << '\n';
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
}
printWithDots("Image : ", getFileName(file_path), "[ LOADED ]");
int pitch;
SDL_PixelFormat pixel_format;
// STBI_rgb_alpha (RGBA)
pitch = 4 * width;
pixel_format = SDL_PIXELFORMAT_RGBA32;
// Limpia
unloadTexture();
// La textura final
SDL_Texture* new_texture = nullptr;
// Carga la imagen desde una ruta específica
auto* loaded_surface = SDL_CreateSurfaceFrom(width, height, pixel_format, static_cast<void*>(data), pitch);
if (loaded_surface == nullptr) {
std::cout << "Unable to load image " << file_path << '\n';
} else {
// Crea la textura desde los pixels de la surface
new_texture = SDL_CreateTextureFromSurface(renderer_, loaded_surface);
if (new_texture == nullptr) {
std::cout << "Unable to create texture from " << file_path << "! SDL Error: " << SDL_GetError() << '\n';
} else {
// Obtiene las dimensiones de la imagen
width_ = loaded_surface->w;
height_ = loaded_surface->h;
}
// Elimina la textura cargada
SDL_DestroySurface(loaded_surface);
}
// Return success
stbi_image_free(data);
texture_ = new_texture;
return texture_ != nullptr;
}
// Crea una textura en blanco
auto Texture::createBlank(int width, int height, SDL_PixelFormat format, SDL_TextureAccess access) -> bool {
// Crea una textura sin inicializar
texture_ = SDL_CreateTexture(renderer_, format, access, width, height);
if (texture_ == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create blank texture! SDL Error: %s", SDL_GetError());
} else {
width_ = width;
height_ = height;
}
return texture_ != nullptr;
}
// Libera la memoria de la textura
void Texture::unloadTexture() {
// Libera la textura
if (texture_ != nullptr) {
SDL_DestroyTexture(texture_);
texture_ = nullptr;
width_ = 0;
height_ = 0;
}
}
// Establece el color para la modulacion
void Texture::setColor(Uint8 red, Uint8 green, Uint8 blue) { SDL_SetTextureColorMod(texture_, red, green, blue); }
void Texture::setColor(Color color) { SDL_SetTextureColorMod(texture_, color.r, color.g, color.b); }
// Establece el blending
void Texture::setBlendMode(SDL_BlendMode blending) { SDL_SetTextureBlendMode(texture_, blending); }
// Establece el alpha para la modulación
void Texture::setAlpha(Uint8 alpha) { SDL_SetTextureAlphaMod(texture_, alpha); }
// Renderiza la textura en un punto específico
void Texture::render(float x, float y, SDL_FRect* clip, float zoom_w, float zoom_h, double angle, SDL_FPoint* center, SDL_FlipMode flip) {
// Establece el destino de renderizado en la pantalla
SDL_FRect render_quad = {x, y, width_, height_};
// Obtiene las dimesiones del clip de renderizado
if (clip != nullptr) {
render_quad.w = clip->w;
render_quad.h = clip->h;
}
// Calcula el zoom y las coordenadas
if (zoom_h != 1.0F || zoom_w != 1.0F) {
render_quad.x = render_quad.x + (render_quad.w / 2);
render_quad.y = render_quad.y + (render_quad.h / 2);
render_quad.w = render_quad.w * zoom_w;
render_quad.h = render_quad.h * zoom_h;
render_quad.x = render_quad.x - (render_quad.w / 2);
render_quad.y = render_quad.y - (render_quad.h / 2);
}
// Renderiza a pantalla
SDL_RenderTextureRotated(renderer_, texture_, clip, &render_quad, angle, center, flip);
}
// Establece la textura como objetivo de renderizado
void Texture::setAsRenderTarget(SDL_Renderer* renderer) { SDL_SetRenderTarget(renderer, texture_); }
// Recarga la textura
auto Texture::reLoad() -> bool { return loadFromFile(path_); }
// Obtiene la textura
auto Texture::getSDLTexture() -> SDL_Texture* { return texture_; }
// Obtiene el renderizador
auto Texture::getRenderer() -> SDL_Renderer* { return renderer_; }

View File

@@ -1,43 +0,0 @@
#pragma once
#include <SDL3/SDL.h>
#include <string> // Para string
#include <vector> // Para vector
struct Color; // lines 11-11
class Texture {
public:
explicit Texture(SDL_Renderer* renderer, std::string path = std::string()); // Constructor
~Texture(); // Destructor
auto loadFromFile(const std::string& path) -> bool; // Carga una imagen desde un fichero
auto createBlank(int width, int height, SDL_PixelFormat format = SDL_PIXELFORMAT_RGBA8888, SDL_TextureAccess access = SDL_TEXTUREACCESS_STREAMING) -> bool; // Crea una textura en blanco
auto reLoad() -> bool; // Recarga la textura
void setColor(Uint8 red, Uint8 green, Uint8 blue); // Establece el color para la modulacion
void setColor(Color color); // Establece el color para la modulacion
void setBlendMode(SDL_BlendMode blending); // Establece el blending
void setAlpha(Uint8 alpha); // Establece el alpha para la modulación
void setAsRenderTarget(SDL_Renderer* renderer); // Establece la textura como objetivo de renderizado
void render(float x, float y, SDL_FRect* clip = nullptr, float zoom_w = 1, float zoom_h = 1, double angle = 0.0, SDL_FPoint* center = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE); // Renderiza la textura en un punto específico
[[nodiscard]] auto getWidth() const -> int { return width_; } // Obtiene el ancho de la imagen
[[nodiscard]] auto getHeight() const -> int { return height_; } // Obtiene el alto de la imagen
auto getSDLTexture() -> SDL_Texture*; // Obtiene la textura
auto getRenderer() -> SDL_Renderer*; // Obtiene el renderizador
private:
void unloadTexture(); // Libera la memoria de la textura
// Objetos y punteros
SDL_Renderer* renderer_; // Renderizador donde dibujar la textura
SDL_Texture* texture_ = nullptr; // La textura
// Variables
std::string path_; // Ruta de la imagen de la textura
float width_ = 0.0F; // Ancho de la imagen
float height_ = 0.0F; // Alto de la imagen
std::vector<std::vector<Uint32>> palettes_; // Vector con las diferentes paletas
};

View File

@@ -55,6 +55,8 @@ namespace Resource {
// Carga todos los recursos // Carga todos los recursos
void Cache::load() { void Cache::load() {
// Nota: el overlay de debug (RenderInfo) se inicializa después de esta carga,
// por lo que updateZoomFactor() se llamará correctamente en RenderInfo::init().
calculateTotal(); calculateTotal();
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK)); Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
std::cout << "\n** LOADING RESOURCES" << '\n'; std::cout << "\n** LOADING RESOURCES" << '\n';
@@ -76,8 +78,8 @@ namespace Resource {
} }
// Obtiene el sonido a partir de un nombre // Obtiene el sonido a partir de un nombre
auto Cache::getSound(const std::string& name) -> JA_Sound_t* { auto Cache::getSound(const std::string& name) -> JA_Sound_t* { // NOLINT(readability-convert-member-functions-to-static)
auto it = std::ranges::find_if(sounds_, [&name](const auto& s) { return s.name == name; }); auto it = std::ranges::find_if(sounds_, [&name](const auto& s) -> bool { return s.name == name; });
if (it != sounds_.end()) { if (it != sounds_.end()) {
return it->sound; return it->sound;
@@ -88,8 +90,8 @@ namespace Resource {
} }
// Obtiene la música a partir de un nombre // Obtiene la música a partir de un nombre
auto Cache::getMusic(const std::string& name) -> JA_Music_t* { auto Cache::getMusic(const std::string& name) -> JA_Music_t* { // NOLINT(readability-convert-member-functions-to-static)
auto it = std::ranges::find_if(musics_, [&name](const auto& m) { return m.name == name; }); auto it = std::ranges::find_if(musics_, [&name](const auto& m) -> bool { return m.name == name; });
if (it != musics_.end()) { if (it != musics_.end()) {
return it->music; return it->music;
@@ -100,8 +102,8 @@ namespace Resource {
} }
// Obtiene la surface a partir de un nombre // Obtiene la surface a partir de un nombre
auto Cache::getSurface(const std::string& name) -> std::shared_ptr<Surface> { auto Cache::getSurface(const std::string& name) -> std::shared_ptr<Surface> { // NOLINT(readability-convert-member-functions-to-static)
auto it = std::ranges::find_if(surfaces_, [&name](const auto& t) { return t.name == name; }); auto it = std::ranges::find_if(surfaces_, [&name](const auto& t) -> bool { return t.name == name; });
if (it != surfaces_.end()) { if (it != surfaces_.end()) {
return it->surface; return it->surface;
@@ -112,8 +114,8 @@ namespace Resource {
} }
// Obtiene la paleta a partir de un nombre // Obtiene la paleta a partir de un nombre
auto Cache::getPalette(const std::string& name) -> Palette { auto Cache::getPalette(const std::string& name) -> Palette { // NOLINT(readability-convert-member-functions-to-static)
auto it = std::ranges::find_if(palettes_, [&name](const auto& t) { return t.name == name; }); auto it = std::ranges::find_if(palettes_, [&name](const auto& t) -> bool { return t.name == name; });
if (it != palettes_.end()) { if (it != palettes_.end()) {
return it->palette; return it->palette;
@@ -124,8 +126,8 @@ namespace Resource {
} }
// Obtiene el fichero de texto a partir de un nombre // Obtiene el fichero de texto a partir de un nombre
auto Cache::getTextFile(const std::string& name) -> std::shared_ptr<Text::File> { auto Cache::getTextFile(const std::string& name) -> std::shared_ptr<Text::File> { // NOLINT(readability-convert-member-functions-to-static)
auto it = std::ranges::find_if(text_files_, [&name](const auto& t) { return t.name == name; }); auto it = std::ranges::find_if(text_files_, [&name](const auto& t) -> bool { return t.name == name; });
if (it != text_files_.end()) { if (it != text_files_.end()) {
return it->text_file; return it->text_file;
@@ -136,8 +138,8 @@ namespace Resource {
} }
// Obtiene el objeto de texto a partir de un nombre // Obtiene el objeto de texto a partir de un nombre
auto Cache::getText(const std::string& name) -> std::shared_ptr<Text> { auto Cache::getText(const std::string& name) -> std::shared_ptr<Text> { // NOLINT(readability-convert-member-functions-to-static)
auto it = std::ranges::find_if(texts_, [&name](const auto& t) { return t.name == name; }); auto it = std::ranges::find_if(texts_, [&name](const auto& t) -> bool { return t.name == name; });
if (it != texts_.end()) { if (it != texts_.end()) {
return it->text; return it->text;
@@ -148,8 +150,8 @@ namespace Resource {
} }
// Obtiene los datos de animación parseados a partir de un nombre // Obtiene los datos de animación parseados a partir de un nombre
auto Cache::getAnimationData(const std::string& name) -> const AnimationResource& { auto Cache::getAnimationData(const std::string& name) -> const AnimationResource& { // NOLINT(readability-convert-member-functions-to-static)
auto it = std::ranges::find_if(animations_, [&name](const auto& a) { return a.name == name; }); auto it = std::ranges::find_if(animations_, [&name](const auto& a) -> bool { return a.name == name; });
if (it != animations_.end()) { if (it != animations_.end()) {
return *it; return *it;
@@ -160,8 +162,8 @@ namespace Resource {
} }
// Obtiene la habitación a partir de un nombre // Obtiene la habitación a partir de un nombre
auto Cache::getRoom(const std::string& name) -> std::shared_ptr<Room::Data> { auto Cache::getRoom(const std::string& name) -> std::shared_ptr<Room::Data> { // NOLINT(readability-convert-member-functions-to-static)
auto it = std::ranges::find_if(rooms_, [&name](const auto& r) { return r.name == name; }); auto it = std::ranges::find_if(rooms_, [&name](const auto& r) -> bool { return r.name == name; });
if (it != rooms_.end()) { if (it != rooms_.end()) {
return it->room; return it->room;
@@ -177,7 +179,7 @@ namespace Resource {
} }
// Helper para lanzar errores de carga con formato consistente // Helper para lanzar errores de carga con formato consistente
[[noreturn]] void Cache::throwLoadError(const std::string& asset_type, const std::string& file_path, const std::exception& e) { [[noreturn]] void Cache::throwLoadError(const std::string& asset_type, const std::string& file_path, const std::exception& e) { // NOLINT(readability-convert-member-functions-to-static)
std::cerr << "\n[ ERROR ] Failed to load " << asset_type << ": " << getFileName(file_path) << '\n'; std::cerr << "\n[ ERROR ] Failed to load " << asset_type << ": " << getFileName(file_path) << '\n';
std::cerr << "[ ERROR ] Path: " << file_path << '\n'; std::cerr << "[ ERROR ] Path: " << file_path << '\n';
std::cerr << "[ ERROR ] Reason: " << e.what() << '\n'; std::cerr << "[ ERROR ] Reason: " << e.what() << '\n';
@@ -186,7 +188,7 @@ namespace Resource {
} }
// Carga los sonidos // Carga los sonidos
void Cache::loadSounds() { void Cache::loadSounds() { // NOLINT(readability-convert-member-functions-to-static)
std::cout << "\n>> SOUND FILES" << '\n'; std::cout << "\n>> SOUND FILES" << '\n';
auto list = List::get()->getListByType(List::Type::SOUND); auto list = List::get()->getListByType(List::Type::SOUND);
sounds_.clear(); sounds_.clear();
@@ -221,7 +223,7 @@ namespace Resource {
} }
// Carga las musicas // Carga las musicas
void Cache::loadMusics() { void Cache::loadMusics() { // NOLINT(readability-convert-member-functions-to-static)
std::cout << "\n>> MUSIC FILES" << '\n'; std::cout << "\n>> MUSIC FILES" << '\n';
auto list = List::get()->getListByType(List::Type::MUSIC); auto list = List::get()->getListByType(List::Type::MUSIC);
musics_.clear(); musics_.clear();
@@ -256,7 +258,7 @@ namespace Resource {
} }
// Carga las texturas // Carga las texturas
void Cache::loadSurfaces() { void Cache::loadSurfaces() { // NOLINT(readability-convert-member-functions-to-static)
std::cout << "\n>> SURFACES" << '\n'; std::cout << "\n>> SURFACES" << '\n';
auto list = List::get()->getListByType(List::Type::BITMAP); auto list = List::get()->getListByType(List::Type::BITMAP);
surfaces_.clear(); surfaces_.clear();
@@ -283,7 +285,7 @@ namespace Resource {
} }
// Carga las paletas // Carga las paletas
void Cache::loadPalettes() { void Cache::loadPalettes() { // NOLINT(readability-convert-member-functions-to-static)
std::cout << "\n>> PALETTES" << '\n'; std::cout << "\n>> PALETTES" << '\n';
auto list = List::get()->getListByType(List::Type::PALETTE); auto list = List::get()->getListByType(List::Type::PALETTE);
palettes_.clear(); palettes_.clear();
@@ -300,7 +302,7 @@ namespace Resource {
} }
// Carga los ficheros de texto // Carga los ficheros de texto
void Cache::loadTextFiles() { void Cache::loadTextFiles() { // NOLINT(readability-convert-member-functions-to-static)
std::cout << "\n>> TEXT FILES" << '\n'; std::cout << "\n>> TEXT FILES" << '\n';
auto list = List::get()->getListByType(List::Type::FONT); auto list = List::get()->getListByType(List::Type::FONT);
text_files_.clear(); text_files_.clear();
@@ -317,7 +319,7 @@ namespace Resource {
} }
// Carga las animaciones // Carga las animaciones
void Cache::loadAnimations() { void Cache::loadAnimations() { // NOLINT(readability-convert-member-functions-to-static)
std::cout << "\n>> ANIMATIONS" << '\n'; std::cout << "\n>> ANIMATIONS" << '\n';
auto list = List::get()->getListByType(List::Type::ANIMATION); auto list = List::get()->getListByType(List::Type::ANIMATION);
animations_.clear(); animations_.clear();
@@ -343,7 +345,7 @@ namespace Resource {
} }
// Carga las habitaciones desde archivos YAML // Carga las habitaciones desde archivos YAML
void Cache::loadRooms() { void Cache::loadRooms() { // NOLINT(readability-convert-member-functions-to-static)
std::cout << "\n>> ROOMS" << '\n'; std::cout << "\n>> ROOMS" << '\n';
auto list = List::get()->getListByType(List::Type::ROOM); auto list = List::get()->getListByType(List::Type::ROOM);
rooms_.clear(); rooms_.clear();
@@ -360,7 +362,7 @@ namespace Resource {
} }
} }
void Cache::createText() { void Cache::createText() { // NOLINT(readability-convert-member-functions-to-static)
struct ResourceInfo { struct ResourceInfo {
std::string key; // Identificador del recurso std::string key; // Identificador del recurso
std::string texture_file; // Nombre del archivo de textura std::string texture_file; // Nombre del archivo de textura
@@ -461,12 +463,12 @@ namespace Resource {
// Draw progress bar border // Draw progress bar border
const float WIRED_BAR_WIDTH = Options::game.width - (X_PADDING * 2); const float WIRED_BAR_WIDTH = Options::game.width - (X_PADDING * 2);
SDL_FRect rect_wired = {X_PADDING, BAR_POSITION, WIRED_BAR_WIDTH, BAR_HEIGHT}; SDL_FRect rect_wired = {.x = X_PADDING, .y = BAR_POSITION, .w = WIRED_BAR_WIDTH, .h = BAR_HEIGHT};
surface->drawRectBorder(&rect_wired, BAR_COLOR); surface->drawRectBorder(&rect_wired, BAR_COLOR);
// Draw progress bar fill // Draw progress bar fill
const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * count_.getPercentage(); const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * count_.getPercentage();
SDL_FRect rect_full = {X_PADDING, BAR_POSITION, FULL_BAR_WIDTH, BAR_HEIGHT}; SDL_FRect rect_full = {.x = X_PADDING, .y = BAR_POSITION, .w = FULL_BAR_WIDTH, .h = BAR_HEIGHT};
surface->fillRect(&rect_full, BAR_COLOR); surface->fillRect(&rect_full, BAR_COLOR);
Screen::get()->render(); Screen::get()->render();

View File

@@ -19,11 +19,11 @@ namespace Resource {
// Singleton // Singleton
List* List::instance = nullptr; List* List::instance = nullptr;
void List::init(const std::string& executable_path) { void List::init(const std::string& executable_path) { // NOLINT(readability-convert-member-functions-to-static)
List::instance = new List(executable_path); List::instance = new List(executable_path);
} }
void List::destroy() { void List::destroy() { // NOLINT(readability-convert-member-functions-to-static)
delete List::instance; delete List::instance;
} }
@@ -32,12 +32,12 @@ namespace Resource {
} }
// Añade un elemento al mapa (función auxiliar) // Añade un elemento al mapa (función auxiliar)
void List::addToMap(const std::string& file_path, Type type, bool required, bool absolute) { void List::addToMap(const std::string& file_path, Type type, bool required, bool absolute) { // NOLINT(readability-convert-member-functions-to-static)
std::string full_path = absolute ? file_path : executable_path_ + file_path; std::string full_path = absolute ? file_path : executable_path_ + file_path;
std::string filename = getFileName(full_path); std::string filename = getFileName(full_path);
// Verificar si ya existe el archivo // Verificar si ya existe el archivo
if (file_list_.find(filename) != file_list_.end()) { if (file_list_.contains(filename)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Asset '%s' already exists, overwriting", "Warning: Asset '%s' already exists, overwriting",
filename.c_str()); filename.c_str());
@@ -52,7 +52,7 @@ namespace Resource {
} }
// Carga recursos desde un archivo de configuración con soporte para variables // Carga recursos desde un archivo de configuración con soporte para variables
void List::loadFromFile(const std::string& config_file_path, const std::string& prefix, const std::string& system_folder) { void List::loadFromFile(const std::string& config_file_path, const std::string& prefix, const std::string& system_folder) { // NOLINT(readability-convert-member-functions-to-static)
std::ifstream file(config_file_path); std::ifstream file(config_file_path);
if (!file.is_open()) { if (!file.is_open()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -71,7 +71,7 @@ namespace Resource {
} }
// Carga recursos desde un string de configuración (para release con pack) // Carga recursos desde un string de configuración (para release con pack)
void List::loadFromString(const std::string& config_content, const std::string& prefix, const std::string& system_folder) { void List::loadFromString(const std::string& config_content, const std::string& prefix, const std::string& system_folder) { // NOLINT(readability-convert-member-functions-to-static)
try { try {
// Parsear YAML // Parsear YAML
auto yaml = fkyaml::node::deserialize(config_content); auto yaml = fkyaml::node::deserialize(config_content);
@@ -156,7 +156,7 @@ namespace Resource {
} }
// Devuelve la ruta completa a un fichero (búsqueda O(1)) // Devuelve la ruta completa a un fichero (búsqueda O(1))
auto List::get(const std::string& filename) const -> std::string { auto List::get(const std::string& filename) const -> std::string { // NOLINT(readability-convert-member-functions-to-static)
auto it = file_list_.find(filename); auto it = file_list_.find(filename);
if (it != file_list_.end()) { if (it != file_list_.end()) {
return it->second.file; return it->second.file;
@@ -167,7 +167,7 @@ namespace Resource {
} }
// Carga datos del archivo // Carga datos del archivo
auto List::loadData(const std::string& filename) const -> std::vector<uint8_t> { auto List::loadData(const std::string& filename) const -> std::vector<uint8_t> { // NOLINT(readability-convert-member-functions-to-static)
auto it = file_list_.find(filename); auto it = file_list_.find(filename);
if (it != file_list_.end()) { if (it != file_list_.end()) {
std::ifstream file(it->second.file, std::ios::binary); std::ifstream file(it->second.file, std::ios::binary);
@@ -197,11 +197,11 @@ namespace Resource {
// Verifica si un recurso existe // Verifica si un recurso existe
auto List::exists(const std::string& filename) const -> bool { auto List::exists(const std::string& filename) const -> bool {
return file_list_.find(filename) != file_list_.end(); return file_list_.contains(filename);
} }
// Parsea string a Type // Parsea string a Type
auto List::parseAssetType(const std::string& type_str) -> Type { auto List::parseAssetType(const std::string& type_str) -> Type { // NOLINT(readability-convert-member-functions-to-static)
if (type_str == "DATA") { if (type_str == "DATA") {
return Type::DATA; return Type::DATA;
} }
@@ -235,7 +235,7 @@ namespace Resource {
} }
// Devuelve el nombre del tipo de recurso // Devuelve el nombre del tipo de recurso
auto List::getTypeName(Type type) -> std::string { auto List::getTypeName(Type type) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
switch (type) { switch (type) {
case Type::DATA: case Type::DATA:
return "DATA"; return "DATA";
@@ -259,7 +259,7 @@ namespace Resource {
} }
// Devuelve la lista de recursos de un tipo // Devuelve la lista de recursos de un tipo
auto List::getListByType(Type type) const -> std::vector<std::string> { auto List::getListByType(Type type) const -> std::vector<std::string> { // NOLINT(readability-convert-member-functions-to-static)
std::vector<std::string> list; std::vector<std::string> list;
for (const auto& [filename, item] : file_list_) { for (const auto& [filename, item] : file_list_) {
@@ -275,7 +275,7 @@ namespace Resource {
} }
// Reemplaza variables en las rutas // Reemplaza variables en las rutas
auto List::replaceVariables(const std::string& path, const std::string& prefix, const std::string& system_folder) -> std::string { auto List::replaceVariables(const std::string& path, const std::string& prefix, const std::string& system_folder) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
std::string result = path; std::string result = path;
// Reemplazar ${PREFIX} // Reemplazar ${PREFIX}
@@ -296,7 +296,7 @@ namespace Resource {
} }
// Parsea las opciones de una línea de configuración // Parsea las opciones de una línea de configuración
auto List::parseOptions(const std::string& options, bool& required, bool& absolute) -> void { auto List::parseOptions(const std::string& options, bool& required, bool& absolute) -> void { // NOLINT(readability-convert-member-functions-to-static)
if (options.empty()) { if (options.empty()) {
return; return;
} }

View File

@@ -53,7 +53,7 @@ namespace Resource {
} }
// Load a resource // Load a resource
auto Loader::loadResource(const std::string& filename) -> std::vector<uint8_t> { auto Loader::loadResource(const std::string& filename) -> std::vector<uint8_t> { // NOLINT(readability-make-member-function-const)
if (!initialized_) { if (!initialized_) {
std::cerr << "Loader: Not initialized\n"; std::cerr << "Loader: Not initialized\n";
return {}; return {};
@@ -81,7 +81,7 @@ namespace Resource {
} }
// Check if a resource exists // Check if a resource exists
auto Loader::resourceExists(const std::string& filename) -> bool { auto Loader::resourceExists(const std::string& filename) -> bool { // NOLINT(readability-make-member-function-const)
if (!initialized_) { if (!initialized_) {
return false; return false;
} }
@@ -107,7 +107,7 @@ namespace Resource {
} }
// Get pack statistics // Get pack statistics
auto Loader::getPackResourceCount() const -> size_t { auto Loader::getPackResourceCount() const -> size_t { // NOLINT(readability-convert-member-functions-to-static)
if (resource_pack_ && resource_pack_->isLoaded()) { if (resource_pack_ && resource_pack_->isLoaded()) {
return resource_pack_->getResourceCount(); return resource_pack_->getResourceCount();
} }
@@ -122,7 +122,7 @@ namespace Resource {
} }
// Load from filesystem // Load from filesystem
auto Loader::loadFromFilesystem(const std::string& filepath) auto Loader::loadFromFilesystem(const std::string& filepath) // NOLINT(readability-convert-member-functions-to-static)
-> std::vector<uint8_t> { -> std::vector<uint8_t> {
std::ifstream file(filepath, std::ios::binary | std::ios::ate); std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file) { if (!file) {
@@ -147,7 +147,7 @@ namespace Resource {
} }
// Validate pack integrity // Validate pack integrity
auto Loader::validatePack() const -> bool { auto Loader::validatePack() const -> bool { // NOLINT(readability-convert-member-functions-to-static)
if (!initialized_ || !resource_pack_ || !resource_pack_->isLoaded()) { if (!initialized_ || !resource_pack_ || !resource_pack_->isLoaded()) {
std::cerr << "Loader: Cannot validate - pack not loaded\n"; std::cerr << "Loader: Cannot validate - pack not loaded\n";
return false; return false;
@@ -158,7 +158,7 @@ namespace Resource {
if (checksum == 0) { if (checksum == 0) {
std::cerr << "Loader: Pack checksum is zero (invalid)\n"; std::cerr << "Loader: Pack checksum is zero (invalid)\n";
return false; return false; // NOLINT(readability-simplify-boolean-expr)
} }
std::cout << "Loader: Pack checksum: 0x" << std::hex << checksum << std::dec std::cout << "Loader: Pack checksum: 0x" << std::hex << checksum << std::dec
@@ -168,7 +168,7 @@ namespace Resource {
} }
// Load assets.yaml from pack // Load assets.yaml from pack
auto Loader::loadAssetsConfig() const -> std::string { auto Loader::loadAssetsConfig() const -> std::string { // NOLINT(readability-convert-member-functions-to-static)
if (!initialized_ || !resource_pack_ || !resource_pack_->isLoaded()) { if (!initialized_ || !resource_pack_ || !resource_pack_->isLoaded()) {
std::cerr << "Loader: Cannot load assets config - pack not loaded\n"; std::cerr << "Loader: Cannot load assets config - pack not loaded\n";
return ""; return "";

View File

@@ -13,7 +13,7 @@
namespace Resource { namespace Resource {
// Calculate CRC32 checksum for data verification // Calculate CRC32 checksum for data verification
auto Pack::calculateChecksum(const std::vector<uint8_t>& data) -> uint32_t { auto Pack::calculateChecksum(const std::vector<uint8_t>& data) -> uint32_t { // NOLINT(readability-convert-member-functions-to-static)
uint32_t checksum = 0x12345678; uint32_t checksum = 0x12345678;
for (unsigned char byte : data) { for (unsigned char byte : data) {
checksum = ((checksum << 5) + checksum) + byte; checksum = ((checksum << 5) + checksum) + byte;
@@ -22,7 +22,7 @@ namespace Resource {
} }
// XOR encryption (symmetric - same function for encrypt/decrypt) // XOR encryption (symmetric - same function for encrypt/decrypt)
void Pack::encryptData(std::vector<uint8_t>& data, const std::string& key) { void Pack::encryptData(std::vector<uint8_t>& data, const std::string& key) { // NOLINT(readability-identifier-naming)
if (key.empty()) { if (key.empty()) {
return; return;
} }
@@ -31,13 +31,13 @@ namespace Resource {
} }
} }
void Pack::decryptData(std::vector<uint8_t>& data, const std::string& key) { void Pack::decryptData(std::vector<uint8_t>& data, const std::string& key) { // NOLINT(readability-identifier-naming)
// XOR is symmetric // XOR is symmetric
encryptData(data, key); encryptData(data, key);
} }
// Read entire file into memory // Read entire file into memory
auto Pack::readFile(const std::string& filepath) -> std::vector<uint8_t> { auto Pack::readFile(const std::string& filepath) -> std::vector<uint8_t> { // NOLINT(readability-convert-member-functions-to-static)
std::ifstream file(filepath, std::ios::binary | std::ios::ate); std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file) { if (!file) {
std::cerr << "ResourcePack: Failed to open file: " << filepath << '\n'; std::cerr << "ResourcePack: Failed to open file: " << filepath << '\n';
@@ -57,7 +57,7 @@ namespace Resource {
} }
// Add a single file to the pack // Add a single file to the pack
auto Pack::addFile(const std::string& filepath, const std::string& pack_name) auto Pack::addFile(const std::string& filepath, const std::string& pack_name) // NOLINT(readability-convert-member-functions-to-static)
-> bool { -> bool {
auto file_data = readFile(filepath); auto file_data = readFile(filepath);
if (file_data.empty()) { if (file_data.empty()) {
@@ -80,9 +80,9 @@ namespace Resource {
} }
// Add all files from a directory recursively // Add all files from a directory recursively
auto Pack::addDirectory(const std::string& dir_path, auto Pack::addDirectory(const std::string& dir_path, // NOLINT(readability-convert-member-functions-to-static)
const std::string& base_path) -> bool { const std::string& base_path) -> bool {
namespace fs = std::filesystem; namespace fs = std::filesystem; // NOLINT(readability-identifier-naming)
if (!fs::exists(dir_path) || !fs::is_directory(dir_path)) { if (!fs::exists(dir_path) || !fs::is_directory(dir_path)) {
std::cerr << "ResourcePack: Directory not found: " << dir_path << '\n'; std::cerr << "ResourcePack: Directory not found: " << dir_path << '\n';
@@ -117,7 +117,7 @@ namespace Resource {
} }
// Save the pack to a file // Save the pack to a file
auto Pack::savePack(const std::string& pack_file) -> bool { auto Pack::savePack(const std::string& pack_file) -> bool { // NOLINT(readability-convert-member-functions-to-static)
std::ofstream file(pack_file, std::ios::binary); std::ofstream file(pack_file, std::ios::binary);
if (!file) { if (!file) {
std::cerr << "ResourcePack: Failed to create pack file: " << pack_file << '\n'; std::cerr << "ResourcePack: Failed to create pack file: " << pack_file << '\n';
@@ -229,7 +229,7 @@ namespace Resource {
} }
// Get a resource by name // Get a resource by name
auto Pack::getResource(const std::string& filename) -> std::vector<uint8_t> { auto Pack::getResource(const std::string& filename) -> std::vector<uint8_t> { // NOLINT(readability-convert-member-functions-to-static)
auto it = resources_.find(filename); auto it = resources_.find(filename);
if (it == resources_.end()) { if (it == resources_.end()) {
return {}; return {};
@@ -259,11 +259,11 @@ namespace Resource {
// Check if a resource exists // Check if a resource exists
auto Pack::hasResource(const std::string& filename) const -> bool { auto Pack::hasResource(const std::string& filename) const -> bool {
return resources_.find(filename) != resources_.end(); return resources_.contains(filename);
} }
// Get list of all resources // Get list of all resources
auto Pack::getResourceList() const -> std::vector<std::string> { auto Pack::getResourceList() const -> std::vector<std::string> { // NOLINT(readability-convert-member-functions-to-static)
std::vector<std::string> list; std::vector<std::string> list;
list.reserve(resources_.size()); list.reserve(resources_.size());
for (const auto& [name, entry] : resources_) { for (const auto& [name, entry] : resources_) {
@@ -274,7 +274,7 @@ namespace Resource {
} }
// Calculate overall pack checksum for validation // Calculate overall pack checksum for validation
auto Pack::calculatePackChecksum() const -> uint32_t { auto Pack::calculatePackChecksum() const -> uint32_t { // NOLINT(readability-convert-member-functions-to-static)
if (!loaded_ || data_.empty()) { if (!loaded_ || data_.empty()) {
return 0; return 0;
} }

View File

@@ -40,13 +40,13 @@ namespace Resource {
auto loadPack(const std::string& pack_file) -> bool; auto loadPack(const std::string& pack_file) -> bool;
auto getResource(const std::string& filename) -> std::vector<uint8_t>; // Resource access auto getResource(const std::string& filename) -> std::vector<uint8_t>; // Resource access
auto hasResource(const std::string& filename) const -> bool; [[nodiscard]] auto hasResource(const std::string& filename) const -> bool;
auto getResourceList() const -> std::vector<std::string>; [[nodiscard]] auto getResourceList() const -> std::vector<std::string>;
auto isLoaded() const -> bool { return loaded_; } // Status queries [[nodiscard]] auto isLoaded() const -> bool { return loaded_; } // Status queries
auto getResourceCount() const -> size_t { return resources_.size(); } [[nodiscard]] auto getResourceCount() const -> size_t { return resources_.size(); }
auto getDataSize() const -> size_t { return data_.size(); } [[nodiscard]] auto getDataSize() const -> size_t { return data_.size(); }
auto calculatePackChecksum() const -> uint32_t; // Validation [[nodiscard]] auto calculatePackChecksum() const -> uint32_t; // Validation
private: private:
static constexpr std::array<char, 4> MAGIC_HEADER = {'J', 'D', 'D', 'I'}; // Pack format constants static constexpr std::array<char, 4> MAGIC_HEADER = {'J', 'D', 'D', 'I'}; // Pack format constants

View File

@@ -3,11 +3,15 @@
#ifdef _DEBUG #ifdef _DEBUG
#include <algorithm> // Para max #include <algorithm> // Para max
#include <fstream> // Para ifstream, ofstream
#include <memory> // Para __shared_ptr_access, shared_ptr #include <memory> // Para __shared_ptr_access, shared_ptr
#include "core/rendering/text.hpp" // Para Text #include "core/rendering/text.hpp" // Para Text
#include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_cache.hpp" // Para Resource
#include "utils/utils.hpp" // Para Color #include "external/fkyaml_node.hpp" // Para fkyaml::node
#include "game/defaults.hpp" // Para Defaults::Game::*
#include "utils/defines.hpp" // Para Tile::SIZE
#include "utils/utils.hpp" // Para Color, Flip::
// [SINGLETON] // [SINGLETON]
Debug* Debug::debug = nullptr; Debug* Debug::debug = nullptr;
@@ -28,32 +32,158 @@ auto Debug::get() -> Debug* {
} }
// Dibuja en pantalla // Dibuja en pantalla
void Debug::render() { void Debug::render() { // NOLINT(readability-make-member-function-const)
auto text = Resource::Cache::get()->getText("aseprite"); auto text = Resource::Cache::get()->getText("aseprite");
int y = y_; int y = y_;
int w = 0; int w = 0;
constexpr int DESP_Y = 7;
const int CHAR_SIZE = text->getCharacterSize();
// Watch window: valores persistentes (key: value)
for (const auto& [key, value] : watches_) {
const std::string LINE = key + ": " + value;
text->write(x_, y, LINE);
w = std::max(w, text->length(LINE));
y += DESP_Y;
if (y > 192 - CHAR_SIZE) {
y = y_;
x_ += w + 2;
w = 0;
}
}
// Slot one-shot: mensajes de un solo frame
for (const auto& s : slot_) { for (const auto& s : slot_) {
text->write(x_, y, s); text->write(x_, y, s);
w = (std::max(w, (int)s.length())); w = std::max(w, text->length(s));
y += text->getCharacterSize() + 1; y += DESP_Y;
if (y > 192 - text->getCharacterSize()) { if (y > 192 - CHAR_SIZE) {
y = y_; y = y_;
x_ += w * text->getCharacterSize() + 2; x_ += w + 2;
w = 0;
} }
} }
y = 0; y = 0;
for (const auto& l : log_) { for (const auto& l : log_) {
text->writeColored(x_ + 10, y, l, static_cast<Uint8>(PaletteColor::WHITE)); text->writeColored(x_ + 10, y, l, static_cast<Uint8>(PaletteColor::WHITE));
y += text->getCharacterSize() + 1; y += CHAR_SIZE + 1;
} }
} }
// Establece/actualiza un valor persistente en el watch window
void Debug::set(const std::string& key, const std::string& value) {
watches_[key] = value;
}
// Elimina un valor del watch window
void Debug::unset(const std::string& key) {
watches_.erase(key);
}
// Establece la posición donde se colocará la información de debug // Establece la posición donde se colocará la información de debug
void Debug::setPos(SDL_FPoint p) { void Debug::setPos(SDL_FPoint p) {
x_ = p.x; x_ = p.x;
y_ = p.y; y_ = p.y;
} }
// Establece la ruta del archivo debug.yaml
void Debug::setDebugFile(const std::string& path) {
debug_file_path_ = path;
}
// Convierte string a SceneManager::Scene (para debug.yaml)
static auto sceneFromString(const std::string& s) -> SceneManager::Scene {
if (s == "LOGO") { return SceneManager::Scene::LOGO; }
if (s == "LOADING") { return SceneManager::Scene::LOADING_SCREEN; }
if (s == "TITLE") { return SceneManager::Scene::TITLE; }
if (s == "CREDITS") { return SceneManager::Scene::CREDITS; }
if (s == "DEMO") { return SceneManager::Scene::DEMO; }
if (s == "ENDING") { return SceneManager::Scene::ENDING; }
if (s == "ENDING2") { return SceneManager::Scene::ENDING2; }
return SceneManager::Scene::GAME; // Fallback seguro
}
// Convierte SceneManager::Scene a string (para debug.yaml)
static auto sceneToString(SceneManager::Scene scene) -> std::string {
switch (scene) {
case SceneManager::Scene::LOGO:
return "LOGO";
case SceneManager::Scene::LOADING_SCREEN:
return "LOADING";
case SceneManager::Scene::TITLE:
return "TITLE";
case SceneManager::Scene::CREDITS:
return "CREDITS";
case SceneManager::Scene::DEMO:
return "DEMO";
case SceneManager::Scene::ENDING:
return "ENDING";
case SceneManager::Scene::ENDING2:
return "ENDING2";
default:
return "GAME";
}
}
// Carga la configuración de debug desde debug.yaml
void Debug::loadFromFile() {
// Inicializar con valores de release por defecto
spawn_settings_.room = Defaults::Game::Room::INITIAL;
spawn_settings_.spawn_x = Defaults::Game::Player::SPAWN_X;
spawn_settings_.spawn_y = Defaults::Game::Player::SPAWN_Y;
spawn_settings_.flip = Defaults::Game::Player::SPAWN_FLIP;
initial_scene_ = SceneManager::Scene::GAME;
std::ifstream file(debug_file_path_);
if (!file.good()) {
saveToFile(); // No existe: crear con valores por defecto
return;
}
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
file.close();
try {
auto yaml = fkyaml::node::deserialize(content);
if (yaml.contains("room")) {
spawn_settings_.room = yaml["room"].get_value<std::string>();
}
if (yaml.contains("spawn_x")) {
spawn_settings_.spawn_x = yaml["spawn_x"].get_value<int>() * Tile::SIZE;
}
if (yaml.contains("spawn_y")) {
spawn_settings_.spawn_y = yaml["spawn_y"].get_value<int>() * Tile::SIZE;
}
if (yaml.contains("spawn_flip")) {
auto s = yaml["spawn_flip"].get_value<std::string>();
spawn_settings_.flip = (s == "right") ? Flip::RIGHT : Flip::LEFT;
}
if (yaml.contains("initial_scene")) {
initial_scene_ = sceneFromString(yaml["initial_scene"].get_value<std::string>());
}
} catch (...) {
// YAML inválido: resetear a defaults y sobreescribir
spawn_settings_.room = Defaults::Game::Room::INITIAL;
spawn_settings_.spawn_x = Defaults::Game::Player::SPAWN_X;
spawn_settings_.spawn_y = Defaults::Game::Player::SPAWN_Y;
spawn_settings_.flip = Defaults::Game::Player::SPAWN_FLIP;
initial_scene_ = SceneManager::Scene::GAME;
saveToFile();
}
}
// Guarda la configuración de debug en debug.yaml
void Debug::saveToFile() const {
std::ofstream file(debug_file_path_);
if (!file.is_open()) { return; }
file << "# JailDoctor's Dilemma - Debug Configuration\n";
file << "# Edita para cambiar la habitacion y spawn del jugador en builds debug.\n\n";
file << "room: \"" << spawn_settings_.room << "\"\n";
file << "spawn_x: " << (spawn_settings_.spawn_x / Tile::SIZE) << " # en tiles\n";
file << "spawn_y: " << (spawn_settings_.spawn_y / Tile::SIZE) << " # en tiles\n";
file << "spawn_flip: " << ((spawn_settings_.flip == Flip::RIGHT) ? "right" : "left") << "\n";
file << "initial_scene: " << sceneToString(initial_scene_) << "\n";
}
#endif // _DEBUG #endif // _DEBUG

View File

@@ -4,12 +4,22 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <map> // Para map
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector #include <vector> // Para vector
#include "game/scene_manager.hpp" // Para SceneManager::Scene
// Clase Debug // Clase Debug
class Debug { class Debug {
public: public:
struct SpawnSettings {
std::string room;
int spawn_x = 0;
int spawn_y = 0;
SDL_FlipMode flip = SDL_FLIP_NONE;
};
static void init(); // [SINGLETON] Crearemos el objeto con esta función estática static void init(); // [SINGLETON] Crearemos el objeto con esta función estática
static void destroy(); // [SINGLETON] Destruiremos el objeto con esta función estática static void destroy(); // [SINGLETON] Destruiremos el objeto con esta función estática
static auto get() -> Debug*; // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él static auto get() -> Debug*; // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
@@ -20,13 +30,24 @@ class Debug {
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; } // Obtiene si el debug está activo [[nodiscard]] auto isEnabled() const -> bool { return enabled_; } // Obtiene si el debug está activo
void add(const std::string& text) { slot_.push_back(text); } // Añade texto al slot de debug void add(const std::string& text) { slot_.push_back(text); } // Añade texto one-shot al slot (se limpia cada frame)
void clear() { slot_.clear(); } // Limpia el slot de debug void clear() { slot_.clear(); } // Limpia el slot one-shot (no afecta a watches)
void addToLog(const std::string& text) { log_.push_back(text); } // Añade texto al log void addToLog(const std::string& text) { log_.push_back(text); } // Añade texto al log
void clearLog() { log_.clear(); } // Limpia el log void clearLog() { log_.clear(); } // Limpia el log
void set(const std::string& key, const std::string& value); // Establece/actualiza un valor persistente en el watch window
void unset(const std::string& key); // Elimina un valor del watch window
void clearWatches() { watches_.clear(); } // Limpia todos los watches
void setEnabled(bool value) { enabled_ = value; } // Establece si el debug está activo void setEnabled(bool value) { enabled_ = value; } // Establece si el debug está activo
void toggleEnabled() { enabled_ = !enabled_; } // Alterna el estado del debug void toggleEnabled() { enabled_ = !enabled_; } // Alterna el estado del debug
void setDebugFile(const std::string& path); // Establece la ruta del archivo debug.yaml
void loadFromFile(); // Carga la configuración de debug desde debug.yaml
void saveToFile() const; // Guarda la configuración de debug en debug.yaml
[[nodiscard]] auto getSpawnSettings() const -> const SpawnSettings& { return spawn_settings_; } // Obtiene los valores de spawn
void setSpawnSettings(const SpawnSettings& s) { spawn_settings_ = s; } // Establece los valores de spawn
[[nodiscard]] auto getInitialScene() const -> SceneManager::Scene { return initial_scene_; } // Obtiene la escena inicial de debug
void setInitialScene(SceneManager::Scene s) { initial_scene_ = s; } // Establece la escena inicial de debug
private: private:
static Debug* debug; // [SINGLETON] Objeto privado static Debug* debug; // [SINGLETON] Objeto privado
@@ -34,11 +55,15 @@ class Debug {
~Debug() = default; // Destructor ~Debug() = default; // Destructor
// Variables // Variables
std::vector<std::string> slot_; // Vector con los textos a escribir std::map<std::string, std::string> watches_; // Watch window: valores persistentes (key→value)
std::vector<std::string> log_; // Vector con los textos a escribir std::vector<std::string> slot_; // One-shot: textos que se limpian cada frame
std::vector<std::string> log_; // Log persistente
int x_ = 0; // Posicion donde escribir el texto de debug int x_ = 0; // Posicion donde escribir el texto de debug
int y_ = 0; // Posición donde escribir el texto de debug int y_ = 0; // Posición donde escribir el texto de debug
bool enabled_ = false; // Indica si esta activo el modo debug bool enabled_ = false; // Indica si esta activo el modo debug
std::string debug_file_path_; // Ruta del archivo debug.yaml
SpawnSettings spawn_settings_; // Configuración de spawn para debug
SceneManager::Scene initial_scene_ = SceneManager::Scene::GAME; // Escena inicial en debug
}; };
#endif // _DEBUG #endif // _DEBUG

View File

@@ -14,6 +14,7 @@
#include "core/audio/audio.hpp" // Para Audio #include "core/audio/audio.hpp" // Para Audio
#include "core/input/input.hpp" // Para Input, InputAction #include "core/input/input.hpp" // Para Input, InputAction
#include "core/locale/locale.hpp" // Para Locale #include "core/locale/locale.hpp" // Para Locale
#include "core/rendering/render_info.hpp" // Para RenderInfo
#include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/screen.hpp" // Para Screen
#include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_cache.hpp" // Para Resource
#include "core/resources/resource_helper.hpp" // Para ResourceHelper #include "core/resources/resource_helper.hpp" // Para ResourceHelper
@@ -30,6 +31,7 @@
#include "game/scenes/loading_screen.hpp" // Para LoadingScreen #include "game/scenes/loading_screen.hpp" // Para LoadingScreen
#include "game/scenes/logo.hpp" // Para Logo #include "game/scenes/logo.hpp" // Para Logo
#include "game/scenes/title.hpp" // Para Title #include "game/scenes/title.hpp" // Para Title
#include "game/ui/console.hpp" // Para Console
#include "game/ui/notifier.hpp" // Para Notifier #include "game/ui/notifier.hpp" // Para Notifier
#include "utils/defines.hpp" // Para WINDOW_CAPTION #include "utils/defines.hpp" // Para WINDOW_CAPTION
@@ -42,19 +44,32 @@
#endif #endif
// Constructor // Constructor
Director::Director(std::vector<std::string> const& args) { Director::Director() {
std::cout << "Game start" << '\n'; std::cout << "Game start" << '\n';
// Crea e inicializa las opciones del programa // Obtiene la ruta del ejecutable
Options::init(); std::string base = SDL_GetBasePath();
if (!base.empty() && base.back() == '/') base.pop_back();
// Comprueba los parametros del programa executable_path_ = base;
executable_path_ = getPath(checkProgramArguments(args));
// Crea la carpeta del sistema donde guardar datos // Crea la carpeta del sistema donde guardar datos
createSystemFolder("jailgames"); createSystemFolder("jailgames");
createSystemFolder("jailgames/jaildoctors_dilemma"); createSystemFolder("jailgames/jaildoctors_dilemma");
// Crea el subdirectorio shaders/ dentro de system_folder_ sin modificar system_folder_
{
std::string shaders_dir = system_folder_ + "/shaders";
struct stat st = {.st_dev = 0};
if (stat(shaders_dir.c_str(), &st) == -1) {
errno = 0;
#ifdef _WIN32
mkdir(shaders_dir.c_str());
#else
mkdir(shaders_dir.c_str(), S_IRWXU);
#endif
}
}
// Determinar el prefijo de ruta según la plataforma // Determinar el prefijo de ruta según la plataforma
#ifdef MACOS_BUNDLE #ifdef MACOS_BUNDLE
const std::string PREFIX = "/../Resources"; const std::string PREFIX = "/../Resources";
@@ -120,13 +135,17 @@ Director::Director(std::vector<std::string> const& args) {
#endif #endif
// Configura la ruta y carga las opciones desde un fichero // Configura la ruta y carga las opciones desde un fichero
Options::setConfigFile(Resource::List::get()->get("config.yaml")); Options::setConfigFile(Resource::List::get()->get("config.yaml")); // NOLINT(readability-static-accessed-through-instance)
Options::loadFromFile(); Options::loadFromFile();
// Configura la ruta y carga los presets de PostFX // Configura la ruta y carga los presets de PostFX
Options::setPostFXFile(Resource::List::get()->get("postfx.yaml")); Options::setPostFXFile(Resource::List::get()->get("postfx.yaml")); // NOLINT(readability-static-accessed-through-instance)
Options::loadPostFXFromFile(); Options::loadPostFXFromFile();
// Configura la ruta y carga los presets del shader CrtPi
Options::setCrtPiFile(Resource::List::get()->get("crtpi.yaml")); // NOLINT(readability-static-accessed-through-instance)
Options::loadCrtPiFromFile();
// En mode quiosc, forçar pantalla completa independentment de la configuració // En mode quiosc, forçar pantalla completa independentment de la configuració
if (Options::kiosk.enabled) { if (Options::kiosk.enabled) {
Options::video.fullscreen = true; Options::video.fullscreen = true;
@@ -141,6 +160,10 @@ Director::Director(std::vector<std::string> const& args) {
// Initialize resources (works for both release and development) // Initialize resources (works for both release and development)
Resource::Cache::init(); Resource::Cache::init();
Notifier::init("", "8bithud"); Notifier::init("", "8bithud");
#ifdef _DEBUG
RenderInfo::init(); // En DEBUG, se activa y notifica a Notifier del offset inicial
#endif
Console::init("8bithud");
Screen::get()->setNotificationsEnabled(true); Screen::get()->setNotificationsEnabled(true);
// Special handling for gamecontrollerdb.txt - SDL needs filesystem path // Special handling for gamecontrollerdb.txt - SDL needs filesystem path
@@ -150,7 +173,7 @@ Director::Director(std::vector<std::string> const& args) {
Input::init(gamecontroller_db); Input::init(gamecontroller_db);
#else #else
// In development, use Asset as normal // In development, use Asset as normal
Input::init(Resource::List::get()->get("gamecontrollerdb.txt")); // Carga configuración de controles Input::init(Resource::List::get()->get("gamecontrollerdb.txt")); // NOLINT(readability-static-accessed-through-instance) Carga configuración de controles
#endif #endif
// Aplica las teclas y botones del gamepad configurados desde Options // Aplica las teclas y botones del gamepad configurados desde Options
@@ -159,6 +182,9 @@ Director::Director(std::vector<std::string> const& args) {
#ifdef _DEBUG #ifdef _DEBUG
Debug::init(); Debug::init();
Debug::get()->setDebugFile(Resource::List::get()->get("debug.yaml"));
Debug::get()->loadFromFile();
SceneManager::current = Debug::get()->getInitialScene();
#endif #endif
std::cout << "\n"; // Fin de inicialización de sistemas std::cout << "\n"; // Fin de inicialización de sistemas
@@ -168,7 +194,7 @@ Director::Director(std::vector<std::string> const& args) {
std::string locale_path = executable_path_ + PREFIX + "/data/locale/" + Options::language + ".yaml"; std::string locale_path = executable_path_ + PREFIX + "/data/locale/" + Options::language + ".yaml";
Locale::init(locale_path); Locale::init(locale_path);
#else #else
Locale::init(Resource::List::get()->get(Options::language + ".yaml")); Locale::init(Resource::List::get()->get(Options::language + ".yaml")); // NOLINT(readability-static-accessed-through-instance)
#endif #endif
// Special handling for cheevos.bin - also needs filesystem path // Special handling for cheevos.bin - also needs filesystem path
@@ -191,6 +217,10 @@ Director::~Director() {
Debug::destroy(); Debug::destroy();
#endif #endif
Input::destroy(); Input::destroy();
Console::destroy();
#ifdef _DEBUG
RenderInfo::destroy();
#endif
Notifier::destroy(); Notifier::destroy();
Resource::Cache::destroy(); Resource::Cache::destroy();
Resource::Helper::shutdownResourceSystem(); // Shutdown resource pack system Resource::Helper::shutdownResourceSystem(); // Shutdown resource pack system
@@ -203,30 +233,8 @@ Director::~Director() {
std::cout << "\nBye!" << '\n'; std::cout << "\nBye!" << '\n';
} }
// Comprueba los parametros del programa
auto Director::checkProgramArguments(std::vector<std::string> const& args) -> std::string {
// Iterar sobre los argumentos del programa (saltando args[0] que es el ejecutable)
for (std::size_t i = 1; i < args.size(); ++i) {
const std::string& argument = args[i];
if (argument == "--console") {
Options::console = true;
} else if (argument == "--infiniteLives") {
Options::cheats.infinite_lives = Options::Cheat::State::ENABLED;
} else if (argument == "--invincible") {
Options::cheats.invincible = Options::Cheat::State::ENABLED;
} else if (argument == "--jailEnabled") {
Options::cheats.jail_is_open = Options::Cheat::State::ENABLED;
} else if (argument == "--altSkin") {
Options::cheats.alternate_skin = Options::Cheat::State::ENABLED;
}
}
return args[0];
}
// Crea la carpeta del sistema donde guardar datos // Crea la carpeta del sistema donde guardar datos
void Director::createSystemFolder(const std::string& folder) { void Director::createSystemFolder(const std::string& folder) { // NOLINT(readability-convert-member-functions-to-static)
#ifdef _WIN32 #ifdef _WIN32
system_folder_ = std::string(getenv("APPDATA")) + "/" + folder; system_folder_ = std::string(getenv("APPDATA")) + "/" + folder;
#elif __APPLE__ #elif __APPLE__
@@ -281,7 +289,7 @@ void Director::createSystemFolder(const std::string& folder) {
} }
// Carga la configuración de assets desde assets.yaml // Carga la configuración de assets desde assets.yaml
void Director::setFileList() { void Director::setFileList() { // NOLINT(readability-convert-member-functions-to-static)
// Determinar el prefijo de ruta según la plataforma // Determinar el prefijo de ruta según la plataforma
#ifdef MACOS_BUNDLE #ifdef MACOS_BUNDLE
const std::string PREFIX = "/../Resources"; const std::string PREFIX = "/../Resources";
@@ -355,6 +363,8 @@ void Director::runGame() {
auto Director::run() -> int { auto Director::run() -> int {
// Bucle principal // Bucle principal
while (SceneManager::current != SceneManager::Scene::QUIT) { while (SceneManager::current != SceneManager::Scene::QUIT) {
const SceneManager::Scene ACTIVE = SceneManager::current;
switch (SceneManager::current) { switch (SceneManager::current) {
case SceneManager::Scene::LOGO: case SceneManager::Scene::LOGO:
runLogo(); runLogo();
@@ -392,9 +402,20 @@ auto Director::run() -> int {
runEnding2(); runEnding2();
break; break;
case SceneManager::Scene::RESTART_CURRENT:
// La escena salió por RESTART_CURRENT → relanzar la escena guardada
SceneManager::current = SceneManager::scene_before_restart;
break;
default: default:
break; break;
} }
// Si la escena que acaba de correr dejó RESTART_CURRENT pendiente,
// restaurar la escena que estaba activa para relanzarla en la próxima iteración
if (SceneManager::current == SceneManager::Scene::RESTART_CURRENT) {
SceneManager::current = ACTIVE;
}
} }
return 0; return 0;

View File

@@ -3,11 +3,10 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector
class Director { class Director {
public: public:
explicit Director(std::vector<std::string> const& args); // Constructor Director(); // Constructor
~Director(); // Destructor ~Director(); // Destructor
static auto run() -> int; // Bucle principal static auto run() -> int; // Bucle principal
@@ -15,7 +14,6 @@ class Director {
// --- Variables --- // --- Variables ---
std::string executable_path_; // Path del ejecutable std::string executable_path_; // Path del ejecutable
std::string system_folder_; // Carpeta del sistema donde guardar datos std::string system_folder_; // Carpeta del sistema donde guardar datos
static auto checkProgramArguments(std::vector<std::string> const& args) -> std::string; // Comprueba los parametros del programa
// --- Funciones --- // --- Funciones ---
void createSystemFolder(const std::string& folder); // Crea la carpeta del sistema donde guardar datos void createSystemFolder(const std::string& folder); // Crea la carpeta del sistema donde guardar datos

View File

@@ -3,6 +3,7 @@
#include "core/input/mouse.hpp" #include "core/input/mouse.hpp"
#include "game/options.hpp" // Para Options, options, OptionsGame, OptionsAudio #include "game/options.hpp" // Para Options, options, OptionsGame, OptionsAudio
#include "game/scene_manager.hpp" // Para SceneManager #include "game/scene_manager.hpp" // Para SceneManager
#include "game/ui/console.hpp" // Para Console
namespace GlobalEvents { namespace GlobalEvents {
// Comprueba los eventos que se pueden producir en cualquier sección del juego // Comprueba los eventos que se pueden producir en cualquier sección del juego
@@ -17,6 +18,14 @@ namespace GlobalEvents {
// reLoadTextures(); // reLoadTextures();
} }
// Enrutar eventos de texto a la consola cuando está activa
if (Console::get() != nullptr && Console::get()->isActive()) {
if (event.type == SDL_EVENT_TEXT_INPUT || event.type == SDL_EVENT_KEY_DOWN) {
Console::get()->handleEvent(event);
return;
}
}
Mouse::handleEvent(event); Mouse::handleEvent(event);
} }
} // namespace GlobalEvents } // namespace GlobalEvents

View File

@@ -30,6 +30,8 @@ namespace Defaults::Video {
constexpr bool INTEGER_SCALE = true; // Escalado entero activado por defecto constexpr bool INTEGER_SCALE = true; // Escalado entero activado por defecto
constexpr bool KEEP_ASPECT = true; // Mantener aspecto activado por defecto constexpr bool KEEP_ASPECT = true; // Mantener aspecto activado por defecto
constexpr const char* PALETTE_NAME = "zx-spectrum"; // Paleta por defecto constexpr const char* PALETTE_NAME = "zx-spectrum"; // Paleta por defecto
constexpr bool LINEAR_UPSCALE = false; // Upscale NEAREST por defecto
constexpr int DOWNSCALE_ALGO = 1; // Downscale Lanczos2 por defecto
} // namespace Defaults::Video } // namespace Defaults::Video
namespace Defaults::Border { namespace Defaults::Border {
@@ -57,7 +59,6 @@ namespace Defaults::Cheat {
constexpr bool INFINITE_LIVES = false; // Vidas infinitas desactivadas por defecto constexpr bool INFINITE_LIVES = false; // Vidas infinitas desactivadas por defecto
constexpr bool INVINCIBLE = false; // Invencibilidad desactivada por defecto constexpr bool INVINCIBLE = false; // Invencibilidad desactivada por defecto
constexpr bool JAIL_IS_OPEN = false; // Jail abierta desactivada por defecto constexpr bool JAIL_IS_OPEN = false; // Jail abierta desactivada por defecto
constexpr bool ALTERNATE_SKIN = false; // Skin alternativa desactivada por defecto
} // namespace Defaults::Cheat } // namespace Defaults::Cheat
namespace Defaults::Stats { namespace Defaults::Stats {
@@ -78,30 +79,26 @@ namespace Defaults::Controls {
namespace Defaults::Kiosk { namespace Defaults::Kiosk {
constexpr bool ENABLED = false; // Modo kiosko desactivado por defecto constexpr bool ENABLED = false; // Modo kiosko desactivado por defecto
constexpr const char* TEXT = ""; // Texto del modo kiosko por defecto constexpr const char* TEXT = "KIOSK MODE"; // Texto del modo kiosko por defecto
constexpr bool INFINITE_LIVES = false; // Vidas infinitas en modo kiosko desactivadas por defecto constexpr bool INFINITE_LIVES = true; // Vidas infinitas en modo kiosko desactivadas por defecto
} // namespace Defaults::Kiosk } // namespace Defaults::Kiosk
namespace Defaults::Localization { namespace Defaults::Localization {
constexpr const char* LANGUAGE = "en"; // Idioma por defecto (en = inglés, ca = catalán) constexpr const char* LANGUAGE = "ca"; // Idioma por defecto (en = inglés, ca = catalán)
} // namespace Defaults::Localization } // namespace Defaults::Localization
namespace Defaults::Game::Items {
constexpr const float PERCENT_TO_OPEN_THE_JAIL = 0.9F; // Porcentaje de items necesarios para abrir la jail
} // namespace Defaults::Game::Items
namespace Defaults::Game::Room { namespace Defaults::Game::Room {
#ifdef _DEBUG constexpr const char* INITIAL = "03.yaml"; // Habitación de inicio
constexpr const char* INITIAL = "51.yaml"; // Habitación de inicio en debug constexpr const char* END_ROOM = "01"; // Habitación final (jail)
#else
constexpr const char* INITIAL = "03.yaml"; // Habitación de inicio en release
#endif
} // namespace Defaults::Game::Room } // namespace Defaults::Game::Room
namespace Defaults::Game::Player { namespace Defaults::Game::Player {
#ifdef _DEBUG constexpr int SPAWN_X = 25 * Tile::SIZE; // Posición X inicial
constexpr int SPAWN_X = 26 * Tile::SIZE; // Posición X inicial en debug constexpr int SPAWN_Y = 13 * Tile::SIZE; // Posición Y inicial
constexpr int SPAWN_Y = 10 * Tile::SIZE; // Posición Y inicial en debug constexpr SDL_FlipMode SPAWN_FLIP = Flip::LEFT; // Orientación inicial
constexpr SDL_FlipMode SPAWN_FLIP = Flip::LEFT; // Orientación inicial en debug constexpr int SKIN = 1; // Skin del jugador por defecto (1=normal, 2=alternativa)
#else
constexpr int SPAWN_X = 25 * Tile::SIZE; // Posición X inicial en release
constexpr int SPAWN_Y = 13 * Tile::SIZE; // Posición Y inicial en release
constexpr SDL_FlipMode SPAWN_FLIP = Flip::LEFT; // Orientación inicial en release
#endif
} // namespace Defaults::Game::Player } // namespace Defaults::Game::Player

View File

@@ -4,13 +4,13 @@
#include <cstdlib> // Para rand #include <cstdlib> // Para rand
#include "core/rendering/surface_animated_sprite.hpp" // Para SAnimatedSprite #include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite
#include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_cache.hpp" // Para Resource
#include "utils/utils.hpp" // Para stringToColor #include "utils/utils.hpp" // Para stringToColor
// Constructor // Constructor
Enemy::Enemy(const Data& enemy) Enemy::Enemy(const Data& enemy)
: sprite_(std::make_shared<SurfaceAnimatedSprite>(Resource::Cache::get()->getAnimationData(enemy.animation_path))), : sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData(enemy.animation_path))),
color_string_(enemy.color), color_string_(enemy.color),
x1_(enemy.x1), x1_(enemy.x1),
x2_(enemy.x2), x2_(enemy.x2),
@@ -49,7 +49,7 @@ void Enemy::update(float delta_time) {
} }
// Comprueba si ha llegado al limite del recorrido para darse media vuelta // Comprueba si ha llegado al limite del recorrido para darse media vuelta
void Enemy::checkPath() { void Enemy::checkPath() { // NOLINT(readability-make-member-function-const)
if (sprite_->getPosX() > x2_ || sprite_->getPosX() < x1_) { if (sprite_->getPosX() > x2_ || sprite_->getPosX() < x1_) {
// Recoloca // Recoloca
if (sprite_->getPosX() > x2_) { if (sprite_->getPosX() > x2_) {

View File

@@ -4,7 +4,7 @@
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string #include <string> // Para string
class SurfaceAnimatedSprite; // lines 7-7 class AnimatedSprite; // lines 7-7
class Enemy { class Enemy {
public: public:
@@ -36,7 +36,7 @@ class Enemy {
private: private:
void checkPath(); // Comprueba si ha llegado al limite del recorrido para darse media vuelta void checkPath(); // Comprueba si ha llegado al limite del recorrido para darse media vuelta
std::shared_ptr<SurfaceAnimatedSprite> sprite_; // Sprite del enemigo std::shared_ptr<AnimatedSprite> sprite_; // Sprite del enemigo
// Variables // Variables
Uint8 color_{0}; // Color del enemigo Uint8 color_{0}; // Color del enemigo

View File

@@ -1,11 +1,11 @@
#include "game/entities/item.hpp" #include "game/entities/item.hpp"
#include "core/rendering/surface_sprite.hpp" // Para SSprite #include "core/rendering/sprite/sprite.hpp" // Para SSprite
#include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_cache.hpp" // Para Resource
// Constructor // Constructor
Item::Item(const Data& item) Item::Item(const Data& item)
: sprite_(std::make_shared<SurfaceSprite>(Resource::Cache::get()->getSurface(item.tile_set_file), item.x, item.y, ITEM_SIZE, ITEM_SIZE)), : sprite_(std::make_shared<Sprite>(Resource::Cache::get()->getSurface(item.tile_set_file), item.x, item.y, ITEM_SIZE, ITEM_SIZE)),
time_accumulator_(static_cast<float>(item.counter) * COLOR_CHANGE_INTERVAL) { time_accumulator_(static_cast<float>(item.counter) * COLOR_CHANGE_INTERVAL) {
// Inicia variables // Inicia variables
sprite_->setClip((item.tile % 10) * ITEM_SIZE, (item.tile / 10) * ITEM_SIZE, ITEM_SIZE, ITEM_SIZE); sprite_->setClip((item.tile % 10) * ITEM_SIZE, (item.tile / 10) * ITEM_SIZE, ITEM_SIZE, ITEM_SIZE);
@@ -29,15 +29,15 @@ void Item::update(float delta_time) {
} }
// Pinta el objeto en pantalla // Pinta el objeto en pantalla
void Item::render() const { void Item::render() const { // NOLINT(readability-convert-member-functions-to-static)
// Calcula el índice de color basado en el tiempo acumulado // Calcula el índice de color basado en el tiempo acumulado
const int INDEX = static_cast<int>(time_accumulator_ / COLOR_CHANGE_INTERVAL) % static_cast<int>(color_.size()); const int INDEX = static_cast<int>(time_accumulator_ / COLOR_CHANGE_INTERVAL) % static_cast<int>(color_.size());
sprite_->render(1, color_.at(INDEX)); sprite_->render(1, color_.at(INDEX));
} }
// Obtiene su ubicación // Obtiene su ubicación
auto Item::getPos() -> SDL_FPoint { auto Item::getPos() -> SDL_FPoint { // NOLINT(readability-convert-member-functions-to-static)
const SDL_FPoint P = {sprite_->getX(), sprite_->getY()}; const SDL_FPoint P = {.x = sprite_->getX(), .y = sprite_->getY()};
return P; return P;
} }

View File

@@ -5,7 +5,7 @@
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector #include <vector> // Para vector
class SurfaceSprite; class Sprite;
class Item { class Item {
public: public:
@@ -34,7 +34,7 @@ class Item {
static constexpr float ITEM_SIZE = 8.0F; // Tamaño del item en pixels static constexpr float ITEM_SIZE = 8.0F; // Tamaño del item en pixels
static constexpr float COLOR_CHANGE_INTERVAL = 0.06F; // Intervalo de cambio de color en segundos (4 frames a 66.67fps) static constexpr float COLOR_CHANGE_INTERVAL = 0.06F; // Intervalo de cambio de color en segundos (4 frames a 66.67fps)
std::shared_ptr<SurfaceSprite> sprite_; // SSprite del objeto std::shared_ptr<Sprite> sprite_; // SSprite del objeto
// Variables // Variables
std::vector<Uint8> color_; // Vector con los colores del objeto std::vector<Uint8> color_; // Vector con los colores del objeto

View File

@@ -8,7 +8,7 @@
#include "core/audio/audio.hpp" // Para Audio #include "core/audio/audio.hpp" // Para Audio
#include "core/input/input.hpp" // Para Input, InputAction #include "core/input/input.hpp" // Para Input, InputAction
#include "core/rendering/surface_animated_sprite.hpp" // Para SAnimatedSprite #include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite
#include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_cache.hpp" // Para Resource
#include "game/gameplay/room.hpp" // Para Room, TileType #include "game/gameplay/room.hpp" // Para Room, TileType
#include "game/options.hpp" // Para Cheat, Options, options #include "game/options.hpp" // Para Cheat, Options, options
@@ -55,6 +55,7 @@ void Player::update(float delta_time) {
// Comprueba las entradas y modifica variables // Comprueba las entradas y modifica variables
void Player::handleInput() { void Player::handleInput() {
if (ignore_input_) { return; }
if (Input::get()->checkAction(InputAction::LEFT)) { if (Input::get()->checkAction(InputAction::LEFT)) {
wanna_go_ = Direction::LEFT; wanna_go_ = Direction::LEFT;
} else if (Input::get()->checkAction(InputAction::RIGHT)) { } else if (Input::get()->checkAction(InputAction::RIGHT)) {
@@ -84,21 +85,21 @@ void Player::move(float delta_time) {
} }
syncSpriteAndCollider(); // Actualiza la posición del sprite y las colisiones syncSpriteAndCollider(); // Actualiza la posición del sprite y las colisiones
#ifdef _DEBUG #ifdef _DEBUG
Debug::get()->add(std::string("X : " + std::to_string(static_cast<int>(x_)))); Debug::get()->set("P.X", std::to_string(static_cast<int>(x_)));
Debug::get()->add(std::string("Y : " + std::to_string(static_cast<int>(y_)))); Debug::get()->set("P.Y", std::to_string(static_cast<int>(y_)));
Debug::get()->add(std::string("LGP: " + std::to_string(last_grounded_position_))); Debug::get()->set("P.LGP", std::to_string(last_grounded_position_));
switch (state_) { switch (state_) {
case State::ON_GROUND: case State::ON_GROUND:
Debug::get()->add(std::string("ON_GROUND")); Debug::get()->set("P.STATE", "ON_GROUND");
break; break;
case State::ON_SLOPE: case State::ON_SLOPE:
Debug::get()->add(std::string("ON_SLOPE")); Debug::get()->set("P.STATE", "ON_SLOPE");
break; break;
case State::JUMPING: case State::JUMPING:
Debug::get()->add(std::string("JUMPING")); Debug::get()->set("P.STATE", "JUMPING");
break; break;
case State::FALLING: case State::FALLING:
Debug::get()->add(std::string("FALLING")); Debug::get()->set("P.STATE", "FALLING");
break; break;
} }
#endif #endif
@@ -136,6 +137,10 @@ void Player::transitionToState(State state) {
handleDeathByFalling(); handleDeathByFalling();
resetSoundControllersOnLanding(); resetSoundControllersOnLanding();
updateCurrentSlope(); updateCurrentSlope();
if (current_slope_ == nullptr) {
// Los pies no coinciden con ninguna rampa: tratar como suelo plano
state_ = State::ON_GROUND;
}
break; break;
case State::JUMPING: case State::JUMPING:
// Puede saltar desde ON_GROUND o ON_SLOPE // Puede saltar desde ON_GROUND o ON_SLOPE
@@ -235,6 +240,9 @@ void Player::moveOnGround(float delta_time) {
y_ = SLOPE_Y - HEIGHT; y_ = SLOPE_Y - HEIGHT;
transitionToState(State::ON_SLOPE); transitionToState(State::ON_SLOPE);
} }
#ifdef _DEBUG
Debug::get()->set("sl.detect_y", SLOPE_Y != Collision::NONE ? std::to_string(SLOPE_Y) : "-");
#endif
// Comprueba si está sobre una rampa // Comprueba si está sobre una rampa
if (isOnSlope()) { transitionToState(State::ON_SLOPE); } if (isOnSlope()) { transitionToState(State::ON_SLOPE); }
@@ -245,14 +253,15 @@ void Player::moveOnSlope(float delta_time) {
// Determinama cuál debe ser la velocidad a partir de automovement o de wanna_go_ // Determinama cuál debe ser la velocidad a partir de automovement o de wanna_go_
updateVelocity(); updateVelocity();
if (vx_ == 0.0F) { return; } // Verificar rampa válida antes de comprobar velocidad: si no hay rampa siempre caer,
// independientemente de si hay o no input (evita bloqueo con vx_=0 y slope null)
// Verificar que tenemos una rampa válida
if (current_slope_ == nullptr) { if (current_slope_ == nullptr) {
transitionToState(State::FALLING); transitionToState(State::FALLING);
return; return;
} }
if (vx_ == 0.0F) { return; }
// Determinar el tipo de rampa // Determinar el tipo de rampa
const bool IS_LEFT_SLOPE = isLeftSlope(); const bool IS_LEFT_SLOPE = isLeftSlope();
@@ -279,12 +288,21 @@ void Player::moveOnSlope(float delta_time) {
const int MAX_X = std::max(current_slope_->x1, current_slope_->x2); const int MAX_X = std::max(current_slope_->x1, current_slope_->x2);
const bool OUT_OF_BOUNDS = (X < MIN_X) || (X > MAX_X); const bool OUT_OF_BOUNDS = (X < MIN_X) || (X > MAX_X);
#ifdef _DEBUG
Debug::get()->set("sl.foot", std::to_string(X));
Debug::get()->set("sl.y_c", std::to_string(static_cast<int>(y_)));
Debug::get()->set("sl.oob", OUT_OF_BOUNDS ? "YES" : "ok");
#endif
if (OUT_OF_BOUNDS) { if (OUT_OF_BOUNDS) {
// Determinar si estamos saliendo por arriba o por abajo de la rampa // Determinar si estamos saliendo por arriba o por abajo de la rampa
const bool EXITING_DOWNWARD = (X > current_slope_->x2 && IS_LEFT_SLOPE) || const bool EXITING_DOWNWARD = (X > current_slope_->x2 && IS_LEFT_SLOPE) ||
(X < current_slope_->x1 && !IS_LEFT_SLOPE); (X < current_slope_->x1 && !IS_LEFT_SLOPE);
const bool EXITING_UPWARD = (X < current_slope_->x1 && IS_LEFT_SLOPE) || const bool EXITING_UPWARD = (X < current_slope_->x1 && IS_LEFT_SLOPE) ||
(X > current_slope_->x2 && !IS_LEFT_SLOPE); (X > current_slope_->x2 && !IS_LEFT_SLOPE);
#ifdef _DEBUG
Debug::get()->set("sl.oob", EXITING_DOWNWARD ? "DOWN" : "UP");
#endif
if (EXITING_DOWNWARD) { if (EXITING_DOWNWARD) {
// Salida por abajo: no hacer nada // Salida por abajo: no hacer nada
@@ -352,7 +370,7 @@ void Player::moveJumping(float delta_time) {
// Comprueba la colisión con las superficies y las cintas transportadoras (sin rampas) // Comprueba la colisión con las superficies y las cintas transportadoras (sin rampas)
// Extendemos 1px hacia arriba para detectar suelos traversados ligeramente al // Extendemos 1px hacia arriba para detectar suelos traversados ligeramente al
// entrar horizontalmente (consecuencia del margen h=HEIGHT-1 en la proyección horizontal) // entrar horizontalmente (consecuencia del margen h=HEIGHT-1 en la proyección horizontal)
const SDL_FRect ADJ = {PROJECTION.x, PROJECTION.y - 1.0F, PROJECTION.w, PROJECTION.h + 1.0F}; const SDL_FRect ADJ = {.x = PROJECTION.x, .y = PROJECTION.y - 1.0F, .w = PROJECTION.w, .h = PROJECTION.h + 1.0F};
const float POS = std::max(room_->checkTopSurfaces(ADJ), room_->checkAutoSurfaces(ADJ)); const float POS = std::max(room_->checkTopSurfaces(ADJ), room_->checkAutoSurfaces(ADJ));
if (POS != Collision::NONE) { if (POS != Collision::NONE) {
// Si hay colisión lo mueve hasta donde no colisiona y pasa a estar sobre la superficie // Si hay colisión lo mueve hasta donde no colisiona y pasa a estar sobre la superficie
@@ -445,7 +463,7 @@ void Player::applyGravity(float delta_time) {
} }
// Establece la animación del jugador // Establece la animación del jugador
void Player::animate(float delta_time) { void Player::animate(float delta_time) { // NOLINT(readability-make-member-function-const)
if (vx_ != 0) { if (vx_ != 0) {
sprite_->update(delta_time); sprite_->update(delta_time);
} }
@@ -461,7 +479,7 @@ void Player::handleJumpEnd() {
} }
// Calcula y reproduce el sonido de salto basado en tiempo transcurrido // Calcula y reproduce el sonido de salto basado en tiempo transcurrido
void Player::playJumpSound(float delta_time) { void Player::playJumpSound(float delta_time) { // NOLINT(readability-convert-member-functions-to-static)
size_t sound_index; size_t sound_index;
if (jump_sound_ctrl_.shouldPlay(delta_time, sound_index)) { if (jump_sound_ctrl_.shouldPlay(delta_time, sound_index)) {
if (sound_index < jumping_sound_.size()) { if (sound_index < jumping_sound_.size()) {
@@ -471,7 +489,7 @@ void Player::playJumpSound(float delta_time) {
} }
// Calcula y reproduce el sonido de caída basado en distancia vertical recorrida // Calcula y reproduce el sonido de caída basado en distancia vertical recorrida
void Player::playFallSound(float delta_time) { void Player::playFallSound(float delta_time) { // NOLINT(readability-convert-member-functions-to-static)
size_t sound_index; size_t sound_index;
if (fall_sound_ctrl_.shouldPlay(delta_time, y_, sound_index)) { if (fall_sound_ctrl_.shouldPlay(delta_time, y_, sound_index)) {
if (sound_index < falling_sound_.size()) { if (sound_index < falling_sound_.size()) {
@@ -576,24 +594,23 @@ void Player::updateCurrentSlope() {
} }
} }
// Debug output #ifdef _DEBUG
/*
if (current_slope_ != nullptr) { if (current_slope_ != nullptr) {
const char* TYPE = isLeftSlope() ? "Left \\" : "Right /"; Debug::get()->set("sl.type", isLeftSlope() ? "L\\" : "R/");
std::cout << "[SLOPE] " << TYPE Debug::get()->set("sl.p1", std::to_string(current_slope_->x1) + "," + std::to_string(current_slope_->y1));
<< " from (" << current_slope_->x1 << "," << current_slope_->y1 << ")" Debug::get()->set("sl.p2", std::to_string(current_slope_->x2) + "," + std::to_string(current_slope_->y2));
<< " to (" << current_slope_->x2 << "," << current_slope_->y2 << ")\n";
} else { } else {
std::cout << "[SLOPE] nullptr\n"; Debug::get()->set("sl.type", "null");
Debug::get()->unset("sl.p1");
Debug::get()->unset("sl.p2");
} }
*/ #endif
} }
// Comprueba que el jugador no toque ningun tile de los que matan // Comprueba que el jugador no toque ningun tile de los que matan
auto Player::handleKillingTiles() -> bool { auto Player::handleKillingTiles() -> bool {
// Comprueba si hay contacto con algún tile que mata // Comprueba si hay contacto con algún tile que mata
if (std::ranges::any_of(collider_points_, [this](const auto& c) { if (std::ranges::any_of(collider_points_, [this](const auto& c) -> bool {
return room_->getTile(c) == Room::Tile::KILL; return room_->getTile(c) == Room::Tile::KILL;
})) { })) {
markAsDead(); // Mata al jugador inmediatamente markAsDead(); // Mata al jugador inmediatamente
@@ -643,7 +660,7 @@ void Player::updateFeet() {
} }
// Inicializa los sonidos de salto y caida // Inicializa los sonidos de salto y caida
void Player::initSounds() { void Player::initSounds() { // NOLINT(readability-convert-member-functions-to-static)
for (int i = 0; i < 24; ++i) { for (int i = 0; i < 24; ++i) {
std::string sound_file = "jump" + std::to_string(i + 1) + ".wav"; std::string sound_file = "jump" + std::to_string(i + 1) + ".wav";
jumping_sound_[i] = Resource::Cache::get()->getSound(sound_file); jumping_sound_[i] = Resource::Cache::get()->getSound(sound_file);
@@ -678,14 +695,14 @@ auto Player::JumpSoundController::shouldPlay(float delta_time, size_t& out_index
elapsed_time += delta_time; elapsed_time += delta_time;
// Calcula qué sonido debería estar sonando según el tiempo // Calcula qué sonido debería estar sonando según el tiempo
size_t target_index = FIRST_SOUND + static_cast<size_t>(elapsed_time / SECONDS_PER_SOUND); size_t target_index = FIRST_SOUND + static_cast<size_t>((elapsed_time / SECONDS_PER_SOUND));
target_index = std::min(target_index, LAST_SOUND); target_index = std::min(target_index, LAST_SOUND);
// Reproduce si hemos avanzado a un nuevo sonido // Reproduce si hemos avanzado a un nuevo sonido
if (target_index > current_index) { if (target_index > current_index) {
current_index = target_index; current_index = target_index;
out_index = current_index; out_index = current_index;
return true; return true; // NOLINT(readability-simplify-boolean-expr)
} }
return false; return false;
@@ -721,7 +738,7 @@ auto Player::FallSoundController::shouldPlay(float delta_time, float current_y,
last_y = current_y; last_y = current_y;
// Calcula qué sonido debería estar sonando según el intervalo // Calcula qué sonido debería estar sonando según el intervalo
size_t target_index = FIRST_SOUND + static_cast<size_t>(distance_traveled / PIXELS_PER_SOUND); size_t target_index = FIRST_SOUND + static_cast<size_t>((distance_traveled / PIXELS_PER_SOUND));
// El sonido a reproducir se limita a LAST_SOUND (13), pero el índice interno sigue creciendo // El sonido a reproducir se limita a LAST_SOUND (13), pero el índice interno sigue creciendo
size_t sound_to_play = std::min(target_index, LAST_SOUND); size_t sound_to_play = std::min(target_index, LAST_SOUND);
@@ -748,10 +765,18 @@ void Player::applySpawnValues(const SpawnData& spawn) {
sprite_->setFlip(spawn.flip); sprite_->setFlip(spawn.flip);
} }
// Cambia la skin del jugador en caliente preservando la orientación actual
void Player::setSkin(int skin_num) {
const auto FLIP = sprite_->getFlip();
const std::string PATH = (skin_num == 2) ? "player2.yaml" : "player.yaml";
initSprite(PATH);
sprite_->setFlip(FLIP);
}
// Inicializa el sprite del jugador // Inicializa el sprite del jugador
void Player::initSprite(const std::string& animations_path) { void Player::initSprite(const std::string& animations_path) { // NOLINT(readability-convert-member-functions-to-static)
const auto& animation_data = Resource::Cache::get()->getAnimationData(animations_path); const auto& animation_data = Resource::Cache::get()->getAnimationData(animations_path);
sprite_ = std::make_unique<SurfaceAnimatedSprite>(animation_data); sprite_ = std::make_unique<AnimatedSprite>(animation_data);
sprite_->setWidth(WIDTH); sprite_->setWidth(WIDTH);
sprite_->setHeight(HEIGHT); sprite_->setHeight(HEIGHT);
sprite_->setCurrentAnimation("walk"); sprite_->setCurrentAnimation("walk");
@@ -865,7 +890,7 @@ void Player::resetSoundControllersOnLanding() {
} }
// Devuelve el rectangulo de proyeccion // Devuelve el rectangulo de proyeccion
auto Player::getProjection(Direction direction, float displacement) -> SDL_FRect { auto Player::getProjection(Direction direction, float displacement) -> SDL_FRect { // NOLINT(readability-convert-member-functions-to-static)
switch (direction) { switch (direction) {
case Direction::LEFT: case Direction::LEFT:
return { return {

View File

@@ -8,7 +8,7 @@
#include <string> // Para string #include <string> // Para string
#include <utility> #include <utility>
#include "core/rendering/surface_animated_sprite.hpp" // Para SAnimatedSprite #include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite
#include "game/gameplay/room.hpp" #include "game/gameplay/room.hpp"
#include "game/options.hpp" // Para Cheat, Options, options #include "game/options.hpp" // Para Cheat, Options, options
#include "utils/defines.hpp" // Para BORDER_TOP, BLOCK #include "utils/defines.hpp" // Para BORDER_TOP, BLOCK
@@ -96,14 +96,16 @@ class Player {
[[nodiscard]] auto isOnBorder() const -> bool { return border_ != Room::Border::NONE; } // Indica si el jugador esta en uno de los cuatro bordes de la pantalla [[nodiscard]] auto isOnBorder() const -> bool { return border_ != Room::Border::NONE; } // Indica si el jugador esta en uno de los cuatro bordes de la pantalla
[[nodiscard]] auto getBorder() const -> Room::Border { return border_; } // Indica en cual de los cuatro bordes se encuentra [[nodiscard]] auto getBorder() const -> Room::Border { return border_; } // Indica en cual de los cuatro bordes se encuentra
void switchBorders(); // Cambia al jugador de un borde al opuesto. Util para el cambio de pantalla void switchBorders(); // Cambia al jugador de un borde al opuesto. Util para el cambio de pantalla
auto getRect() -> SDL_FRect { return {x_, y_, WIDTH, HEIGHT}; } // Obtiene el rectangulo que delimita al jugador auto getRect() -> SDL_FRect { return {.x = x_, .y = y_, .w = WIDTH, .h = HEIGHT}; } // Obtiene el rectangulo que delimita al jugador
auto getCollider() -> SDL_FRect& { return collider_box_; } // Obtiene el rectangulo de colision del jugador auto getCollider() -> SDL_FRect& { return collider_box_; } // Obtiene el rectangulo de colision del jugador
auto getSpawnParams() -> SpawnData { return {.x = x_, .y = y_, .vx = vx_, .vy = vy_, .last_grounded_position = last_grounded_position_, .state = state_, .flip = sprite_->getFlip()}; } // Obtiene el estado de reaparición del jugador auto getSpawnParams() -> SpawnData { return {.x = x_, .y = y_, .vx = vx_, .vy = vy_, .last_grounded_position = last_grounded_position_, .state = state_, .flip = sprite_->getFlip()}; } // Obtiene el estado de reaparición del jugador
void setColor(Uint8 color = 0); // Establece el color del jugador (0 = automático según cheats) void setColor(Uint8 color = 0); // Establece el color del jugador (0 = automático según cheats)
void setSkin(int skin_num); // Cambia la skin del jugador en caliente (1=normal, 2=alternativa)
void setRoom(std::shared_ptr<Room> room) { room_ = std::move(room); } // Establece la habitación en la que se encuentra el jugador void setRoom(std::shared_ptr<Room> room) { room_ = std::move(room); } // Establece la habitación en la que se encuentra el jugador
//[[nodiscard]] auto isAlive() const -> bool { return is_alive_ || (Options::cheats.invincible == Options::Cheat::State::ENABLED); } // Comprueba si el jugador esta vivo //[[nodiscard]] auto isAlive() const -> bool { return is_alive_ || (Options::cheats.invincible == Options::Cheat::State::ENABLED); } // Comprueba si el jugador esta vivo
[[nodiscard]] auto isAlive() const -> bool { return is_alive_; } // Comprueba si el jugador esta vivo [[nodiscard]] auto isAlive() const -> bool { return is_alive_; } // Comprueba si el jugador esta vivo
void setPaused(bool value) { is_paused_ = value; } // Pone el jugador en modo pausa void setPaused(bool value) { is_paused_ = value; } // Pone el jugador en modo pausa
void setIgnoreInput(bool value) { ignore_input_ = value; } // Ignora inputs del jugador (física sigue activa)
#ifdef _DEBUG #ifdef _DEBUG
// --- Funciones de debug --- // --- Funciones de debug ---
@@ -119,7 +121,7 @@ class Player {
// --- Objetos y punteros --- // --- Objetos y punteros ---
std::shared_ptr<Room> room_; // Objeto encargado de gestionar cada habitación del juego std::shared_ptr<Room> room_; // Objeto encargado de gestionar cada habitación del juego
std::unique_ptr<SurfaceAnimatedSprite> sprite_; // Sprite del jugador std::unique_ptr<AnimatedSprite> sprite_; // Sprite del jugador
// --- Variables de posición y física --- // --- Variables de posición y física ---
float x_ = 0.0F; // Posición del jugador en el eje X float x_ = 0.0F; // Posición del jugador en el eje X
@@ -138,13 +140,14 @@ class Player {
// --- Variables de colisión --- // --- Variables de colisión ---
SDL_FRect collider_box_{}; // Caja de colisión con los enemigos u objetos SDL_FRect collider_box_{}; // Caja de colisión con los enemigos u objetos
std::array<SDL_FPoint, 8> collider_points_{}; // Puntos de colisión con el mapa std::array<SDL_FPoint, 8> collider_points_{}; // Puntos de colisión con el mapa
SDL_FPoint under_left_foot_ = {0.0F, 0.0F}; // El punto bajo la esquina inferior izquierda del jugador SDL_FPoint under_left_foot_ = {.x = 0.0F, .y = 0.0F}; // El punto bajo la esquina inferior izquierda del jugador
SDL_FPoint under_right_foot_ = {0.0F, 0.0F}; // El punto bajo la esquina inferior derecha del jugador SDL_FPoint under_right_foot_ = {.x = 0.0F, .y = 0.0F}; // El punto bajo la esquina inferior derecha del jugador
const LineDiagonal* current_slope_{nullptr}; // Rampa actual sobe la que está el jugador const LineDiagonal* current_slope_{nullptr}; // Rampa actual sobe la que está el jugador
// --- Variables de juego --- // --- Variables de juego ---
bool is_alive_ = true; // Indica si el jugador esta vivo o no bool is_alive_ = true; // Indica si el jugador esta vivo o no
bool is_paused_ = false; // Indica si el jugador esta en modo pausa bool is_paused_ = false; // Indica si el jugador esta en modo pausa
bool ignore_input_ = false; // Ignora inputs pero mantiene la física activa
bool auto_movement_ = false; // Indica si esta siendo arrastrado por una superficie automatica bool auto_movement_ = false; // Indica si esta siendo arrastrado por una superficie automatica
Room::Border border_ = Room::Border::TOP; // Indica en cual de los cuatro bordes se encuentra Room::Border border_ = Room::Border::TOP; // Indica en cual de los cuatro bordes se encuentra
int last_grounded_position_ = 0; // Ultima posición en Y en la que se estaba en contacto con el suelo (hace doble función: tracking de caída + altura inicial del salto) int last_grounded_position_ = 0; // Ultima posición en Y en la que se estaba en contacto con el suelo (hace doble función: tracking de caída + altura inicial del salto)

View File

@@ -0,0 +1,27 @@
#pragma once
#include <functional>
#include <string>
namespace GameControl {
// Disponible en todos los builds — refresca el color del jugador según cheats
inline std::function<void()> refresh_player_color;
// Disponible en todos los builds — cambia la skin del jugador (1=normal, 2=alternativa)
inline std::function<void(int)> change_player_skin;
} // namespace GameControl
#ifdef _DEBUG
namespace GameControl {
// Registrada por Game::Game() — cambia la habitación activa
inline std::function<bool(const std::string&)> change_room;
// Registrada por Game::Game() — devuelve el fichero de la habitación activa (ej. "03.yaml")
inline std::function<std::string()> get_current_room;
// Registrada por Game::Game() — fija el contador de items recogidos
inline std::function<void(int)> set_items;
// Registrada por Game::Game() — hace toggle del modo debug (equivale a tecla 0)
inline std::function<void()> toggle_debug_mode;
// Registrada por Game::Game() — guarda la habitación actual como habitación de inicio en debug.yaml
inline std::function<std::string()> set_initial_room;
// Registrada por Game::Game() — guarda la posición/flip actuales del jugador como posición de inicio en debug.yaml
inline std::function<std::string()> set_initial_pos;
} // namespace GameControl
#endif

View File

@@ -16,7 +16,7 @@
Cheevos* Cheevos::cheevos = nullptr; Cheevos* Cheevos::cheevos = nullptr;
// [SINGLETON] Crearemos el objeto con esta función estática // [SINGLETON] Crearemos el objeto con esta función estática
void Cheevos::init(const std::string& file) { void Cheevos::init(const std::string& file) { // NOLINT(readability-convert-member-functions-to-static)
Cheevos::cheevos = new Cheevos(file); Cheevos::cheevos = new Cheevos(file);
} }
@@ -43,7 +43,7 @@ Cheevos::~Cheevos() {
} }
// Inicializa los logros // Inicializa los logros
void Cheevos::init() { void Cheevos::init() { // NOLINT(readability-convert-member-functions-to-static)
cheevos_list_.clear(); cheevos_list_.clear();
auto* loc = Locale::get(); auto* loc = Locale::get();
cheevos_list_.emplace_back(Achievement{.id = 1, .caption = loc->get("achievements.c1"), .description = loc->get("achievements.d1"), .icon = 2}); cheevos_list_.emplace_back(Achievement{.id = 1, .caption = loc->get("achievements.c1"), .description = loc->get("achievements.d1"), .icon = 2});
@@ -61,7 +61,7 @@ void Cheevos::init() {
} }
// Busca un logro por id y devuelve el indice // Busca un logro por id y devuelve el indice
auto Cheevos::find(int id) -> int { auto Cheevos::find(int id) -> int { // NOLINT(readability-convert-member-functions-to-static)
for (int i = 0; i < (int)cheevos_list_.size(); ++i) { for (int i = 0; i < (int)cheevos_list_.size(); ++i) {
if (cheevos_list_[i].id == id) { if (cheevos_list_[i].id == id) {
return i; return i;
@@ -101,38 +101,30 @@ void Cheevos::setUnobtainable(int id) {
} }
// Carga el estado de los logros desde un fichero // Carga el estado de los logros desde un fichero
void Cheevos::loadFromFile() { void Cheevos::loadFromFile() { // NOLINT(readability-convert-member-functions-to-static)
std::ifstream file(file_, std::ios::binary); std::ifstream file(file_, std::ios::binary);
// El fichero no existe // El fichero no existe
if (!file) { if (!file) {
if (Options::console) {
std::cout << "Warning: Unable to open " << file_ << "! Creating new file..." << '\n'; std::cout << "Warning: Unable to open " << file_ << "! Creating new file..." << '\n';
}
// Crea el fichero en modo escritura (binario) // Crea el fichero en modo escritura (binario)
std::ofstream new_file(file_, std::ios::binary); std::ofstream new_file(file_, std::ios::binary);
if (new_file) { if (new_file) {
if (Options::console) {
std::cout << "New " << file_ << " created!" << '\n'; std::cout << "New " << file_ << " created!" << '\n';
}
// Guarda la información // Guarda la información
for (const auto& cheevo : cheevos_list_) { for (const auto& cheevo : cheevos_list_) {
new_file.write(reinterpret_cast<const char*>(&cheevo.completed), sizeof(bool)); new_file.write(reinterpret_cast<const char*>(&cheevo.completed), sizeof(bool));
} }
} else { } else {
if (Options::console) {
std::cerr << "Error: Unable to create " << file_ << "!" << '\n'; std::cerr << "Error: Unable to create " << file_ << "!" << '\n';
} }
} }
}
// El fichero existe // El fichero existe
else { else {
if (Options::console) {
std::cout << "Reading " << file_ << '\n'; std::cout << "Reading " << file_ << '\n';
}
// Carga los datos // Carga los datos
for (auto& cheevo : cheevos_list_) { for (auto& cheevo : cheevos_list_) {
@@ -154,15 +146,13 @@ void Cheevos::saveToFile() {
// Cierra el fichero // Cierra el fichero
SDL_CloseIO(file); SDL_CloseIO(file);
} else { } else {
if (Options::console) {
std::cout << "Error: Unable to save file! " << SDL_GetError() << '\n'; std::cout << "Error: Unable to save file! " << SDL_GetError() << '\n';
} }
}
} }
// Devuelve el número total de logros desbloqueados // Devuelve el número total de logros desbloqueados
auto Cheevos::getTotalUnlockedAchievements() -> int { auto Cheevos::getTotalUnlockedAchievements() -> int {
return std::count_if(cheevos_list_.begin(), cheevos_list_.end(), [](const auto& cheevo) { return cheevo.completed; }); return std::count_if(cheevos_list_.begin(), cheevos_list_.end(), [](const auto& cheevo) -> bool { return cheevo.completed; });
} }
// Elimina el estado "no obtenible" // Elimina el estado "no obtenible"

View File

@@ -36,7 +36,7 @@ auto CollisionMap::getTile(SDL_FPoint point) const -> Tile {
} }
// Devuelve el tipo de tile que hay en ese indice // Devuelve el tipo de tile que hay en ese indice
auto CollisionMap::getTile(int index) const -> Tile { auto CollisionMap::getTile(int index) const -> Tile { // NOLINT(readability-convert-member-functions-to-static)
const bool ON_RANGE = (index > -1) && (index < (int)tile_map_.size()); const bool ON_RANGE = (index > -1) && (index < (int)tile_map_.size());
if (ON_RANGE) { if (ON_RANGE) {
@@ -79,25 +79,25 @@ auto CollisionMap::getSlopeHeight(SDL_FPoint p, Tile slope) -> int {
// Calcula la base del tile // Calcula la base del tile
int base = ((p.y / TILE_SIZE) * TILE_SIZE) + TILE_SIZE; int base = ((p.y / TILE_SIZE) * TILE_SIZE) + TILE_SIZE;
#ifdef _DEBUG #ifdef _DEBUG
Debug::get()->add("BASE = " + std::to_string(base)); Debug::get()->set("slope.BASE", std::to_string(base));
#endif #endif
// Calcula cuanto se ha entrado en el tile horizontalmente // Calcula cuanto se ha entrado en el tile horizontalmente
const int POS = (static_cast<int>(p.x) % TILE_SIZE); // Esto da un valor entre 0 y 7 const int POS = (static_cast<int>(p.x) % TILE_SIZE); // Esto da un valor entre 0 y 7
#ifdef _DEBUG #ifdef _DEBUG
Debug::get()->add("POS = " + std::to_string(POS)); Debug::get()->set("slope.POS", std::to_string(POS));
#endif #endif
// Se resta a la base la cantidad de pixeles pos en funcion de la rampa // Se resta a la base la cantidad de pixeles pos en funcion de la rampa
if (slope == Tile::SLOPE_R) { if (slope == Tile::SLOPE_R) {
base -= POS + 1; base -= POS + 1;
#ifdef _DEBUG #ifdef _DEBUG
Debug::get()->add("BASE_R = " + std::to_string(base)); Debug::get()->set("slope.result", "BASE_R=" + std::to_string(base));
#endif #endif
} else { } else {
base -= (TILE_SIZE - POS); base -= (TILE_SIZE - POS);
#ifdef _DEBUG #ifdef _DEBUG
Debug::get()->add("BASE_L = " + std::to_string(base)); Debug::get()->set("slope.result", "BASE_L=" + std::to_string(base));
#endif #endif
} }
@@ -107,7 +107,7 @@ auto CollisionMap::getSlopeHeight(SDL_FPoint p, Tile slope) -> int {
// === Queries de colisión === // === Queries de colisión ===
// Comprueba las colisiones con paredes derechas // Comprueba las colisiones con paredes derechas
auto CollisionMap::checkRightSurfaces(const SDL_FRect& rect) -> int { auto CollisionMap::checkRightSurfaces(const SDL_FRect& rect) -> int { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& s : right_walls_) { for (const auto& s : right_walls_) {
if (checkCollision(s, rect)) { if (checkCollision(s, rect)) {
return s.x; return s.x;
@@ -117,7 +117,7 @@ auto CollisionMap::checkRightSurfaces(const SDL_FRect& rect) -> int {
} }
// Comprueba las colisiones con paredes izquierdas // Comprueba las colisiones con paredes izquierdas
auto CollisionMap::checkLeftSurfaces(const SDL_FRect& rect) -> int { auto CollisionMap::checkLeftSurfaces(const SDL_FRect& rect) -> int { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& s : left_walls_) { for (const auto& s : left_walls_) {
if (checkCollision(s, rect)) { if (checkCollision(s, rect)) {
return s.x; return s.x;
@@ -127,7 +127,7 @@ auto CollisionMap::checkLeftSurfaces(const SDL_FRect& rect) -> int {
} }
// Comprueba las colisiones con techos // Comprueba las colisiones con techos
auto CollisionMap::checkTopSurfaces(const SDL_FRect& rect) -> int { auto CollisionMap::checkTopSurfaces(const SDL_FRect& rect) -> int { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& s : top_floors_) { for (const auto& s : top_floors_) {
if (checkCollision(s, rect)) { if (checkCollision(s, rect)) {
return s.y; return s.y;
@@ -138,13 +138,13 @@ auto CollisionMap::checkTopSurfaces(const SDL_FRect& rect) -> int {
// Comprueba las colisiones punto con techos // Comprueba las colisiones punto con techos
auto CollisionMap::checkTopSurfaces(const SDL_FPoint& p) -> bool { auto CollisionMap::checkTopSurfaces(const SDL_FPoint& p) -> bool {
return std::ranges::any_of(top_floors_, [&](const auto& s) { return std::ranges::any_of(top_floors_, [&](const auto& s) -> bool {
return checkCollision(s, p); return checkCollision(s, p);
}); });
} }
// Comprueba las colisiones con suelos // Comprueba las colisiones con suelos
auto CollisionMap::checkBottomSurfaces(const SDL_FRect& rect) -> int { auto CollisionMap::checkBottomSurfaces(const SDL_FRect& rect) -> int { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& s : bottom_floors_) { for (const auto& s : bottom_floors_) {
if (checkCollision(s, rect)) { if (checkCollision(s, rect)) {
return s.y; return s.y;
@@ -154,7 +154,7 @@ auto CollisionMap::checkBottomSurfaces(const SDL_FRect& rect) -> int {
} }
// Comprueba las colisiones con conveyor belts // Comprueba las colisiones con conveyor belts
auto CollisionMap::checkAutoSurfaces(const SDL_FRect& rect) -> int { auto CollisionMap::checkAutoSurfaces(const SDL_FRect& rect) -> int { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& s : conveyor_belt_floors_) { for (const auto& s : conveyor_belt_floors_) {
if (checkCollision(s, rect)) { if (checkCollision(s, rect)) {
return s.y; return s.y;
@@ -165,13 +165,13 @@ auto CollisionMap::checkAutoSurfaces(const SDL_FRect& rect) -> int {
// Comprueba las colisiones punto con conveyor belts // Comprueba las colisiones punto con conveyor belts
auto CollisionMap::checkConveyorBelts(const SDL_FPoint& p) -> bool { auto CollisionMap::checkConveyorBelts(const SDL_FPoint& p) -> bool {
return std::ranges::any_of(conveyor_belt_floors_, [&](const auto& s) { return std::ranges::any_of(conveyor_belt_floors_, [&](const auto& s) -> bool {
return checkCollision(s, p); return checkCollision(s, p);
}); });
} }
// Comprueba las colisiones línea con rampas izquierdas // Comprueba las colisiones línea con rampas izquierdas
auto CollisionMap::checkLeftSlopes(const LineVertical& line) -> int { auto CollisionMap::checkLeftSlopes(const LineVertical& line) -> int { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& slope : left_slopes_) { for (const auto& slope : left_slopes_) {
const auto P = checkCollision(slope, line); const auto P = checkCollision(slope, line);
if (P.x != -1) { if (P.x != -1) {
@@ -183,13 +183,13 @@ auto CollisionMap::checkLeftSlopes(const LineVertical& line) -> int {
// Comprueba las colisiones punto con rampas izquierdas // Comprueba las colisiones punto con rampas izquierdas
auto CollisionMap::checkLeftSlopes(const SDL_FPoint& p) -> bool { auto CollisionMap::checkLeftSlopes(const SDL_FPoint& p) -> bool {
return std::ranges::any_of(left_slopes_, [&](const auto& slope) { return std::ranges::any_of(left_slopes_, [&](const auto& slope) -> bool {
return checkCollision(p, slope); return checkCollision(p, slope);
}); });
} }
// Comprueba las colisiones línea con rampas derechas // Comprueba las colisiones línea con rampas derechas
auto CollisionMap::checkRightSlopes(const LineVertical& line) -> int { auto CollisionMap::checkRightSlopes(const LineVertical& line) -> int { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& slope : right_slopes_) { for (const auto& slope : right_slopes_) {
const auto P = checkCollision(slope, line); const auto P = checkCollision(slope, line);
if (P.x != -1) { if (P.x != -1) {
@@ -201,13 +201,13 @@ auto CollisionMap::checkRightSlopes(const LineVertical& line) -> int {
// Comprueba las colisiones punto con rampas derechas // Comprueba las colisiones punto con rampas derechas
auto CollisionMap::checkRightSlopes(const SDL_FPoint& p) -> bool { auto CollisionMap::checkRightSlopes(const SDL_FPoint& p) -> bool {
return std::ranges::any_of(right_slopes_, [&](const auto& slope) { return std::ranges::any_of(right_slopes_, [&](const auto& slope) -> bool {
return checkCollision(p, slope); return checkCollision(p, slope);
}); });
} }
// Obtiene puntero a slope en un punto (prioriza left_slopes_ sobre right_slopes_) // Obtiene puntero a slope en un punto (prioriza left_slopes_ sobre right_slopes_)
auto CollisionMap::getSlopeAtPoint(const SDL_FPoint& p) const -> const LineDiagonal* { auto CollisionMap::getSlopeAtPoint(const SDL_FPoint& p) const -> const LineDiagonal* { // NOLINT(readability-convert-member-functions-to-static)
// Primero busca en rampas izquierdas // Primero busca en rampas izquierdas
for (const auto& slope : left_slopes_) { for (const auto& slope : left_slopes_) {
if (checkCollision(p, slope)) { if (checkCollision(p, slope)) {
@@ -229,7 +229,7 @@ auto CollisionMap::getSlopeAtPoint(const SDL_FPoint& p) const -> const LineDiago
// === Helpers para recopilar tiles === // === Helpers para recopilar tiles ===
// Helper: recopila tiles inferiores (muros sin muro debajo) // Helper: recopila tiles inferiores (muros sin muro debajo)
auto CollisionMap::collectBottomTiles() -> std::vector<int> { auto CollisionMap::collectBottomTiles() -> std::vector<int> { // NOLINT(readability-make-member-function-const)
std::vector<int> tile; std::vector<int> tile;
// Busca todos los tiles de tipo muro que no tengan debajo otro muro // Busca todos los tiles de tipo muro que no tengan debajo otro muro
@@ -251,7 +251,7 @@ auto CollisionMap::collectBottomTiles() -> std::vector<int> {
} }
// Helper: recopila tiles superiores (muros o pasables sin muro encima) // Helper: recopila tiles superiores (muros o pasables sin muro encima)
auto CollisionMap::collectTopTiles() -> std::vector<int> { auto CollisionMap::collectTopTiles() -> std::vector<int> { // NOLINT(readability-make-member-function-const)
std::vector<int> tile; std::vector<int> tile;
// Busca todos los tiles de tipo muro o pasable que no tengan encima un muro // Busca todos los tiles de tipo muro o pasable que no tengan encima un muro
@@ -273,7 +273,7 @@ auto CollisionMap::collectTopTiles() -> std::vector<int> {
} }
// Helper: recopila tiles animados (para superficies automaticas/conveyor belts) // Helper: recopila tiles animados (para superficies automaticas/conveyor belts)
auto CollisionMap::collectAnimatedTiles() -> std::vector<int> { auto CollisionMap::collectAnimatedTiles() -> std::vector<int> { // NOLINT(readability-make-member-function-const)
std::vector<int> tile; std::vector<int> tile;
// Busca todos los tiles de tipo animado // Busca todos los tiles de tipo animado
@@ -298,13 +298,13 @@ auto CollisionMap::collectAnimatedTiles() -> std::vector<int> {
} }
// Helper: construye lineas horizontales a partir de tiles consecutivos // Helper: construye lineas horizontales a partir de tiles consecutivos
void CollisionMap::buildHorizontalLines(const std::vector<int>& tiles, std::vector<LineHorizontal>& lines, bool is_bottom_surface) { void CollisionMap::buildHorizontalLines(const std::vector<int>& tiles, std::vector<LineHorizontal>& lines, bool is_bottom_surface) { // NOLINT(readability-convert-member-functions-to-static)
if (tiles.size() <= 1) { if (tiles.size() <= 1) {
return; return;
} }
int i = 0; int i = 0;
while (i < (int)tiles.size() - 1) { while (i < static_cast<int>(tiles.size()) - 1) {
LineHorizontal line; LineHorizontal line;
line.x1 = (tiles[i] % MAP_WIDTH) * TILE_SIZE; line.x1 = (tiles[i] % MAP_WIDTH) * TILE_SIZE;
@@ -319,11 +319,11 @@ void CollisionMap::buildHorizontalLines(const std::vector<int>& tiles, std::vect
i++; i++;
// Encuentra tiles consecutivos // Encuentra tiles consecutivos
if (i < (int)tiles.size()) { if (i < static_cast<int>(tiles.size())) {
while (tiles[i] == tiles[i - 1] + 1) { while (tiles[i] == tiles[i - 1] + 1) {
last_one = i; last_one = i;
i++; i++;
if (i >= (int)tiles.size()) { if (i >= static_cast<int>(tiles.size())) {
break; break;
} }
} }
@@ -333,7 +333,7 @@ void CollisionMap::buildHorizontalLines(const std::vector<int>& tiles, std::vect
lines.push_back(line); lines.push_back(line);
// Salta separadores // Salta separadores
if (i < (int)tiles.size() && tiles[i] == -1) { if (i < static_cast<int>(tiles.size()) && tiles[i] == -1) {
i++; i++;
} }
} }
@@ -354,7 +354,7 @@ void CollisionMap::setTopSurfaces() {
} }
// Calcula las superficies laterales izquierdas // Calcula las superficies laterales izquierdas
void CollisionMap::setLeftSurfaces() { void CollisionMap::setLeftSurfaces() { // NOLINT(readability-make-member-function-const)
std::vector<int> tile; std::vector<int> tile;
// Busca todos los tiles de tipo muro que no tienen a su izquierda un tile de tipo muro // Busca todos los tiles de tipo muro que no tienen a su izquierda un tile de tipo muro
@@ -394,7 +394,7 @@ void CollisionMap::setLeftSurfaces() {
} }
// Calcula las superficies laterales derechas // Calcula las superficies laterales derechas
void CollisionMap::setRightSurfaces() { void CollisionMap::setRightSurfaces() { // NOLINT(readability-make-member-function-const)
std::vector<int> tile; std::vector<int> tile;
// Busca todos los tiles de tipo muro que no tienen a su derecha un tile de tipo muro // Busca todos los tiles de tipo muro que no tienen a su derecha un tile de tipo muro
@@ -434,7 +434,7 @@ void CollisionMap::setRightSurfaces() {
} }
// Encuentra todas las rampas que suben hacia la izquierda // Encuentra todas las rampas que suben hacia la izquierda
void CollisionMap::setLeftSlopes() { void CollisionMap::setLeftSlopes() { // NOLINT(readability-make-member-function-const)
// Recorre la habitación entera por filas buscando tiles de tipo t_slope_l // Recorre la habitación entera por filas buscando tiles de tipo t_slope_l
std::vector<int> found; std::vector<int> found;
for (int i = 0; i < (int)tile_map_.size(); ++i) { for (int i = 0; i < (int)tile_map_.size(); ++i) {
@@ -469,7 +469,7 @@ void CollisionMap::setLeftSlopes() {
} }
// Encuentra todas las rampas que suben hacia la derecha // Encuentra todas las rampas que suben hacia la derecha
void CollisionMap::setRightSlopes() { void CollisionMap::setRightSlopes() { // NOLINT(readability-make-member-function-const)
// Recorre la habitación entera por filas buscando tiles de tipo t_slope_r // Recorre la habitación entera por filas buscando tiles de tipo t_slope_r
std::vector<int> found; std::vector<int> found;
for (int i = 0; i < (int)tile_map_.size(); ++i) { for (int i = 0; i < (int)tile_map_.size(); ++i) {

View File

@@ -6,7 +6,7 @@
#include "utils/utils.hpp" // Para checkCollision #include "utils/utils.hpp" // Para checkCollision
// Añade un enemigo a la colección // Añade un enemigo a la colección
void EnemyManager::addEnemy(std::shared_ptr<Enemy> enemy) { void EnemyManager::addEnemy(std::shared_ptr<Enemy> enemy) { // NOLINT(readability-identifier-naming)
enemies_.push_back(std::move(enemy)); enemies_.push_back(std::move(enemy));
} }
@@ -16,7 +16,7 @@ void EnemyManager::clear() {
} }
// Elimina el último enemigo de la colección // Elimina el último enemigo de la colección
void EnemyManager::removeLastEnemy() { void EnemyManager::removeLastEnemy() { // NOLINT(readability-convert-member-functions-to-static)
if (!enemies_.empty()) { if (!enemies_.empty()) {
enemies_.pop_back(); enemies_.pop_back();
} }

View File

@@ -14,7 +14,7 @@ ItemManager::ItemManager(std::string room_name, std::shared_ptr<Scoreboard::Data
} }
// Añade un item a la colección // Añade un item a la colección
void ItemManager::addItem(std::shared_ptr<Item> item) { void ItemManager::addItem(std::shared_ptr<Item> item) { // NOLINT(readability-identifier-naming)
items_.push_back(std::move(item)); items_.push_back(std::move(item));
} }
@@ -45,7 +45,7 @@ void ItemManager::setPaused(bool paused) {
} }
// Comprueba si hay colisión con algún item // Comprueba si hay colisión con algún item
auto ItemManager::checkCollision(SDL_FRect& rect) -> bool { auto ItemManager::checkCollision(SDL_FRect& rect) -> bool { // NOLINT(readability-convert-member-functions-to-static)
for (int i = 0; i < static_cast<int>(items_.size()); ++i) { for (int i = 0; i < static_cast<int>(items_.size()); ++i) {
if (::checkCollision(rect, items_.at(i)->getCollider())) { if (::checkCollision(rect, items_.at(i)->getCollider())) {
// Registra el item como recogido // Registra el item como recogido

View File

@@ -32,7 +32,7 @@ auto ItemTracker::hasBeenPicked(const std::string& name, SDL_FPoint pos) -> bool
} }
// Añade el objeto a la lista de objetos cogidos // Añade el objeto a la lista de objetos cogidos
void ItemTracker::addItem(const std::string& name, SDL_FPoint pos) { void ItemTracker::addItem(const std::string& name, SDL_FPoint pos) { // NOLINT(readability-convert-member-functions-to-static)
// Comprueba si el objeto no ha sido recogido con anterioridad // Comprueba si el objeto no ha sido recogido con anterioridad
if (!hasBeenPicked(name, pos)) { if (!hasBeenPicked(name, pos)) {
// Primero busca si ya hay una entrada con ese nombre // Primero busca si ya hay una entrada con ese nombre
@@ -47,7 +47,7 @@ void ItemTracker::addItem(const std::string& name, SDL_FPoint pos) {
} }
// Busca una entrada en la lista por nombre // Busca una entrada en la lista por nombre
auto ItemTracker::findByName(const std::string& name) -> int { auto ItemTracker::findByName(const std::string& name) -> int { // NOLINT(readability-convert-member-functions-to-static)
int i = 0; int i = 0;
for (const auto& item : items_) { for (const auto& item : items_) {
@@ -61,7 +61,7 @@ auto ItemTracker::findByName(const std::string& name) -> int {
} }
// Busca una entrada en la lista por posición // Busca una entrada en la lista por posición
auto ItemTracker::findByPos(int index, SDL_FPoint pos) -> int { auto ItemTracker::findByPos(int index, SDL_FPoint pos) -> int { // NOLINT(readability-convert-member-functions-to-static)
int i = 0; int i = 0;
for (const auto& item : items_[index].pos) { for (const auto& item : items_[index].pos) {

View File

@@ -5,6 +5,7 @@
#include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface.hpp" // Para Surface
#include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_cache.hpp" // Para Resource
#include "game/defaults.hpp" // Para Defaults::Game
#include "game/gameplay/collision_map.hpp" // Para CollisionMap #include "game/gameplay/collision_map.hpp" // Para CollisionMap
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager #include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
#include "game/gameplay/item_manager.hpp" // Para ItemManager #include "game/gameplay/item_manager.hpp" // Para ItemManager
@@ -81,8 +82,8 @@ void Room::initializeRoom(const Data& room) {
} }
// Abre la jail para poder entrar // Abre la jail para poder entrar
void Room::openTheJail() { void Room::openTheJail() { // NOLINT(readability-convert-member-functions-to-static)
if (data_->jail_is_open && name_ == "THE JAIL") { if (data_->jail_is_open && number_ == Defaults::Game::Room::END_ROOM) {
// Elimina el último enemigo (Bry debe ser el último enemigo definido en el fichero) // Elimina el último enemigo (Bry debe ser el último enemigo definido en el fichero)
if (!enemy_manager_->isEmpty()) { if (!enemy_manager_->isEmpty()) {
enemy_manager_->removeLastEnemy(); enemy_manager_->removeLastEnemy();
@@ -123,7 +124,7 @@ void Room::redrawMap() {
#endif #endif
// Actualiza las variables y objetos de la habitación // Actualiza las variables y objetos de la habitación
void Room::update(float delta_time) { void Room::update(float delta_time) { // NOLINT(readability-make-member-function-const)
if (is_paused_) { if (is_paused_) {
// Si está en modo pausa no se actualiza nada // Si está en modo pausa no se actualiza nada
return; return;
@@ -147,7 +148,7 @@ void Room::setPaused(bool value) {
} }
// Devuelve la cadena del fichero de la habitación contigua segun el borde // Devuelve la cadena del fichero de la habitación contigua segun el borde
auto Room::getRoom(Border border) -> std::string { auto Room::getRoom(Border border) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
switch (border) { switch (border) {
case Border::TOP: case Border::TOP:
return upper_room_; return upper_room_;
@@ -163,14 +164,14 @@ auto Room::getRoom(Border border) -> std::string {
} }
// Devuelve el tipo de tile que hay en ese pixel // Devuelve el tipo de tile que hay en ese pixel
auto Room::getTile(SDL_FPoint point) -> Tile { auto Room::getTile(SDL_FPoint point) -> Tile { // NOLINT(readability-convert-member-functions-to-static)
// Delega a CollisionMap y convierte el resultado // Delega a CollisionMap y convierte el resultado
const auto COLLISION_TILE = collision_map_->getTile(point); const auto COLLISION_TILE = collision_map_->getTile(point);
return static_cast<Tile>(COLLISION_TILE); return static_cast<Tile>(COLLISION_TILE);
} }
// Devuelve el tipo de tile en un índice del tilemap // Devuelve el tipo de tile en un índice del tilemap
auto Room::getTile(int index) -> Tile { auto Room::getTile(int index) -> Tile { // NOLINT(readability-convert-member-functions-to-static)
// Delega a CollisionMap y convierte el resultado // Delega a CollisionMap y convierte el resultado
const auto COLLISION_TILE = collision_map_->getTile(index); const auto COLLISION_TILE = collision_map_->getTile(index);
return static_cast<Tile>(COLLISION_TILE); return static_cast<Tile>(COLLISION_TILE);
@@ -256,6 +257,6 @@ auto Room::getSlopeAtPoint(const SDL_FPoint& p) const -> const LineDiagonal* {
} }
// Carga una habitación desde un archivo YAML (delegado a RoomLoader) // Carga una habitación desde un archivo YAML (delegado a RoomLoader)
auto Room::loadYAML(const std::string& file_path, bool verbose) -> Data { auto Room::loadYAML(const std::string& file_path, bool verbose) -> Data { // NOLINT(readability-convert-member-functions-to-static)
return RoomLoader::loadYAML(file_path, verbose); return RoomLoader::loadYAML(file_path, verbose);
} }

View File

@@ -10,7 +10,7 @@
#include "game/entities/item.hpp" // Para ItemData #include "game/entities/item.hpp" // Para ItemData
#include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data #include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data
#include "utils/utils.hpp" // Para LineHorizontal, LineDiagonal, LineVertical #include "utils/utils.hpp" // Para LineHorizontal, LineDiagonal, LineVertical
class SurfaceSprite; // lines 12-12 class Sprite; // lines 12-12
class Surface; // lines 13-13 class Surface; // lines 13-13
class EnemyManager; class EnemyManager;
class ItemManager; class ItemManager;
@@ -58,9 +58,10 @@ class Room {
// Constructor y destructor // Constructor y destructor
Room(const std::string& room_path, std::shared_ptr<Scoreboard::Data> data); Room(const std::string& room_path, std::shared_ptr<Scoreboard::Data> data);
~Room(); // Definido en .cpp para poder usar unique_ptr con forward declarations ~Room(); // NOLINT(modernize-use-equals-default, performance-trivially-destructible) -- defined in .cpp for unique_ptr with forward declarations
// --- Funciones --- // --- Funciones ---
[[nodiscard]] auto getNumber() const -> const std::string& { return number_; } // Devuelve el numero de la habitación
[[nodiscard]] auto getName() const -> const std::string& { return name_; } // Devuelve el nombre de la habitación [[nodiscard]] auto getName() const -> const std::string& { return name_; } // Devuelve el nombre de la habitación
[[nodiscard]] auto getBGColor() const -> Uint8 { return stringToColor(bg_color_); } // Devuelve el color de la habitación [[nodiscard]] auto getBGColor() const -> Uint8 { return stringToColor(bg_color_); } // Devuelve el color de la habitación
[[nodiscard]] auto getBorderColor() const -> Uint8 { return stringToColor(border_color_); } // Devuelve el color del borde [[nodiscard]] auto getBorderColor() const -> Uint8 { return stringToColor(border_color_); } // Devuelve el color del borde

View File

@@ -10,7 +10,7 @@
#include "utils/utils.hpp" // Para stringToColor #include "utils/utils.hpp" // Para stringToColor
// Convierte room connection de YAML a formato interno // Convierte room connection de YAML a formato interno
auto RoomLoader::convertRoomConnection(const std::string& value) -> std::string { auto RoomLoader::convertRoomConnection(const std::string& value) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
if (value == "null" || value.empty()) { if (value == "null" || value.empty()) {
return "0"; return "0";
} }
@@ -22,7 +22,7 @@ auto RoomLoader::convertRoomConnection(const std::string& value) -> std::string
} }
// Convierte string de autoSurface a int // Convierte string de autoSurface a int
auto RoomLoader::convertAutoSurface(const fkyaml::node& node) -> int { auto RoomLoader::convertAutoSurface(const fkyaml::node& node) -> int { // NOLINT(readability-convert-member-functions-to-static)
if (node.is_integer()) { if (node.is_integer()) {
return node.get_value<int>(); return node.get_value<int>();
} }
@@ -39,7 +39,7 @@ auto RoomLoader::convertAutoSurface(const fkyaml::node& node) -> int {
} }
// Convierte un tilemap 2D a vector 1D flat // Convierte un tilemap 2D a vector 1D flat
auto RoomLoader::flattenTilemap(const std::vector<std::vector<int>>& tilemap_2d) -> std::vector<int> { auto RoomLoader::flattenTilemap(const std::vector<std::vector<int>>& tilemap_2d) -> std::vector<int> { // NOLINT(readability-convert-member-functions-to-static, readability-named-parameter)
std::vector<int> tilemap_flat; std::vector<int> tilemap_flat;
tilemap_flat.reserve(512); // 16 rows × 32 cols tilemap_flat.reserve(512); // 16 rows × 32 cols
@@ -53,7 +53,7 @@ auto RoomLoader::flattenTilemap(const std::vector<std::vector<int>>& tilemap_2d)
} }
// Parsea la configuración general de la habitación // Parsea la configuración general de la habitación
void RoomLoader::parseRoomConfig(const fkyaml::node& yaml, Room::Data& room, const std::string& file_name) { void RoomLoader::parseRoomConfig(const fkyaml::node& yaml, Room::Data& room, const std::string& file_name) { // NOLINT(readability-convert-member-functions-to-static)
if (!yaml.contains("room")) { if (!yaml.contains("room")) {
return; return;
} }
@@ -120,7 +120,7 @@ void RoomLoader::parseRoomConnections(const fkyaml::node& conn_node, Room::Data&
} }
// Parsea el tilemap de la habitación // Parsea el tilemap de la habitación
void RoomLoader::parseTilemap(const fkyaml::node& yaml, Room::Data& room, const std::string& file_name, bool verbose) { void RoomLoader::parseTilemap(const fkyaml::node& yaml, Room::Data& room, const std::string& file_name, bool verbose) { // NOLINT(readability-convert-member-functions-to-static)
if (!yaml.contains("tilemap")) { if (!yaml.contains("tilemap")) {
std::cerr << "Warning: No tilemap found in " << file_name << '\n'; std::cerr << "Warning: No tilemap found in " << file_name << '\n';
return; return;
@@ -152,7 +152,7 @@ void RoomLoader::parseTilemap(const fkyaml::node& yaml, Room::Data& room, const
} }
// Parsea los límites de movimiento de un enemigo // Parsea los límites de movimiento de un enemigo
void RoomLoader::parseEnemyBoundaries(const fkyaml::node& bounds_node, Enemy::Data& enemy) { void RoomLoader::parseEnemyBoundaries(const fkyaml::node& bounds_node, Enemy::Data& enemy) { // NOLINT(readability-convert-member-functions-to-static)
// Nuevo formato: position1 y position2 // Nuevo formato: position1 y position2
if (bounds_node.contains("position1")) { if (bounds_node.contains("position1")) {
const auto& pos1 = bounds_node["position1"]; const auto& pos1 = bounds_node["position1"];
@@ -189,7 +189,7 @@ void RoomLoader::parseEnemyBoundaries(const fkyaml::node& bounds_node, Enemy::Da
} }
// Parsea los datos de un enemigo individual // Parsea los datos de un enemigo individual
auto RoomLoader::parseEnemyData(const fkyaml::node& enemy_node) -> Enemy::Data { auto RoomLoader::parseEnemyData(const fkyaml::node& enemy_node) -> Enemy::Data { // NOLINT(readability-convert-member-functions-to-static)
Enemy::Data enemy; Enemy::Data enemy;
// Animation path // Animation path
@@ -246,7 +246,7 @@ auto RoomLoader::parseEnemyData(const fkyaml::node& enemy_node) -> Enemy::Data {
} }
// Parsea la lista de enemigos de la habitación // Parsea la lista de enemigos de la habitación
void RoomLoader::parseEnemies(const fkyaml::node& yaml, Room::Data& room, bool verbose) { void RoomLoader::parseEnemies(const fkyaml::node& yaml, Room::Data& room, bool verbose) { // NOLINT(readability-convert-member-functions-to-static)
if (!yaml.contains("enemies") || yaml["enemies"].is_null()) { if (!yaml.contains("enemies") || yaml["enemies"].is_null()) {
return; return;
} }
@@ -263,7 +263,7 @@ void RoomLoader::parseEnemies(const fkyaml::node& yaml, Room::Data& room, bool v
} }
// Parsea los datos de un item individual // Parsea los datos de un item individual
auto RoomLoader::parseItemData(const fkyaml::node& item_node, const Room::Data& room) -> Item::Data { auto RoomLoader::parseItemData(const fkyaml::node& item_node, const Room::Data& room) -> Item::Data { // NOLINT(readability-convert-member-functions-to-static)
Item::Data item; Item::Data item;
// Tileset file // Tileset file
@@ -300,7 +300,7 @@ auto RoomLoader::parseItemData(const fkyaml::node& item_node, const Room::Data&
} }
// Parsea la lista de items de la habitación // Parsea la lista de items de la habitación
void RoomLoader::parseItems(const fkyaml::node& yaml, Room::Data& room, bool verbose) { void RoomLoader::parseItems(const fkyaml::node& yaml, Room::Data& room, bool verbose) { // NOLINT(readability-convert-member-functions-to-static)
if (!yaml.contains("items") || yaml["items"].is_null()) { if (!yaml.contains("items") || yaml["items"].is_null()) {
return; return;
} }
@@ -317,7 +317,7 @@ void RoomLoader::parseItems(const fkyaml::node& yaml, Room::Data& room, bool ver
} }
// Carga un archivo de room en formato YAML // Carga un archivo de room en formato YAML
auto RoomLoader::loadYAML(const std::string& file_path, bool verbose) -> Room::Data { auto RoomLoader::loadYAML(const std::string& file_path, bool verbose) -> Room::Data { // NOLINT(readability-convert-member-functions-to-static)
Room::Data room; Room::Data room;
// Extract filename for logging // Extract filename for logging

View File

@@ -67,7 +67,7 @@ class RoomLoader {
* @param tilemap_2d Array 2D de tiles (16 rows × 32 cols) * @param tilemap_2d Array 2D de tiles (16 rows × 32 cols)
* @return Vector 1D flat con 512 elementos * @return Vector 1D flat con 512 elementos
*/ */
static auto flattenTilemap(const std::vector<std::vector<int>>& tilemap_2d) -> std::vector<int>; static auto flattenTilemap(const std::vector<std::vector<int>>& tilemap_2d) -> std::vector<int>; // NOLINT(readability-avoid-const-params-in-decls)
/** /**
* @brief Parsea la configuración general de la habitación * @brief Parsea la configuración general de la habitación

View File

@@ -3,17 +3,17 @@
#include <algorithm> // Para std::ranges::any_of #include <algorithm> // Para std::ranges::any_of
// Comprueba si la habitación ya ha sido visitada // Comprueba si la habitación ya ha sido visitada
auto RoomTracker::hasBeenVisited(const std::string& name) -> bool { auto RoomTracker::hasBeenVisited(const std::string& name) -> bool { // NOLINT(readability-convert-member-functions-to-static)
return std::ranges::any_of(rooms_, [&name](const auto& l) { return l == name; }); return std::ranges::any_of(rooms_, [&name](const auto& l) -> bool { return l == name; });
} }
// Añade la habitación a la lista // Añade la habitación a la lista
auto RoomTracker::addRoom(const std::string& name) -> bool { auto RoomTracker::addRoom(const std::string& name) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Comprueba si la habitación ya ha sido visitada // Comprueba si la habitación ya ha sido visitada
if (!hasBeenVisited(name)) { if (!hasBeenVisited(name)) {
// En caso contrario añádela a la lista // En caso contrario añádela a la lista
rooms_.push_back(name); rooms_.push_back(name);
return true; return true; // NOLINT(readability-simplify-boolean-expr)
} }
return false; return false;

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