Compare commits
56 Commits
5e013a8414
...
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 | |||
| 49ae2ae41f | |||
| c701421a8f | |||
| 1ecb427106 | |||
| c87779cc09 | |||
| 24594fa89a | |||
| 030779794e | |||
| 495c23a3d2 | |||
| 911ee7a13e | |||
| b876ccbb09 | |||
| 94684e8758 | |||
| 0c116665bc | |||
| d0ed49d192 |
@@ -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..."
|
||||||
|
|||||||
@@ -7,23 +7,23 @@ assets:
|
|||||||
- type: BITMAP
|
- type: BITMAP
|
||||||
path: ${PREFIX}/data/font/smb2.gif
|
path: ${PREFIX}/data/font/smb2.gif
|
||||||
- type: FONT
|
- type: FONT
|
||||||
path: ${PREFIX}/data/font/smb2.txt
|
path: ${PREFIX}/data/font/smb2.fnt
|
||||||
- type: BITMAP
|
- type: BITMAP
|
||||||
path: ${PREFIX}/data/font/aseprite.gif
|
path: ${PREFIX}/data/font/aseprite.gif
|
||||||
- type: FONT
|
- type: FONT
|
||||||
path: ${PREFIX}/data/font/aseprite.txt
|
path: ${PREFIX}/data/font/aseprite.fnt
|
||||||
- type: BITMAP
|
- type: BITMAP
|
||||||
path: ${PREFIX}/data/font/gauntlet.gif
|
path: ${PREFIX}/data/font/gauntlet.gif
|
||||||
- type: FONT
|
- type: FONT
|
||||||
path: ${PREFIX}/data/font/gauntlet.txt
|
path: ${PREFIX}/data/font/gauntlet.fnt
|
||||||
- type: BITMAP
|
- type: BITMAP
|
||||||
path: ${PREFIX}/data/font/subatomic.gif
|
path: ${PREFIX}/data/font/subatomic.gif
|
||||||
- type: FONT
|
- type: FONT
|
||||||
path: ${PREFIX}/data/font/subatomic.txt
|
path: ${PREFIX}/data/font/subatomic.fnt
|
||||||
- type: BITMAP
|
- type: BITMAP
|
||||||
path: ${PREFIX}/data/font/8bithud.gif
|
path: ${PREFIX}/data/font/8bithud.gif
|
||||||
- type: FONT
|
- type: FONT
|
||||||
path: ${PREFIX}/data/font/8bithud.txt
|
path: ${PREFIX}/data/font/8bithud.fnt
|
||||||
|
|
||||||
# PALETTES
|
# PALETTES
|
||||||
palettes:
|
palettes:
|
||||||
@@ -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
|
||||||
|
|||||||
132
data/font/8bithud.fnt
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# Font: 8bithud — generado desde 8-bit-hud.ttf size 5
|
||||||
|
# Generado con tools/font_gen/font_gen.py
|
||||||
|
|
||||||
|
box_width 8
|
||||||
|
box_height 8
|
||||||
|
columns 15
|
||||||
|
|
||||||
|
# codepoint_decimal ancho_visual
|
||||||
|
32 3 # U+0020
|
||||||
|
33 2 # !
|
||||||
|
34 5 # "
|
||||||
|
35 6 # #
|
||||||
|
36 6 # $
|
||||||
|
37 6 # %
|
||||||
|
38 6 # &
|
||||||
|
39 2 # '
|
||||||
|
40 3 # (
|
||||||
|
41 3 # )
|
||||||
|
42 4 # *
|
||||||
|
43 3 # +
|
||||||
|
44 2 # ,
|
||||||
|
45 3 # -
|
||||||
|
46 2 # .
|
||||||
|
47 4 # /
|
||||||
|
48 6 # 0
|
||||||
|
49 3 # 1
|
||||||
|
50 6 # 2
|
||||||
|
51 6 # 3
|
||||||
|
52 6 # 4
|
||||||
|
53 6 # 5
|
||||||
|
54 6 # 6
|
||||||
|
55 6 # 7
|
||||||
|
56 6 # 8
|
||||||
|
57 6 # 9
|
||||||
|
58 2 # :
|
||||||
|
59 2 # ;
|
||||||
|
60 4 # <
|
||||||
|
61 3 # =
|
||||||
|
62 4 # >
|
||||||
|
63 6 # ?
|
||||||
|
64 8 # @
|
||||||
|
65 6 # A
|
||||||
|
66 6 # B
|
||||||
|
67 6 # C
|
||||||
|
68 6 # D
|
||||||
|
69 6 # E
|
||||||
|
70 6 # F
|
||||||
|
71 6 # G
|
||||||
|
72 6 # H
|
||||||
|
73 6 # I
|
||||||
|
74 6 # J
|
||||||
|
75 6 # K
|
||||||
|
76 6 # L
|
||||||
|
77 6 # M
|
||||||
|
78 6 # N
|
||||||
|
79 6 # O
|
||||||
|
80 6 # P
|
||||||
|
81 6 # Q
|
||||||
|
82 6 # R
|
||||||
|
83 6 # S
|
||||||
|
84 6 # T
|
||||||
|
85 6 # U
|
||||||
|
86 5 # V
|
||||||
|
87 6 # W
|
||||||
|
88 6 # X
|
||||||
|
89 6 # Y
|
||||||
|
90 6 # Z
|
||||||
|
91 3 # [
|
||||||
|
92 4 # \
|
||||||
|
93 3 # ]
|
||||||
|
94 4 # ^
|
||||||
|
95 6 # _
|
||||||
|
96 2 # `
|
||||||
|
97 5 # a
|
||||||
|
98 5 # b
|
||||||
|
99 5 # c
|
||||||
|
100 5 # d
|
||||||
|
101 5 # e
|
||||||
|
102 5 # f
|
||||||
|
103 5 # g
|
||||||
|
104 5 # h
|
||||||
|
105 4 # i
|
||||||
|
106 5 # j
|
||||||
|
107 5 # k
|
||||||
|
108 5 # l
|
||||||
|
109 6 # m
|
||||||
|
110 5 # n
|
||||||
|
111 5 # o
|
||||||
|
112 5 # p
|
||||||
|
113 5 # q
|
||||||
|
114 5 # r
|
||||||
|
115 5 # s
|
||||||
|
116 4 # t
|
||||||
|
117 5 # u
|
||||||
|
118 5 # v
|
||||||
|
119 6 # w
|
||||||
|
120 4 # x
|
||||||
|
121 4 # y
|
||||||
|
122 5 # z
|
||||||
|
123 4 # {
|
||||||
|
124 1 # |
|
||||||
|
125 4 # }
|
||||||
|
126 4 # ~
|
||||||
|
192 6 # À
|
||||||
|
193 6 # Á
|
||||||
|
200 6 # È
|
||||||
|
201 6 # É
|
||||||
|
205 6 # Í
|
||||||
|
207 6 # Ï
|
||||||
|
210 6 # Ò
|
||||||
|
211 6 # Ó
|
||||||
|
218 6 # Ú
|
||||||
|
220 6 # Ü
|
||||||
|
209 6 # Ñ
|
||||||
|
199 6 # Ç
|
||||||
|
224 5 # à
|
||||||
|
225 5 # á
|
||||||
|
232 5 # è
|
||||||
|
233 5 # é
|
||||||
|
237 4 # í
|
||||||
|
239 4 # ï
|
||||||
|
242 5 # ò
|
||||||
|
243 5 # ó
|
||||||
|
250 5 # ú
|
||||||
|
252 5 # ü
|
||||||
|
241 5 # ñ
|
||||||
|
231 5 # ç
|
||||||
|
161 2 # ¡
|
||||||
|
191 6 # ¿
|
||||||
|
171 4 # «
|
||||||
|
187 4 # »
|
||||||
|
183 2 # ·
|
||||||
|
Before Width: | Height: | Size: 680 B After Width: | Height: | Size: 837 B |
@@ -1,194 +0,0 @@
|
|||||||
# box width
|
|
||||||
8
|
|
||||||
# box height
|
|
||||||
8
|
|
||||||
# 32 espacio ( )
|
|
||||||
2
|
|
||||||
# 33 !
|
|
||||||
2
|
|
||||||
# 34 "
|
|
||||||
5
|
|
||||||
# 35 #
|
|
||||||
6
|
|
||||||
# 36 $
|
|
||||||
6
|
|
||||||
# 37 %
|
|
||||||
6
|
|
||||||
# 38 &
|
|
||||||
6
|
|
||||||
# 39 '
|
|
||||||
2
|
|
||||||
# 40 (
|
|
||||||
3
|
|
||||||
# 41 )
|
|
||||||
3
|
|
||||||
# 42 *
|
|
||||||
4
|
|
||||||
# 43 +
|
|
||||||
3
|
|
||||||
# 44 ,
|
|
||||||
2
|
|
||||||
# 45 -
|
|
||||||
3
|
|
||||||
# 46 .
|
|
||||||
2
|
|
||||||
# 47 /
|
|
||||||
4
|
|
||||||
# 48 0
|
|
||||||
6
|
|
||||||
# 49 1
|
|
||||||
6
|
|
||||||
# 50 2
|
|
||||||
6
|
|
||||||
# 51 3
|
|
||||||
6
|
|
||||||
# 52 4
|
|
||||||
6
|
|
||||||
# 53 5
|
|
||||||
6
|
|
||||||
# 54 6
|
|
||||||
6
|
|
||||||
# 55 7
|
|
||||||
6
|
|
||||||
# 56 8
|
|
||||||
6
|
|
||||||
# 57 9
|
|
||||||
6
|
|
||||||
# 58 :
|
|
||||||
2
|
|
||||||
# 59 ;
|
|
||||||
2
|
|
||||||
# 60 <
|
|
||||||
4
|
|
||||||
# 61 =
|
|
||||||
3
|
|
||||||
# 62 >
|
|
||||||
4
|
|
||||||
# 63 ?
|
|
||||||
6
|
|
||||||
# 64 @
|
|
||||||
8
|
|
||||||
# 65 A
|
|
||||||
6
|
|
||||||
# 66 B
|
|
||||||
6
|
|
||||||
# 67 C
|
|
||||||
6
|
|
||||||
# 68 D
|
|
||||||
6
|
|
||||||
# 69 E
|
|
||||||
6
|
|
||||||
# 70 F
|
|
||||||
6
|
|
||||||
# 71 G
|
|
||||||
6
|
|
||||||
# 72 H
|
|
||||||
6
|
|
||||||
# 73 I
|
|
||||||
6
|
|
||||||
# 74 J
|
|
||||||
6
|
|
||||||
# 75 K
|
|
||||||
6
|
|
||||||
# 76 L
|
|
||||||
6
|
|
||||||
# 77 M
|
|
||||||
6
|
|
||||||
# 78 N
|
|
||||||
6
|
|
||||||
# 79 O
|
|
||||||
6
|
|
||||||
# 80 P
|
|
||||||
6
|
|
||||||
# 81 Q
|
|
||||||
6
|
|
||||||
# 82 R
|
|
||||||
6
|
|
||||||
# 83 S
|
|
||||||
6
|
|
||||||
# 84 T
|
|
||||||
6
|
|
||||||
# 85 U
|
|
||||||
6
|
|
||||||
# 86 V
|
|
||||||
5
|
|
||||||
# 87 W
|
|
||||||
6
|
|
||||||
# 88 X
|
|
||||||
6
|
|
||||||
# 89 Y
|
|
||||||
6
|
|
||||||
# 90 Z
|
|
||||||
6
|
|
||||||
# 91 [
|
|
||||||
3
|
|
||||||
# 92 \
|
|
||||||
5
|
|
||||||
# 93 ]
|
|
||||||
3
|
|
||||||
# 94 ^
|
|
||||||
4
|
|
||||||
# 95 _
|
|
||||||
6
|
|
||||||
# 96 `
|
|
||||||
2
|
|
||||||
# 97 a
|
|
||||||
5
|
|
||||||
# 98 b
|
|
||||||
5
|
|
||||||
# 99 c
|
|
||||||
5
|
|
||||||
# 100 d
|
|
||||||
5
|
|
||||||
# 101 e
|
|
||||||
5
|
|
||||||
# 102 f
|
|
||||||
5
|
|
||||||
# 103 g
|
|
||||||
5
|
|
||||||
# 104 h
|
|
||||||
5
|
|
||||||
# 105 i
|
|
||||||
4
|
|
||||||
# 106 j
|
|
||||||
5
|
|
||||||
# 107 k
|
|
||||||
5
|
|
||||||
# 108 l
|
|
||||||
5
|
|
||||||
# 109 m
|
|
||||||
6
|
|
||||||
# 110 n
|
|
||||||
5
|
|
||||||
# 111 o
|
|
||||||
5
|
|
||||||
# 112 p
|
|
||||||
5
|
|
||||||
# 113 q
|
|
||||||
5
|
|
||||||
# 114 r
|
|
||||||
5
|
|
||||||
# 115 s
|
|
||||||
5
|
|
||||||
# 116 t
|
|
||||||
4
|
|
||||||
# 117 u
|
|
||||||
5
|
|
||||||
# 118 v
|
|
||||||
5
|
|
||||||
# 119 w
|
|
||||||
6
|
|
||||||
# 120 x
|
|
||||||
4
|
|
||||||
# 121 y
|
|
||||||
4
|
|
||||||
# 122 z
|
|
||||||
5
|
|
||||||
# 123 {
|
|
||||||
3
|
|
||||||
# 124 |
|
|
||||||
2
|
|
||||||
# 125 }
|
|
||||||
3
|
|
||||||
# 126 ~
|
|
||||||
3
|
|
||||||
134
data/font/aseprite.fnt
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# Font: aseprite — generado desde aseprite_font.gif
|
||||||
|
# Generado con tools/font_gen/font_gen.py
|
||||||
|
|
||||||
|
box_width 10
|
||||||
|
box_height 7
|
||||||
|
columns 16
|
||||||
|
cell_spacing 1
|
||||||
|
row_spacing 4
|
||||||
|
|
||||||
|
# codepoint_decimal ancho_visual
|
||||||
|
32 3 # U+0020
|
||||||
|
33 1 # !
|
||||||
|
34 3 # "
|
||||||
|
35 5 # #
|
||||||
|
36 4 # $
|
||||||
|
37 5 # %
|
||||||
|
38 5 # &
|
||||||
|
39 2 # '
|
||||||
|
40 2 # (
|
||||||
|
41 2 # )
|
||||||
|
42 5 # *
|
||||||
|
43 5 # +
|
||||||
|
44 2 # ,
|
||||||
|
45 3 # -
|
||||||
|
46 1 # .
|
||||||
|
47 3 # /
|
||||||
|
48 4 # 0
|
||||||
|
49 2 # 1
|
||||||
|
50 4 # 2
|
||||||
|
51 4 # 3
|
||||||
|
52 4 # 4
|
||||||
|
53 4 # 5
|
||||||
|
54 4 # 6
|
||||||
|
55 4 # 7
|
||||||
|
56 4 # 8
|
||||||
|
57 4 # 9
|
||||||
|
58 1 # :
|
||||||
|
59 2 # ;
|
||||||
|
60 3 # <
|
||||||
|
61 4 # =
|
||||||
|
62 3 # >
|
||||||
|
63 4 # ?
|
||||||
|
64 8 # @
|
||||||
|
65 4 # A
|
||||||
|
66 4 # B
|
||||||
|
67 4 # C
|
||||||
|
68 4 # D
|
||||||
|
69 4 # E
|
||||||
|
70 4 # F
|
||||||
|
71 4 # G
|
||||||
|
72 4 # H
|
||||||
|
73 1 # I
|
||||||
|
74 2 # J
|
||||||
|
75 4 # K
|
||||||
|
76 4 # L
|
||||||
|
77 5 # M
|
||||||
|
78 4 # N
|
||||||
|
79 5 # O
|
||||||
|
80 4 # P
|
||||||
|
81 5 # Q
|
||||||
|
82 4 # R
|
||||||
|
83 4 # S
|
||||||
|
84 5 # T
|
||||||
|
85 4 # U
|
||||||
|
86 5 # V
|
||||||
|
87 7 # W
|
||||||
|
88 5 # X
|
||||||
|
89 5 # Y
|
||||||
|
90 4 # Z
|
||||||
|
91 2 # [
|
||||||
|
92 3 # \
|
||||||
|
93 2 # ]
|
||||||
|
94 5 # ^
|
||||||
|
95 5 # _
|
||||||
|
96 3 # `
|
||||||
|
97 4 # a
|
||||||
|
98 4 # b
|
||||||
|
99 4 # c
|
||||||
|
100 4 # d
|
||||||
|
101 4 # e
|
||||||
|
102 2 # f
|
||||||
|
103 4 # g
|
||||||
|
104 4 # h
|
||||||
|
105 1 # i
|
||||||
|
106 2 # j
|
||||||
|
107 4 # k
|
||||||
|
108 1 # l
|
||||||
|
109 7 # m
|
||||||
|
110 4 # n
|
||||||
|
111 4 # o
|
||||||
|
112 4 # p
|
||||||
|
113 4 # q
|
||||||
|
114 3 # r
|
||||||
|
115 3 # s
|
||||||
|
116 2 # t
|
||||||
|
117 4 # u
|
||||||
|
118 4 # v
|
||||||
|
119 5 # w
|
||||||
|
120 5 # x
|
||||||
|
121 4 # y
|
||||||
|
122 4 # z
|
||||||
|
123 3 # {
|
||||||
|
124 1 # |
|
||||||
|
125 3 # }
|
||||||
|
126 4 # ~
|
||||||
|
192 5 # À
|
||||||
|
193 5 # Á
|
||||||
|
200 5 # È
|
||||||
|
201 5 # É
|
||||||
|
205 5 # Í
|
||||||
|
207 5 # Ï
|
||||||
|
210 5 # Ò
|
||||||
|
211 5 # Ó
|
||||||
|
218 5 # Ú
|
||||||
|
220 5 # Ü
|
||||||
|
209 5 # Ñ
|
||||||
|
199 5 # Ç
|
||||||
|
224 5 # à
|
||||||
|
225 5 # á
|
||||||
|
232 5 # è
|
||||||
|
233 5 # é
|
||||||
|
237 5 # í
|
||||||
|
239 5 # ï
|
||||||
|
242 5 # ò
|
||||||
|
243 5 # ó
|
||||||
|
250 5 # ú
|
||||||
|
252 5 # ü
|
||||||
|
241 5 # ñ
|
||||||
|
231 5 # ç
|
||||||
|
161 5 # ¡
|
||||||
|
191 5 # ¿
|
||||||
|
171 5 # «
|
||||||
|
187 5 # »
|
||||||
|
183 5 # ·
|
||||||
|
Before Width: | Height: | Size: 640 B After Width: | Height: | Size: 3.9 KiB |
@@ -1,194 +0,0 @@
|
|||||||
# box width
|
|
||||||
8
|
|
||||||
# box height
|
|
||||||
8
|
|
||||||
# 32 espacio ( )
|
|
||||||
3
|
|
||||||
# 33 !
|
|
||||||
1
|
|
||||||
# 34 "
|
|
||||||
3
|
|
||||||
# 35 #
|
|
||||||
3
|
|
||||||
# 36 $
|
|
||||||
4
|
|
||||||
# 37 %
|
|
||||||
5
|
|
||||||
# 38 &
|
|
||||||
5
|
|
||||||
# 39 '
|
|
||||||
2
|
|
||||||
# 40 (
|
|
||||||
2
|
|
||||||
# 41 )
|
|
||||||
2
|
|
||||||
# 42 *
|
|
||||||
5
|
|
||||||
# 43 +
|
|
||||||
5
|
|
||||||
# 44 ,
|
|
||||||
3
|
|
||||||
# 45 -
|
|
||||||
3
|
|
||||||
# 46 .
|
|
||||||
1
|
|
||||||
# 47 /
|
|
||||||
4
|
|
||||||
# 48 0
|
|
||||||
4
|
|
||||||
# 49 1
|
|
||||||
2
|
|
||||||
# 50 2
|
|
||||||
4
|
|
||||||
# 51 3
|
|
||||||
4
|
|
||||||
# 52 4
|
|
||||||
4
|
|
||||||
# 53 5
|
|
||||||
4
|
|
||||||
# 54 6
|
|
||||||
4
|
|
||||||
# 55 7
|
|
||||||
4
|
|
||||||
# 56 8
|
|
||||||
4
|
|
||||||
# 57 9
|
|
||||||
4
|
|
||||||
# 58 :
|
|
||||||
1
|
|
||||||
# 59 ;
|
|
||||||
1
|
|
||||||
# 60 <
|
|
||||||
3
|
|
||||||
# 61 =
|
|
||||||
4
|
|
||||||
# 62 >
|
|
||||||
4
|
|
||||||
# 63 ?
|
|
||||||
4
|
|
||||||
# 64 @
|
|
||||||
7
|
|
||||||
# 65 A
|
|
||||||
4
|
|
||||||
# 66 B
|
|
||||||
4
|
|
||||||
# 67 C
|
|
||||||
4
|
|
||||||
# 68 D
|
|
||||||
4
|
|
||||||
# 69 E
|
|
||||||
4
|
|
||||||
# 70 F
|
|
||||||
4
|
|
||||||
# 71 G
|
|
||||||
4
|
|
||||||
# 72 H
|
|
||||||
4
|
|
||||||
# 73 I
|
|
||||||
2
|
|
||||||
# 74 J
|
|
||||||
2
|
|
||||||
# 75 K
|
|
||||||
4
|
|
||||||
# 76 L
|
|
||||||
4
|
|
||||||
# 77 M
|
|
||||||
5
|
|
||||||
# 78 N
|
|
||||||
4
|
|
||||||
# 79 O
|
|
||||||
5
|
|
||||||
# 80 P
|
|
||||||
4
|
|
||||||
# 81 Q
|
|
||||||
5
|
|
||||||
# 82 R
|
|
||||||
4
|
|
||||||
# 83 S
|
|
||||||
4
|
|
||||||
# 84 T
|
|
||||||
5
|
|
||||||
# 85 U
|
|
||||||
4
|
|
||||||
# 86 V
|
|
||||||
5
|
|
||||||
# 87 W
|
|
||||||
7
|
|
||||||
# 88 X
|
|
||||||
5
|
|
||||||
# 89 Y
|
|
||||||
5
|
|
||||||
# 90 Z
|
|
||||||
4
|
|
||||||
# 91 [
|
|
||||||
2
|
|
||||||
# 92 \
|
|
||||||
3
|
|
||||||
# 93 ]
|
|
||||||
2
|
|
||||||
# 94 ^
|
|
||||||
5
|
|
||||||
# 95 _
|
|
||||||
5
|
|
||||||
# 96 `
|
|
||||||
3
|
|
||||||
# 97 a
|
|
||||||
4
|
|
||||||
# 98 b
|
|
||||||
4
|
|
||||||
# 99 c
|
|
||||||
4
|
|
||||||
# 100 d
|
|
||||||
4
|
|
||||||
# 101 e
|
|
||||||
4
|
|
||||||
# 102 f
|
|
||||||
2
|
|
||||||
# 103 g
|
|
||||||
4
|
|
||||||
# 104 h
|
|
||||||
4
|
|
||||||
# 105 i
|
|
||||||
1
|
|
||||||
# 106 j
|
|
||||||
2
|
|
||||||
# 107 k
|
|
||||||
4
|
|
||||||
# 108 l
|
|
||||||
1
|
|
||||||
# 109 m
|
|
||||||
7
|
|
||||||
# 110 n
|
|
||||||
4
|
|
||||||
# 111 o
|
|
||||||
4
|
|
||||||
# 112 p
|
|
||||||
4
|
|
||||||
# 113 q
|
|
||||||
4
|
|
||||||
# 114 r
|
|
||||||
3
|
|
||||||
# 115 s
|
|
||||||
3
|
|
||||||
# 116 t
|
|
||||||
2
|
|
||||||
# 117 u
|
|
||||||
4
|
|
||||||
# 118 v
|
|
||||||
4
|
|
||||||
# 119 w
|
|
||||||
5
|
|
||||||
# 120 x
|
|
||||||
5
|
|
||||||
# 121 y
|
|
||||||
4
|
|
||||||
# 122 z
|
|
||||||
4
|
|
||||||
# 123 {
|
|
||||||
3
|
|
||||||
# 124 |
|
|
||||||
3
|
|
||||||
# 125 }
|
|
||||||
3
|
|
||||||
# 126 ~
|
|
||||||
5
|
|
||||||
128
data/font/gauntlet.fnt
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Font: gauntlet — generado desde Gauntlet.ttf size 7
|
||||||
|
# Generado con tools/font_gen/font_gen.py
|
||||||
|
|
||||||
|
box_width 8
|
||||||
|
box_height 8
|
||||||
|
columns 15
|
||||||
|
|
||||||
|
# codepoint_decimal ancho_visual
|
||||||
|
32 3 # U+0020
|
||||||
|
33 2 # !
|
||||||
|
34 5 # "
|
||||||
|
35 6 # #
|
||||||
|
36 6 # $
|
||||||
|
37 7 # %
|
||||||
|
38 7 # &
|
||||||
|
39 2 # '
|
||||||
|
40 4 # (
|
||||||
|
41 4 # )
|
||||||
|
42 6 # *
|
||||||
|
43 8 # +
|
||||||
|
44 2 # ,
|
||||||
|
45 7 # -
|
||||||
|
46 2 # .
|
||||||
|
47 7 # /
|
||||||
|
48 7 # 0
|
||||||
|
49 6 # 1
|
||||||
|
50 6 # 2
|
||||||
|
51 6 # 3
|
||||||
|
52 7 # 4
|
||||||
|
53 6 # 5
|
||||||
|
54 6 # 6
|
||||||
|
55 6 # 7
|
||||||
|
56 6 # 8
|
||||||
|
57 6 # 9
|
||||||
|
58 2 # :
|
||||||
|
59 3 # ;
|
||||||
|
60 5 # <
|
||||||
|
61 6 # =
|
||||||
|
62 5 # >
|
||||||
|
63 6 # ?
|
||||||
|
64 6 # @
|
||||||
|
65 6 # A
|
||||||
|
66 7 # B
|
||||||
|
67 7 # C
|
||||||
|
68 7 # D
|
||||||
|
69 7 # E
|
||||||
|
70 7 # F
|
||||||
|
71 7 # G
|
||||||
|
72 6 # H
|
||||||
|
73 6 # I
|
||||||
|
74 7 # J
|
||||||
|
75 7 # K
|
||||||
|
76 7 # L
|
||||||
|
77 7 # M
|
||||||
|
78 7 # N
|
||||||
|
79 7 # O
|
||||||
|
80 7 # P
|
||||||
|
81 7 # Q
|
||||||
|
82 7 # R
|
||||||
|
83 6 # S
|
||||||
|
84 6 # T
|
||||||
|
85 6 # U
|
||||||
|
86 6 # V
|
||||||
|
87 7 # W
|
||||||
|
88 7 # X
|
||||||
|
89 6 # Y
|
||||||
|
90 7 # Z
|
||||||
|
91 8 # [
|
||||||
|
92 3 # \
|
||||||
|
93 7 # ]
|
||||||
|
94 7 # ^
|
||||||
|
95 8 # _
|
||||||
|
97 6 # a
|
||||||
|
98 7 # b
|
||||||
|
99 7 # c
|
||||||
|
100 7 # d
|
||||||
|
101 7 # e
|
||||||
|
102 7 # f
|
||||||
|
103 7 # g
|
||||||
|
104 6 # h
|
||||||
|
105 6 # i
|
||||||
|
106 7 # j
|
||||||
|
107 7 # k
|
||||||
|
108 7 # l
|
||||||
|
109 7 # m
|
||||||
|
110 7 # n
|
||||||
|
111 7 # o
|
||||||
|
112 7 # p
|
||||||
|
113 7 # q
|
||||||
|
114 7 # r
|
||||||
|
115 6 # s
|
||||||
|
116 6 # t
|
||||||
|
117 6 # u
|
||||||
|
118 6 # v
|
||||||
|
119 7 # w
|
||||||
|
120 7 # x
|
||||||
|
121 6 # y
|
||||||
|
122 7 # z
|
||||||
|
126 6 # ~
|
||||||
|
192 6 # À
|
||||||
|
193 6 # Á
|
||||||
|
200 7 # È
|
||||||
|
201 7 # É
|
||||||
|
205 6 # Í
|
||||||
|
207 6 # Ï
|
||||||
|
210 7 # Ò
|
||||||
|
211 7 # Ó
|
||||||
|
218 6 # Ú
|
||||||
|
220 6 # Ü
|
||||||
|
209 7 # Ñ
|
||||||
|
199 7 # Ç
|
||||||
|
224 6 # à
|
||||||
|
225 6 # á
|
||||||
|
232 7 # è
|
||||||
|
233 7 # é
|
||||||
|
237 6 # í
|
||||||
|
239 6 # ï
|
||||||
|
242 7 # ò
|
||||||
|
243 7 # ó
|
||||||
|
250 6 # ú
|
||||||
|
252 6 # ü
|
||||||
|
241 7 # ñ
|
||||||
|
231 7 # ç
|
||||||
|
161 2 # ¡
|
||||||
|
191 6 # ¿
|
||||||
|
171 5 # «
|
||||||
|
187 5 # »
|
||||||
|
183 2 # ·
|
||||||
|
Before Width: | Height: | Size: 810 B After Width: | Height: | Size: 959 B |
@@ -1,194 +0,0 @@
|
|||||||
# box width
|
|
||||||
8
|
|
||||||
# box height
|
|
||||||
8
|
|
||||||
# 32 espacio ( )
|
|
||||||
6
|
|
||||||
# 33 !
|
|
||||||
2
|
|
||||||
# 34 "
|
|
||||||
5
|
|
||||||
# 35 #
|
|
||||||
6
|
|
||||||
# 36 $
|
|
||||||
6
|
|
||||||
# 37 %
|
|
||||||
7
|
|
||||||
# 38 &
|
|
||||||
7
|
|
||||||
# 39 '
|
|
||||||
2
|
|
||||||
# 40 (
|
|
||||||
4
|
|
||||||
# 41 )
|
|
||||||
4
|
|
||||||
# 42 *
|
|
||||||
6
|
|
||||||
# 43 +
|
|
||||||
8
|
|
||||||
# 44 ,
|
|
||||||
2
|
|
||||||
# 45 -
|
|
||||||
7
|
|
||||||
# 46 .
|
|
||||||
2
|
|
||||||
# 47 /
|
|
||||||
7
|
|
||||||
# 48 0
|
|
||||||
7
|
|
||||||
# 49 1
|
|
||||||
6
|
|
||||||
# 50 2
|
|
||||||
6
|
|
||||||
# 51 3
|
|
||||||
6
|
|
||||||
# 52 4
|
|
||||||
7
|
|
||||||
# 53 5
|
|
||||||
6
|
|
||||||
# 54 6
|
|
||||||
6
|
|
||||||
# 55 7
|
|
||||||
6
|
|
||||||
# 56 8
|
|
||||||
6
|
|
||||||
# 57 9
|
|
||||||
6
|
|
||||||
# 58 :
|
|
||||||
2
|
|
||||||
# 59 ;
|
|
||||||
2
|
|
||||||
# 60 <
|
|
||||||
5
|
|
||||||
# 61 =
|
|
||||||
6
|
|
||||||
# 62 >
|
|
||||||
5
|
|
||||||
# 63 ?
|
|
||||||
6
|
|
||||||
# 64 @
|
|
||||||
6
|
|
||||||
# 65 A
|
|
||||||
6
|
|
||||||
# 66 B
|
|
||||||
7
|
|
||||||
# 67 C
|
|
||||||
7
|
|
||||||
# 68 D
|
|
||||||
7
|
|
||||||
# 69 E
|
|
||||||
7
|
|
||||||
# 70 F
|
|
||||||
7
|
|
||||||
# 71 G
|
|
||||||
7
|
|
||||||
# 72 H
|
|
||||||
6
|
|
||||||
# 73 I
|
|
||||||
6
|
|
||||||
# 74 J
|
|
||||||
7
|
|
||||||
# 75 K
|
|
||||||
7
|
|
||||||
# 76 L
|
|
||||||
7
|
|
||||||
# 77 M
|
|
||||||
7
|
|
||||||
# 78 N
|
|
||||||
7
|
|
||||||
# 79 O
|
|
||||||
7
|
|
||||||
# 80 P
|
|
||||||
7
|
|
||||||
# 81 Q
|
|
||||||
7
|
|
||||||
# 82 R
|
|
||||||
7
|
|
||||||
# 83 S
|
|
||||||
6
|
|
||||||
# 84 T
|
|
||||||
6
|
|
||||||
# 85 U
|
|
||||||
6
|
|
||||||
# 86 V
|
|
||||||
6
|
|
||||||
# 87 W
|
|
||||||
7
|
|
||||||
# 88 X
|
|
||||||
7
|
|
||||||
# 89 Y
|
|
||||||
6
|
|
||||||
# 90 Z
|
|
||||||
7
|
|
||||||
# 91 [
|
|
||||||
8
|
|
||||||
# 92 \
|
|
||||||
3
|
|
||||||
# 93 ]
|
|
||||||
7
|
|
||||||
# 94 ^
|
|
||||||
7
|
|
||||||
# 95 _
|
|
||||||
8
|
|
||||||
# 96 `
|
|
||||||
0
|
|
||||||
# 97 a
|
|
||||||
6
|
|
||||||
# 98 b
|
|
||||||
7
|
|
||||||
# 99 c
|
|
||||||
7
|
|
||||||
# 100 d
|
|
||||||
7
|
|
||||||
# 101 e
|
|
||||||
7
|
|
||||||
# 102 f
|
|
||||||
7
|
|
||||||
# 103 g
|
|
||||||
7
|
|
||||||
# 104 h
|
|
||||||
6
|
|
||||||
# 105 i
|
|
||||||
6
|
|
||||||
# 106 j
|
|
||||||
7
|
|
||||||
# 107 k
|
|
||||||
7
|
|
||||||
# 108 l
|
|
||||||
7
|
|
||||||
# 109 m
|
|
||||||
7
|
|
||||||
# 110 n
|
|
||||||
7
|
|
||||||
# 111 o
|
|
||||||
7
|
|
||||||
# 112 p
|
|
||||||
7
|
|
||||||
# 113 q
|
|
||||||
7
|
|
||||||
# 114 r
|
|
||||||
7
|
|
||||||
# 115 s
|
|
||||||
6
|
|
||||||
# 116 t
|
|
||||||
6
|
|
||||||
# 117 u
|
|
||||||
6
|
|
||||||
# 118 v
|
|
||||||
6
|
|
||||||
# 119 w
|
|
||||||
7
|
|
||||||
# 120 x
|
|
||||||
7
|
|
||||||
# 121 y
|
|
||||||
6
|
|
||||||
# 122 z
|
|
||||||
7
|
|
||||||
# 123 {
|
|
||||||
0
|
|
||||||
# 124 |
|
|
||||||
0
|
|
||||||
# 125 }
|
|
||||||
0
|
|
||||||
# 126 ~
|
|
||||||
0
|
|
||||||
133
data/font/smb2.fnt
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# Font: smb2 — generado desde Super Mario Bros. 2.ttf size 8
|
||||||
|
# Generado con tools/font_gen/font_gen.py
|
||||||
|
|
||||||
|
box_width 8
|
||||||
|
box_height 8
|
||||||
|
columns 15
|
||||||
|
|
||||||
|
# codepoint_decimal ancho_visual
|
||||||
|
32 7 # U+0020
|
||||||
|
33 7 # !
|
||||||
|
34 7 # "
|
||||||
|
35 7 # #
|
||||||
|
36 7 # $
|
||||||
|
37 7 # %
|
||||||
|
38 7 # &
|
||||||
|
39 7 # '
|
||||||
|
40 7 # (
|
||||||
|
41 7 # )
|
||||||
|
42 7 # *
|
||||||
|
43 7 # +
|
||||||
|
44 7 # ,
|
||||||
|
45 7 # -
|
||||||
|
46 7 # .
|
||||||
|
47 7 # /
|
||||||
|
48 7 # 0
|
||||||
|
49 7 # 1
|
||||||
|
50 7 # 2
|
||||||
|
51 7 # 3
|
||||||
|
52 7 # 4
|
||||||
|
53 7 # 5
|
||||||
|
54 7 # 6
|
||||||
|
55 7 # 7
|
||||||
|
56 7 # 8
|
||||||
|
57 7 # 9
|
||||||
|
58 7 # :
|
||||||
|
59 7 # ;
|
||||||
|
60 7 # <
|
||||||
|
61 7 # =
|
||||||
|
62 7 # >
|
||||||
|
63 7 # ?
|
||||||
|
64 7 # @
|
||||||
|
65 7 # A
|
||||||
|
66 7 # B
|
||||||
|
67 7 # C
|
||||||
|
68 7 # D
|
||||||
|
69 7 # E
|
||||||
|
70 7 # F
|
||||||
|
71 7 # G
|
||||||
|
72 7 # H
|
||||||
|
73 7 # I
|
||||||
|
74 7 # J
|
||||||
|
75 7 # K
|
||||||
|
76 7 # L
|
||||||
|
77 7 # M
|
||||||
|
78 7 # N
|
||||||
|
79 7 # O
|
||||||
|
80 7 # P
|
||||||
|
81 7 # Q
|
||||||
|
82 7 # R
|
||||||
|
83 7 # S
|
||||||
|
84 7 # T
|
||||||
|
85 7 # U
|
||||||
|
86 7 # V
|
||||||
|
87 7 # W
|
||||||
|
88 7 # X
|
||||||
|
89 7 # Y
|
||||||
|
90 7 # Z
|
||||||
|
91 7 # [
|
||||||
|
92 7 # \
|
||||||
|
93 7 # ]
|
||||||
|
94 7 # ^
|
||||||
|
95 7 # _
|
||||||
|
96 7 # `
|
||||||
|
97 7 # a
|
||||||
|
98 7 # b
|
||||||
|
99 7 # c
|
||||||
|
100 7 # d
|
||||||
|
101 7 # e
|
||||||
|
102 7 # f
|
||||||
|
103 7 # g
|
||||||
|
104 7 # h
|
||||||
|
105 7 # i
|
||||||
|
106 7 # j
|
||||||
|
107 7 # k
|
||||||
|
108 7 # l
|
||||||
|
109 7 # m
|
||||||
|
110 7 # n
|
||||||
|
111 7 # o
|
||||||
|
112 7 # p
|
||||||
|
113 7 # q
|
||||||
|
114 7 # r
|
||||||
|
115 7 # s
|
||||||
|
116 7 # t
|
||||||
|
117 7 # u
|
||||||
|
118 7 # v
|
||||||
|
119 7 # w
|
||||||
|
120 7 # x
|
||||||
|
121 7 # y
|
||||||
|
122 7 # z
|
||||||
|
123 7 # {
|
||||||
|
124 7 # |
|
||||||
|
125 7 # }
|
||||||
|
126 7 # ~
|
||||||
|
192 7 # À
|
||||||
|
193 7 # Á
|
||||||
|
200 7 # È
|
||||||
|
201 7 # É
|
||||||
|
205 7 # Í
|
||||||
|
207 7 # Ï
|
||||||
|
210 7 # Ò
|
||||||
|
211 7 # Ó
|
||||||
|
218 7 # Ú
|
||||||
|
220 7 # Ü
|
||||||
|
209 7 # Ñ
|
||||||
|
199 7 # Ç
|
||||||
|
224 7 # à
|
||||||
|
225 7 # á
|
||||||
|
232 7 # è
|
||||||
|
233 7 # é
|
||||||
|
237 7 # í
|
||||||
|
239 7 # ï
|
||||||
|
242 7 # ò
|
||||||
|
243 7 # ó
|
||||||
|
250 7 # ú
|
||||||
|
252 7 # ü
|
||||||
|
241 7 # ñ
|
||||||
|
231 7 # ç
|
||||||
|
161 7 # ¡
|
||||||
|
191 7 # ¿
|
||||||
|
171 7 # «
|
||||||
|
187 7 # »
|
||||||
|
183 7 # ·
|
||||||
|
228 7 # ä (corazón)
|
||||||
|
Before Width: | Height: | Size: 798 B After Width: | Height: | Size: 1001 B |
@@ -1,194 +0,0 @@
|
|||||||
# box width
|
|
||||||
8
|
|
||||||
# box height
|
|
||||||
8
|
|
||||||
# 32 espacio ( )
|
|
||||||
7
|
|
||||||
# 33 !
|
|
||||||
7
|
|
||||||
# 34 "
|
|
||||||
7
|
|
||||||
# 35 #
|
|
||||||
7
|
|
||||||
# 36 $
|
|
||||||
7
|
|
||||||
# 37 %
|
|
||||||
7
|
|
||||||
# 38 &
|
|
||||||
7
|
|
||||||
# 39 '
|
|
||||||
7
|
|
||||||
# 40 (
|
|
||||||
7
|
|
||||||
# 41 )
|
|
||||||
7
|
|
||||||
# 42 *
|
|
||||||
7
|
|
||||||
# 43 +
|
|
||||||
7
|
|
||||||
# 44 ,
|
|
||||||
7
|
|
||||||
# 45 -
|
|
||||||
7
|
|
||||||
# 46 .
|
|
||||||
7
|
|
||||||
# 47 /
|
|
||||||
7
|
|
||||||
# 48 0
|
|
||||||
7
|
|
||||||
# 49 1
|
|
||||||
7
|
|
||||||
# 50 2
|
|
||||||
7
|
|
||||||
# 51 3
|
|
||||||
7
|
|
||||||
# 52 4
|
|
||||||
7
|
|
||||||
# 53 5
|
|
||||||
7
|
|
||||||
# 54 6
|
|
||||||
7
|
|
||||||
# 55 7
|
|
||||||
7
|
|
||||||
# 56 8
|
|
||||||
7
|
|
||||||
# 57 9
|
|
||||||
7
|
|
||||||
# 58 :
|
|
||||||
7
|
|
||||||
# 59 ;
|
|
||||||
7
|
|
||||||
# 60 <
|
|
||||||
7
|
|
||||||
# 61 =
|
|
||||||
7
|
|
||||||
# 62 >
|
|
||||||
7
|
|
||||||
# 63 ?
|
|
||||||
7
|
|
||||||
# 64 @
|
|
||||||
7
|
|
||||||
# 65 A
|
|
||||||
7
|
|
||||||
# 66 B
|
|
||||||
7
|
|
||||||
# 67 C
|
|
||||||
7
|
|
||||||
# 68 D
|
|
||||||
7
|
|
||||||
# 69 E
|
|
||||||
7
|
|
||||||
# 70 F
|
|
||||||
7
|
|
||||||
# 71 G
|
|
||||||
7
|
|
||||||
# 72 H
|
|
||||||
7
|
|
||||||
# 73 I
|
|
||||||
7
|
|
||||||
# 74 J
|
|
||||||
7
|
|
||||||
# 75 K
|
|
||||||
7
|
|
||||||
# 76 L
|
|
||||||
7
|
|
||||||
# 77 M
|
|
||||||
7
|
|
||||||
# 78 N
|
|
||||||
7
|
|
||||||
# 79 O
|
|
||||||
7
|
|
||||||
# 80 P
|
|
||||||
7
|
|
||||||
# 81 Q
|
|
||||||
7
|
|
||||||
# 82 R
|
|
||||||
7
|
|
||||||
# 83 S
|
|
||||||
7
|
|
||||||
# 84 T
|
|
||||||
7
|
|
||||||
# 85 U
|
|
||||||
7
|
|
||||||
# 86 V
|
|
||||||
7
|
|
||||||
# 87 W
|
|
||||||
7
|
|
||||||
# 88 X
|
|
||||||
7
|
|
||||||
# 89 Y
|
|
||||||
7
|
|
||||||
# 90 Z
|
|
||||||
7
|
|
||||||
# 91 [
|
|
||||||
7
|
|
||||||
# 92 \
|
|
||||||
7
|
|
||||||
# 93 ]
|
|
||||||
7
|
|
||||||
# 94 ^
|
|
||||||
7
|
|
||||||
# 95 _
|
|
||||||
7
|
|
||||||
# 96 `
|
|
||||||
7
|
|
||||||
# 97 a
|
|
||||||
7
|
|
||||||
# 98 b
|
|
||||||
7
|
|
||||||
# 99 c
|
|
||||||
7
|
|
||||||
# 100 d
|
|
||||||
7
|
|
||||||
# 101 e
|
|
||||||
7
|
|
||||||
# 102 f
|
|
||||||
7
|
|
||||||
# 103 g
|
|
||||||
7
|
|
||||||
# 104 h
|
|
||||||
7
|
|
||||||
# 105 i
|
|
||||||
7
|
|
||||||
# 106 j
|
|
||||||
7
|
|
||||||
# 107 k
|
|
||||||
7
|
|
||||||
# 108 l
|
|
||||||
7
|
|
||||||
# 109 m
|
|
||||||
7
|
|
||||||
# 110 n
|
|
||||||
7
|
|
||||||
# 111 o
|
|
||||||
7
|
|
||||||
# 112 p
|
|
||||||
7
|
|
||||||
# 113 q
|
|
||||||
7
|
|
||||||
# 114 r
|
|
||||||
7
|
|
||||||
# 115 s
|
|
||||||
7
|
|
||||||
# 116 t
|
|
||||||
7
|
|
||||||
# 117 u
|
|
||||||
7
|
|
||||||
# 118 v
|
|
||||||
7
|
|
||||||
# 119 w
|
|
||||||
7
|
|
||||||
# 120 x
|
|
||||||
7
|
|
||||||
# 121 y
|
|
||||||
7
|
|
||||||
# 122 z
|
|
||||||
7
|
|
||||||
# 123 {
|
|
||||||
7
|
|
||||||
# 124 |
|
|
||||||
7
|
|
||||||
# 125 }
|
|
||||||
7
|
|
||||||
# 126 ~
|
|
||||||
7
|
|
||||||
132
data/font/subatomic.fnt
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# Font: subatomic — generado desde atomics.TTF size 6
|
||||||
|
# Generado con tools/font_gen/font_gen.py
|
||||||
|
|
||||||
|
box_width 7
|
||||||
|
box_height 7
|
||||||
|
columns 15
|
||||||
|
|
||||||
|
# codepoint_decimal ancho_visual
|
||||||
|
32 4 # U+0020
|
||||||
|
33 1 # !
|
||||||
|
34 3 # "
|
||||||
|
35 5 # #
|
||||||
|
36 5 # $
|
||||||
|
37 5 # %
|
||||||
|
38 6 # &
|
||||||
|
39 1 # '
|
||||||
|
40 2 # (
|
||||||
|
41 2 # )
|
||||||
|
42 5 # *
|
||||||
|
43 5 # +
|
||||||
|
44 1 # ,
|
||||||
|
45 5 # -
|
||||||
|
46 1 # .
|
||||||
|
47 5 # /
|
||||||
|
48 5 # 0
|
||||||
|
49 2 # 1
|
||||||
|
50 5 # 2
|
||||||
|
51 5 # 3
|
||||||
|
52 5 # 4
|
||||||
|
53 5 # 5
|
||||||
|
54 5 # 6
|
||||||
|
55 5 # 7
|
||||||
|
56 5 # 8
|
||||||
|
57 5 # 9
|
||||||
|
58 1 # :
|
||||||
|
59 1 # ;
|
||||||
|
60 3 # <
|
||||||
|
61 5 # =
|
||||||
|
62 3 # >
|
||||||
|
63 4 # ?
|
||||||
|
64 5 # @
|
||||||
|
65 5 # A
|
||||||
|
66 5 # B
|
||||||
|
67 5 # C
|
||||||
|
68 5 # D
|
||||||
|
69 4 # E
|
||||||
|
70 5 # F
|
||||||
|
71 5 # G
|
||||||
|
72 5 # H
|
||||||
|
73 1 # I
|
||||||
|
74 5 # J
|
||||||
|
75 5 # K
|
||||||
|
76 4 # L
|
||||||
|
77 5 # M
|
||||||
|
78 5 # N
|
||||||
|
79 5 # O
|
||||||
|
80 5 # P
|
||||||
|
81 5 # Q
|
||||||
|
82 5 # R
|
||||||
|
83 5 # S
|
||||||
|
84 5 # T
|
||||||
|
85 5 # U
|
||||||
|
86 5 # V
|
||||||
|
87 5 # W
|
||||||
|
88 5 # X
|
||||||
|
89 5 # Y
|
||||||
|
90 5 # Z
|
||||||
|
91 2 # [
|
||||||
|
92 5 # \
|
||||||
|
93 2 # ]
|
||||||
|
94 3 # ^
|
||||||
|
95 5 # _
|
||||||
|
96 2 # `
|
||||||
|
97 4 # a
|
||||||
|
98 4 # b
|
||||||
|
99 3 # c
|
||||||
|
100 4 # d
|
||||||
|
101 4 # e
|
||||||
|
102 3 # f
|
||||||
|
103 4 # g
|
||||||
|
104 4 # h
|
||||||
|
105 1 # i
|
||||||
|
106 2 # j
|
||||||
|
107 3 # k
|
||||||
|
108 1 # l
|
||||||
|
109 5 # m
|
||||||
|
110 4 # n
|
||||||
|
111 4 # o
|
||||||
|
112 4 # p
|
||||||
|
113 4 # q
|
||||||
|
114 3 # r
|
||||||
|
115 4 # s
|
||||||
|
116 2 # t
|
||||||
|
117 4 # u
|
||||||
|
118 4 # v
|
||||||
|
119 5 # w
|
||||||
|
120 3 # x
|
||||||
|
121 4 # y
|
||||||
|
122 4 # z
|
||||||
|
123 3 # {
|
||||||
|
124 1 # |
|
||||||
|
125 3 # }
|
||||||
|
126 4 # ~
|
||||||
|
192 5 # À
|
||||||
|
193 5 # Á
|
||||||
|
200 4 # È
|
||||||
|
201 4 # É
|
||||||
|
205 1 # Í
|
||||||
|
207 1 # Ï
|
||||||
|
210 5 # Ò
|
||||||
|
211 5 # Ó
|
||||||
|
218 5 # Ú
|
||||||
|
220 5 # Ü
|
||||||
|
209 5 # Ñ
|
||||||
|
199 5 # Ç
|
||||||
|
224 4 # à
|
||||||
|
225 4 # á
|
||||||
|
232 4 # è
|
||||||
|
233 4 # é
|
||||||
|
237 1 # í
|
||||||
|
239 1 # ï
|
||||||
|
242 4 # ò
|
||||||
|
243 4 # ó
|
||||||
|
250 4 # ú
|
||||||
|
252 4 # ü
|
||||||
|
241 4 # ñ
|
||||||
|
231 3 # ç
|
||||||
|
161 1 # ¡
|
||||||
|
191 4 # ¿
|
||||||
|
171 3 # «
|
||||||
|
187 3 # »
|
||||||
|
183 1 # ·
|
||||||
|
Before Width: | Height: | Size: 540 B After Width: | Height: | Size: 648 B |
@@ -1,194 +0,0 @@
|
|||||||
# box width
|
|
||||||
7
|
|
||||||
# box height
|
|
||||||
7
|
|
||||||
# 32 espacio ( )
|
|
||||||
4
|
|
||||||
# 33 !
|
|
||||||
1
|
|
||||||
# 34 "
|
|
||||||
3
|
|
||||||
# 35 #
|
|
||||||
5
|
|
||||||
# 36 $
|
|
||||||
5
|
|
||||||
# 37 %
|
|
||||||
5
|
|
||||||
# 38 &
|
|
||||||
6
|
|
||||||
# 39 '
|
|
||||||
1
|
|
||||||
# 40 (
|
|
||||||
2
|
|
||||||
# 41 )
|
|
||||||
2
|
|
||||||
# 42 *
|
|
||||||
5
|
|
||||||
# 43 +
|
|
||||||
5
|
|
||||||
# 44 ,
|
|
||||||
1
|
|
||||||
# 45 -
|
|
||||||
5
|
|
||||||
# 46 .
|
|
||||||
1
|
|
||||||
# 47 /
|
|
||||||
5
|
|
||||||
# 48 0
|
|
||||||
5
|
|
||||||
# 49 1
|
|
||||||
2
|
|
||||||
# 50 2
|
|
||||||
5
|
|
||||||
# 51 3
|
|
||||||
5
|
|
||||||
# 52 4
|
|
||||||
5
|
|
||||||
# 53 5
|
|
||||||
5
|
|
||||||
# 54 6
|
|
||||||
5
|
|
||||||
# 55 7
|
|
||||||
5
|
|
||||||
# 56 8
|
|
||||||
5
|
|
||||||
# 57 9
|
|
||||||
5
|
|
||||||
# 58 :
|
|
||||||
1
|
|
||||||
# 59 ;
|
|
||||||
2
|
|
||||||
# 60 <
|
|
||||||
3
|
|
||||||
# 61 =
|
|
||||||
5
|
|
||||||
# 62 >
|
|
||||||
3
|
|
||||||
# 63 ?
|
|
||||||
4
|
|
||||||
# 64 @
|
|
||||||
5
|
|
||||||
# 65 A
|
|
||||||
5
|
|
||||||
# 66 B
|
|
||||||
5
|
|
||||||
# 67 C
|
|
||||||
5
|
|
||||||
# 68 D
|
|
||||||
5
|
|
||||||
# 69 E
|
|
||||||
4
|
|
||||||
# 70 F
|
|
||||||
5
|
|
||||||
# 71 G
|
|
||||||
5
|
|
||||||
# 72 H
|
|
||||||
5
|
|
||||||
# 73 I
|
|
||||||
1
|
|
||||||
# 74 J
|
|
||||||
5
|
|
||||||
# 75 K
|
|
||||||
5
|
|
||||||
# 76 L
|
|
||||||
2
|
|
||||||
# 77 M
|
|
||||||
5
|
|
||||||
# 78 N
|
|
||||||
5
|
|
||||||
# 79 O
|
|
||||||
5
|
|
||||||
# 80 P
|
|
||||||
5
|
|
||||||
# 81 Q
|
|
||||||
5
|
|
||||||
# 82 R
|
|
||||||
5
|
|
||||||
# 83 S
|
|
||||||
5
|
|
||||||
# 84 T
|
|
||||||
5
|
|
||||||
# 85 U
|
|
||||||
5
|
|
||||||
# 86 V
|
|
||||||
5
|
|
||||||
# 87 W
|
|
||||||
5
|
|
||||||
# 88 X
|
|
||||||
5
|
|
||||||
# 89 Y
|
|
||||||
5
|
|
||||||
# 90 Z
|
|
||||||
5
|
|
||||||
# 91 [
|
|
||||||
2
|
|
||||||
# 92 \
|
|
||||||
5
|
|
||||||
# 93 ]
|
|
||||||
2
|
|
||||||
# 94 ^
|
|
||||||
3
|
|
||||||
# 95 _
|
|
||||||
5
|
|
||||||
# 96 `
|
|
||||||
2
|
|
||||||
# 97 a
|
|
||||||
4
|
|
||||||
# 98 b
|
|
||||||
4
|
|
||||||
# 99 c
|
|
||||||
3
|
|
||||||
# 100 d
|
|
||||||
4
|
|
||||||
# 101 e
|
|
||||||
4
|
|
||||||
# 102 f
|
|
||||||
3
|
|
||||||
# 103 g
|
|
||||||
4
|
|
||||||
# 104 h
|
|
||||||
4
|
|
||||||
# 105 i
|
|
||||||
1
|
|
||||||
# 106 j
|
|
||||||
2
|
|
||||||
# 107 k
|
|
||||||
3
|
|
||||||
# 108 l
|
|
||||||
1
|
|
||||||
# 109 m
|
|
||||||
5
|
|
||||||
# 110 n
|
|
||||||
4
|
|
||||||
# 111 o
|
|
||||||
4
|
|
||||||
# 112 p
|
|
||||||
4
|
|
||||||
# 113 q
|
|
||||||
4
|
|
||||||
# 114 r
|
|
||||||
3
|
|
||||||
# 115 s
|
|
||||||
4
|
|
||||||
# 116 t
|
|
||||||
2
|
|
||||||
# 117 u
|
|
||||||
4
|
|
||||||
# 118 v
|
|
||||||
4
|
|
||||||
# 119 w
|
|
||||||
5
|
|
||||||
# 120 x
|
|
||||||
3
|
|
||||||
# 121 y
|
|
||||||
4
|
|
||||||
# 122 z
|
|
||||||
4
|
|
||||||
# 123 {
|
|
||||||
0
|
|
||||||
# 124 |
|
|
||||||
0
|
|
||||||
# 125 }
|
|
||||||
0
|
|
||||||
# 126 ~
|
|
||||||
0
|
|
||||||
@@ -1,117 +1,118 @@
|
|||||||
# JailDoctor's Dilemma - Catalan Locale
|
# JailDoctor's Dilemma - Catalan Locale
|
||||||
# lang: ca
|
# lang: ca
|
||||||
# Nota: s'utilitzen nomes caracters ASCII per compatibilitat amb la font del joc
|
|
||||||
|
|
||||||
title:
|
title:
|
||||||
marquee: "EH JAILEROS!! ES EL 2022 I ENCARA HO PETEM COM EL 1998!!! HEU SENTIT? ELS JAILGAMES HAN TORNAT!! SIII HAN TORNAT!! MES DE 10 TITOLS A LA CUINA DEL JAILDOC!! AIXO ES MOLT, PERO QUIN SERA EL PRIMER? TAMBE HI HA UN NOU APARELL QUE US FARA VOLAR EL CAP AMB JAILGAMES A TOT ARREU: P.A.C.O. PERO ESPERA! QUE ES AQUELLA BELLESA QUE VEIG ALLA? OOOH AQUELLA PETITA MINIASCII ES PUR AMOR!! VULL LLEPAR CADA BYTE! OH MERDA! I NO OBLIDEU PORTAR AQUELLS VELLS I GRASSOS JAILGAMES DE MS-DOS A GITHUB PER MANTENIR-LOS VIUS!! QUIN SERA EL PROPER LLANCAMENT DEL JAILDOC? QUIN PROJECTE COBRARA VIDA?? OH NANOS NO HO SABEM PERO AQUI PODEU TROBAR LA RESPOSTA, NOMES HEU DE COMPLETAR EL DILEMA DEL JAILDOCTOR ... PODEU?"
|
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:
|
||||||
prompt0: "PREM TECLA PER ESQUERRA"
|
prompt0: "PREM UNA TECLA PER A ESQUERRA"
|
||||||
prompt1: "PREM TECLA PER DRETA"
|
prompt1: "PREM UNA TECLA PER A DRETA"
|
||||||
prompt2: "PREM TECLA PER SALTAR"
|
prompt2: "PREM UNA TECLA PER A SALTAR"
|
||||||
defined: "TECLES DEFINIDES"
|
defined: "TECLES DEFINIDES"
|
||||||
label0: "ESQUERRA: "
|
label0: "ESQUERRA: "
|
||||||
label1: "DRETA: "
|
label1: "DRETA: "
|
||||||
label2: "SALTAR: "
|
label2: "SALTAR: "
|
||||||
invalid: "TECLA INVALIDA! PROVA UNA ALTRA"
|
invalid: "TECLA INVÀLIDA! PROVA'N UNA ALTRA"
|
||||||
already_used: "TECLA JA USADA! PROVA UNA ALTRA"
|
already_used: "TECLA JA USADA! PROVA'N UNA ALTRA"
|
||||||
buttons:
|
buttons:
|
||||||
prompt0: "PREM BOTO PER ESQUERRA"
|
prompt0: "PREM UN BOTÓ PER A ESQUERRA"
|
||||||
prompt1: "PREM BOTO PER DRETA"
|
prompt1: "PREM UN BOTÓ PER A DRETA"
|
||||||
prompt2: "PREM BOTO PER SALTAR"
|
prompt2: "PREM UN BOTÓ PER A SALTAR"
|
||||||
defined: "BOTONS DEFINITS"
|
defined: "BOTONS DEFINITS"
|
||||||
already_used: "BOTO JA USAT! PROVA UN ALTRE"
|
already_used: "BOTÓ JA USAT! PROVA'N UN ALTRE"
|
||||||
projects: "PROJECTES"
|
projects: "PROJECTES"
|
||||||
|
|
||||||
game_over:
|
game_over:
|
||||||
title: "G A M E O V E R"
|
title: "G A M E O V E R"
|
||||||
items: "OBJECTES: "
|
items: "OBJECTES: "
|
||||||
rooms: "SALES: "
|
rooms: "SALES: "
|
||||||
worst_nightmare: "EL TEU PITJOR MALSON ES"
|
worst_nightmare: "EL TEU PITJOR MALSON ÉS"
|
||||||
|
|
||||||
ending:
|
ending:
|
||||||
t0: "FINALMENT HO VA ACONSEGUIR"
|
t0: "FINALMENT HO VA ACONSEGUIR"
|
||||||
t1: "ARRIBAR A LA JAIL"
|
t1: "ARRIBAR A LA JAIL"
|
||||||
t2: "AMB TOTS ELS SEUS PROJECTES"
|
t2: "AMB TOTS ELS SEUS PROJECTES"
|
||||||
t3: "A PUNT PER SER ALLIBERATS"
|
t3: "A PUNT D'ALLIBERAR-LOS"
|
||||||
t4: "ALLI ESTAVEN TOTS ELS JAILERS"
|
t4: "ALLÍ ESTAVEN TOTS ELS JAILERS"
|
||||||
t5: "ESPERANT QUE ELS JAILGAMES"
|
t5: "ESPERANT QUE ELS JAILGAMES"
|
||||||
t6: "FOSSIN ALLIBERATS"
|
t6: "FOREN ALLIBERATS"
|
||||||
t7: "HI HAVIA FINS I TOT BARRULLS"
|
t7: "HI HAVIA FINS I TOT BARRULLS"
|
||||||
t8: "I BEGGINERS ENTRE LA MULTITUD"
|
t8: "I BEGGINERS ENTRE LA GENT"
|
||||||
t9: "BRY ESTAVA PLORANT..."
|
t9: "BRY ESTAVA PLORANT..."
|
||||||
t10: "PERO DE SOBTE ALGUNA COSA"
|
t10: "PERÒ DE SOBTE ALGUNA COSA"
|
||||||
t11: "VA ATREURE LA SEVA ATENCIO"
|
t11: "LI VA CRIDAR L'ATENCIÓ"
|
||||||
t12: "UN MUNT DE FERALLA!"
|
t12: "UN MUNT DE FERRALLA!"
|
||||||
t13: "PLE DE TRASTOS QUE NO FUNCIONEN!!"
|
t13: "PLE DE TRASTOS QUE NI ANAVEN!!"
|
||||||
t14: "I ALESHORES,"
|
t14: "I ALESHORES,"
|
||||||
t15: "QUARANTA NOUS PROJECTES"
|
t15: "QUARANTA PROJECTES NOUS"
|
||||||
t16: "VAN NEIXER..."
|
t16: "VAN NÀIXER..."
|
||||||
|
|
||||||
ending2:
|
ending2:
|
||||||
starring: "PROTAGONISTES"
|
starring: "PROTAGONISTES"
|
||||||
jaildoctor: "JAILDOCTOR"
|
jaildoctor: "JAILDOCTOR"
|
||||||
thank_you: "GRACIES"
|
thank_you: "GRÀCIES"
|
||||||
for_playing: "PER JUGAR!"
|
for_playing: "PER JUGAR!"
|
||||||
|
|
||||||
credits:
|
credits:
|
||||||
instructions: "INSTRUCCIONS:"
|
instructions: "INSTRUCCIONS:"
|
||||||
l0: "AJUDA A JAILDOC A RECUPERAR"
|
l0: "AJUDA A JAILDOC A RECUPERAR"
|
||||||
l1: "ELS SEUS PROJECTES I ANAR A"
|
l1: "ELS SEUS PROJECTES I ARRIBAR"
|
||||||
l2: "LA JAIL PER ACABAR-LOS"
|
l2: "A LA JAIL PER ACABAR-LOS"
|
||||||
keys: "TECLES:"
|
keys: "TECLES:"
|
||||||
keys_move: "CURSORS PER MOURE I SALTAR"
|
keys_move: "CURSORS PER A MOURE I SALTAR"
|
||||||
f8: "F8 ACTIVAR/DESACTIVAR MUSICA"
|
f8: "F8 ACTIVAR/DESACTIVAR MÚSICA"
|
||||||
f11: "F11 PAUSAR EL JOC"
|
f11: "F11 PAUSAR EL JOC"
|
||||||
f1f2: "F1-F2 MIDA DE LA FINESTRA"
|
f1f2: "F1-F2 MIDA DE LA FINESTRA"
|
||||||
f3: "F3 PANTALLA COMPLETA"
|
f3: "F3 PANTALLA COMPLETA"
|
||||||
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!"
|
||||||
c1: "COSES BRILLANTS"
|
c1: "COSES BRILLANTS"
|
||||||
d1: "Obteniu el 25% dels objectes"
|
d1: "Aconseguiu el 25% dels objectes"
|
||||||
c2: "A MEITAT DE CAMI"
|
c2: "A MITJAN CAMÍ"
|
||||||
d2: "Obteniu el 50% dels objectes"
|
d2: "Aconseguiu el 50% dels objectes"
|
||||||
c3: "QUASI HI SOM"
|
c3: "QUASI HI SOM"
|
||||||
d3: "Obteniu el 75% dels objectes"
|
d3: "Aconseguiu el 75% dels objectes"
|
||||||
c4: "EL COL LECCIONISTA"
|
c4: "EL COL·LECCIONISTA"
|
||||||
d4: "Obteniu el 100% dels objectes"
|
d4: "Aconseguiu el 100% dels objectes"
|
||||||
c5: "PASSEJANT PER AQUI"
|
c5: "PASSEJANT PER ACÍ"
|
||||||
d5: "Visiteu 20 sales"
|
d5: "Visiteu 20 sales"
|
||||||
c6: "M'HE PERDUT"
|
c6: "M'HE PERDUT"
|
||||||
d6: "Visiteu 40 sales"
|
d6: "Visiteu 40 sales"
|
||||||
c7: "M'AGRADA EXPLORAR"
|
c7: "M'AGRADA EXPLORAR"
|
||||||
d7: "Visiteu totes les sales"
|
d7: "Visiteu totes les sales"
|
||||||
c8: "JA ESTA?"
|
c8: "JA ESTÀ?"
|
||||||
d8: "Completa el joc"
|
d8: "Completeu el joc"
|
||||||
c9: "EM VA XUCLAR UN FORAT"
|
c9: "UN FORAT EM VA ENGOLIR"
|
||||||
d9: "Completa el joc sense entrar a la preso"
|
d9: "Completeu el joc sense entrar a la presó"
|
||||||
c10: "ELS MEUS PROJECTES"
|
c10: "ELS MEUS PROJECTES"
|
||||||
d10: "Completa el joc amb tots els objectes"
|
d10: "Completeu el joc amb tots els objectes"
|
||||||
c11: "M'AGRADEN ELS MEUS AMICS DE COLORS"
|
c11: "M'AGRADEN ELS MEUS AMICS DE COLORS"
|
||||||
d11: "Completa el joc sense morir"
|
d11: "Completeu el joc sense morir"
|
||||||
c12: "PROJECTES MALS FETS DE PRESSA"
|
c12: "PROJECTES A CORRE-CUITA"
|
||||||
d12: "Completa el joc en menys de 30 minuts"
|
d12: "Completeu el joc en menys de 30 minuts"
|
||||||
|
|
||||||
ui:
|
ui:
|
||||||
press_again_menu: "PREM DE NOU PER TORNAR AL MENU"
|
press_again_menu: "PREM DE NOU PER TORNAR AL MENÚ"
|
||||||
press_again_exit: "PREM DE NOU PER SORTIR"
|
press_again_exit: "PREM DE NOU PER EIXIR"
|
||||||
border_enabled: "BORDE ACTIVAT"
|
border_enabled: "VORA ACTIVADA"
|
||||||
border_disabled: "BORDE DESACTIVAT"
|
border_disabled: "VORA DESACTIVADA"
|
||||||
fullscreen_enabled: "PANTALLA COMPLETA ACTIVADA"
|
fullscreen_enabled: "PANTALLA COMPLETA ACTIVADA"
|
||||||
fullscreen_disabled: "PANTALLA COMPLETA DESACTIVADA"
|
fullscreen_disabled: "PANTALLA COMPLETA DESACTIVADA"
|
||||||
window_zoom: "ZOOM FINESTRA x"
|
window_zoom: "ZOOM FINESTRA x"
|
||||||
postfx_enabled: "POSTFX ACTIVAT"
|
postfx_enabled: "POSTFX ACTIVAT"
|
||||||
postfx_disabled: "POSTFX DESACTIVAT"
|
postfx_disabled: "POSTFX DESACTIVAT"
|
||||||
postfx: "POSTFX"
|
postfx: "POSTFX"
|
||||||
|
supersampling_enabled: "SUPERMOSTREIG ACTIVAT"
|
||||||
|
supersampling_disabled: "SUPERMOSTREIG DESACTIVAT"
|
||||||
palette: "PALETA"
|
palette: "PALETA"
|
||||||
integer_scale_enabled: "ESCALAT SENCER ACTIVAT"
|
integer_scale_enabled: "ESCALAT SENCER ACTIVAT"
|
||||||
integer_scale_disabled: "ESCALAT SENCER DESACTIVAT"
|
integer_scale_disabled: "ESCALAT SENCER DESACTIVAT"
|
||||||
@@ -124,8 +125,8 @@ scoreboard:
|
|||||||
rooms: "SALES"
|
rooms: "SALES"
|
||||||
|
|
||||||
game:
|
game:
|
||||||
music_enabled: "MUSICA ACTIVADA"
|
music_enabled: "MÚSICA ACTIVADA"
|
||||||
music_disabled: "MUSICA DESACTIVADA"
|
music_disabled: "MÚSICA DESACTIVADA"
|
||||||
paused: "JOC EN PAUSA"
|
paused: "JOC EN PAUSA"
|
||||||
running: "JOC EN MARXA"
|
running: "JOC EN MARXA"
|
||||||
enabled: " ACTIVAT"
|
enabled: " ACTIVAT"
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ ui:
|
|||||||
postfx_enabled: "POSTFX ENABLED"
|
postfx_enabled: "POSTFX ENABLED"
|
||||||
postfx_disabled: "POSTFX DISABLED"
|
postfx_disabled: "POSTFX DISABLED"
|
||||||
postfx: "POSTFX"
|
postfx: "POSTFX"
|
||||||
|
supersampling_enabled: "SUPERSAMPLING ON"
|
||||||
|
supersampling_disabled: "SUPERSAMPLING OFF"
|
||||||
palette: "PALETTE"
|
palette: "PALETTE"
|
||||||
integer_scale_enabled: "INTEGER SCALE ENABLED"
|
integer_scale_enabled: "INTEGER SCALE ENABLED"
|
||||||
integer_scale_disabled: "INTEGER SCALE DISABLED"
|
integer_scale_disabled: "INTEGER SCALE DISABLED"
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
|
}
|
||||||
@@ -22,6 +22,10 @@ layout(set = 3, binding = 0) uniform PostFXUniforms {
|
|||||||
float gamma_strength;
|
float gamma_strength;
|
||||||
float curvature;
|
float curvature;
|
||||||
float bleeding;
|
float bleeding;
|
||||||
|
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
|
||||||
|
float time; // seconds since SDL init
|
||||||
|
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
|
||||||
|
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
|
||||||
@@ -64,23 +68,25 @@ void main() {
|
|||||||
// Muestra base
|
// Muestra base
|
||||||
vec3 base = texture(scene, uv).rgb;
|
vec3 base = texture(scene, uv).rgb;
|
||||||
|
|
||||||
// Sangrado NTSC — difuminado horizontal de crominancia
|
// Sangrado NTSC — difuminado horizontal de crominancia.
|
||||||
|
// step = 1 pixel lógico de juego en UV (corrige SS: textureSize.x = game_w * oversample).
|
||||||
vec3 colour;
|
vec3 colour;
|
||||||
if (u.bleeding > 0.0) {
|
if (u.bleeding > 0.0) {
|
||||||
float tw = float(textureSize(scene, 0).x);
|
float tw = float(textureSize(scene, 0).x);
|
||||||
|
float step = u.oversample / tw; // 1 pixel lógico en UV
|
||||||
vec3 ycc = rgb_to_ycc(base);
|
vec3 ycc = rgb_to_ycc(base);
|
||||||
vec3 ycc_l2 = rgb_to_ycc(texture(scene, uv - vec2(2.0/tw, 0.0)).rgb);
|
vec3 ycc_l2 = rgb_to_ycc(texture(scene, uv - vec2(2.0*step, 0.0)).rgb);
|
||||||
vec3 ycc_l1 = rgb_to_ycc(texture(scene, uv - vec2(1.0/tw, 0.0)).rgb);
|
vec3 ycc_l1 = rgb_to_ycc(texture(scene, uv - vec2(1.0*step, 0.0)).rgb);
|
||||||
vec3 ycc_r1 = rgb_to_ycc(texture(scene, uv + vec2(1.0/tw, 0.0)).rgb);
|
vec3 ycc_r1 = rgb_to_ycc(texture(scene, uv + vec2(1.0*step, 0.0)).rgb);
|
||||||
vec3 ycc_r2 = rgb_to_ycc(texture(scene, uv + vec2(2.0/tw, 0.0)).rgb);
|
vec3 ycc_r2 = rgb_to_ycc(texture(scene, uv + vec2(2.0*step, 0.0)).rgb);
|
||||||
ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0 + ycc.yz*2.0 + ycc_r1.yz*2.0 + ycc_r2.yz) / 8.0;
|
ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0 + ycc.yz*2.0 + ycc_r1.yz*2.0 + ycc_r2.yz) / 8.0;
|
||||||
colour = mix(base, ycc_to_rgb(ycc), u.bleeding);
|
colour = mix(base, ycc_to_rgb(ycc), u.bleeding);
|
||||||
} else {
|
} else {
|
||||||
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;
|
||||||
|
|
||||||
@@ -90,14 +96,24 @@ void main() {
|
|||||||
colour = mix(colour, lin, u.gamma_strength);
|
colour = mix(colour, lin, u.gamma_strength);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scanlines
|
// Scanlines — proporción 2/3 brillantes + 1/3 oscuras por fila lógica.
|
||||||
float texHeight = float(textureSize(scene, 0).y);
|
// Casos especiales: 1 subfila → sin efecto; 2 subfilas → 1+1 (50/50).
|
||||||
float scaleY = u.screen_height / texHeight;
|
// Constantes ajustables:
|
||||||
float screenY = uv.y * u.screen_height;
|
const float SCAN_DARK_RATIO = 0.333; // fracción de subfilas oscuras (ps >= 3)
|
||||||
float posInRow = mod(screenY, scaleY);
|
const float SCAN_DARK_FLOOR = 0.42; // multiplicador de brillo de subfilas oscuras
|
||||||
float scanLineDY = posInRow / scaleY - 0.5;
|
if (u.scanline_strength > 0.0) {
|
||||||
float scan = max(1.0 - scanLineDY * scanLineDY * 6.0, 0.12) * 3.5;
|
float ps = max(1.0, round(u.pixel_scale));
|
||||||
colour *= mix(1.0, scan, u.scanline_strength);
|
float frac_in_row = fract(uv.y * u.screen_height);
|
||||||
|
float row_pos = floor(frac_in_row * ps);
|
||||||
|
// bright_rows: cuántas subfilas son brillantes
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
if (u.gamma_strength > 0.0) {
|
if (u.gamma_strength > 0.0) {
|
||||||
vec3 enc = pow(colour, vec3(1.0 / 2.2));
|
vec3 enc = pow(colour, vec3(1.0 / 2.2));
|
||||||
@@ -109,7 +125,8 @@ void main() {
|
|||||||
float vignette = 1.0 - dot(d, d) * u.vignette_strength;
|
float vignette = 1.0 - dot(d, d) * u.vignette_strength;
|
||||||
colour *= clamp(vignette, 0.0, 1.0);
|
colour *= clamp(vignette, 0.0, 1.0);
|
||||||
|
|
||||||
// Máscara de fósforo RGB
|
// Máscara de fósforo RGB — después de scanlines (orden original):
|
||||||
|
// filas brillantes saturadas → máscara invisible, filas oscuras → RGB visible.
|
||||||
if (u.mask_strength > 0.0) {
|
if (u.mask_strength > 0.0) {
|
||||||
float whichMask = fract(gl_FragCoord.x * 0.3333333);
|
float whichMask = fract(gl_FragCoord.x * 0.3333333);
|
||||||
vec3 mask = vec3(0.80);
|
vec3 mask = vec3(0.80);
|
||||||
@@ -122,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
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
@@ -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,61 +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() {
|
||||||
|
Screen::get()->toggleSupersampling();
|
||||||
|
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)) {
|
||||||
@@ -146,10 +152,13 @@ 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 (Options::video.postfx && ((SDL_GetModState() & SDL_KMOD_SHIFT) != 0U)) {
|
if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
|
||||||
return InputAction::NEXT_POSTFX_PRESET;
|
return InputAction::TOGGLE_SUPERSAMPLING; // Ctrl+F4
|
||||||
}
|
}
|
||||||
return InputAction::TOGGLE_POSTFX;
|
if (Options::video.postfx && ((SDL_GetModState() & SDL_KMOD_SHIFT) != 0U)) {
|
||||||
|
return InputAction::NEXT_POSTFX_PRESET; // Shift+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;
|
||||||
@@ -169,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,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:
|
||||||
@@ -225,6 +253,10 @@ namespace GlobalInputs {
|
|||||||
handleNextPostFXPreset();
|
handleNextPostFXPreset();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case InputAction::TOGGLE_SUPERSAMPLING:
|
||||||
|
handleToggleSupersampling();
|
||||||
|
break;
|
||||||
|
|
||||||
case InputAction::NEXT_PALETTE:
|
case InputAction::NEXT_PALETTE:
|
||||||
handleNextPalette();
|
handleNextPalette();
|
||||||
break;
|
break;
|
||||||
@@ -241,6 +273,19 @@ namespace GlobalInputs {
|
|||||||
handleToggleVSync();
|
handleToggleVSync();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case InputAction::TOGGLE_CONSOLE:
|
||||||
|
if (Console::get() != nullptr) { Console::get()->toggle(); }
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
case InputAction::TOGGLE_DEBUG:
|
||||||
|
if (RenderInfo::get() != nullptr) { RenderInfo::get()->toggle(); }
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InputAction::SHOW_DEBUG_INFO:
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case InputAction::NONE:
|
case InputAction::NONE:
|
||||||
default:
|
default:
|
||||||
// No se presionó ninguna acción global
|
// No se presionó ninguna acción global
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,14 @@ enum class InputAction : int { // Acciones de entrada posibles en el juego
|
|||||||
TOGGLE_INTEGER_SCALE,
|
TOGGLE_INTEGER_SCALE,
|
||||||
TOGGLE_POSTFX,
|
TOGGLE_POSTFX,
|
||||||
NEXT_POSTFX_PRESET,
|
NEXT_POSTFX_PRESET,
|
||||||
|
TOGGLE_SUPERSAMPLING,
|
||||||
TOGGLE_BORDER,
|
TOGGLE_BORDER,
|
||||||
TOGGLE_MUSIC,
|
TOGGLE_MUSIC,
|
||||||
NEXT_PALETTE,
|
NEXT_PALETTE,
|
||||||
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
@@ -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
@@ -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,18 +504,53 @@ 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)
|
||||||
|
void Screen::toggleSupersampling() {
|
||||||
|
Options::video.supersampling = !Options::video.supersampling;
|
||||||
|
if (Options::video.postfx && shader_backend_ && shader_backend_->isHardwareAccelerated()) {
|
||||||
|
applyCurrentPostFXPreset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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)];
|
||||||
Rendering::PostFXParams params{.vignette = p.vignette, .scanlines = p.scanlines, .chroma = p.chroma, .mask = p.mask, .gamma = p.gamma, .curvature = p.curvature, .bleeding = p.bleeding};
|
// Supersampling es un toggle global (Options::video.supersampling), no por preset.
|
||||||
|
// setOversample primero: puede recrear texturas antes de que setPostFXParams
|
||||||
|
// decida si hornear scanlines en CPU o aplicarlas en GPU.
|
||||||
|
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, .flicker = p.flicker};
|
||||||
shader_backend_->setPostFXParams(params);
|
shader_backend_->setPostFXParams(params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -476,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();
|
||||||
@@ -492,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;
|
||||||
@@ -597,10 +688,10 @@ 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"));
|
||||||
|
|
||||||
// Crea el objeto de texto (el constructor de Text carga el archivo text_file internamente)
|
// Crea el objeto de texto (el constructor de Text carga el archivo text_file internamente)
|
||||||
text_ = std::make_shared<Text>(surface, Resource::List::get()->get("aseprite.txt"));
|
text_ = std::make_shared<Text>(surface, Resource::List::get()->get("aseprite.fnt"));
|
||||||
}
|
}
|
||||||
@@ -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,23 +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 reloadPostFX(); // Recarga el shader del preset actual sin toggle
|
void toggleSupersampling(); // Activa/desactiva el supersampling global
|
||||||
|
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
|
||||||
@@ -116,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
|
||||||
@@ -138,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
|
||||||
@@ -154,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
@@ -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__
|
||||||
@@ -54,6 +56,10 @@ struct PostFXUniforms {
|
|||||||
float gamma_strength;
|
float gamma_strength;
|
||||||
float curvature;
|
float curvature;
|
||||||
float bleeding;
|
float bleeding;
|
||||||
|
float pixel_scale;
|
||||||
|
float time;
|
||||||
|
float oversample; // 1.0 = sin SS, 3.0 = 3× supersampling
|
||||||
|
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz
|
||||||
};
|
};
|
||||||
|
|
||||||
// YCbCr helpers for NTSC bleeding
|
// YCbCr helpers for NTSC bleeding
|
||||||
@@ -98,23 +104,25 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]],
|
|||||||
// Muestra base
|
// Muestra base
|
||||||
float3 base = scene.sample(samp, uv).rgb;
|
float3 base = scene.sample(samp, uv).rgb;
|
||||||
|
|
||||||
// Sangrado NTSC — difuminado horizontal de crominancia
|
// Sangrado NTSC — difuminado horizontal de crominancia.
|
||||||
|
// step = 1 pixel de juego en espacio UV (corrige SS: scene.get_width() = game_w * oversample).
|
||||||
float3 colour;
|
float3 colour;
|
||||||
if (u.bleeding > 0.0f) {
|
if (u.bleeding > 0.0f) {
|
||||||
float tw = float(scene.get_width());
|
float tw = float(scene.get_width());
|
||||||
float3 ycc = rgb_to_ycc(base);
|
float step = u.oversample / tw; // 1 pixel lógico en UV
|
||||||
float3 ycc_l2 = rgb_to_ycc(scene.sample(samp, uv - float2(2.0f/tw, 0.0f)).rgb);
|
float3 ycc = rgb_to_ycc(base);
|
||||||
float3 ycc_l1 = rgb_to_ycc(scene.sample(samp, uv - float2(1.0f/tw, 0.0f)).rgb);
|
float3 ycc_l2 = rgb_to_ycc(scene.sample(samp, uv - float2(2.0f*step, 0.0f)).rgb);
|
||||||
float3 ycc_r1 = rgb_to_ycc(scene.sample(samp, uv + float2(1.0f/tw, 0.0f)).rgb);
|
float3 ycc_l1 = rgb_to_ycc(scene.sample(samp, uv - float2(1.0f*step, 0.0f)).rgb);
|
||||||
float3 ycc_r2 = rgb_to_ycc(scene.sample(samp, uv + float2(2.0f/tw, 0.0f)).rgb);
|
float3 ycc_r1 = rgb_to_ycc(scene.sample(samp, uv + float2(1.0f*step, 0.0f)).rgb);
|
||||||
|
float3 ycc_r2 = rgb_to_ycc(scene.sample(samp, uv + float2(2.0f*step, 0.0f)).rgb);
|
||||||
ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0f + ycc.yz*2.0f + ycc_r1.yz*2.0f + ycc_r2.yz) / 8.0f;
|
ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0f + ycc.yz*2.0f + ycc_r1.yz*2.0f + ycc_r2.yz) / 8.0f;
|
||||||
colour = mix(base, ycc_to_rgb(ycc), u.bleeding);
|
colour = mix(base, ycc_to_rgb(ycc), u.bleeding);
|
||||||
} else {
|
} else {
|
||||||
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;
|
||||||
|
|
||||||
@@ -124,14 +132,20 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]],
|
|||||||
colour = mix(colour, lin, u.gamma_strength);
|
colour = mix(colour, lin, u.gamma_strength);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scanlines
|
// Scanlines — proporción 2/3 brillantes + 1/3 oscuras por fila lógica.
|
||||||
float texHeight = float(scene.get_height());
|
// Casos especiales: 1 subfila → sin efecto; 2 subfilas → 1+1 (50/50).
|
||||||
float scaleY = u.screen_height / texHeight;
|
// Constantes ajustables:
|
||||||
float screenY = uv.y * u.screen_height;
|
const float SCAN_DARK_RATIO = 0.333f; // fracción de subfilas oscuras (ps >= 3)
|
||||||
float posInRow = fmod(screenY, scaleY);
|
const float SCAN_DARK_FLOOR = 0.42f; // multiplicador de brillo de subfilas oscuras
|
||||||
float scanLineDY = posInRow / scaleY - 0.5f;
|
if (u.scanline_strength > 0.0f) {
|
||||||
float scan = max(1.0f - scanLineDY * scanLineDY * 6.0f, 0.12f) * 3.5f;
|
float ps = max(1.0f, round(u.pixel_scale));
|
||||||
colour *= mix(1.0f, scan, u.scanline_strength);
|
float frac_in_row = fract(uv.y * u.screen_height);
|
||||||
|
float row_pos = floor(frac_in_row * ps);
|
||||||
|
float bright_rows = (ps < 2.0f) ? ps : ((ps < 3.0f) ? 1.0f : floor(ps * (1.0f - SCAN_DARK_RATIO)));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
if (u.gamma_strength > 0.0f) {
|
if (u.gamma_strength > 0.0f) {
|
||||||
float3 enc = pow(colour, float3(1.0f/2.2f));
|
float3 enc = pow(colour, float3(1.0f/2.2f));
|
||||||
@@ -143,7 +157,8 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]],
|
|||||||
float vignette = 1.0f - dot(d, d) * u.vignette_strength;
|
float vignette = 1.0f - dot(d, d) * u.vignette_strength;
|
||||||
colour *= clamp(vignette, 0.0f, 1.0f);
|
colour *= clamp(vignette, 0.0f, 1.0f);
|
||||||
|
|
||||||
// Máscara de fósforo RGB
|
// Máscara de fósforo RGB — después de scanlines (orden original):
|
||||||
|
// filas brillantes saturadas → máscara invisible, filas oscuras → RGB visible.
|
||||||
if (u.mask_strength > 0.0f) {
|
if (u.mask_strength > 0.0f) {
|
||||||
float whichMask = fract(in.pos.x * 0.3333333f);
|
float whichMask = fract(in.pos.x * 0.3333333f);
|
||||||
float3 mask = float3(0.80f);
|
float3 mask = float3(0.80f);
|
||||||
@@ -153,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__
|
||||||
@@ -189,25 +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);
|
||||||
tex_width_ = static_cast<int>(fw);
|
game_width_ = static_cast<int>(fw);
|
||||||
tex_height_ = static_cast<int>(fh);
|
game_height_ = static_cast<int>(fh);
|
||||||
uniforms_.screen_height = fh; // Altura lógica del juego (no el swapchain físico)
|
uniforms_.screen_height = static_cast<float>(game_height_);
|
||||||
|
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())
|
||||||
@@ -222,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);
|
||||||
@@ -240,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());
|
||||||
@@ -254,7 +341,7 @@ namespace Rendering {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// 5. Create nearest-neighbour sampler (retro pixel art)
|
// 5. Create samplers: NEAREST (pixel art) + LINEAR (supersampling)
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
SDL_GPUSamplerCreateInfo samp_info = {};
|
SDL_GPUSamplerCreateInfo samp_info = {};
|
||||||
samp_info.min_filter = SDL_GPU_FILTER_NEAREST;
|
samp_info.min_filter = SDL_GPU_FILTER_NEAREST;
|
||||||
@@ -270,6 +357,20 @@ namespace Rendering {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_GPUSamplerCreateInfo lsamp_info = {};
|
||||||
|
lsamp_info.min_filter = SDL_GPU_FILTER_LINEAR;
|
||||||
|
lsamp_info.mag_filter = SDL_GPU_FILTER_LINEAR;
|
||||||
|
lsamp_info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST;
|
||||||
|
lsamp_info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
||||||
|
lsamp_info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
||||||
|
lsamp_info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
||||||
|
linear_sampler_ = SDL_CreateGPUSampler(device_, &lsamp_info);
|
||||||
|
if (linear_sampler_ == nullptr) {
|
||||||
|
SDL_Log("SDL3GPUShader: failed to create linear sampler: %s", SDL_GetError());
|
||||||
|
cleanup();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// 6. Create PostFX graphics pipeline
|
// 6. Create PostFX graphics pipeline
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
@@ -279,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,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);
|
||||||
@@ -328,14 +430,132 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// uploadPixels — copies ARGB8888 CPU pixels into the GPU transfer buffer
|
// uploadPixels — copies ARGB8888 CPU pixels into the GPU transfer buffer.
|
||||||
|
// Con supersampling (oversample_ > 1) expande cada pixel del juego a un bloque
|
||||||
|
// oversample × oversample y hornea la scanline oscura en la última fila del bloque.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
void SDL3GPUShader::uploadPixels(const Uint32* pixels, int width, int height) {
|
void SDL3GPUShader::uploadPixels(const Uint32* pixels, int width, int height) {
|
||||||
if (!is_initialized_ || (upload_buffer_ == nullptr)) { return; }
|
if (!is_initialized_ || (upload_buffer_ == nullptr)) { return; }
|
||||||
@@ -345,7 +565,10 @@ namespace Rendering {
|
|||||||
SDL_Log("SDL3GPUShader: SDL_MapGPUTransferBuffer failed: %s", SDL_GetError());
|
SDL_Log("SDL3GPUShader: SDL_MapGPUTransferBuffer failed: %s", SDL_GetError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copia directa — el upscale lo hace la GPU en el primer render pass
|
||||||
std::memcpy(mapped, pixels, static_cast<size_t>(width * height * 4));
|
std::memcpy(mapped, pixels, static_cast<size_t>(width * height * 4));
|
||||||
|
|
||||||
SDL_UnmapGPUTransferBuffer(device_, upload_buffer_);
|
SDL_UnmapGPUTransferBuffer(device_, upload_buffer_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,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;
|
||||||
@@ -395,47 +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 para mantener relación de aspecto (letterbox o integer scale)
|
// ---- Determinar si usar el path Lanczos (SS activo + algo seleccionado) ----
|
||||||
float vx = 0.0F;
|
const bool USE_LANCZOS = (oversample_ > 1 && downscale_algo_ > 0 && scaled_texture_ != nullptr && postfx_texture_ != nullptr && postfx_offscreen_pipeline_ != nullptr && downscale_pipeline_ != nullptr);
|
||||||
float vy = 0.0F;
|
|
||||||
float vw = 0.0F;
|
if (USE_LANCZOS) {
|
||||||
float vh = 0.0F;
|
// ---- Pass A: PostFX → postfx_texture_ (full scaled size, sin viewport) ----
|
||||||
if (integer_scale_) {
|
SDL_GPUColorTargetInfo postfx_target = {};
|
||||||
const int SCALE = std::max(1, std::min(static_cast<int>(sw) / tex_width_, static_cast<int>(sh) / tex_height_));
|
postfx_target.texture = postfx_texture_;
|
||||||
vw = static_cast<float>(tex_width_ * SCALE);
|
postfx_target.load_op = SDL_GPU_LOADOP_CLEAR;
|
||||||
vh = static_cast<float>(tex_height_ * SCALE);
|
postfx_target.store_op = SDL_GPU_STOREOP_STORE;
|
||||||
} else {
|
postfx_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
|
||||||
const float SCALE = std::min(
|
|
||||||
static_cast<float>(sw) / static_cast<float>(tex_width_),
|
SDL_GPURenderPass* ppass = SDL_BeginGPURenderPass(cmd, &postfx_target, 1, nullptr);
|
||||||
static_cast<float>(sh) / static_cast<float>(tex_height_));
|
if (ppass != nullptr) {
|
||||||
vw = static_cast<float>(tex_width_) * SCALE;
|
SDL_BindGPUGraphicsPipeline(ppass, postfx_offscreen_pipeline_);
|
||||||
vh = static_cast<float>(tex_height_) * SCALE;
|
SDL_GPUTextureSamplerBinding pbinding = {};
|
||||||
|
pbinding.texture = scaled_texture_;
|
||||||
|
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);
|
|
||||||
|
|
||||||
SDL_GPUTextureSamplerBinding binding = {};
|
// ---- Pass B: Downscale Lanczos → swapchain (con viewport/letterbox) ----
|
||||||
binding.texture = scene_texture_;
|
SDL_GPUColorTargetInfo ds_target = {};
|
||||||
binding.sampler = sampler_;
|
ds_target.texture = swapchain;
|
||||||
SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1);
|
ds_target.load_op = SDL_GPU_LOADOP_CLEAR;
|
||||||
|
ds_target.store_op = SDL_GPU_STOREOP_STORE;
|
||||||
|
ds_target.clear_color = {.r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F};
|
||||||
|
|
||||||
SDL_PushGPUFragmentUniformData(cmd, 0, &uniforms_, sizeof(PostFXUniforms));
|
SDL_GPURenderPass* dpass = SDL_BeginGPURenderPass(cmd, &ds_target, 1, nullptr);
|
||||||
|
if (dpass != nullptr) {
|
||||||
|
SDL_BindGPUGraphicsPipeline(dpass, downscale_pipeline_);
|
||||||
|
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_DrawGPUPrimitives(pass, 3, 1, 0, 0);
|
SDL_GPURenderPass* pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, nullptr);
|
||||||
SDL_EndGPURenderPass(pass);
|
if (pass != nullptr) {
|
||||||
|
SDL_BindGPUGraphicsPipeline(pass, pipeline_);
|
||||||
|
SDL_GPUViewport vp = {.x = vx, .y = vy, .w = vw, .h = vh, .min_depth = 0.0F, .max_depth = 1.0F};
|
||||||
|
SDL_SetGPUViewport(pass, &vp);
|
||||||
|
|
||||||
|
// 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_GPUTextureSamplerBinding binding = {};
|
||||||
|
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);
|
||||||
@@ -454,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;
|
||||||
@@ -466,6 +809,10 @@ namespace Rendering {
|
|||||||
SDL_ReleaseGPUSampler(device_, sampler_);
|
SDL_ReleaseGPUSampler(device_, sampler_);
|
||||||
sampler_ = nullptr;
|
sampler_ = nullptr;
|
||||||
}
|
}
|
||||||
|
if (linear_sampler_ != nullptr) {
|
||||||
|
SDL_ReleaseGPUSampler(device_, linear_sampler_);
|
||||||
|
linear_sampler_ = nullptr;
|
||||||
|
}
|
||||||
// device_ y el claim de la ventana se mantienen vivos
|
// device_ y el claim de la ventana se mantienen vivos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -510,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,
|
||||||
@@ -534,12 +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_.scanline_strength = p.scanlines;
|
|
||||||
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;
|
||||||
|
|
||||||
|
// Las scanlines siempre las aplica el shader PostFX en GPU.
|
||||||
|
uniforms_.scanline_strength = p.scanlines;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDL3GPUShader::setVSync(bool vsync) {
|
void SDL3GPUShader::setVSync(bool vsync) {
|
||||||
@@ -553,4 +903,155 @@ namespace Rendering {
|
|||||||
integer_scale_ = integer_scale;
|
integer_scale_ = integer_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// setOversample — cambia el factor SS; recrea texturas si ya está inicializado
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
void SDL3GPUShader::setOversample(int factor) {
|
||||||
|
const int NEW_FACTOR = std::max(1, factor);
|
||||||
|
if (NEW_FACTOR == oversample_) { return; }
|
||||||
|
oversample_ = NEW_FACTOR;
|
||||||
|
if (is_initialized_) {
|
||||||
|
reinitTexturesAndBuffer();
|
||||||
|
// scanline_strength se actualizará en el próximo setPostFXParams
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_, scaled_texture_ y
|
||||||
|
// upload_buffer_ con el factor oversample_ actual. No toca pipelines ni samplers.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
auto SDL3GPUShader::reinitTexturesAndBuffer() -> bool {
|
||||||
|
if (device_ == nullptr) { return false; }
|
||||||
|
SDL_WaitForGPUIdle(device_);
|
||||||
|
|
||||||
|
if (scene_texture_ != nullptr) {
|
||||||
|
SDL_ReleaseGPUTexture(device_, scene_texture_);
|
||||||
|
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) {
|
||||||
|
SDL_ReleaseGPUTransferBuffer(device_, upload_buffer_);
|
||||||
|
upload_buffer_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uniforms_.screen_height = static_cast<float>(game_height_);
|
||||||
|
uniforms_.oversample = static_cast<float>(oversample_);
|
||||||
|
|
||||||
|
// scene_texture_: siempre a resolución del juego
|
||||||
|
SDL_GPUTextureCreateInfo tex_info = {};
|
||||||
|
tex_info.type = SDL_GPU_TEXTURETYPE_2D;
|
||||||
|
tex_info.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
|
||||||
|
tex_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
|
||||||
|
tex_info.width = static_cast<Uint32>(game_width_);
|
||||||
|
tex_info.height = static_cast<Uint32>(game_height_);
|
||||||
|
tex_info.layer_count_or_depth = 1;
|
||||||
|
tex_info.num_levels = 1;
|
||||||
|
scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info);
|
||||||
|
if (scene_texture_ == nullptr) {
|
||||||
|
SDL_Log("SDL3GPUShader: reinit — failed to create scene texture: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload_buffer_: siempre a resolución del juego
|
||||||
|
SDL_GPUTransferBufferCreateInfo tb_info = {};
|
||||||
|
tb_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
|
||||||
|
tb_info.size = static_cast<Uint32>(game_width_ * game_height_ * 4);
|
||||||
|
upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info);
|
||||||
|
if (upload_buffer_ == nullptr) {
|
||||||
|
SDL_Log("SDL3GPUShader: reinit — failed to create upload buffer: %s", SDL_GetError());
|
||||||
|
SDL_ReleaseGPUTexture(device_, scene_texture_);
|
||||||
|
scene_texture_ = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Rendering
|
} // namespace Rendering
|
||||||
|
|||||||
@@ -7,16 +7,29 @@
|
|||||||
|
|
||||||
// PostFX uniforms pushed to fragment stage each frame.
|
// PostFX uniforms pushed to fragment stage each frame.
|
||||||
// Must match the MSL struct and GLSL uniform block layout.
|
// Must match the MSL struct and GLSL uniform block layout.
|
||||||
// 8 floats = 32 bytes — meets Metal/Vulkan 16-byte alignment requirement.
|
// 12 floats = 48 bytes — meets Metal/Vulkan 16-byte alignment requirement.
|
||||||
struct PostFXUniforms {
|
struct PostFXUniforms {
|
||||||
float vignette_strength; // 0 = none, ~0.8 = subtle
|
float vignette_strength; // 0 = none, ~0.8 = subtle
|
||||||
float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration
|
float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration
|
||||||
float scanline_strength; // 0 = off, 1 = full
|
float scanline_strength; // 0 = off, 1 = full
|
||||||
float screen_height; // logical height in pixels (for resolution-independent scanlines)
|
float screen_height; // logical height in pixels (used by bleeding effect)
|
||||||
float mask_strength; // 0 = off, 1 = full phosphor dot mask
|
float mask_strength; // 0 = off, 1 = full phosphor dot mask
|
||||||
float gamma_strength; // 0 = off, 1 = full gamma 2.4/2.2 correction
|
float gamma_strength; // 0 = off, 1 = full gamma 2.4/2.2 correction
|
||||||
float curvature; // 0 = flat, 1 = max barrel distortion
|
float curvature; // 0 = flat, 1 = max barrel distortion
|
||||||
float bleeding; // 0 = off, 1 = max NTSC chrominance bleeding
|
float bleeding; // 0 = off, 1 = max NTSC chrominance bleeding
|
||||||
|
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
|
||||||
|
float time; // seconds since SDL init (SDL_GetTicks() / 1000.0f)
|
||||||
|
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
|
||||||
|
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 {
|
||||||
@@ -43,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;
|
||||||
@@ -56,6 +73,18 @@ namespace Rendering {
|
|||||||
// Activa/desactiva escalado entero (integer scale)
|
// Activa/desactiva escalado entero (integer scale)
|
||||||
void setScaleMode(bool integer_scale) override;
|
void setScaleMode(bool integer_scale) override;
|
||||||
|
|
||||||
|
// Establece factor de supersampling (1 = off, 3 = 3×SS)
|
||||||
|
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,
|
||||||
@@ -73,21 +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 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;
|
SDL_GPUSampler* sampler_ = nullptr; // NEAREST
|
||||||
|
SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR
|
||||||
|
|
||||||
PostFXUniforms uniforms_{.vignette_strength = 0.6F, .chroma_strength = 0.15F, .scanline_strength = 0.7F, .screen_height = 192.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 tex_width_ = 0;
|
int game_width_ = 0; // Dimensiones originales del canvas
|
||||||
int tex_height_ = 0;
|
int game_height_ = 0;
|
||||||
|
int ss_factor_ = 0; // Factor SS activo (3, 6, 9...) o 0 si SS desactivado
|
||||||
|
int oversample_ = 1; // SS on/off (1 = off, >1 = on)
|
||||||
|
int downscale_algo_ = 1; // 0 = bilinear legacy, 1 = Lanczos2, 2 = Lanczos3
|
||||||
|
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
@@ -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 {
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ namespace Rendering {
|
|||||||
float gamma = 0.0F; // Corrección gamma (blend 0=off, 1=full)
|
float gamma = 0.0F; // Corrección gamma (blend 0=off, 1=full)
|
||||||
float curvature = 0.0F; // Curvatura barrel CRT
|
float curvature = 0.0F; // Curvatura barrel CRT
|
||||||
float bleeding = 0.0F; // Sangrado de color NTSC
|
float bleeding = 0.0F; // Sangrado de color NTSC
|
||||||
|
float flicker = 0.0F; // Parpadeo de fósforo CRT ~50 Hz
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,11 +84,51 @@ namespace Rendering {
|
|||||||
*/
|
*/
|
||||||
virtual void setScaleMode(bool /*integer_scale*/) {}
|
virtual void setScaleMode(bool /*integer_scale*/) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Establece el factor de supersampling (1 = off, 3 = 3× SS)
|
||||||
|
* Con factor > 1, la textura GPU se crea a game×factor resolución y
|
||||||
|
* las scanlines se hornean en CPU (uploadPixels). El sampler usa LINEAR.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|||||||
@@ -3,131 +3,174 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <cstddef> // Para size_t
|
#include <cstddef> // Para size_t
|
||||||
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream
|
|
||||||
#include <iostream> // Para cerr
|
#include <iostream> // Para cerr
|
||||||
#include <sstream> // Para istringstream
|
#include <sstream> // Para istringstream
|
||||||
#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
|
||||||
|
|
||||||
// Llena una estructuta TextFile desde un fichero
|
// Extrae el siguiente codepoint UTF-8 de la cadena, avanzando 'pos' al byte siguiente
|
||||||
auto Text::loadTextFile(const std::string& file_path) -> std::shared_ptr<File> {
|
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]);
|
||||||
|
uint32_t cp = 0;
|
||||||
|
size_t extra = 0;
|
||||||
|
|
||||||
|
if (c < 0x80) {
|
||||||
|
cp = c;
|
||||||
|
extra = 0;
|
||||||
|
} else if (c < 0xC0) {
|
||||||
|
pos++;
|
||||||
|
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++;
|
||||||
|
for (size_t i = 0; i < extra && pos < s.size(); ++i, ++pos) {
|
||||||
|
auto cb = static_cast<unsigned char>(s[pos]);
|
||||||
|
if ((cb & 0xC0) != 0x80) { return 0xFFFD; }
|
||||||
|
cp = (cp << 6) | (cb & 0x3F);
|
||||||
|
}
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convierte un codepoint Unicode a una cadena UTF-8
|
||||||
|
auto Text::codepointToUtf8(uint32_t cp) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
|
std::string result;
|
||||||
|
if (cp < 0x80) {
|
||||||
|
result += static_cast<char>(cp);
|
||||||
|
} else if (cp < 0x800) {
|
||||||
|
result += static_cast<char>(0xC0 | (cp >> 6));
|
||||||
|
result += static_cast<char>(0x80 | (cp & 0x3F));
|
||||||
|
} else if (cp < 0x10000) {
|
||||||
|
result += static_cast<char>(0xE0 | (cp >> 12));
|
||||||
|
result += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
||||||
|
result += static_cast<char>(0x80 | (cp & 0x3F));
|
||||||
|
} else {
|
||||||
|
result += static_cast<char>(0xF0 | (cp >> 18));
|
||||||
|
result += static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
|
||||||
|
result += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
||||||
|
result += static_cast<char>(0x80 | (cp & 0x3F));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carga un fichero de definición de fuente .fnt
|
||||||
|
// Formato: líneas "clave valor", comentarios con #, gliphos como "codepoint ancho"
|
||||||
|
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>();
|
||||||
|
|
||||||
// No es necesario inicializar - los miembros tienen valores por defecto
|
|
||||||
|
|
||||||
// Load file using ResourceHelper (supports both filesystem and pack)
|
|
||||||
auto file_data = Resource::Helper::loadFile(file_path);
|
auto file_data = Resource::Helper::loadFile(file_path);
|
||||||
if (file_data.empty()) {
|
if (file_data.empty()) {
|
||||||
std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << '\n';
|
std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << '\n';
|
||||||
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
|
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert bytes to string and parse
|
|
||||||
std::string content(file_data.begin(), file_data.end());
|
std::string content(file_data.begin(), file_data.end());
|
||||||
std::istringstream stream(content);
|
std::istringstream stream(content);
|
||||||
std::string buffer;
|
std::string line;
|
||||||
|
int glyph_index = 0;
|
||||||
|
|
||||||
// Lee los dos primeros valores del fichero
|
while (std::getline(stream, line)) {
|
||||||
std::getline(stream, buffer);
|
if (!line.empty() && line.back() == '\r') { line.pop_back(); }
|
||||||
// Remove Windows line ending if present
|
if (line.empty() || line[0] == '#') { continue; }
|
||||||
if (!buffer.empty() && buffer.back() == '\r') {
|
|
||||||
buffer.pop_back();
|
|
||||||
}
|
|
||||||
std::getline(stream, buffer);
|
|
||||||
// Remove Windows line ending if present
|
|
||||||
if (!buffer.empty() && buffer.back() == '\r') {
|
|
||||||
buffer.pop_back();
|
|
||||||
}
|
|
||||||
tf->box_width = std::stoi(buffer);
|
|
||||||
|
|
||||||
std::getline(stream, buffer);
|
std::istringstream ls(line);
|
||||||
// Remove Windows line ending if present
|
std::string key;
|
||||||
if (!buffer.empty() && buffer.back() == '\r') {
|
ls >> key;
|
||||||
buffer.pop_back();
|
|
||||||
}
|
|
||||||
std::getline(stream, buffer);
|
|
||||||
// Remove Windows line ending if present
|
|
||||||
if (!buffer.empty() && buffer.back() == '\r') {
|
|
||||||
buffer.pop_back();
|
|
||||||
}
|
|
||||||
tf->box_height = std::stoi(buffer);
|
|
||||||
|
|
||||||
// lee el resto de datos del fichero
|
if (key == "box_width") {
|
||||||
auto index = 32;
|
ls >> tf->box_width;
|
||||||
auto line_read = 0;
|
} else if (key == "box_height") {
|
||||||
while (std::getline(stream, buffer)) {
|
ls >> tf->box_height;
|
||||||
// Remove Windows line ending if present
|
} else if (key == "columns") {
|
||||||
if (!buffer.empty() && buffer.back() == '\r') {
|
ls >> tf->columns;
|
||||||
buffer.pop_back();
|
} else if (key == "cell_spacing") {
|
||||||
|
ls >> tf->cell_spacing;
|
||||||
|
} else if (key == "row_spacing") {
|
||||||
|
ls >> tf->row_spacing;
|
||||||
|
} else {
|
||||||
|
// Línea de glifo: codepoint_decimal ancho_visual
|
||||||
|
uint32_t codepoint = 0;
|
||||||
|
int width = 0;
|
||||||
|
try {
|
||||||
|
codepoint = static_cast<uint32_t>(std::stoul(key));
|
||||||
|
ls >> width;
|
||||||
|
} catch (...) {
|
||||||
|
continue; // línea mal formateada, ignorar
|
||||||
|
}
|
||||||
|
Offset off{};
|
||||||
|
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.y = ((glyph_index / tf->columns) * (tf->box_height + ROW_SP)) + tf->cell_spacing;
|
||||||
|
off.w = width;
|
||||||
|
tf->offset[codepoint] = off;
|
||||||
|
++glyph_index;
|
||||||
}
|
}
|
||||||
// Almacena solo las lineas impares
|
}
|
||||||
if (line_read % 2 == 1) {
|
|
||||||
tf->offset[index++].w = std::stoi(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limpia el buffer
|
|
||||||
buffer.clear();
|
|
||||||
line_read++;
|
|
||||||
};
|
|
||||||
|
|
||||||
printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]");
|
printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]");
|
||||||
|
|
||||||
// Establece las coordenadas para cada caracter ascii de la cadena y su ancho
|
|
||||||
for (int i = 32; i < 128; ++i) {
|
|
||||||
tf->offset[i].x = ((i - 32) % 15) * tf->box_width;
|
|
||||||
tf->offset[i].y = ((i - 32) / 15) * tf->box_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tf;
|
return tf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor desde fichero
|
||||||
Text::Text(const std::shared_ptr<Surface>& surface, const std::string& text_file) {
|
Text::Text(const std::shared_ptr<Surface>& surface, const std::string& text_file) {
|
||||||
// Carga los offsets desde el fichero
|
|
||||||
auto tf = loadTextFile(text_file);
|
auto tf = loadTextFile(text_file);
|
||||||
|
|
||||||
// Inicializa variables desde la estructura
|
|
||||||
box_height_ = tf->box_height;
|
box_height_ = tf->box_height;
|
||||||
box_width_ = tf->box_width;
|
box_width_ = tf->box_width;
|
||||||
offset_ = tf->offset;
|
offset_ = tf->offset;
|
||||||
|
|
||||||
// Crea los objetos
|
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_)});
|
||||||
sprite_ = std::make_unique<SurfaceSprite>(surface, (SDL_FRect){0.0F, 0.0F, static_cast<float>(box_width_), static_cast<float>(box_height_)});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// 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;
|
||||||
if (lenght == -1) {
|
size_t pos = 0;
|
||||||
lenght = text.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
sprite_->setY(y);
|
sprite_->setY(y);
|
||||||
for (int i = 0; i < lenght; ++i) {
|
while (pos < text.size()) {
|
||||||
auto index = static_cast<int>(text[i]);
|
if (lenght != -1 && glyphs_done >= lenght) { break; }
|
||||||
sprite_->setClip(offset_[index].x, offset_[index].y, box_width_, box_height_);
|
uint32_t cp = nextCodepoint(text, pos);
|
||||||
sprite_->setX(x + shift);
|
auto it = offset_.find(cp);
|
||||||
sprite_->render(1, 15);
|
if (it == offset_.end()) { it = offset_.find('?'); }
|
||||||
shift += offset_[static_cast<int>(text[i])].w + kerning;
|
if (it != offset_.end()) {
|
||||||
|
sprite_->setClip(it->second.x, it->second.y, box_width_, box_height_);
|
||||||
|
sprite_->setX(x + shift);
|
||||||
|
sprite_->render(1, 15);
|
||||||
|
shift += it->second.w + kerning;
|
||||||
|
}
|
||||||
|
++glyphs_done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
@@ -141,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);
|
||||||
@@ -155,20 +198,24 @@ 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;
|
||||||
if (lenght == -1) {
|
size_t pos = 0;
|
||||||
lenght = text.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
sprite_->setY(y);
|
sprite_->setY(y);
|
||||||
for (int i = 0; i < lenght; ++i) {
|
while (pos < text.size()) {
|
||||||
auto index = static_cast<int>(text[i]);
|
if (lenght != -1 && glyphs_done >= lenght) { break; }
|
||||||
sprite_->setClip(offset_[index].x, offset_[index].y, box_width_, box_height_);
|
uint32_t cp = nextCodepoint(text, pos);
|
||||||
sprite_->setX(x + shift);
|
auto it = offset_.find(cp);
|
||||||
sprite_->render(1, color);
|
if (it == offset_.end()) { it = offset_.find('?'); }
|
||||||
shift += offset_[static_cast<int>(text[i])].w + kerning;
|
if (it != offset_.end()) {
|
||||||
|
sprite_->setClip(it->second.x, it->second.y, box_width_, box_height_);
|
||||||
|
sprite_->setX(x + shift);
|
||||||
|
sprite_->render(1, color);
|
||||||
|
shift += it->second.w + kerning;
|
||||||
|
}
|
||||||
|
++glyphs_done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +232,7 @@ void Text::writeCentered(int x, int y, const std::string& text, int kerning, int
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Escribe texto con extras
|
// Escribe texto con extras
|
||||||
void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning, Uint8 text_color, Uint8 shadow_distance, Uint8 shadow_color, int lenght) {
|
void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning, Uint8 text_color, Uint8 shadow_distance, Uint8 shadow_color, int lenght) { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
const auto CENTERED = ((flags & CENTER_FLAG) == CENTER_FLAG);
|
const auto CENTERED = ((flags & CENTER_FLAG) == CENTER_FLAG);
|
||||||
const auto SHADOWED = ((flags & SHADOW_FLAG) == SHADOW_FLAG);
|
const auto SHADOWED = ((flags & SHADOW_FLAG) == SHADOW_FLAG);
|
||||||
const auto COLORED = ((flags & COLOR_FLAG) == COLOR_FLAG);
|
const auto COLORED = ((flags & COLOR_FLAG) == COLOR_FLAG);
|
||||||
@@ -200,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);
|
||||||
@@ -213,22 +261,46 @@ void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerni
|
|||||||
writeColored(x, y, text, text_color, kerning, lenght);
|
writeColored(x, y, text, text_color, kerning, lenght);
|
||||||
} else {
|
} else {
|
||||||
writeColored(x, y, text, text_color, kerning, lenght);
|
writeColored(x, y, text, text_color, kerning, lenght);
|
||||||
// write(x, y, text, kerning, lenght);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene la longitud en pixels de una cadena
|
// 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;
|
||||||
for (size_t i = 0; i < text.length(); ++i) {
|
size_t pos = 0;
|
||||||
shift += (offset_[static_cast<int>(text[i])].w + kerning);
|
|
||||||
|
while (pos < text.size()) {
|
||||||
|
uint32_t cp = nextCodepoint(text, pos);
|
||||||
|
auto it = offset_.find(cp);
|
||||||
|
if (it == offset_.end()) { it = offset_.find('?'); }
|
||||||
|
if (it != offset_.end()) {
|
||||||
|
shift += it->second.w + kerning;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Descuenta el kerning del último caracter
|
return shift > 0 ? shift - kerning : 0;
|
||||||
return shift - kerning;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Devuelve el valor de la variable
|
// Devuelve el ancho en pixels de un glifo dado su codepoint Unicode
|
||||||
|
auto Text::glyphWidth(uint32_t codepoint, int kerning) const -> int { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
|
auto it = offset_.find(codepoint);
|
||||||
|
if (it == offset_.end()) { it = offset_.find('?'); }
|
||||||
|
if (it != offset_.end()) { return it->second.w + kerning; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devuelve el clip rect (región en el bitmap) de un glifo dado su codepoint
|
||||||
|
auto Text::getGlyphClip(uint32_t codepoint) const -> SDL_FRect {
|
||||||
|
auto it = offset_.find(codepoint);
|
||||||
|
if (it == offset_.end()) { it = offset_.find('?'); }
|
||||||
|
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),
|
||||||
|
.y = static_cast<float>(it->second.y),
|
||||||
|
.w = static_cast<float>(box_width_),
|
||||||
|
.h = static_cast<float>(box_height_)};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devuelve el tamaño de la caja de cada caracter
|
||||||
auto Text::getCharacterSize() const -> int {
|
auto Text::getCharacterSize() const -> int {
|
||||||
return box_width_;
|
return box_width_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <array> // Para std::array
|
#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 "core/rendering/surface_sprite.hpp" // Para SSprite
|
#include "core/rendering/sprite/sprite.hpp" // Para SSprite
|
||||||
class Surface; // lines 8-8
|
class Surface; // Forward declaration
|
||||||
|
|
||||||
// Clase texto. Pinta texto en pantalla a partir de un bitmap
|
// Clase texto. Pinta texto en pantalla a partir de un bitmap con soporte UTF-8
|
||||||
class Text {
|
class Text {
|
||||||
public:
|
public:
|
||||||
// Tipos anidados públicos
|
// Tipos anidados públicos
|
||||||
@@ -18,9 +18,12 @@ 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
|
||||||
std::array<Offset, 128> offset{}; // Vector con las posiciones y ancho de cada letra
|
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 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)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
@@ -47,18 +50,23 @@ class Text {
|
|||||||
|
|
||||||
[[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 getGlyphClip(uint32_t codepoint) const -> SDL_FRect; // Devuelve el clip rect del glifo
|
||||||
|
[[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
|
||||||
|
|
||||||
static auto loadTextFile(const std::string& file_path) -> std::shared_ptr<File>; // Método de utilidad para cargar ficheros de texto
|
static auto loadTextFile(const std::string& file_path) -> std::shared_ptr<File>; // Carga un fichero de definición de fuente .fnt
|
||||||
|
static auto codepointToUtf8(uint32_t cp) -> std::string; // Convierte un codepoint Unicode a string UTF-8
|
||||||
|
static auto nextCodepoint(const std::string& s, size_t& pos) -> uint32_t; // Extrae el siguiente codepoint UTF-8
|
||||||
|
|
||||||
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 en todas las letras
|
bool fixed_width_ = false; // Indica si el texto se ha de escribir con longitud fija
|
||||||
std::array<Offset, 128> offset_{}; // Vector con las posiciones y ancho de cada letra
|
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
|
||||||
@@ -370,11 +372,11 @@ namespace Resource {
|
|||||||
std::cout << "\n>> CREATING TEXT_OBJECTS" << '\n';
|
std::cout << "\n>> CREATING TEXT_OBJECTS" << '\n';
|
||||||
|
|
||||||
std::vector<ResourceInfo> resources = {
|
std::vector<ResourceInfo> resources = {
|
||||||
{.key = "aseprite", .texture_file = "aseprite.gif", .text_file = "aseprite.txt"},
|
{.key = "aseprite", .texture_file = "aseprite.gif", .text_file = "aseprite.fnt"},
|
||||||
{.key = "gauntlet", .texture_file = "gauntlet.gif", .text_file = "gauntlet.txt"},
|
{.key = "gauntlet", .texture_file = "gauntlet.gif", .text_file = "gauntlet.fnt"},
|
||||||
{.key = "smb2", .texture_file = "smb2.gif", .text_file = "smb2.txt"},
|
{.key = "smb2", .texture_file = "smb2.gif", .text_file = "smb2.fnt"},
|
||||||
{.key = "subatomic", .texture_file = "subatomic.gif", .text_file = "subatomic.txt"},
|
{.key = "subatomic", .texture_file = "subatomic.gif", .text_file = "subatomic.fnt"},
|
||||||
{.key = "8bithud", .texture_file = "8bithud.gif", .text_file = "8bithud.txt"}};
|
{.key = "8bithud", .texture_file = "8bithud.gif", .text_file = "8bithud.fnt"}};
|
||||||
|
|
||||||
for (const auto& res_info : resources) {
|
for (const auto& res_info : resources) {
|
||||||
texts_.emplace_back(TextResource{.name = res_info.key, .text = std::make_shared<Text>(getSurface(res_info.texture_file), getTextFile(res_info.text_file))});
|
texts_.emplace_back(TextResource{.name = res_info.key, .text = std::make_shared<Text>(getSurface(res_info.texture_file), getTextFile(res_info.text_file))});
|
||||||
@@ -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
|
||||||
@@ -26,9 +26,12 @@ namespace Defaults::Video {
|
|||||||
constexpr Screen::Filter FILTER = Screen::Filter::NEAREST; // Filtro por defecto
|
constexpr Screen::Filter FILTER = Screen::Filter::NEAREST; // Filtro por defecto
|
||||||
constexpr bool VERTICAL_SYNC = true; // Vsync activado por defecto
|
constexpr bool VERTICAL_SYNC = true; // Vsync activado por defecto
|
||||||
constexpr bool POSTFX = false; // PostFX desactivado por defecto
|
constexpr bool POSTFX = false; // PostFX desactivado por defecto
|
||||||
|
constexpr bool SUPERSAMPLING = false; // Supersampling desactivado por defecto
|
||||||
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 {
|
||||||
@@ -76,31 +79,21 @@ 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 {
|
||||||
constexpr const char* LANGUAGE = "en"; // Idioma por defecto (en = inglés, ca = catalán)
|
constexpr const char* LANGUAGE = "ca"; // Idioma por defecto (en = inglés, ca = catalán)
|
||||||
} // namespace Defaults::Localization
|
} // namespace Defaults::Localization
|
||||||
|
|
||||||
namespace Defaults::Game::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
|
||||||
|
|||||||