Compare commits
44 Commits
49ae2ae41f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6717c1e307 | |||
| 9282d661aa | |||
| a21f530dd4 | |||
| 7483bf63c8 | |||
| 268763f162 | |||
| 71c7b8e553 | |||
| 854a5f04b2 | |||
| ed21a47f92 | |||
| 637df23bc7 | |||
| ea421b4e17 | |||
| 3a5ff06dab | |||
| dfb0d2134f | |||
| b459e2106f | |||
| 065f66d40e | |||
| f15658a767 | |||
| 21c8d1e8ca | |||
| a06eb8c8e9 | |||
| 6b73a76d31 | |||
| 348a76090b | |||
| 02c1bf647e | |||
| a7f0a18e6d | |||
| 8355c266a6 | |||
| 7bad27d686 | |||
| d39622c7e2 | |||
| 4910d201f9 | |||
| e85800c5ed | |||
| f25ee18329 | |||
| 3712f0c8d9 | |||
| c063488e8e | |||
| deb0a8677f | |||
| c5a7c9e70d | |||
| 92453a6104 | |||
| 7aff3e2109 | |||
| 8d213e7b3e | |||
| c6d409c303 | |||
| 6914f7df93 | |||
| 1dbfff2c17 | |||
| 3493636954 | |||
| 8ff1073e4a | |||
| 6497e26202 | |||
| e0e37204d7 | |||
| 6595b28790 | |||
| 0ddb6c85e1 | |||
| f84007902e |
@@ -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
|
||||||
@@ -132,7 +133,11 @@ if(NOT APPLE)
|
|||||||
if(GLSLC_EXE)
|
if(GLSLC_EXE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT "${SHADER_VERT_H}" "${SHADER_FRAG_H}"
|
OUTPUT "${SHADER_VERT_H}" "${SHADER_FRAG_H}"
|
||||||
COMMAND "${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.sh"
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-D GLSLC=${GLSLC_EXE}
|
||||||
|
-D SHADERS_DIR=${CMAKE_SOURCE_DIR}/data/shaders
|
||||||
|
-D HEADERS_DIR=${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu
|
||||||
|
-P ${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.cmake
|
||||||
DEPENDS "${SHADER_VERT_SRC}" "${SHADER_FRAG_SRC}"
|
DEPENDS "${SHADER_VERT_SRC}" "${SHADER_FRAG_SRC}"
|
||||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||||
COMMENT "Compilando shaders SPIR-V..."
|
COMMENT "Compilando shaders SPIR-V..."
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -130,3 +130,4 @@ columns 15
|
|||||||
171 7 # «
|
171 7 # «
|
||||||
187 7 # »
|
187 7 # »
|
||||||
183 7 # ·
|
183 7 # ·
|
||||||
|
228 7 # ä (corazón)
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 1001 B |
@@ -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É 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 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!"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# ROAD TO THE JAIL
|
# ROAD TO THE JAIL
|
||||||
room:
|
room:
|
||||||
name_en: "ROAD TO THE JAIL"
|
name_en: "ROAD TO THE JAIL"
|
||||||
name_ca: "CAMI A LA JAIL"
|
name_ca: "CAMÍ A LA JAIL"
|
||||||
bgColor: black
|
bgColor: black
|
||||||
border: blue
|
border: blue
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# JUMP THROUGH
|
# JUMP THROUGH
|
||||||
room:
|
room:
|
||||||
name_en: "JUMP THROUGH"
|
name_en: "JUMP THROUGH"
|
||||||
name_ca: "SALTA A TRAVES"
|
name_ca: "SALTA A TRAVÉS"
|
||||||
bgColor: black
|
bgColor: black
|
||||||
border: cyan
|
border: cyan
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# THE GARDEN
|
# THE GARDEN
|
||||||
room:
|
room:
|
||||||
name_en: "THE GARDEN"
|
name_en: "THE GARDEN"
|
||||||
name_ca: "EL JARDI"
|
name_ca: "EL JARDÍ"
|
||||||
bgColor: black
|
bgColor: black
|
||||||
border: cyan
|
border: cyan
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# YOU SHALL NOT PASS
|
# YOU SHALL NOT PASS
|
||||||
room:
|
room:
|
||||||
name_en: "YOU SHALL NOT PASS"
|
name_en: "YOU SHALL NOT PASS"
|
||||||
name_ca: "NO PASSARAS"
|
name_ca: "NO PASSARÀS"
|
||||||
bgColor: bright_black
|
bgColor: bright_black
|
||||||
border: black
|
border: black
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# QVOID IS A JAILGAME!
|
# QVOID IS A JAILGAME!
|
||||||
room:
|
room:
|
||||||
name_en: "QVOID IS A JAILGAME!"
|
name_en: "QVOID IS A JAILGAME!"
|
||||||
name_ca: "QVOID ES UN JAILGAME!"
|
name_ca: "QVOID ÉS UN JAILGAME!"
|
||||||
bgColor: blue
|
bgColor: blue
|
||||||
border: bright_black
|
border: bright_black
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# YOU'LL BELIEVE AROUNDER CAN FLY
|
# YOU'LL BELIEVE AROUNDER CAN FLY
|
||||||
room:
|
room:
|
||||||
name_en: "YOU'LL BELIEVE AROUNDER CAN FLY"
|
name_en: "YOU'LL BELIEVE AROUNDER CAN FLY"
|
||||||
name_ca: "CREURAS QUE ELS AROUNDERS VOLEN"
|
name_ca: "CREURÀS QUE ELS AROUNDERS VOLEN"
|
||||||
bgColor: black
|
bgColor: black
|
||||||
border: cyan
|
border: cyan
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
@@ -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: "PREVÉ LA CRISI"
|
||||||
bgColor: black
|
bgColor: black
|
||||||
border: bright_magenta
|
border: bright_magenta
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# FEEL THE PRESSURE
|
# FEEL THE PRESSURE
|
||||||
room:
|
room:
|
||||||
name_en: "FEEL THE PRESSURE"
|
name_en: "FEEL THE PRESSURE"
|
||||||
name_ca: "NOTA LA PRESSIO"
|
name_ca: "NOTA LA PRESSIÓ"
|
||||||
bgColor: bright_black
|
bgColor: bright_black
|
||||||
border: bright_yellow
|
border: bright_yellow
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# THAT'S A GUITAR
|
# THAT'S A GUITAR
|
||||||
room:
|
room:
|
||||||
name_en: "THAT'S A GUITAR"
|
name_en: "THAT'S A GUITAR"
|
||||||
name_ca: "AIXO ES UNA GUITARRA"
|
name_ca: "AIXÒ ÉS UNA GUITARRA"
|
||||||
bgColor: black
|
bgColor: black
|
||||||
border: black
|
border: black
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# STATIC
|
# STATIC
|
||||||
room:
|
room:
|
||||||
name_en: "STATIC"
|
name_en: "STATIC"
|
||||||
name_ca: "ESTATICA"
|
name_ca: "ESTÀTICA"
|
||||||
bgColor: black
|
bgColor: black
|
||||||
border: bright_magenta
|
border: bright_magenta
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# MAGNETIC FIELDS
|
# MAGNETIC FIELDS
|
||||||
room:
|
room:
|
||||||
name_en: "MAGNETIC FIELDS"
|
name_en: "MAGNETIC FIELDS"
|
||||||
name_ca: "CAMPS MAGNETICS"
|
name_ca: "CAMPS MAGNÈTICS"
|
||||||
bgColor: black
|
bgColor: black
|
||||||
border: bright_red
|
border: bright_red
|
||||||
tileSetFile: standard.gif
|
tileSetFile: standard.gif
|
||||||
|
|||||||
48
data/shaders/downscale.frag
Normal file
48
data/shaders/downscale.frag
Normal 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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
data/shaders/postfx.frag.spv
Normal file
BIN
data/shaders/postfx.frag.spv
Normal file
Binary file not shown.
15
data/shaders/upscale.frag
Normal file
15
data/shaders/upscale.frag
Normal 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);
|
||||||
|
}
|
||||||
BIN
data/shaders/upscale.frag.spv
Normal file
BIN
data/shaders/upscale.frag.spv
Normal file
Binary file not shown.
@@ -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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,15 @@
|
|||||||
#include <string> // Para allocator, operator+, char_traits, string
|
#include <string> // Para allocator, operator+, char_traits, string
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#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/screen.hpp" // Para Screen
|
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
||||||
#include "game/options.hpp" // Para Options, options, OptionsVideo, Section
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "game/scene_manager.hpp" // Para SceneManager
|
#include "game/options.hpp" // Para Options, options, OptionsVideo, Section
|
||||||
#include "game/ui/notifier.hpp" // Para Notifier, NotificationText
|
#include "game/scene_manager.hpp" // Para SceneManager
|
||||||
#include "utils/utils.hpp" // Para stringInVector
|
#include "game/ui/console.hpp" // Para Console
|
||||||
|
#include "game/ui/notifier.hpp" // Para Notifier, NotificationText
|
||||||
|
#include "utils/utils.hpp" // Para stringInVector
|
||||||
|
|
||||||
namespace GlobalInputs {
|
namespace GlobalInputs {
|
||||||
|
|
||||||
@@ -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,65 @@ 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 handleTogglePostFX() {
|
||||||
Screen::get()->togglePostFX();
|
Screen::get()->togglePostFX();
|
||||||
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.postfx_enabled" : "ui.postfx_disabled")}); // NOLINT(readability-static-accessed-through-instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleToggleSupersampling() {
|
void handleToggleSupersampling() {
|
||||||
Screen::get()->toggleSupersampling();
|
Screen::get()->toggleSupersampling();
|
||||||
Notifier::get()->show({Locale::get()->get(Options::video.supersampling ? "ui.supersampling_enabled" : "ui.supersampling_disabled")});
|
Notifier::get()->show({Locale::get()->get(Options::video.supersampling ? "ui.supersampling_enabled" : "ui.supersampling_disabled")}); // NOLINT(readability-static-accessed-through-instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleNextPostFXPreset() {
|
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 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)) {
|
||||||
@@ -152,12 +153,12 @@ namespace GlobalInputs {
|
|||||||
}
|
}
|
||||||
if (Input::get()->checkAction(InputAction::TOGGLE_POSTFX, Input::DO_NOT_ALLOW_REPEAT)) {
|
if (Input::get()->checkAction(InputAction::TOGGLE_POSTFX, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||||
if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
|
if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
|
||||||
return InputAction::TOGGLE_SUPERSAMPLING; // Ctrl+F4
|
return InputAction::TOGGLE_SUPERSAMPLING; // Ctrl+F4
|
||||||
}
|
}
|
||||||
if (Options::video.postfx && ((SDL_GetModState() & SDL_KMOD_SHIFT) != 0U)) {
|
if (Options::video.postfx && ((SDL_GetModState() & SDL_KMOD_SHIFT) != 0U)) {
|
||||||
return InputAction::NEXT_POSTFX_PRESET; // Shift+F4
|
return InputAction::NEXT_POSTFX_PRESET; // Shift+F4
|
||||||
}
|
}
|
||||||
return InputAction::TOGGLE_POSTFX; // F4
|
return InputAction::TOGGLE_POSTFX; // F4
|
||||||
}
|
}
|
||||||
if (Input::get()->checkAction(InputAction::NEXT_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) {
|
if (Input::get()->checkAction(InputAction::NEXT_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||||
return InputAction::NEXT_PALETTE;
|
return InputAction::NEXT_PALETTE;
|
||||||
@@ -177,6 +178,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,19 +190,35 @@ 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() {
|
||||||
// Salida de administrador en modo kiosko (Ctrl+Shift+Alt+Q)
|
const bool CONSOLE_ACTIVE = Console::get() != nullptr && Console::get()->isActive();
|
||||||
if (Options::kiosk.enabled) {
|
|
||||||
SDL_Keymod mod = SDL_GetModState();
|
if (CONSOLE_ACTIVE) {
|
||||||
const bool* ks = SDL_GetKeyboardState(nullptr);
|
// TAB/ESC cierran la consola en lugar de ejecutar sus acciones normales
|
||||||
if (((mod & SDL_KMOD_CTRL) != 0U) && ((mod & SDL_KMOD_SHIFT) != 0U) && ((mod & SDL_KMOD_ALT) != 0U) && ks[SDL_SCANCODE_Q]) {
|
if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT) ||
|
||||||
SceneManager::current = SceneManager::Scene::QUIT;
|
Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||||
|
Console::get()->toggle();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Salida de administrador en modo kiosko (Ctrl+Shift+Alt+Q)
|
||||||
|
if (Options::kiosk.enabled) {
|
||||||
|
SDL_Keymod mod = SDL_GetModState();
|
||||||
|
const bool* ks = SDL_GetKeyboardState(nullptr);
|
||||||
|
if (((mod & SDL_KMOD_CTRL) != 0U) && ((mod & SDL_KMOD_SHIFT) != 0U) && ((mod & SDL_KMOD_ALT) != 0U) && ks[SDL_SCANCODE_Q]) {
|
||||||
|
SceneManager::current = SceneManager::Scene::QUIT;
|
||||||
|
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:
|
||||||
@@ -253,9 +273,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:
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
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,7 +28,7 @@ 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;
|
||||||
@@ -41,7 +41,7 @@ auto Locale::get(const std::string& key) const -> std::string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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,7 +59,7 @@ 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) {
|
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";
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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_; });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
120
source/core/rendering/render_info.cpp
Normal file
120
source/core/rendering/render_info.cpp
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
#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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renderiza el overlay de información por pantalla
|
||||||
|
void RenderInfo::render() const {
|
||||||
|
if (!active_) { 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 preset y supersampling, o nada si está desactivado
|
||||||
|
if (Options::video.postfx) {
|
||||||
|
std::string preset_name = "-";
|
||||||
|
if (!Options::postfx_presets.empty()) {
|
||||||
|
preset_name = Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)].name;
|
||||||
|
}
|
||||||
|
line += " | " + preset_name + (Options::video.supersampling ? " (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 si está visible)
|
||||||
|
const int Y = (Console::get() != nullptr) ? Console::get()->getVisibleHeight() : 0;
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
active_ = !active_;
|
||||||
|
if (active_) {
|
||||||
|
Screen::get()->updateZoomFactor();
|
||||||
|
if (Notifier::get() != nullptr) { Notifier::get()->addYOffset(HEIGHT); }
|
||||||
|
} else {
|
||||||
|
if (Notifier::get() != nullptr) { Notifier::get()->removeYOffset(HEIGHT); }
|
||||||
|
}
|
||||||
|
}
|
||||||
29
source/core/rendering/render_info.hpp
Normal file
29
source/core/rendering/render_info.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class RenderInfo {
|
||||||
|
public:
|
||||||
|
// Singleton
|
||||||
|
static void init();
|
||||||
|
static void destroy();
|
||||||
|
static auto get() -> RenderInfo*;
|
||||||
|
|
||||||
|
// Métodos principales
|
||||||
|
void render() const;
|
||||||
|
void toggle();
|
||||||
|
|
||||||
|
// Consultas
|
||||||
|
[[nodiscard]] auto isActive() const -> bool { return active_; }
|
||||||
|
|
||||||
|
// Altura fija del overlay (TEXT_SIZE(6) + PADDING_V(2) * 2)
|
||||||
|
static constexpr int HEIGHT = 10;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Singleton
|
||||||
|
static RenderInfo* render_info;
|
||||||
|
|
||||||
|
// Constructor y destructor privados [SINGLETON]
|
||||||
|
RenderInfo();
|
||||||
|
~RenderInfo() = default;
|
||||||
|
|
||||||
|
bool active_{false}; // Estado del overlay
|
||||||
|
};
|
||||||
@@ -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
|
||||||
@@ -87,6 +93,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 +154,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 +202,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
|
||||||
@@ -204,6 +221,9 @@ void Screen::renderNotifications() const {
|
|||||||
if (notifications_enabled_) {
|
if (notifications_enabled_) {
|
||||||
Notifier::get()->render();
|
Notifier::get()->render();
|
||||||
}
|
}
|
||||||
|
if (Console::get() != nullptr) {
|
||||||
|
Console::get()->render();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cambia el estado del PostFX
|
// Cambia el estado del PostFX
|
||||||
@@ -236,6 +256,9 @@ void Screen::reloadPostFX() {
|
|||||||
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);
|
||||||
|
}
|
||||||
Mouse::updateCursorVisibility();
|
Mouse::updateCursorVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,21 +267,28 @@ 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_X = old_x + ((old_w - (window_width_ * Options::window.zoom)) / 2);
|
||||||
int old_pos_y;
|
const int NEW_Y = old_y + ((old_h - (window_height_ * Options::window.zoom)) / 2);
|
||||||
SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y);
|
|
||||||
|
|
||||||
const int NEW_POS_X = old_pos_x + ((old_width - (window_width_ * Options::window.zoom)) / 2);
|
|
||||||
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);
|
SDL_SetWindowSize(window_, window_width_ * Options::window.zoom, window_height_ * Options::window.zoom);
|
||||||
SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS), std::max(NEW_POS_Y, 0));
|
SDL_SetWindowPosition(window_, std::max(NEW_X, WINDOWS_DECORATIONS), std::max(NEW_Y, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,6 +297,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 +343,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 +357,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 +373,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,44 +384,61 @@ 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_);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Path B: borde dinámico (escena de carga — bandas de colores animadas)
|
||||||
|
// Conversión completa: la escena modifica border_surface_ cada frame
|
||||||
|
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
|
||||||
}
|
}
|
||||||
shader_backend_->uploadPixels(pixel_buffer_.data(), BORDER_W, BORDER_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 {
|
} else {
|
||||||
const int GAME_W = static_cast<int>(game_surface_->getWidth());
|
// Caso sin borde: subida directa simplificada
|
||||||
const int GAME_H = static_cast<int>(game_surface_->getHeight());
|
game_surface_->toARGBBuffer(game_pixel_buffer_.data());
|
||||||
pixel_buffer_.resize(static_cast<size_t>(GAME_W * GAME_H));
|
shader_backend_->uploadPixels(game_pixel_buffer_.data(), GAME_W, GAME_H);
|
||||||
game_surface_->toARGBBuffer(pixel_buffer_.data());
|
|
||||||
shader_backend_->uploadPixels(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_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,13 +446,11 @@ void Screen::textureToRenderer() {
|
|||||||
// Renderiza todos los overlays
|
// Renderiza todos los overlays
|
||||||
void Screen::renderOverlays() {
|
void Screen::renderOverlays() {
|
||||||
renderNotifications();
|
renderNotifications();
|
||||||
#ifdef _DEBUG
|
if (RenderInfo::get() != nullptr) { RenderInfo::get()->render(); }
|
||||||
renderInfo();
|
|
||||||
#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 +461,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 +482,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 +489,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 +504,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,14 +543,14 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,12 +563,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();
|
||||||
@@ -504,7 +583,7 @@ void Screen::initShaders() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 +688,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"));
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#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 "utils/utils.hpp" // Para Color
|
#include "utils/utils.hpp" // Para Color
|
||||||
@@ -53,24 +54,31 @@ class Screen {
|
|||||||
void toggleBorder(); // Cambia entre borde visible y no visible
|
void toggleBorder(); // Cambia entre borde visible y no visible
|
||||||
|
|
||||||
// Paletas y PostFX
|
// Paletas y PostFX
|
||||||
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 togglePostFX(); // Cambia el estado del PostFX
|
||||||
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 setLinearUpscale(bool linear); // Upscale NEAREST (false) o LINEAR (true) en el paso SS
|
||||||
|
void setDownscaleAlgo(int algo); // 0=bilinear legacy, 1=Lanczos2, 2=Lanczos3
|
||||||
|
|
||||||
// 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
|
||||||
@@ -117,7 +125,6 @@ class Screen {
|
|||||||
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 actual al backend
|
||||||
void renderInfo() const; // Muestra información por pantalla
|
|
||||||
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 +146,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
|
||||||
@@ -155,12 +171,6 @@ class Screen {
|
|||||||
DisplayMonitor display_monitor_; // Información de la pantalla
|
DisplayMonitor display_monitor_; // Información de la pantalla
|
||||||
|
|
||||||
// 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
|
|
||||||
};
|
};
|
||||||
4253
source/core/rendering/sdl3gpu/downscale_frag_spv.h
Normal file
4253
source/core/rendering/sdl3gpu/downscale_frag_spv.h
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,10 @@
|
|||||||
#include <cstring> // memcpy, strlen
|
#include <cstring> // memcpy, strlen
|
||||||
|
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
|
#include "core/rendering/sdl3gpu/downscale_frag_spv.h"
|
||||||
#include "core/rendering/sdl3gpu/postfx_frag_spv.h"
|
#include "core/rendering/sdl3gpu/postfx_frag_spv.h"
|
||||||
#include "core/rendering/sdl3gpu/postfx_vert_spv.h"
|
#include "core/rendering/sdl3gpu/postfx_vert_spv.h"
|
||||||
|
#include "core/rendering/sdl3gpu/upscale_frag_spv.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
@@ -57,7 +59,7 @@ struct PostFXUniforms {
|
|||||||
float pixel_scale;
|
float pixel_scale;
|
||||||
float time;
|
float time;
|
||||||
float oversample; // 1.0 = sin SS, 3.0 = 3× supersampling
|
float oversample; // 1.0 = sin SS, 3.0 = 3× supersampling
|
||||||
float pad1;
|
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz
|
||||||
};
|
};
|
||||||
|
|
||||||
// YCbCr helpers for NTSC bleeding
|
// YCbCr helpers for NTSC bleeding
|
||||||
@@ -119,8 +121,8 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]],
|
|||||||
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.005f;
|
float ca = u.chroma_strength * 0.005f * (1.0f + 0.15f * sin(u.time * 7.3f));
|
||||||
colour.r = scene.sample(samp, uv + float2(ca, 0.0f)).r;
|
colour.r = scene.sample(samp, uv + float2(ca, 0.0f)).r;
|
||||||
colour.b = scene.sample(samp, uv - float2(ca, 0.0f)).b;
|
colour.b = scene.sample(samp, uv - float2(ca, 0.0f)).b;
|
||||||
|
|
||||||
@@ -130,17 +132,18 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]],
|
|||||||
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.333f; // fracción de subfilas oscuras (ps >= 3)
|
||||||
// bright=3.5×, dark floor=0.42 (mantiene aspecto CRT original).
|
const float SCAN_DARK_FLOOR = 0.42f; // multiplicador de brillo de subfilas oscuras
|
||||||
if (u.scanline_strength > 0.0f) {
|
if (u.scanline_strength > 0.0f) {
|
||||||
float ps = max(1.0f, round(u.pixel_scale));
|
float ps = max(1.0f, 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.0f, row_pos);
|
float bright_rows = (ps < 2.0f) ? ps : ((ps < 3.0f) ? 1.0f : floor(ps * (1.0f - SCAN_DARK_RATIO)));
|
||||||
float scan = mix(3.5f, 0.42f, is_dark);
|
float is_dark = step(bright_rows, row_pos);
|
||||||
|
float scan = mix(1.0f, SCAN_DARK_FLOOR, is_dark);
|
||||||
colour *= mix(1.0f, scan, u.scanline_strength);
|
colour *= mix(1.0f, scan, u.scanline_strength);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,9 +168,66 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]],
|
|||||||
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.0f) {
|
||||||
|
float flicker_wave = sin(u.time * 100.0f) * 0.5f + 0.5f;
|
||||||
|
colour *= 1.0f - u.flicker * 0.04f * flicker_wave;
|
||||||
|
}
|
||||||
|
|
||||||
return float4(colour, 1.0f);
|
return float4(colour, 1.0f);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
static const char* UPSCALE_FRAG_MSL = R"(
|
||||||
|
#include <metal_stdlib>
|
||||||
|
using namespace metal;
|
||||||
|
struct VertOut { float4 pos [[position]]; float2 uv; };
|
||||||
|
fragment float4 upscale_fs(VertOut in [[stage_in]],
|
||||||
|
texture2d<float> scene [[texture(0)]],
|
||||||
|
sampler smp [[sampler(0)]])
|
||||||
|
{
|
||||||
|
return scene.sample(smp, in.uv);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static const char* DOWNSCALE_FRAG_MSL = R"(
|
||||||
|
#include <metal_stdlib>
|
||||||
|
using namespace metal;
|
||||||
|
struct VertOut { float4 pos [[position]]; float2 uv; };
|
||||||
|
struct DownscaleUniforms { int algorithm; float pad0; float pad1; float pad2; };
|
||||||
|
|
||||||
|
static float lanczos_w(float t, float a) {
|
||||||
|
t = abs(t);
|
||||||
|
if (t < 0.0001f) { return 1.0f; }
|
||||||
|
if (t >= a) { return 0.0f; }
|
||||||
|
const float PI = 3.14159265358979f;
|
||||||
|
float pt = PI * t;
|
||||||
|
return (a * sin(pt) * sin(pt / a)) / (pt * pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment float4 downscale_fs(VertOut in [[stage_in]],
|
||||||
|
texture2d<float> source [[texture(0)]],
|
||||||
|
sampler smp [[sampler(0)]],
|
||||||
|
constant DownscaleUniforms& u [[buffer(0)]])
|
||||||
|
{
|
||||||
|
float2 src_size = float2(source.get_width(), source.get_height());
|
||||||
|
float2 p = in.uv * src_size;
|
||||||
|
float2 p_floor = floor(p);
|
||||||
|
float a = (u.algorithm == 0) ? 2.0f : 3.0f;
|
||||||
|
int win = int(a);
|
||||||
|
float4 color = float4(0.0f);
|
||||||
|
float weight_sum = 0.0f;
|
||||||
|
for (int j = -win; j <= win; j++) {
|
||||||
|
for (int i = -win; i <= win; i++) {
|
||||||
|
float2 tap_center = p_floor + float2(float(i), float(j)) + 0.5f;
|
||||||
|
float2 offset = tap_center - p;
|
||||||
|
float w = lanczos_w(offset.x, a) * lanczos_w(offset.y, a);
|
||||||
|
color += source.sample(smp, tap_center / src_size) * w;
|
||||||
|
weight_sum += w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (weight_sum > 0.0f) ? (color / weight_sum) : float4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
)";
|
||||||
// NOLINTEND(readability-identifier-naming)
|
// NOLINTEND(readability-identifier-naming)
|
||||||
|
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
@@ -201,28 +261,37 @@ namespace Rendering {
|
|||||||
float fw = 0.0F;
|
float fw = 0.0F;
|
||||||
float fh = 0.0F;
|
float fh = 0.0F;
|
||||||
SDL_GetTextureSize(texture, &fw, &fh);
|
SDL_GetTextureSize(texture, &fw, &fh);
|
||||||
game_width_ = static_cast<int>(fw);
|
game_width_ = static_cast<int>(fw);
|
||||||
game_height_ = static_cast<int>(fh);
|
game_height_ = static_cast<int>(fh);
|
||||||
tex_width_ = game_width_ * oversample_;
|
uniforms_.screen_height = static_cast<float>(game_height_);
|
||||||
tex_height_ = game_height_ * oversample_;
|
uniforms_.oversample = static_cast<float>(oversample_);
|
||||||
uniforms_.screen_height = static_cast<float>(tex_height_); // Altura de la textura GPU
|
|
||||||
uniforms_.oversample = static_cast<float>(oversample_);
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// 1. Create GPU device (solo si no existe ya)
|
// 1. Create GPU device (solo si no existe ya)
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
if (preferred_driver_ == "none") {
|
||||||
|
SDL_Log("SDL3GPUShader: GPU disabled by config, using SDL_Renderer fallback");
|
||||||
|
driver_name_ = "none";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (device_ == nullptr) {
|
if (device_ == nullptr) {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
const SDL_GPUShaderFormat PREFERRED = SDL_GPU_SHADERFORMAT_MSL | SDL_GPU_SHADERFORMAT_METALLIB;
|
const SDL_GPUShaderFormat PREFERRED = SDL_GPU_SHADERFORMAT_MSL | SDL_GPU_SHADERFORMAT_METALLIB;
|
||||||
#else
|
#else
|
||||||
const SDL_GPUShaderFormat PREFERRED = SDL_GPU_SHADERFORMAT_SPIRV;
|
const SDL_GPUShaderFormat PREFERRED = SDL_GPU_SHADERFORMAT_SPIRV;
|
||||||
#endif
|
#endif
|
||||||
device_ = SDL_CreateGPUDevice(PREFERRED, false, nullptr);
|
const char* preferred = preferred_driver_.empty() ? nullptr : preferred_driver_.c_str();
|
||||||
|
device_ = SDL_CreateGPUDevice(PREFERRED, false, preferred);
|
||||||
|
if (device_ == nullptr && preferred != nullptr) {
|
||||||
|
SDL_Log("SDL3GPUShader: driver '%s' not available, falling back to auto", preferred);
|
||||||
|
device_ = SDL_CreateGPUDevice(PREFERRED, false, nullptr);
|
||||||
|
}
|
||||||
if (device_ == nullptr) {
|
if (device_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: SDL_CreateGPUDevice failed: %s", SDL_GetError());
|
SDL_Log("SDL3GPUShader: SDL_CreateGPUDevice failed: %s", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SDL_Log("SDL3GPUShader: driver = %s", SDL_GetGPUDeviceDriver(device_));
|
driver_name_ = SDL_GetGPUDeviceDriver(device_);
|
||||||
|
SDL_Log("SDL3GPUShader: driver = %s", driver_name_.c_str());
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// 2. Claim window (una sola vez — no liberar hasta destroy())
|
// 2. Claim window (una sola vez — no liberar hasta destroy())
|
||||||
@@ -237,15 +306,15 @@ namespace Rendering {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// 3. Create scene texture (upload target + sampler source)
|
// 3. Create scene texture (upload target, always game resolution)
|
||||||
// Format: B8G8R8A8_UNORM matches SDL ARGB8888 byte layout on LE
|
// Format: B8G8R8A8_UNORM matches SDL ARGB8888 byte layout on LE
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
SDL_GPUTextureCreateInfo tex_info = {};
|
SDL_GPUTextureCreateInfo tex_info = {};
|
||||||
tex_info.type = SDL_GPU_TEXTURETYPE_2D;
|
tex_info.type = SDL_GPU_TEXTURETYPE_2D;
|
||||||
tex_info.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
|
tex_info.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
|
||||||
tex_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
|
tex_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
|
||||||
tex_info.width = static_cast<Uint32>(tex_width_);
|
tex_info.width = static_cast<Uint32>(game_width_);
|
||||||
tex_info.height = static_cast<Uint32>(tex_height_);
|
tex_info.height = static_cast<Uint32>(game_height_);
|
||||||
tex_info.layer_count_or_depth = 1;
|
tex_info.layer_count_or_depth = 1;
|
||||||
tex_info.num_levels = 1;
|
tex_info.num_levels = 1;
|
||||||
scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info);
|
scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info);
|
||||||
@@ -255,12 +324,15 @@ namespace Rendering {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scaled_texture_ se creará en el primer render() una vez conocido el zoom de ventana
|
||||||
|
ss_factor_ = 0;
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// 4. Create upload transfer buffer (CPU → GPU, size = w*h*4 bytes)
|
// 4. Create upload transfer buffer (CPU → GPU, always game resolution)
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
SDL_GPUTransferBufferCreateInfo tb_info = {};
|
SDL_GPUTransferBufferCreateInfo tb_info = {};
|
||||||
tb_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
|
tb_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
|
||||||
tb_info.size = static_cast<Uint32>(tex_width_ * tex_height_ * 4);
|
tb_info.size = static_cast<Uint32>(game_width_ * game_height_ * 4);
|
||||||
upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info);
|
upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info);
|
||||||
if (upload_buffer_ == nullptr) {
|
if (upload_buffer_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: failed to create upload buffer: %s", SDL_GetError());
|
SDL_Log("SDL3GPUShader: failed to create upload buffer: %s", SDL_GetError());
|
||||||
@@ -308,7 +380,7 @@ namespace Rendering {
|
|||||||
}
|
}
|
||||||
|
|
||||||
is_initialized_ = true;
|
is_initialized_ = true;
|
||||||
SDL_Log("SDL3GPUShader: initialized OK (%dx%d)", tex_width_, tex_height_);
|
SDL_Log("SDL3GPUShader: initialized OK — game %dx%d, oversample %d", game_width_, game_height_, oversample_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,6 +390,7 @@ namespace Rendering {
|
|||||||
auto SDL3GPUShader::createPipeline() -> bool {
|
auto SDL3GPUShader::createPipeline() -> bool {
|
||||||
const SDL_GPUTextureFormat SWAPCHAIN_FMT = SDL_GetGPUSwapchainTextureFormat(device_, window_);
|
const SDL_GPUTextureFormat SWAPCHAIN_FMT = SDL_GetGPUSwapchainTextureFormat(device_, window_);
|
||||||
|
|
||||||
|
// ---- PostFX pipeline (scene/scaled → swapchain) ----
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
SDL_GPUShader* vert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
|
SDL_GPUShader* vert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
|
||||||
SDL_GPUShader* frag = createShaderMSL(device_, POSTFX_FRAG_MSL, "postfx_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
|
SDL_GPUShader* frag = createShaderMSL(device_, POSTFX_FRAG_MSL, "postfx_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
|
||||||
@@ -357,9 +430,125 @@ namespace Rendering {
|
|||||||
SDL_ReleaseGPUShader(device_, frag);
|
SDL_ReleaseGPUShader(device_, frag);
|
||||||
|
|
||||||
if (pipeline_ == nullptr) {
|
if (pipeline_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: pipeline creation failed: %s", SDL_GetError());
|
SDL_Log("SDL3GPUShader: PostFX pipeline creation failed: %s", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Upscale pipeline (scene → scaled_texture_, nearest) ----
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SDL_GPUShader* uvert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
|
||||||
|
SDL_GPUShader* ufrag = createShaderMSL(device_, UPSCALE_FRAG_MSL, "upscale_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0);
|
||||||
|
#else
|
||||||
|
SDL_GPUShader* uvert = createShaderSPIRV(device_, kpostfx_vert_spv, kpostfx_vert_spv_size, "main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
|
||||||
|
SDL_GPUShader* ufrag = createShaderSPIRV(device_, kupscale_frag_spv, kupscale_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((uvert == nullptr) || (ufrag == nullptr)) {
|
||||||
|
SDL_Log("SDL3GPUShader: failed to compile upscale shaders");
|
||||||
|
if (uvert != nullptr) { SDL_ReleaseGPUShader(device_, uvert); }
|
||||||
|
if (ufrag != nullptr) { SDL_ReleaseGPUShader(device_, ufrag); }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GPUColorTargetDescription upscale_color_target = {};
|
||||||
|
upscale_color_target.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
|
||||||
|
upscale_color_target.blend_state = no_blend;
|
||||||
|
|
||||||
|
SDL_GPUGraphicsPipelineCreateInfo upscale_pipe_info = {};
|
||||||
|
upscale_pipe_info.vertex_shader = uvert;
|
||||||
|
upscale_pipe_info.fragment_shader = ufrag;
|
||||||
|
upscale_pipe_info.vertex_input_state = no_input;
|
||||||
|
upscale_pipe_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
|
||||||
|
upscale_pipe_info.target_info.num_color_targets = 1;
|
||||||
|
upscale_pipe_info.target_info.color_target_descriptions = &upscale_color_target;
|
||||||
|
|
||||||
|
upscale_pipeline_ = SDL_CreateGPUGraphicsPipeline(device_, &upscale_pipe_info);
|
||||||
|
|
||||||
|
SDL_ReleaseGPUShader(device_, uvert);
|
||||||
|
SDL_ReleaseGPUShader(device_, ufrag);
|
||||||
|
|
||||||
|
if (upscale_pipeline_ == nullptr) {
|
||||||
|
SDL_Log("SDL3GPUShader: upscale pipeline creation failed: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- PostFX offscreen pipeline (scaled_texture_ → postfx_texture_, B8G8R8A8) ----
|
||||||
|
// Mismos shaders que pipeline_ pero con formato de salida B8G8R8A8_UNORM para textura intermedia.
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SDL_GPUShader* ofvert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
|
||||||
|
SDL_GPUShader* offrag = createShaderMSL(device_, POSTFX_FRAG_MSL, "postfx_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
|
||||||
|
#else
|
||||||
|
SDL_GPUShader* ofvert = createShaderSPIRV(device_, kpostfx_vert_spv, kpostfx_vert_spv_size, "main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
|
||||||
|
SDL_GPUShader* offrag = createShaderSPIRV(device_, kpostfx_frag_spv, kpostfx_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((ofvert == nullptr) || (offrag == nullptr)) {
|
||||||
|
SDL_Log("SDL3GPUShader: failed to compile PostFX offscreen shaders");
|
||||||
|
if (ofvert != nullptr) { SDL_ReleaseGPUShader(device_, ofvert); }
|
||||||
|
if (offrag != nullptr) { SDL_ReleaseGPUShader(device_, offrag); }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GPUColorTargetDescription offscreen_color_target = {};
|
||||||
|
offscreen_color_target.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
|
||||||
|
offscreen_color_target.blend_state = no_blend;
|
||||||
|
|
||||||
|
SDL_GPUGraphicsPipelineCreateInfo offscreen_pipe_info = {};
|
||||||
|
offscreen_pipe_info.vertex_shader = ofvert;
|
||||||
|
offscreen_pipe_info.fragment_shader = offrag;
|
||||||
|
offscreen_pipe_info.vertex_input_state = no_input;
|
||||||
|
offscreen_pipe_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
|
||||||
|
offscreen_pipe_info.target_info.num_color_targets = 1;
|
||||||
|
offscreen_pipe_info.target_info.color_target_descriptions = &offscreen_color_target;
|
||||||
|
|
||||||
|
postfx_offscreen_pipeline_ = SDL_CreateGPUGraphicsPipeline(device_, &offscreen_pipe_info);
|
||||||
|
|
||||||
|
SDL_ReleaseGPUShader(device_, ofvert);
|
||||||
|
SDL_ReleaseGPUShader(device_, offrag);
|
||||||
|
|
||||||
|
if (postfx_offscreen_pipeline_ == nullptr) {
|
||||||
|
SDL_Log("SDL3GPUShader: PostFX offscreen pipeline creation failed: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Downscale pipeline (postfx_texture_ → swapchain, Lanczos) ----
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SDL_GPUShader* dvert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
|
||||||
|
SDL_GPUShader* dfrag = createShaderMSL(device_, DOWNSCALE_FRAG_MSL, "downscale_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
|
||||||
|
#else
|
||||||
|
SDL_GPUShader* dvert = createShaderSPIRV(device_, kpostfx_vert_spv, kpostfx_vert_spv_size, "main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0);
|
||||||
|
SDL_GPUShader* dfrag = createShaderSPIRV(device_, kdownscale_frag_spv, kdownscale_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((dvert == nullptr) || (dfrag == nullptr)) {
|
||||||
|
SDL_Log("SDL3GPUShader: failed to compile downscale shaders");
|
||||||
|
if (dvert != nullptr) { SDL_ReleaseGPUShader(device_, dvert); }
|
||||||
|
if (dfrag != nullptr) { SDL_ReleaseGPUShader(device_, dfrag); }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GPUColorTargetDescription downscale_color_target = {};
|
||||||
|
downscale_color_target.format = SWAPCHAIN_FMT;
|
||||||
|
downscale_color_target.blend_state = no_blend;
|
||||||
|
|
||||||
|
SDL_GPUGraphicsPipelineCreateInfo downscale_pipe_info = {};
|
||||||
|
downscale_pipe_info.vertex_shader = dvert;
|
||||||
|
downscale_pipe_info.fragment_shader = dfrag;
|
||||||
|
downscale_pipe_info.vertex_input_state = no_input;
|
||||||
|
downscale_pipe_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
|
||||||
|
downscale_pipe_info.target_info.num_color_targets = 1;
|
||||||
|
downscale_pipe_info.target_info.color_target_descriptions = &downscale_color_target;
|
||||||
|
|
||||||
|
downscale_pipeline_ = SDL_CreateGPUGraphicsPipeline(device_, &downscale_pipe_info);
|
||||||
|
|
||||||
|
SDL_ReleaseGPUShader(device_, dvert);
|
||||||
|
SDL_ReleaseGPUShader(device_, dfrag);
|
||||||
|
|
||||||
|
if (downscale_pipeline_ == nullptr) {
|
||||||
|
SDL_Log("SDL3GPUShader: downscale pipeline creation failed: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,43 +566,8 @@ namespace Rendering {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oversample_ <= 1) {
|
// Copia directa — el upscale lo hace la GPU en el primer render pass
|
||||||
// Path sin supersampling: copia directa
|
std::memcpy(mapped, pixels, static_cast<size_t>(width * height * 4));
|
||||||
std::memcpy(mapped, pixels, static_cast<size_t>(width * height * 4));
|
|
||||||
} else {
|
|
||||||
// Path con supersampling: expande cada pixel a OS×OS, oscurece última fila.
|
|
||||||
// Replica la fórmula del shader: mix(3.5, 0.42, scanline_strength).
|
|
||||||
auto* out = static_cast<Uint32*>(mapped);
|
|
||||||
const int OS = oversample_;
|
|
||||||
const float BRIGHT_MUL = 1.0F + (baked_scanline_strength_ * 2.5F); // rows 0..OS-2
|
|
||||||
const float DARK_MUL = 1.0F - (baked_scanline_strength_ * 0.58F); // row OS-1
|
|
||||||
|
|
||||||
for (int y = 0; y < height; ++y) {
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
const Uint32 SRC = pixels[y * width + x];
|
|
||||||
const Uint32 ALPHA = (SRC >> 24) & 0xFFU;
|
|
||||||
const auto FR = static_cast<float>((SRC >> 16) & 0xFFU);
|
|
||||||
const auto FG = static_cast<float>((SRC >> 8) & 0xFFU);
|
|
||||||
const auto FB = static_cast<float>( SRC & 0xFFU);
|
|
||||||
|
|
||||||
auto make_px = [ALPHA](float rv, float gv, float bv) -> Uint32 {
|
|
||||||
auto cl = [](float v) -> Uint32 { return static_cast<Uint32>(std::min(255.0F, v)); };
|
|
||||||
return (ALPHA << 24) | (cl(rv) << 16) | (cl(gv) << 8) | cl(bv);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Uint32 BRIGHT = make_px(FR * BRIGHT_MUL, FG * BRIGHT_MUL, FB * BRIGHT_MUL);
|
|
||||||
const Uint32 DARK = make_px(FR * DARK_MUL, FG * DARK_MUL, FB * DARK_MUL);
|
|
||||||
|
|
||||||
for (int dy = 0; dy < OS; ++dy) {
|
|
||||||
const Uint32 OUT_PX = (dy == OS - 1) ? DARK : BRIGHT;
|
|
||||||
const int DST_Y = (y * OS) + dy;
|
|
||||||
for (int dx = 0; dx < OS; ++dx) {
|
|
||||||
out[DST_Y * (width * OS) + (x * OS) + dx] = OUT_PX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_UnmapGPUTransferBuffer(device_, upload_buffer_);
|
SDL_UnmapGPUTransferBuffer(device_, upload_buffer_);
|
||||||
}
|
}
|
||||||
@@ -424,31 +578,64 @@ namespace Rendering {
|
|||||||
void SDL3GPUShader::render() {
|
void SDL3GPUShader::render() {
|
||||||
if (!is_initialized_) { return; }
|
if (!is_initialized_) { return; }
|
||||||
|
|
||||||
|
// Paso 0: si SS activo, calcular el factor necesario según el zoom actual y recrear si cambió.
|
||||||
|
// Factor = primer múltiplo de 3 >= zoom (mín 3). Se recrea solo en saltos de factor.
|
||||||
|
if (oversample_ > 1 && game_height_ > 0) {
|
||||||
|
int win_w = 0;
|
||||||
|
int win_h = 0;
|
||||||
|
SDL_GetWindowSizeInPixels(window_, &win_w, &win_h);
|
||||||
|
const float ZOOM = static_cast<float>(win_h) / static_cast<float>(game_height_);
|
||||||
|
const int NEED_FACTOR = calcSsFactor(ZOOM);
|
||||||
|
if (NEED_FACTOR != ss_factor_) {
|
||||||
|
SDL_WaitForGPUIdle(device_);
|
||||||
|
recreateScaledTexture(NEED_FACTOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(device_);
|
SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(device_);
|
||||||
if (cmd == nullptr) {
|
if (cmd == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: SDL_AcquireGPUCommandBuffer failed: %s", SDL_GetError());
|
SDL_Log("SDL3GPUShader: SDL_AcquireGPUCommandBuffer failed: %s", SDL_GetError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Copy pass: transfer buffer → scene texture ----
|
// ---- Copy pass: transfer buffer → scene texture (siempre a resolución del juego) ----
|
||||||
SDL_GPUCopyPass* copy = SDL_BeginGPUCopyPass(cmd);
|
SDL_GPUCopyPass* copy = SDL_BeginGPUCopyPass(cmd);
|
||||||
if (copy != nullptr) {
|
if (copy != nullptr) {
|
||||||
SDL_GPUTextureTransferInfo src = {};
|
SDL_GPUTextureTransferInfo src = {};
|
||||||
src.transfer_buffer = upload_buffer_;
|
src.transfer_buffer = upload_buffer_;
|
||||||
src.offset = 0;
|
src.offset = 0;
|
||||||
src.pixels_per_row = static_cast<Uint32>(tex_width_);
|
src.pixels_per_row = static_cast<Uint32>(game_width_);
|
||||||
src.rows_per_layer = static_cast<Uint32>(tex_height_);
|
src.rows_per_layer = static_cast<Uint32>(game_height_);
|
||||||
|
|
||||||
SDL_GPUTextureRegion dst = {};
|
SDL_GPUTextureRegion dst = {};
|
||||||
dst.texture = scene_texture_;
|
dst.texture = scene_texture_;
|
||||||
dst.w = static_cast<Uint32>(tex_width_);
|
dst.w = static_cast<Uint32>(game_width_);
|
||||||
dst.h = static_cast<Uint32>(tex_height_);
|
dst.h = static_cast<Uint32>(game_height_);
|
||||||
dst.d = 1;
|
dst.d = 1;
|
||||||
|
|
||||||
SDL_UploadToGPUTexture(copy, &src, &dst, false);
|
SDL_UploadToGPUTexture(copy, &src, &dst, false);
|
||||||
SDL_EndGPUCopyPass(copy);
|
SDL_EndGPUCopyPass(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Upscale pass: scene_texture_ → scaled_texture_ (NEAREST o LINEAR según linear_upscale_) ----
|
||||||
|
if (oversample_ > 1 && scaled_texture_ != nullptr && upscale_pipeline_ != nullptr) {
|
||||||
|
SDL_GPUColorTargetInfo upscale_target = {};
|
||||||
|
upscale_target.texture = scaled_texture_;
|
||||||
|
upscale_target.load_op = SDL_GPU_LOADOP_DONT_CARE;
|
||||||
|
upscale_target.store_op = SDL_GPU_STOREOP_STORE;
|
||||||
|
|
||||||
|
SDL_GPURenderPass* upass = SDL_BeginGPURenderPass(cmd, &upscale_target, 1, nullptr);
|
||||||
|
if (upass != nullptr) {
|
||||||
|
SDL_BindGPUGraphicsPipeline(upass, upscale_pipeline_);
|
||||||
|
SDL_GPUTextureSamplerBinding ubinding = {};
|
||||||
|
ubinding.texture = scene_texture_;
|
||||||
|
ubinding.sampler = (linear_upscale_ && linear_sampler_ != nullptr) ? linear_sampler_ : sampler_;
|
||||||
|
SDL_BindGPUFragmentSamplers(upass, 0, &ubinding, 1);
|
||||||
|
SDL_DrawGPUPrimitives(upass, 3, 1, 0, 0);
|
||||||
|
SDL_EndGPURenderPass(upass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Acquire swapchain texture ----
|
// ---- Acquire swapchain texture ----
|
||||||
SDL_GPUTexture* swapchain = nullptr;
|
SDL_GPUTexture* swapchain = nullptr;
|
||||||
Uint32 sw = 0;
|
Uint32 sw = 0;
|
||||||
@@ -464,62 +651,113 @@ namespace Rendering {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Render pass: PostFX → swapchain ----
|
// ---- Calcular viewport (dimensiones lógicas del canvas, no de textura GPU) ----
|
||||||
SDL_GPUColorTargetInfo color_target = {};
|
float vx = 0.0F;
|
||||||
color_target.texture = swapchain;
|
float vy = 0.0F;
|
||||||
color_target.load_op = SDL_GPU_LOADOP_CLEAR;
|
float vw = 0.0F;
|
||||||
color_target.store_op = SDL_GPU_STOREOP_STORE;
|
float vh = 0.0F;
|
||||||
color_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
|
if (integer_scale_) {
|
||||||
|
const int SCALE = std::max(1, std::min(static_cast<int>(sw) / game_width_, static_cast<int>(sh) / game_height_));
|
||||||
|
vw = static_cast<float>(game_width_ * SCALE);
|
||||||
|
vh = static_cast<float>(game_height_ * SCALE);
|
||||||
|
} else {
|
||||||
|
const float SCALE = std::min(
|
||||||
|
static_cast<float>(sw) / static_cast<float>(game_width_),
|
||||||
|
static_cast<float>(sh) / static_cast<float>(game_height_));
|
||||||
|
vw = static_cast<float>(game_width_) * SCALE;
|
||||||
|
vh = static_cast<float>(game_height_) * SCALE;
|
||||||
|
}
|
||||||
|
vx = std::floor((static_cast<float>(sw) - vw) * 0.5F);
|
||||||
|
vy = std::floor((static_cast<float>(sh) - vh) * 0.5F);
|
||||||
|
|
||||||
SDL_GPURenderPass* pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, nullptr);
|
// pixel_scale: subpíxeles por pixel lógico.
|
||||||
if (pass != nullptr) {
|
// Sin SS: vh/game_height (zoom de ventana).
|
||||||
SDL_BindGPUGraphicsPipeline(pass, pipeline_);
|
// Con SS: ss_factor_ exacto (3, 6, 9...).
|
||||||
|
uniforms_.pixel_scale = (oversample_ > 1 && ss_factor_ > 0)
|
||||||
|
? static_cast<float>(ss_factor_)
|
||||||
|
: ((game_height_ > 0) ? (vh / static_cast<float>(game_height_)) : 1.0F);
|
||||||
|
uniforms_.time = static_cast<float>(SDL_GetTicks()) / 1000.0F;
|
||||||
|
uniforms_.oversample = (oversample_ > 1 && ss_factor_ > 0)
|
||||||
|
? static_cast<float>(ss_factor_)
|
||||||
|
: 1.0F;
|
||||||
|
|
||||||
// Calcular viewport usando las dimensiones lógicas del canvas (game_width_/height_),
|
// ---- Determinar si usar el path Lanczos (SS activo + algo seleccionado) ----
|
||||||
// no las de la textura GPU (que pueden ser game×3 con supersampling).
|
const bool USE_LANCZOS = (oversample_ > 1 && downscale_algo_ > 0 && scaled_texture_ != nullptr && postfx_texture_ != nullptr && postfx_offscreen_pipeline_ != nullptr && downscale_pipeline_ != nullptr);
|
||||||
// El GPU escala la textura para cubrir el viewport independientemente de su resolución.
|
|
||||||
float vx = 0.0F;
|
if (USE_LANCZOS) {
|
||||||
float vy = 0.0F;
|
// ---- Pass A: PostFX → postfx_texture_ (full scaled size, sin viewport) ----
|
||||||
float vw = 0.0F;
|
SDL_GPUColorTargetInfo postfx_target = {};
|
||||||
float vh = 0.0F;
|
postfx_target.texture = postfx_texture_;
|
||||||
if (integer_scale_) {
|
postfx_target.load_op = SDL_GPU_LOADOP_CLEAR;
|
||||||
const int SCALE = std::max(1, std::min(static_cast<int>(sw) / game_width_, static_cast<int>(sh) / game_height_));
|
postfx_target.store_op = SDL_GPU_STOREOP_STORE;
|
||||||
vw = static_cast<float>(game_width_ * SCALE);
|
postfx_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
|
||||||
vh = static_cast<float>(game_height_ * SCALE);
|
|
||||||
} else {
|
SDL_GPURenderPass* ppass = SDL_BeginGPURenderPass(cmd, &postfx_target, 1, nullptr);
|
||||||
const float SCALE = std::min(
|
if (ppass != nullptr) {
|
||||||
static_cast<float>(sw) / static_cast<float>(game_width_),
|
SDL_BindGPUGraphicsPipeline(ppass, postfx_offscreen_pipeline_);
|
||||||
static_cast<float>(sh) / static_cast<float>(game_height_));
|
SDL_GPUTextureSamplerBinding pbinding = {};
|
||||||
vw = static_cast<float>(game_width_) * SCALE;
|
pbinding.texture = scaled_texture_;
|
||||||
vh = static_cast<float>(game_height_) * SCALE;
|
pbinding.sampler = sampler_; // NEAREST: 1:1 pass, efectos calculados analíticamente
|
||||||
|
SDL_BindGPUFragmentSamplers(ppass, 0, &pbinding, 1);
|
||||||
|
SDL_PushGPUFragmentUniformData(cmd, 0, &uniforms_, sizeof(PostFXUniforms));
|
||||||
|
SDL_DrawGPUPrimitives(ppass, 3, 1, 0, 0);
|
||||||
|
SDL_EndGPURenderPass(ppass);
|
||||||
}
|
}
|
||||||
vx = std::floor((static_cast<float>(sw) - vw) * 0.5F);
|
|
||||||
vy = std::floor((static_cast<float>(sh) - vh) * 0.5F);
|
|
||||||
SDL_GPUViewport vp = {vx, vy, vw, vh, 0.0F, 1.0F};
|
|
||||||
SDL_SetGPUViewport(pass, &vp);
|
|
||||||
|
|
||||||
// pixel_scale: pixels físicos por pixel lógico de juego (para scanlines sin SS).
|
// ---- Pass B: Downscale Lanczos → swapchain (con viewport/letterbox) ----
|
||||||
// Con SS las scanlines están horneadas en CPU → scanline_strength=0 → no se usa.
|
SDL_GPUColorTargetInfo ds_target = {};
|
||||||
uniforms_.pixel_scale = (game_height_ > 0)
|
ds_target.texture = swapchain;
|
||||||
? (vh / static_cast<float>(game_height_))
|
ds_target.load_op = SDL_GPU_LOADOP_CLEAR;
|
||||||
: 1.0F;
|
ds_target.store_op = SDL_GPU_STOREOP_STORE;
|
||||||
uniforms_.time = static_cast<float>(SDL_GetTicks()) / 1000.0F;
|
ds_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
|
||||||
uniforms_.oversample = static_cast<float>(oversample_);
|
|
||||||
|
|
||||||
// Con supersampling usamos LINEAR para que el escalado a zooms no-múltiplo-de-3
|
SDL_GPURenderPass* dpass = SDL_BeginGPURenderPass(cmd, &ds_target, 1, nullptr);
|
||||||
// promedia correctamente las filas de scanline horneadas en CPU.
|
if (dpass != nullptr) {
|
||||||
SDL_GPUSampler* active_sampler = (oversample_ > 1 && linear_sampler_ != nullptr)
|
SDL_BindGPUGraphicsPipeline(dpass, downscale_pipeline_);
|
||||||
? linear_sampler_ : sampler_;
|
SDL_GPUViewport vp = {.x = vx, .y = vy, .w = vw, .h = vh, .min_depth = 0.0F, .max_depth = 1.0F};
|
||||||
|
SDL_SetGPUViewport(dpass, &vp);
|
||||||
|
SDL_GPUTextureSamplerBinding dbinding = {};
|
||||||
|
dbinding.texture = postfx_texture_;
|
||||||
|
dbinding.sampler = sampler_; // NEAREST: el shader Lanczos hace su propia interpolación
|
||||||
|
SDL_BindGPUFragmentSamplers(dpass, 0, &dbinding, 1);
|
||||||
|
// algorithm: 0=Lanczos2, 1=Lanczos3 (downscale_algo_ es 1-based)
|
||||||
|
DownscaleUniforms downscale_u = {.algorithm = downscale_algo_ - 1, .pad0 = 0.0F, .pad1 = 0.0F, .pad2 = 0.0F};
|
||||||
|
SDL_PushGPUFragmentUniformData(cmd, 0, &downscale_u, sizeof(DownscaleUniforms));
|
||||||
|
SDL_DrawGPUPrimitives(dpass, 3, 1, 0, 0);
|
||||||
|
SDL_EndGPURenderPass(dpass);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ---- Render pass: PostFX → swapchain directamente (bilinear, comportamiento original) ----
|
||||||
|
SDL_GPUColorTargetInfo color_target = {};
|
||||||
|
color_target.texture = swapchain;
|
||||||
|
color_target.load_op = SDL_GPU_LOADOP_CLEAR;
|
||||||
|
color_target.store_op = SDL_GPU_STOREOP_STORE;
|
||||||
|
color_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
|
||||||
|
|
||||||
SDL_GPUTextureSamplerBinding binding = {};
|
SDL_GPURenderPass* pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, nullptr);
|
||||||
binding.texture = scene_texture_;
|
if (pass != nullptr) {
|
||||||
binding.sampler = active_sampler;
|
SDL_BindGPUGraphicsPipeline(pass, pipeline_);
|
||||||
SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1);
|
SDL_GPUViewport vp = {.x = vx, .y = vy, .w = vw, .h = vh, .min_depth = 0.0F, .max_depth = 1.0F};
|
||||||
|
SDL_SetGPUViewport(pass, &vp);
|
||||||
|
|
||||||
SDL_PushGPUFragmentUniformData(cmd, 0, &uniforms_, sizeof(PostFXUniforms));
|
// Con SS: leer de scaled_texture_ con LINEAR; sin SS: scene_texture_ con NEAREST.
|
||||||
|
SDL_GPUTexture* input_texture = (oversample_ > 1 && scaled_texture_ != nullptr)
|
||||||
|
? scaled_texture_
|
||||||
|
: scene_texture_;
|
||||||
|
SDL_GPUSampler* active_sampler = (oversample_ > 1 && linear_sampler_ != nullptr)
|
||||||
|
? linear_sampler_
|
||||||
|
: sampler_;
|
||||||
|
|
||||||
SDL_DrawGPUPrimitives(pass, 3, 1, 0, 0);
|
SDL_GPUTextureSamplerBinding binding = {};
|
||||||
SDL_EndGPURenderPass(pass);
|
binding.texture = input_texture;
|
||||||
|
binding.sampler = active_sampler;
|
||||||
|
SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1);
|
||||||
|
|
||||||
|
SDL_PushGPUFragmentUniformData(cmd, 0, &uniforms_, sizeof(PostFXUniforms));
|
||||||
|
|
||||||
|
SDL_DrawGPUPrimitives(pass, 3, 1, 0, 0);
|
||||||
|
SDL_EndGPURenderPass(pass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SubmitGPUCommandBuffer(cmd);
|
SDL_SubmitGPUCommandBuffer(cmd);
|
||||||
@@ -538,10 +776,31 @@ namespace Rendering {
|
|||||||
SDL_ReleaseGPUGraphicsPipeline(device_, pipeline_);
|
SDL_ReleaseGPUGraphicsPipeline(device_, pipeline_);
|
||||||
pipeline_ = nullptr;
|
pipeline_ = nullptr;
|
||||||
}
|
}
|
||||||
|
if (postfx_offscreen_pipeline_ != nullptr) {
|
||||||
|
SDL_ReleaseGPUGraphicsPipeline(device_, postfx_offscreen_pipeline_);
|
||||||
|
postfx_offscreen_pipeline_ = nullptr;
|
||||||
|
}
|
||||||
|
if (upscale_pipeline_ != nullptr) {
|
||||||
|
SDL_ReleaseGPUGraphicsPipeline(device_, upscale_pipeline_);
|
||||||
|
upscale_pipeline_ = nullptr;
|
||||||
|
}
|
||||||
|
if (downscale_pipeline_ != nullptr) {
|
||||||
|
SDL_ReleaseGPUGraphicsPipeline(device_, downscale_pipeline_);
|
||||||
|
downscale_pipeline_ = nullptr;
|
||||||
|
}
|
||||||
if (scene_texture_ != nullptr) {
|
if (scene_texture_ != nullptr) {
|
||||||
SDL_ReleaseGPUTexture(device_, scene_texture_);
|
SDL_ReleaseGPUTexture(device_, scene_texture_);
|
||||||
scene_texture_ = nullptr;
|
scene_texture_ = nullptr;
|
||||||
}
|
}
|
||||||
|
if (scaled_texture_ != nullptr) {
|
||||||
|
SDL_ReleaseGPUTexture(device_, scaled_texture_);
|
||||||
|
scaled_texture_ = nullptr;
|
||||||
|
}
|
||||||
|
if (postfx_texture_ != nullptr) {
|
||||||
|
SDL_ReleaseGPUTexture(device_, postfx_texture_);
|
||||||
|
postfx_texture_ = nullptr;
|
||||||
|
}
|
||||||
|
ss_factor_ = 0;
|
||||||
if (upload_buffer_ != nullptr) {
|
if (upload_buffer_ != nullptr) {
|
||||||
SDL_ReleaseGPUTransferBuffer(device_, upload_buffer_);
|
SDL_ReleaseGPUTransferBuffer(device_, upload_buffer_);
|
||||||
upload_buffer_ = nullptr;
|
upload_buffer_ = nullptr;
|
||||||
@@ -598,7 +857,7 @@ namespace Rendering {
|
|||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SDL3GPUShader::createShaderSPIRV(SDL_GPUDevice* device,
|
auto SDL3GPUShader::createShaderSPIRV(SDL_GPUDevice* device, // NOLINT(readability-convert-member-functions-to-static)
|
||||||
const uint8_t* spv_code,
|
const uint8_t* spv_code,
|
||||||
size_t spv_size,
|
size_t spv_size,
|
||||||
const char* entrypoint,
|
const char* entrypoint,
|
||||||
@@ -622,16 +881,15 @@ namespace Rendering {
|
|||||||
|
|
||||||
void SDL3GPUShader::setPostFXParams(const PostFXParams& p) {
|
void SDL3GPUShader::setPostFXParams(const PostFXParams& p) {
|
||||||
uniforms_.vignette_strength = p.vignette;
|
uniforms_.vignette_strength = p.vignette;
|
||||||
uniforms_.chroma_strength = p.chroma;
|
uniforms_.chroma_strength = p.chroma;
|
||||||
uniforms_.mask_strength = p.mask;
|
uniforms_.mask_strength = p.mask;
|
||||||
uniforms_.gamma_strength = p.gamma;
|
uniforms_.gamma_strength = p.gamma;
|
||||||
uniforms_.curvature = p.curvature;
|
uniforms_.curvature = p.curvature;
|
||||||
uniforms_.bleeding = p.bleeding;
|
uniforms_.bleeding = p.bleeding;
|
||||||
|
uniforms_.flicker = p.flicker;
|
||||||
|
|
||||||
// Con supersampling las scanlines se hornean en CPU (uploadPixels).
|
// Las scanlines siempre las aplica el shader PostFX en GPU.
|
||||||
// El shader recibe strength=0 para no aplicarlas de nuevo en GPU.
|
uniforms_.scanline_strength = p.scanlines;
|
||||||
baked_scanline_strength_ = p.scanlines;
|
|
||||||
uniforms_.scanline_strength = (oversample_ > 1) ? 0.0F : p.scanlines;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDL3GPUShader::setVSync(bool vsync) {
|
void SDL3GPUShader::setVSync(bool vsync) {
|
||||||
@@ -658,9 +916,22 @@ namespace Rendering {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SDL3GPUShader::setLinearUpscale(bool linear) {
|
||||||
|
linear_upscale_ = linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDL3GPUShader::setDownscaleAlgo(int algo) {
|
||||||
|
downscale_algo_ = std::max(0, std::min(algo, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SDL3GPUShader::getSsTextureSize() const -> std::pair<int, int> {
|
||||||
|
if (ss_factor_ <= 1) { return {0, 0}; }
|
||||||
|
return {game_width_ * ss_factor_, game_height_ * ss_factor_};
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// reinitTexturesAndBuffer — recrea scene_texture_ y upload_buffer_ con el
|
// reinitTexturesAndBuffer — recrea scene_texture_, scaled_texture_ y
|
||||||
// tamaño actual (game × oversample_). No toca pipeline ni samplers.
|
// upload_buffer_ con el factor oversample_ actual. No toca pipelines ni samplers.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
auto SDL3GPUShader::reinitTexturesAndBuffer() -> bool {
|
auto SDL3GPUShader::reinitTexturesAndBuffer() -> bool {
|
||||||
if (device_ == nullptr) { return false; }
|
if (device_ == nullptr) { return false; }
|
||||||
@@ -670,22 +941,28 @@ namespace Rendering {
|
|||||||
SDL_ReleaseGPUTexture(device_, scene_texture_);
|
SDL_ReleaseGPUTexture(device_, scene_texture_);
|
||||||
scene_texture_ = nullptr;
|
scene_texture_ = nullptr;
|
||||||
}
|
}
|
||||||
|
// scaled_texture_ se libera aquí; se recreará en el primer render() con el factor correcto
|
||||||
|
if (scaled_texture_ != nullptr) {
|
||||||
|
SDL_ReleaseGPUTexture(device_, scaled_texture_);
|
||||||
|
scaled_texture_ = nullptr;
|
||||||
|
}
|
||||||
|
ss_factor_ = 0;
|
||||||
|
|
||||||
if (upload_buffer_ != nullptr) {
|
if (upload_buffer_ != nullptr) {
|
||||||
SDL_ReleaseGPUTransferBuffer(device_, upload_buffer_);
|
SDL_ReleaseGPUTransferBuffer(device_, upload_buffer_);
|
||||||
upload_buffer_ = nullptr;
|
upload_buffer_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
tex_width_ = game_width_ * oversample_;
|
uniforms_.screen_height = static_cast<float>(game_height_);
|
||||||
tex_height_ = game_height_ * oversample_;
|
uniforms_.oversample = static_cast<float>(oversample_);
|
||||||
uniforms_.screen_height = static_cast<float>(tex_height_);
|
|
||||||
uniforms_.oversample = static_cast<float>(oversample_);
|
|
||||||
|
|
||||||
|
// scene_texture_: siempre a resolución del juego
|
||||||
SDL_GPUTextureCreateInfo tex_info = {};
|
SDL_GPUTextureCreateInfo tex_info = {};
|
||||||
tex_info.type = SDL_GPU_TEXTURETYPE_2D;
|
tex_info.type = SDL_GPU_TEXTURETYPE_2D;
|
||||||
tex_info.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
|
tex_info.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
|
||||||
tex_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
|
tex_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
|
||||||
tex_info.width = static_cast<Uint32>(tex_width_);
|
tex_info.width = static_cast<Uint32>(game_width_);
|
||||||
tex_info.height = static_cast<Uint32>(tex_height_);
|
tex_info.height = static_cast<Uint32>(game_height_);
|
||||||
tex_info.layer_count_or_depth = 1;
|
tex_info.layer_count_or_depth = 1;
|
||||||
tex_info.num_levels = 1;
|
tex_info.num_levels = 1;
|
||||||
scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info);
|
scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info);
|
||||||
@@ -694,9 +971,10 @@ namespace Rendering {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// upload_buffer_: siempre a resolución del juego
|
||||||
SDL_GPUTransferBufferCreateInfo tb_info = {};
|
SDL_GPUTransferBufferCreateInfo tb_info = {};
|
||||||
tb_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
|
tb_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
|
||||||
tb_info.size = static_cast<Uint32>(tex_width_ * tex_height_ * 4);
|
tb_info.size = static_cast<Uint32>(game_width_ * game_height_ * 4);
|
||||||
upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info);
|
upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info);
|
||||||
if (upload_buffer_ == nullptr) {
|
if (upload_buffer_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: reinit — failed to create upload buffer: %s", SDL_GetError());
|
SDL_Log("SDL3GPUShader: reinit — failed to create upload buffer: %s", SDL_GetError());
|
||||||
@@ -705,7 +983,74 @@ namespace Rendering {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Log("SDL3GPUShader: oversample %d → texture %dx%d", oversample_, tex_width_, tex_height_);
|
SDL_Log("SDL3GPUShader: reinit — scene %dx%d, SS %s (scaled se creará en render)",
|
||||||
|
game_width_,
|
||||||
|
game_height_,
|
||||||
|
oversample_ > 1 ? "on" : "off");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// calcSsFactor — primer múltiplo de 3 >= zoom, mínimo 3.
|
||||||
|
// Ejemplos: zoom 1,2,3 → 3; zoom 4,5,6 → 6; zoom 4.4 → 6; zoom 7,8,9 → 9.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
auto SDL3GPUShader::calcSsFactor(float zoom) -> int {
|
||||||
|
const int MULTIPLE = 3;
|
||||||
|
const int n = static_cast<int>(std::ceil(zoom / static_cast<float>(MULTIPLE)));
|
||||||
|
return std::max(1, n) * MULTIPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// recreateScaledTexture — libera y recrea scaled_texture_ para el factor dado.
|
||||||
|
// Llamar solo cuando device_ no esté ejecutando comandos (SDL_WaitForGPUIdle previo).
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
auto SDL3GPUShader::recreateScaledTexture(int factor) -> bool {
|
||||||
|
if (scaled_texture_ != nullptr) {
|
||||||
|
SDL_ReleaseGPUTexture(device_, scaled_texture_);
|
||||||
|
scaled_texture_ = nullptr;
|
||||||
|
}
|
||||||
|
if (postfx_texture_ != nullptr) {
|
||||||
|
SDL_ReleaseGPUTexture(device_, postfx_texture_);
|
||||||
|
postfx_texture_ = nullptr;
|
||||||
|
}
|
||||||
|
ss_factor_ = 0;
|
||||||
|
|
||||||
|
const int W = game_width_ * factor;
|
||||||
|
const int H = game_height_ * factor;
|
||||||
|
|
||||||
|
SDL_GPUTextureCreateInfo info = {};
|
||||||
|
info.type = SDL_GPU_TEXTURETYPE_2D;
|
||||||
|
info.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
|
||||||
|
info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
|
||||||
|
info.width = static_cast<Uint32>(W);
|
||||||
|
info.height = static_cast<Uint32>(H);
|
||||||
|
info.layer_count_or_depth = 1;
|
||||||
|
info.num_levels = 1;
|
||||||
|
|
||||||
|
scaled_texture_ = SDL_CreateGPUTexture(device_, &info);
|
||||||
|
if (scaled_texture_ == nullptr) {
|
||||||
|
SDL_Log("SDL3GPUShader: failed to create scaled texture %dx%d (factor %d): %s",
|
||||||
|
W,
|
||||||
|
H,
|
||||||
|
factor,
|
||||||
|
SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
postfx_texture_ = SDL_CreateGPUTexture(device_, &info);
|
||||||
|
if (postfx_texture_ == nullptr) {
|
||||||
|
SDL_Log("SDL3GPUShader: failed to create postfx texture %dx%d (factor %d): %s",
|
||||||
|
W,
|
||||||
|
H,
|
||||||
|
factor,
|
||||||
|
SDL_GetError());
|
||||||
|
SDL_ReleaseGPUTexture(device_, scaled_texture_);
|
||||||
|
scaled_texture_ = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss_factor_ = factor;
|
||||||
|
SDL_Log("SDL3GPUShader: scaled+postfx textures %dx%d (factor %d×)", W, H, factor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,16 @@ 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)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 +56,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 +76,15 @@ 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;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static auto createShaderMSL(SDL_GPUDevice* device,
|
static auto createShaderMSL(SDL_GPUDevice* device,
|
||||||
const char* msl_source,
|
const char* msl_source,
|
||||||
@@ -80,27 +102,36 @@ 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 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)
|
||||||
|
|
||||||
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* 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};
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
633
source/core/rendering/sdl3gpu/upscale_frag_spv.h
Normal file
633
source/core/rendering/sdl3gpu/upscale_frag_spv.h
Normal file
@@ -0,0 +1,633 @@
|
|||||||
|
#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;
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace Rendering {
|
namespace Rendering {
|
||||||
|
|
||||||
@@ -11,13 +12,14 @@ namespace Rendering {
|
|||||||
* 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
|
||||||
*/
|
*/
|
||||||
struct PostFXParams {
|
struct PostFXParams {
|
||||||
float vignette = 0.0F; // Intensidad de la viñeta
|
float vignette = 0.0F; // Intensidad de la viñeta
|
||||||
float scanlines = 0.0F; // Intensidad de las scanlines
|
float scanlines = 0.0F; // Intensidad de las scanlines
|
||||||
float chroma = 0.0F; // Aberración cromática
|
float chroma = 0.0F; // Aberración cromática
|
||||||
float mask = 0.0F; // Máscara de fósforo RGB
|
float mask = 0.0F; // Máscara de fósforo RGB
|
||||||
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
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,11 +91,44 @@ 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*/) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Rendering
|
} // namespace Rendering
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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)
|
||||||
@@ -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;
|
||||||
@@ -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};
|
||||||
@@ -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_);
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
};
|
};
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,11 +232,11 @@ 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);
|
||||||
const auto STROKED = ((flags & STROKE_FLAG) == STROKE_FLAG);
|
const auto STROKED = ((flags & STROKE_FLAG) == STROKE_FLAG);
|
||||||
|
|
||||||
if (CENTERED) {
|
if (CENTERED) {
|
||||||
x -= (Text::length(text, kerning) / 2);
|
x -= (Text::length(text, kerning) / 2);
|
||||||
@@ -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; }
|
||||||
@@ -280,9 +295,9 @@ auto Text::getGlyphClip(uint32_t codepoint) const -> SDL_FRect {
|
|||||||
if (it == offset_.end()) { it = offset_.find('?'); }
|
if (it == offset_.end()) { it = offset_.find('?'); }
|
||||||
if (it == offset_.end()) { return {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; }
|
if (it == offset_.end()) { return {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; }
|
||||||
return {.x = static_cast<float>(it->second.x),
|
return {.x = static_cast<float>(it->second.x),
|
||||||
.y = static_cast<float>(it->second.y),
|
.y = static_cast<float>(it->second.y),
|
||||||
.w = static_cast<float>(box_width_),
|
.w = static_cast<float>(box_width_),
|
||||||
.h = static_cast<float>(box_height_)};
|
.h = static_cast<float>(box_height_)};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Devuelve el tamaño de la caja de cada caracter
|
// Devuelve el tamaño de la caja de cada caracter
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <memory> // Para shared_ptr, unique_ptr
|
#include <memory> // Para shared_ptr, unique_ptr
|
||||||
#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
|
||||||
class Text {
|
class Text {
|
||||||
@@ -18,11 +18,11 @@ class Text {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
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
|
||||||
int box_height{0}; // Altura de la caja de cada caracter en el png
|
int box_height{0}; // Altura de la caja de cada caracter en el png
|
||||||
int columns{16}; // Número de columnas en el bitmap
|
int columns{16}; // Número de columnas en el bitmap
|
||||||
int cell_spacing{0}; // Píxeles de separación entre columnas (y borde izquierdo/superior)
|
int cell_spacing{0}; // Píxeles de separación entre columnas (y borde izquierdo/superior)
|
||||||
int row_spacing{0}; // Píxeles de separación entre filas (si difiere de cell_spacing)
|
int row_spacing{0}; // Píxeles de separación entre filas (si difiere de cell_spacing)
|
||||||
std::unordered_map<uint32_t, Offset> offset; // Posición y ancho de cada glifo (clave: codepoint Unicode)
|
std::unordered_map<uint32_t, Offset> offset; // Posición y ancho de cada glifo (clave: codepoint Unicode)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -48,11 +48,11 @@ class Text {
|
|||||||
auto writeToSurface(const std::string& text, int zoom = 1, int kerning = 1) -> std::shared_ptr<Surface>; // Escribe el texto en una textura
|
auto writeToSurface(const std::string& text, int zoom = 1, int kerning = 1) -> std::shared_ptr<Surface>; // Escribe el texto en una textura
|
||||||
auto writeDXToSurface(Uint8 flags, const std::string& text, int kerning = 1, Uint8 text_color = Uint8(), Uint8 shadow_distance = 1, Uint8 shadow_color = Uint8(), int lenght = -1) -> std::shared_ptr<Surface>; // Escribe el texto con extras en una textura
|
auto writeDXToSurface(Uint8 flags, const std::string& text, int kerning = 1, Uint8 text_color = Uint8(), Uint8 shadow_distance = 1, Uint8 shadow_color = Uint8(), int lenght = -1) -> std::shared_ptr<Surface>; // Escribe el texto con extras en una textura
|
||||||
|
|
||||||
[[nodiscard]] auto length(const std::string& text, int kerning = 1) const -> int; // Obtiene la longitud en pixels de una cadena
|
[[nodiscard]] auto length(const std::string& text, int kerning = 1) const -> int; // Obtiene la longitud en pixels de una cadena
|
||||||
[[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,11 +62,11 @@ 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
|
||||||
int box_height_ = 0; // Altura de la caja de cada caracter en el png
|
int box_height_ = 0; // Altura de la caja de cada caracter en el png
|
||||||
bool fixed_width_ = false; // Indica si el texto se ha de escribir con longitud fija
|
bool fixed_width_ = false; // Indica si el texto se ha de escribir con longitud fija
|
||||||
std::unordered_map<uint32_t, Offset> offset_; // Posición y ancho de cada glifo (clave: codepoint Unicode)
|
std::unordered_map<uint32_t, Offset> offset_; // Posición y ancho de cada glifo (clave: codepoint Unicode)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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_; }
|
|
||||||
@@ -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
|
|
||||||
};
|
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 "";
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,118 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
} 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;
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
#endif // _DEBUG
|
#endif // _DEBUG
|
||||||
@@ -4,12 +4,20 @@
|
|||||||
|
|
||||||
#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
|
||||||
|
|
||||||
// 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 +28,22 @@ 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
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Debug* debug; // [SINGLETON] Objeto privado
|
static Debug* debug; // [SINGLETON] Objeto privado
|
||||||
|
|
||||||
@@ -34,11 +51,14 @@ 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
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _DEBUG
|
#endif // _DEBUG
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -120,11 +122,11 @@ 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();
|
||||||
|
|
||||||
// En mode quiosc, forçar pantalla completa independentment de la configuració
|
// En mode quiosc, forçar pantalla completa independentment de la configuració
|
||||||
@@ -141,6 +143,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 +156,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 +165,8 @@ 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();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::cout << "\n"; // Fin de inicialización de sistemas
|
std::cout << "\n"; // Fin de inicialización de sistemas
|
||||||
@@ -168,7 +176,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 +199,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
|
||||||
@@ -204,7 +216,7 @@ Director::~Director() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba los parametros del programa
|
// Comprueba los parametros del programa
|
||||||
auto Director::checkProgramArguments(std::vector<std::string> const& args) -> std::string {
|
auto Director::checkProgramArguments(std::vector<std::string> const& args) -> std::string { // NOLINT(readability-identifier-naming)
|
||||||
// Iterar sobre los argumentos del programa (saltando args[0] que es el ejecutable)
|
// Iterar sobre los argumentos del programa (saltando args[0] que es el ejecutable)
|
||||||
for (std::size_t i = 1; i < args.size(); ++i) {
|
for (std::size_t i = 1; i < args.size(); ++i) {
|
||||||
const std::string& argument = args[i];
|
const std::string& argument = args[i];
|
||||||
@@ -226,7 +238,7 @@ auto Director::checkProgramArguments(std::vector<std::string> const& args) -> st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 +293,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 +367,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 +406,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;
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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 {
|
||||||
@@ -77,9 +79,9 @@ namespace Defaults::Controls {
|
|||||||
} // namespace Defaults::Controls
|
} // 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 {
|
||||||
@@ -87,21 +89,11 @@ namespace Defaults::Localization {
|
|||||||
} // namespace Defaults::Localization
|
} // namespace Defaults::Localization
|
||||||
|
|
||||||
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
|
|
||||||
#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
|
|
||||||
#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
|
||||||
|
|||||||
@@ -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_) {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ranges> // Para std::ranges::any_of
|
#include <ranges> // Para std::ranges::any_of
|
||||||
|
|
||||||
#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
|
||||||
#include "utils/defines.hpp" // Para RoomBorder::BOTTOM, RoomBorder::LEFT, RoomBorder::RIGHT
|
#include "utils/defines.hpp" // Para RoomBorder::BOTTOM, RoomBorder::LEFT, RoomBorder::RIGHT
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
#include "core/system/debug.hpp" // Para Debug
|
#include "core/system/debug.hpp" // Para Debug
|
||||||
@@ -84,21 +84,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 +136,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 +239,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 +252,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 +287,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 +369,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 +462,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 +478,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 +488,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 +593,24 @@ void Player::updateCurrentSlope() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug output
|
|
||||||
/*
|
|
||||||
if (current_slope_ != nullptr) {
|
|
||||||
const char* TYPE = isLeftSlope() ? "Left \\" : "Right /";
|
|
||||||
std::cout << "[SLOPE] " << TYPE
|
|
||||||
<< " from (" << current_slope_->x1 << "," << current_slope_->y1 << ")"
|
|
||||||
<< " to (" << current_slope_->x2 << "," << current_slope_->y2 << ")\n";
|
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (current_slope_ != nullptr) {
|
||||||
|
Debug::get()->set("sl.type", isLeftSlope() ? "L\\" : "R/");
|
||||||
|
Debug::get()->set("sl.p1", std::to_string(current_slope_->x1) + "," + std::to_string(current_slope_->y1));
|
||||||
|
Debug::get()->set("sl.p2", std::to_string(current_slope_->x2) + "," + std::to_string(current_slope_->y2));
|
||||||
} 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);
|
||||||
@@ -749,9 +766,9 @@ void Player::applySpawnValues(const SpawnData& spawn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 +882,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 {
|
||||||
|
|||||||
@@ -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,7 +96,7 @@ 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)
|
||||||
@@ -118,8 +118,8 @@ class Player {
|
|||||||
static constexpr int MAX_FALLING_HEIGHT = Tile::SIZE * 4; // Altura maxima permitida de caída en pixels
|
static constexpr int MAX_FALLING_HEIGHT = Tile::SIZE * 4; // Altura maxima permitida de caída en pixels
|
||||||
|
|
||||||
// --- 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
|
||||||
@@ -136,11 +136,11 @@ class Player {
|
|||||||
State previous_state_ = State::ON_GROUND; // Estado previo en el que se encontraba el jugador
|
State previous_state_ = State::ON_GROUND; // Estado previo en el que se encontraba el jugador
|
||||||
|
|
||||||
// --- 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
|
||||||
|
|||||||
18
source/game/game_control.hpp
Normal file
18
source/game/game_control.hpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace GameControl {
|
||||||
|
// Registrada por Game::Game() — cambia la habitación activa
|
||||||
|
inline std::function<bool(const std::string&)> change_room;
|
||||||
|
// Registrada por Game::Game() — refresca el color del jugador según cheats
|
||||||
|
inline std::function<void()> refresh_player_color;
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -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,7 +101,7 @@ 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
|
||||||
@@ -162,7 +162,7 @@ void Cheevos::saveToFile() {
|
|||||||
|
|
||||||
// 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"
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ 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 && name_ == "THE JAIL") {
|
||||||
// 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()) {
|
||||||
@@ -123,7 +123,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 +147,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 +163,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 +256,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,7 +58,7 @@ 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 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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -4,15 +4,15 @@
|
|||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "core/locale/locale.hpp" // Para Locale
|
#include "core/locale/locale.hpp" // Para Locale
|
||||||
#include "core/rendering/screen.hpp" // Para Screen
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "core/rendering/surface.hpp" // Para Surface
|
#include "core/rendering/sprite/animated_sprite.hpp" // Para SAnimatedSprite
|
||||||
#include "core/rendering/surface_animated_sprite.hpp" // Para SAnimatedSprite
|
#include "core/rendering/surface.hpp" // Para Surface
|
||||||
#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 "game/options.hpp" // Para Options, options, Cheat, OptionsGame
|
#include "game/options.hpp" // Para Options, options, Cheat, OptionsGame
|
||||||
#include "utils/defines.hpp" // Para BLOCK
|
#include "utils/defines.hpp" // Para BLOCK
|
||||||
#include "utils/utils.hpp" // Para stringToColor
|
#include "utils/utils.hpp" // Para stringToColor
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Scoreboard::Scoreboard(std::shared_ptr<Data> data)
|
Scoreboard::Scoreboard(std::shared_ptr<Data> data)
|
||||||
@@ -23,7 +23,7 @@ Scoreboard::Scoreboard(std::shared_ptr<Data> data)
|
|||||||
|
|
||||||
// Reserva memoria para los objetos
|
// Reserva memoria para los objetos
|
||||||
const auto& player_animation_data = Resource::Cache::get()->getAnimationData(Options::cheats.alternate_skin == Options::Cheat::State::ENABLED ? "player2.yaml" : "player.yaml");
|
const auto& player_animation_data = Resource::Cache::get()->getAnimationData(Options::cheats.alternate_skin == Options::Cheat::State::ENABLED ? "player2.yaml" : "player.yaml");
|
||||||
player_sprite_ = std::make_shared<SurfaceAnimatedSprite>(player_animation_data);
|
player_sprite_ = std::make_shared<AnimatedSprite>(player_animation_data);
|
||||||
player_sprite_->setCurrentAnimation("walk_menu");
|
player_sprite_->setCurrentAnimation("walk_menu");
|
||||||
|
|
||||||
surface_ = std::make_shared<Surface>(SURFACE_WIDTH, SURFACE_HEIGHT);
|
surface_ = std::make_shared<Surface>(SURFACE_WIDTH, SURFACE_HEIGHT);
|
||||||
@@ -65,7 +65,7 @@ void Scoreboard::update(float delta_time) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene el tiempo transcurrido de partida
|
// Obtiene el tiempo transcurrido de partida
|
||||||
auto Scoreboard::getTime() -> Scoreboard::ClockData {
|
auto Scoreboard::getTime() -> Scoreboard::ClockData { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
const Uint32 TIME_ELAPSED = SDL_GetTicks() - data_->ini_clock - paused_time_elapsed_;
|
const Uint32 TIME_ELAPSED = SDL_GetTicks() - data_->ini_clock - paused_time_elapsed_;
|
||||||
|
|
||||||
ClockData time;
|
ClockData time;
|
||||||
@@ -149,7 +149,7 @@ void Scoreboard::fillTexture() {
|
|||||||
// Muestra si suena la música
|
// Muestra si suena la música
|
||||||
if (data_->music) {
|
if (data_->music) {
|
||||||
const Uint8 C = data_->color;
|
const Uint8 C = data_->color;
|
||||||
SDL_FRect clip = {0, 8, 8, 8};
|
SDL_FRect clip = {.x = 0, .y = 8, .w = 8, .h = 8};
|
||||||
item_surface_->renderWithColorReplace(20 * Tile::SIZE, LINE2, 1, C, &clip);
|
item_surface_->renderWithColorReplace(20 * Tile::SIZE, LINE2, 1, C, &clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,13 +157,13 @@ void Scoreboard::fillTexture() {
|
|||||||
auto text = Resource::Cache::get()->getText("smb2");
|
auto text = Resource::Cache::get()->getText("smb2");
|
||||||
const std::string TIME_TEXT = std::to_string((clock_.minutes % 100) / 10) + std::to_string(clock_.minutes % 10) + clock_.separator + std::to_string((clock_.seconds % 60) / 10) + std::to_string(clock_.seconds % 10);
|
const std::string TIME_TEXT = std::to_string((clock_.minutes % 100) / 10) + std::to_string(clock_.minutes % 10) + clock_.separator + std::to_string((clock_.seconds % 60) / 10) + std::to_string(clock_.seconds % 10);
|
||||||
const std::string ITEMS_TEXT = std::to_string(data_->items / 100) + std::to_string((data_->items % 100) / 10) + std::to_string(data_->items % 10);
|
const std::string ITEMS_TEXT = std::to_string(data_->items / 100) + std::to_string((data_->items % 100) / 10) + std::to_string(data_->items % 10);
|
||||||
text->writeColored(Tile::SIZE, LINE1, Locale::get()->get("scoreboard.items"), data_->color);
|
text->writeColored(Tile::SIZE, LINE1, Locale::get()->get("scoreboard.items"), data_->color); // NOLINT(readability-static-accessed-through-instance)
|
||||||
text->writeColored(17 * Tile::SIZE, LINE1, ITEMS_TEXT, items_color_);
|
text->writeColored(17 * Tile::SIZE, LINE1, ITEMS_TEXT, items_color_);
|
||||||
text->writeColored(20 * Tile::SIZE, LINE1, Locale::get()->get("scoreboard.time"), data_->color);
|
text->writeColored(20 * Tile::SIZE, LINE1, Locale::get()->get("scoreboard.time"), data_->color); // NOLINT(readability-static-accessed-through-instance)
|
||||||
text->writeColored(26 * Tile::SIZE, LINE1, TIME_TEXT, stringToColor("white"));
|
text->writeColored(26 * Tile::SIZE, LINE1, TIME_TEXT, stringToColor("white"));
|
||||||
|
|
||||||
const std::string ROOMS_TEXT = std::to_string(data_->rooms / 100) + std::to_string((data_->rooms % 100) / 10) + std::to_string(data_->rooms % 10);
|
const std::string ROOMS_TEXT = std::to_string(data_->rooms / 100) + std::to_string((data_->rooms % 100) / 10) + std::to_string(data_->rooms % 10);
|
||||||
text->writeColored(22 * Tile::SIZE, LINE2, Locale::get()->get("scoreboard.rooms"), stringToColor("white"));
|
text->writeColored(22 * Tile::SIZE, LINE2, Locale::get()->get("scoreboard.rooms"), stringToColor("white")); // NOLINT(readability-static-accessed-through-instance)
|
||||||
text->writeColored(28 * Tile::SIZE, LINE2, ROOMS_TEXT, stringToColor("white"));
|
text->writeColored(28 * Tile::SIZE, LINE2, ROOMS_TEXT, stringToColor("white"));
|
||||||
|
|
||||||
// Deja el renderizador como estaba
|
// Deja el renderizador como estaba
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
#include <memory> // Para shared_ptr
|
#include <memory> // Para shared_ptr
|
||||||
#include <string> // Para string, basic_string
|
#include <string> // Para string, basic_string
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
class SurfaceAnimatedSprite; // lines 10-10
|
class AnimatedSprite; // lines 10-10
|
||||||
class Surface; // lines 11-11
|
class Surface; // lines 11-11
|
||||||
|
|
||||||
class Scoreboard {
|
class Scoreboard {
|
||||||
public:
|
public:
|
||||||
@@ -50,10 +50,10 @@ class Scoreboard {
|
|||||||
void fillTexture(); // Dibuja los elementos del marcador en la surface
|
void fillTexture(); // Dibuja los elementos del marcador en la surface
|
||||||
|
|
||||||
// Objetos y punteros
|
// Objetos y punteros
|
||||||
std::shared_ptr<SurfaceAnimatedSprite> player_sprite_; // Sprite para mostrar las vidas en el marcador
|
std::shared_ptr<AnimatedSprite> player_sprite_; // Sprite para mostrar las vidas en el marcador
|
||||||
std::shared_ptr<Surface> item_surface_; // Surface con los graficos para los elementos del marcador
|
std::shared_ptr<Surface> item_surface_; // Surface con los graficos para los elementos del marcador
|
||||||
std::shared_ptr<Data> data_; // Contiene las variables a mostrar en el marcador
|
std::shared_ptr<Data> data_; // Contiene las variables a mostrar en el marcador
|
||||||
std::shared_ptr<Surface> surface_; // Surface donde dibujar el marcador
|
std::shared_ptr<Surface> surface_; // Surface donde dibujar el marcador
|
||||||
|
|
||||||
// Variables de estado
|
// Variables de estado
|
||||||
std::vector<Uint8> color_; // Vector con los colores del objeto
|
std::vector<Uint8> color_; // Vector con los colores del objeto
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ void Stats::init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Añade una muerte a las estadisticas
|
// Añade una muerte a las estadisticas
|
||||||
void Stats::addDeath(const std::string& name) {
|
void Stats::addDeath(const std::string& name) { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
// Primero busca si ya hay una entrada con ese nombre
|
// Primero busca si ya hay una entrada con ese nombre
|
||||||
const int INDEX = findByName(name, buffer_list_);
|
const int INDEX = findByName(name, buffer_list_);
|
||||||
if (INDEX != -1) {
|
if (INDEX != -1) {
|
||||||
@@ -58,7 +58,7 @@ void Stats::addDeath(const std::string& name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Añade una visita a las estadisticas
|
// Añade una visita a las estadisticas
|
||||||
void Stats::addVisit(const std::string& name) {
|
void Stats::addVisit(const std::string& name) { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
// Primero busca si ya hay una entrada con ese nombre
|
// Primero busca si ya hay una entrada con ese nombre
|
||||||
const int INDEX = findByName(name, buffer_list_);
|
const int INDEX = findByName(name, buffer_list_);
|
||||||
if (INDEX != -1) {
|
if (INDEX != -1) {
|
||||||
@@ -76,7 +76,7 @@ void Stats::addVisit(const std::string& name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Busca una entrada en la lista por nombre
|
// Busca una entrada en la lista por nombre
|
||||||
auto Stats::findByName(const std::string& name, const std::vector<RoomData>& list) -> int {
|
auto Stats::findByName(const std::string& name, const std::vector<RoomData>& list) -> int { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for (const auto& l : list) {
|
for (const auto& l : list) {
|
||||||
@@ -90,7 +90,7 @@ auto Stats::findByName(const std::string& name, const std::vector<RoomData>& lis
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Carga las estadisticas desde un fichero
|
// Carga las estadisticas desde un fichero
|
||||||
auto Stats::loadFromFile(const std::string& file_path, std::vector<RoomData>& list) -> bool {
|
auto Stats::loadFromFile(const std::string& file_path, std::vector<RoomData>& list) -> bool { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
list.clear();
|
list.clear();
|
||||||
|
|
||||||
// Indicador de éxito en la carga
|
// Indicador de éxito en la carga
|
||||||
@@ -109,7 +109,7 @@ auto Stats::loadFromFile(const std::string& file_path, std::vector<RoomData>& li
|
|||||||
line.pop_back();
|
line.pop_back();
|
||||||
}
|
}
|
||||||
// Comprueba que la linea no sea un comentario
|
// Comprueba que la linea no sea un comentario
|
||||||
if (line.substr(0, 1) != "#") {
|
if (!line.starts_with("#")) {
|
||||||
RoomData stat;
|
RoomData stat;
|
||||||
std::stringstream ss(line);
|
std::stringstream ss(line);
|
||||||
std::string tmp;
|
std::string tmp;
|
||||||
@@ -159,7 +159,7 @@ void Stats::saveToFile(const std::string& file_path, const std::vector<RoomData>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calcula cual es la habitación con más muertes
|
// Calcula cual es la habitación con más muertes
|
||||||
void Stats::checkWorstNightmare() {
|
void Stats::checkWorstNightmare() { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
int deaths = 0;
|
int deaths = 0;
|
||||||
for (const auto& item : list_) {
|
for (const auto& item : list_) {
|
||||||
if (item.died > deaths) {
|
if (item.died > deaths) {
|
||||||
@@ -171,7 +171,7 @@ void Stats::checkWorstNightmare() {
|
|||||||
|
|
||||||
// Añade una entrada al diccionario
|
// Añade una entrada al diccionario
|
||||||
void Stats::addDictionary(const std::string& number, const std::string& name) {
|
void Stats::addDictionary(const std::string& number, const std::string& name) {
|
||||||
dictionary_.push_back({number, name});
|
dictionary_.push_back({.number = number, .name = name});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vuelca los datos del buffer en la lista de estadisticas
|
// Vuelca los datos del buffer en la lista de estadisticas
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user