2 Commits

23 changed files with 1099 additions and 1149 deletions

View File

@@ -7,23 +7,23 @@ assets:
- type: BITMAP
path: ${PREFIX}/data/font/smb2.gif
- type: FONT
path: ${PREFIX}/data/font/smb2.txt
path: ${PREFIX}/data/font/smb2.fnt
- type: BITMAP
path: ${PREFIX}/data/font/aseprite.gif
- type: FONT
path: ${PREFIX}/data/font/aseprite.txt
path: ${PREFIX}/data/font/aseprite.fnt
- type: BITMAP
path: ${PREFIX}/data/font/gauntlet.gif
- type: FONT
path: ${PREFIX}/data/font/gauntlet.txt
path: ${PREFIX}/data/font/gauntlet.fnt
- type: BITMAP
path: ${PREFIX}/data/font/subatomic.gif
- type: FONT
path: ${PREFIX}/data/font/subatomic.txt
path: ${PREFIX}/data/font/subatomic.fnt
- type: BITMAP
path: ${PREFIX}/data/font/8bithud.gif
- type: FONT
path: ${PREFIX}/data/font/8bithud.txt
path: ${PREFIX}/data/font/8bithud.fnt
# PALETTES
palettes:

132
data/font/8bithud.fnt Normal file
View 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 4 # À
193 4 # Á
200 4 # È
201 4 # É
205 4 # Í
207 4 # Ï
210 4 # Ò
211 4 # Ó
218 4 # Ú
220 4 # Ü
209 4 # Ñ
199 4 # Ç
224 4 # à
225 4 # á
232 4 # è
233 4 # é
237 4 # í
239 4 # ï
242 4 # ò
243 4 # ó
250 4 # ú
252 4 # ü
241 4 # ñ
231 4 # ç
161 4 # ¡
191 4 # ¿
171 4 # «
187 4 # »
183 4 # ·

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

After

Width:  |  Height:  |  Size: 759 B

View File

@@ -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

135
data/font/aseprite.fnt Normal file
View File

@@ -0,0 +1,135 @@
# Font: aseprite
# Formato: codepoint_decimal ancho_visual
# Los gliphos se listan en orden de aparición en el bitmap (izquierda→derecha, arriba→abajo)
box_width 8
box_height 8
columns 15
# ASCII 32-126
32 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 4
49 2
50 4
51 4
52 4
53 4
54 4
55 4
56 4
57 4
58 1
59 1
60 3
61 4
62 4
63 4
64 7
65 4
66 4
67 4
68 4
69 4
70 4
71 4
72 4
73 2
74 2
75 4
76 4
77 5
78 4
79 5
80 4
81 5
82 4
83 4
84 5
85 4
86 5
87 7
88 5
89 5
90 4
91 2
92 3
93 2
94 5
95 5
96 3
97 4
98 4
99 4
100 4
101 4
102 2
103 4
104 4
105 1
106 2
107 4
108 1
109 7
110 4
111 4
112 4
113 4
114 3
115 3
116 2
117 4
118 4
119 5
120 5
121 4
122 4
123 3
124 3
125 3
126 5
# Extensiones para ES/CA/VA (descomentar tras añadirlos al bitmap)
# 192 4 # À
# 193 4 # Á
# 200 4 # È
# 201 4 # É
# 205 2 # Í
# 207 2 # Ï
# 210 5 # Ò
# 211 5 # Ó
# 218 4 # Ú
# 220 4 # Ü
# 209 4 # Ñ
# 199 4 # Ç
# 224 4 # à
# 225 4 # á
# 232 4 # è
# 233 4 # é
# 237 1 # í
# 239 2 # ï
# 242 4 # ò
# 243 4 # ó
# 250 4 # ú
# 252 4 # ü
# 241 4 # ñ
# 231 4 # ç
# 161 1 # ¡
# 191 4 # ¿
# 171 5 # «
# 187 5 # »
# 183 1 # · (punt volat)

View File

@@ -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

99
data/font/gauntlet.fnt Normal file
View File

@@ -0,0 +1,99 @@
# 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 # ~

Binary file not shown.

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 784 B

View File

@@ -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

132
data/font/smb2.fnt Normal file
View File

@@ -0,0 +1,132 @@
# 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 8 # U+0020
33 5 # !
34 6 # "
35 7 # #
36 7 # $
37 7 # %
38 7 # &
39 4 # '
40 6 # (
41 5 # )
42 7 # *
43 7 # +
44 4 # ,
45 7 # -
46 4 # .
47 7 # /
48 7 # 0
49 6 # 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 4 # :
59 4 # ;
60 6 # <
61 6 # =
62 6 # >
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 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 7 # S
84 7 # T
85 7 # U
86 7 # V
87 7 # W
88 7 # X
89 7 # Y
90 7 # Z
91 6 # [
92 7 # \
93 5 # ]
94 6 # ^
95 7 # _
96 4 # `
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 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 7 # s
116 7 # t
117 7 # u
118 7 # v
119 7 # w
120 7 # x
121 7 # y
122 7 # z
123 6 # {
124 5 # |
125 5 # }
126 7 # ~
192 7 # À
193 7 # Á
200 7 # È
201 7 # É
205 6 # Í
207 6 # Ï
210 7 # Ò
211 7 # Ó
218 7 # Ú
220 7 # Ü
209 7 # Ñ
199 7 # Ç
224 7 # à
225 7 # á
232 7 # è
233 7 # é
237 6 # í
239 6 # ï
242 7 # ò
243 7 # ó
250 7 # ú
252 7 # ü
241 7 # ñ
231 7 # ç
161 5 # ¡
191 7 # ¿
171 7 # «
187 7 # »
183 4 # ·

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

After

Width:  |  Height:  |  Size: 968 B

View File

@@ -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
View 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 3 # À
193 3 # Á
200 3 # È
201 3 # É
205 3 # Í
207 3 # Ï
210 3 # Ò
211 3 # Ó
218 3 # Ú
220 3 # Ü
209 3 # Ñ
199 3 # Ç
224 3 # à
225 3 # á
232 3 # è
233 3 # é
237 3 # í
239 3 # ï
242 3 # ò
243 3 # ó
250 3 # ú
252 3 # ü
241 3 # ñ
231 3 # ç
161 3 # ¡
191 3 # ¿
171 3 # «
187 3 # »
183 3 # ·

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 B

After

Width:  |  Height:  |  Size: 596 B

View File

@@ -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

View File

@@ -1,111 +1,110 @@
# JailDoctor's Dilemma - Catalan Locale
# lang: ca
# Nota: s'utilitzen nomes caracters ASCII per compatibilitat amb la font del joc
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: "EH JAILEROS!! ESTEM EN 2022 I ENCARA HO PETEM COM EN 1998!! QUE, HO HEU SENTIT O NO? ELS JAILGAMES HAN TORNAT!! SÍ, COLLONS, HAN TORNAT!! MÉS DE 10 TÍTOLS QUE EL JAILDOC TÉ A FOC LENT!! AIXÒ ÉS UNA BARBARITAT, PERÒ... QUIN EIXIRÀ PRIMER? I ATENCIÓ, QUE HI HA UN APARELLET NOU QUE VOS FARÀ VOLAR EL CAP: EL P.A.C.O.! PERÒ UN MOMENT... QUÈ ÉS AQUELL ENCANTET QUE VE ALLÀ? OOOH, AQUELLA MINIASCII ÉS AMOR DEL BO!! LI PEGARIA UNA MOSSEGADA A CADA BYTE! OSTRES! I NO VOS OBLIDEU DE PUJAR AQUELLS JAILGAMES VELLS I PANXUTS DE MS-DOS A GITHUB, QUE SI NO ES PERDRAN!! QUIN SERÀ EL PROPER PROJECTE DEL JAILDOC? QUÈ PRENDRA VIDA? AI MARE... NI IDEA, PERÒ A PODEU SABER-HO SI RESOLGUEU EL DILEMA DEL JAILDOCTOR... VOS ATREVIU O QUÈ?"
menu:
play: "1. JUGAR"
keyboard: "2. REDEFINIR TECLAT"
joystick: "3. REDEFINIR MANDO"
projects: "4. PROJECTES"
keys:
prompt0: "PREM TECLA PER ESQUERRA"
prompt1: "PREM TECLA PER DRETA"
prompt2: "PREM TECLA PER SALTAR"
prompt0: "PREM UNA TECLA PER A ESQUERRA"
prompt1: "PREM UNA TECLA PER A DRETA"
prompt2: "PREM UNA TECLA PER A SALTAR"
defined: "TECLES DEFINIDES"
label0: "ESQUERRA: "
label1: "DRETA: "
label2: "SALTAR: "
invalid: "TECLA INVALIDA! PROVA UNA ALTRA"
already_used: "TECLA JA USADA! PROVA UNA ALTRA"
invalid: "TECLA INVÀLIDA! PROVA'N UNA ALTRA"
already_used: "TECLA JA USADA! PROVA'N UNA ALTRA"
buttons:
prompt0: "PREM BOTO PER ESQUERRA"
prompt1: "PREM BOTO PER DRETA"
prompt2: "PREM BOTO PER SALTAR"
prompt0: "PREM UN BOTÓ PER A ESQUERRA"
prompt1: "PREM UN BOTÓ PER A DRETA"
prompt2: "PREM UN BOTÓ PER A SALTAR"
defined: "BOTONS DEFINITS"
already_used: "BOTO JA USAT! PROVA UN ALTRE"
already_used: "BOTÓ JA USAT! PROVA'N UN ALTRE"
projects: "PROJECTES"
game_over:
title: "G A M E O V E R"
items: "OBJECTES: "
rooms: "SALES: "
worst_nightmare: "EL TEU PITJOR MALSON ES"
worst_nightmare: "EL TEU PITJOR MALSON ÉS"
ending:
t0: "FINALMENT HO VA ACONSEGUIR"
t1: "ARRIBAR A LA JAIL"
t2: "AMB TOTS ELS SEUS PROJECTES"
t3: "A PUNT PER SER ALLIBERATS"
t4: "ALLI ESTAVEN TOTS ELS JAILERS"
t3: "A PUNT D'ALLIBERAR-LOS"
t4: "ALLÍ ESTAVEN TOTS ELS JAILERS"
t5: "ESPERANT QUE ELS JAILGAMES"
t6: "FOSSIN ALLIBERATS"
t6: "FOREN ALLIBERATS"
t7: "HI HAVIA FINS I TOT BARRULLS"
t8: "I BEGGINERS ENTRE LA MULTITUD"
t9: "BRY ESTAVA PLORANT..."
t10: "PERO DE SOBTE ALGUNA COSA"
t11: "VA ATREURE LA SEVA ATENCIO"
t12: "UN MUNT DE FERALLA!"
t13: "PLE DE TRASTOS QUE NO FUNCIONEN!!"
t8: "I BEGGINERS ENTRE LA GENT"
t9: "BRY ESTAVA FENT LLAGRIMETA..."
t10: "PERÒ DE SOBTE ALGUNA COSA"
t11: "LI VA CRIDAR L'ATENCIÓ"
t12: "UN MUNT DE FERRALLA!"
t13: "PLE D'ANDROMINES QUE NI ANAVEN!!"
t14: "I ALESHORES,"
t15: "QUARANTA NOUS PROJECTES"
t16: "VAN NEIXER..."
t15: "QUARANTA PROJECTES NOUS"
t16: "VAN NÀIXER..."
ending2:
starring: "PROTAGONISTES"
jaildoctor: "JAILDOCTOR"
thank_you: "GRACIES"
thank_you: "GRÀCIES"
for_playing: "PER JUGAR!"
credits:
instructions: "INSTRUCCIONS:"
l0: "AJUDA A JAILDOC A RECUPERAR"
l1: "ELS SEUS PROJECTES I ANAR A"
l0: "AJUDA EL JAILDOC A RECUPERAR"
l1: "ELS SEUS PROJECTES I ARRIBAR A"
l2: "LA JAIL PER ACABAR-LOS"
keys: "TECLES:"
keys_move: "CURSORS PER MOURE I SALTAR"
f8: "F8 ACTIVAR/DESACTIVAR MUSICA"
keys_move: "CURSORS PER A MOURE I SALTAR"
f8: "F8 ACTIVAR/DESACTIVAR MÚSICA"
f11: "F11 PAUSAR EL JOC"
f1f2: "F1-F2 MIDA DE LA FINESTRA"
f3: "F3 PANTALLA COMPLETA"
f9: "F9 VORA DE LA PANTALLA"
author: "UN JOC DE JAILDESIGNER"
date: "FET A L'ESTIU/TARDOR DEL 2022"
love: "M'ENCANTEN ELS JAILGAMES! "
love: "M'ENCANTEN ELS JAILGAMES!"
achievements:
header: "ASSOLIMENT DESBLOQUEJAT!"
c1: "COSES BRILLANTS"
d1: "Obteniu el 25% dels objectes"
c2: "A MEITAT DE CAMI"
d2: "Obteniu el 50% dels objectes"
d1: "Aconseguiu el 25% dels objectes"
c2: "A MITJAN CAMÍ"
d2: "Aconseguiu el 50% dels objectes"
c3: "QUASI HI SOM"
d3: "Obteniu el 75% dels objectes"
c4: "EL COL LECCIONISTA"
d4: "Obteniu el 100% dels objectes"
c5: "PASSEJANT PER AQUI"
d3: "Aconseguiu el 75% dels objectes"
c4: "EL COL·LECCIONISTA"
d4: "Aconseguiu el 100% dels objectes"
c5: "PASSEJANT PER A"
d5: "Visiteu 20 sales"
c6: "M'HE PERDUT"
d6: "Visiteu 40 sales"
c7: "M'AGRADA EXPLORAR"
d7: "Visiteu totes les sales"
c8: "JA ESTA?"
d8: "Completa el joc"
c9: "EM VA XUCLAR UN FORAT"
d9: "Completa el joc sense entrar a la preso"
c8: "JA ESTÀ?"
d8: "Completeu el joc"
c9: "UN FORAT EM VA ENGOLIR"
d9: "Completeu el joc sense entrar a la presó"
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"
d11: "Completa el joc sense morir"
c12: "PROJECTES MALS FETS DE PRESSA"
d12: "Completa el joc en menys de 30 minuts"
d11: "Completeu el joc sense morir"
c12: "PROJECTES A CORRE-CUITA"
d12: "Completeu el joc en menys de 30 minuts"
ui:
press_again_menu: "PREM DE NOU PER TORNAR AL MENU"
press_again_exit: "PREM DE NOU PER SORTIR"
border_enabled: "BORDE ACTIVAT"
border_disabled: "BORDE DESACTIVAT"
press_again_menu: "PREM DE NOU PER TORNAR AL MENÚ"
press_again_exit: "PREM DE NOU PER EIXIR"
border_enabled: "VORA ACTIVADA"
border_disabled: "VORA DESACTIVADA"
fullscreen_enabled: "PANTALLA COMPLETA ACTIVADA"
fullscreen_disabled: "PANTALLA COMPLETA DESACTIVADA"
window_zoom: "ZOOM FINESTRA x"
@@ -120,12 +119,12 @@ ui:
scoreboard:
items: "TRESORS PILLATS "
time: " HORA "
time: " TEMPS "
rooms: "SALES"
game:
music_enabled: "MUSICA ACTIVADA"
music_disabled: "MUSICA DESACTIVADA"
music_enabled: "MÚSICA ACTIVADA"
music_disabled: "MÚSICA DESACTIVADA"
paused: "JOC EN PAUSA"
running: "JOC EN MARXA"
enabled: " ACTIVAT"

View File

@@ -602,5 +602,5 @@ void Screen::createText() {
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)
text_ = std::make_shared<Text>(surface, Resource::List::get()->get("aseprite.txt"));
text_ = std::make_shared<Text>(surface, Resource::List::get()->get("aseprite.fnt"));
}

View File

@@ -3,7 +3,6 @@
#include <SDL3/SDL.h>
#include <cstddef> // Para size_t
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream
#include <iostream> // Para cerr
#include <sstream> // Para istringstream
#include <stdexcept> // Para runtime_error
@@ -14,93 +13,114 @@
#include "core/resources/resource_helper.hpp" // Para ResourceHelper
#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::nextCodepoint(const std::string& s, size_t& pos) -> uint32_t {
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 {
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> {
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);
if (file_data.empty()) {
std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << '\n';
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::istringstream stream(content);
std::string buffer;
std::string line;
int glyph_index = 0;
// Lee los dos primeros valores del fichero
std::getline(stream, buffer);
// Remove Windows line ending if present
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);
while (std::getline(stream, line)) {
if (!line.empty() && line.back() == '\r') { line.pop_back(); }
if (line.empty() || line[0] == '#') { continue; }
std::getline(stream, buffer);
// Remove Windows line ending if present
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_height = std::stoi(buffer);
std::istringstream ls(line);
std::string key;
ls >> key;
// lee el resto de datos del fichero
auto index = 32;
auto line_read = 0;
while (std::getline(stream, buffer)) {
// Remove Windows line ending if present
if (!buffer.empty() && buffer.back() == '\r') {
buffer.pop_back();
if (key == "box_width") {
ls >> tf->box_width;
} else if (key == "box_height") {
ls >> tf->box_height;
} else if (key == "columns") {
ls >> tf->columns;
} 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{};
off.x = (glyph_index % tf->columns) * tf->box_width;
off.y = (glyph_index / tf->columns) * tf->box_height;
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 ]");
// 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;
}
// Constructor
// Constructor desde fichero
Text::Text(const std::shared_ptr<Surface>& surface, const std::string& text_file) {
// Carga los offsets desde el fichero
auto tf = loadTextFile(text_file);
// Inicializa variables desde la estructura
box_height_ = tf->box_height;
box_width_ = tf->box_width;
offset_ = tf->offset;
// Crea los objetos
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)
: 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)})),
box_width_(text_file->box_width),
@@ -111,18 +131,22 @@ Text::Text(const std::shared_ptr<Surface>& surface, const std::shared_ptr<File>&
// Escribe texto en pantalla
void Text::write(int x, int y, const std::string& text, int kerning, int lenght) {
int shift = 0;
if (lenght == -1) {
lenght = text.length();
}
int glyphs_done = 0;
size_t pos = 0;
sprite_->setY(y);
for (int i = 0; i < lenght; ++i) {
auto index = static_cast<int>(text[i]);
sprite_->setClip(offset_[index].x, offset_[index].y, box_width_, box_height_);
sprite_->setX(x + shift);
sprite_->render(1, 15);
shift += offset_[static_cast<int>(text[i])].w + kerning;
while (pos < text.size()) {
if (lenght != -1 && glyphs_done >= lenght) { break; }
uint32_t cp = nextCodepoint(text, pos);
auto it = offset_.find(cp);
if (it == offset_.end()) { it = offset_.find('?'); }
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;
}
}
@@ -157,18 +181,22 @@ auto Text::writeDXToSurface(Uint8 flags, const std::string& text, int kerning, U
// Escribe el texto con colores
void Text::writeColored(int x, int y, const std::string& text, Uint8 color, int kerning, int lenght) {
int shift = 0;
if (lenght == -1) {
lenght = text.length();
}
int glyphs_done = 0;
size_t pos = 0;
sprite_->setY(y);
for (int i = 0; i < lenght; ++i) {
auto index = static_cast<int>(text[i]);
sprite_->setClip(offset_[index].x, offset_[index].y, box_width_, box_height_);
sprite_->setX(x + shift);
sprite_->render(1, color);
shift += offset_[static_cast<int>(text[i])].w + kerning;
while (pos < text.size()) {
if (lenght != -1 && glyphs_done >= lenght) { break; }
uint32_t cp = nextCodepoint(text, pos);
auto it = offset_.find(cp);
if (it == offset_.end()) { it = offset_.find('?'); }
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;
}
}
@@ -188,8 +216,8 @@ void Text::writeCentered(int x, int y, const std::string& text, int kerning, int
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) {
const auto CENTERED = ((flags & CENTER_FLAG) == CENTER_FLAG);
const auto SHADOWED = ((flags & SHADOW_FLAG) == SHADOW_FLAG);
const auto COLORED = ((flags & COLOR_FLAG) == COLOR_FLAG);
const auto STROKED = ((flags & STROKE_FLAG) == STROKE_FLAG);
const auto COLORED = ((flags & COLOR_FLAG) == COLOR_FLAG);
const auto STROKED = ((flags & STROKE_FLAG) == STROKE_FLAG);
if (CENTERED) {
x -= (Text::length(text, kerning) / 2);
@@ -213,22 +241,35 @@ void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerni
writeColored(x, y, text, text_color, kerning, lenght);
} else {
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 {
int shift = 0;
for (size_t i = 0; i < text.length(); ++i) {
shift += (offset_[static_cast<int>(text[i])].w + kerning);
size_t pos = 0;
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 - kerning;
return shift > 0 ? shift - kerning : 0;
}
// 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 {
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 tamaño de la caja de cada caracter
auto Text::getCharacterSize() const -> int {
return box_width_;
}
@@ -236,4 +277,4 @@ auto Text::getCharacterSize() const -> int {
// Establece si se usa un tamaño fijo de letra
void Text::setFixedWidth(bool value) {
fixed_width_ = value;
}
}

View File

@@ -2,14 +2,14 @@
#include <SDL3/SDL.h>
#include <array> // Para std::array
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string
#include <unordered_map> // Para unordered_map
#include "core/rendering/surface_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 {
public:
// Tipos anidados públicos
@@ -18,9 +18,10 @@ class Text {
};
struct File {
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
std::array<Offset, 128> offset{}; // Vector con las posiciones y ancho de cada letra
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 columns{16}; // Número de columnas en el bitmap
std::unordered_map<uint32_t, Offset> offset; // Posición y ancho de cada glifo (clave: codepoint Unicode)
};
// Constructor
@@ -45,20 +46,23 @@ class Text {
auto writeToSurface(const std::string& text, int zoom = 1, int kerning = 1) -> std::shared_ptr<Surface>; // Escribe el texto en una textura
auto writeDXToSurface(Uint8 flags, const std::string& text, int kerning = 1, Uint8 text_color = Uint8(), Uint8 shadow_distance = 1, Uint8 shadow_color = Uint8(), int lenght = -1) -> std::shared_ptr<Surface>; // Escribe el texto con extras en una textura
[[nodiscard]] auto length(const std::string& text, int kerning = 1) const -> int; // Obtiene la longitud en pixels de una cadena
[[nodiscard]] auto getCharacterSize() const -> int; // Devuelve el tamaño del caracter
[[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 glyphWidth(uint32_t codepoint, int kerning = 0) const -> int; // Devuelve el ancho en pixels de un glifo
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:
// Objetos y punteros
std::unique_ptr<SurfaceSprite> sprite_ = nullptr; // Objeto con los graficos para el texto
// Variables
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
bool fixed_width_ = false; // Indica si el texto se ha de escribir con longitud fija en todas las letras
std::array<Offset, 128> offset_{}; // Vector con las posiciones y ancho de cada letra
};
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
bool fixed_width_ = false; // Indica si el texto se ha de escribir con longitud fija
std::unordered_map<uint32_t, Offset> offset_; // Posición y ancho de cada glifo (clave: codepoint Unicode)
};

View File

@@ -370,11 +370,11 @@ namespace Resource {
std::cout << "\n>> CREATING TEXT_OBJECTS" << '\n';
std::vector<ResourceInfo> resources = {
{.key = "aseprite", .texture_file = "aseprite.gif", .text_file = "aseprite.txt"},
{.key = "gauntlet", .texture_file = "gauntlet.gif", .text_file = "gauntlet.txt"},
{.key = "smb2", .texture_file = "smb2.gif", .text_file = "smb2.txt"},
{.key = "subatomic", .texture_file = "subatomic.gif", .text_file = "subatomic.txt"},
{.key = "8bithud", .texture_file = "8bithud.gif", .text_file = "8bithud.txt"}};
{.key = "aseprite", .texture_file = "aseprite.gif", .text_file = "aseprite.fnt"},
{.key = "gauntlet", .texture_file = "gauntlet.gif", .text_file = "gauntlet.fnt"},
{.key = "smb2", .texture_file = "smb2.gif", .text_file = "smb2.fnt"},
{.key = "subatomic", .texture_file = "subatomic.gif", .text_file = "subatomic.fnt"},
{.key = "8bithud", .texture_file = "8bithud.gif", .text_file = "8bithud.fnt"}};
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))});

View File

@@ -60,17 +60,21 @@ void Title::initMarquee() {
letters_.clear();
long_text_ = Locale::get()->get("title.marquee");
// Pre-calcular anchos de caracteres para eficiencia
for (size_t i = 0; i < long_text_.length(); ++i) {
// Pre-calcular anchos de caracteres para eficiencia (iteración por codepoints UTF-8)
size_t pos = 0;
while (pos < long_text_.size()) {
uint32_t cp = Text::nextCodepoint(long_text_, pos);
Glyph l;
l.letter = long_text_[i]; // char directo, no substring
l.x = MARQUEE_START_X; // Usar constante
l.width = marquee_text_->length(std::string(1, long_text_[i])); // Pre-calcular ancho
l.codepoint = cp;
l.x = MARQUEE_START_X;
l.width = static_cast<float>(marquee_text_->glyphWidth(cp, 1)); // Pre-calcular ancho con kerning
l.enabled = false;
letters_.push_back(l);
}
letters_[0].enabled = true;
if (!letters_.empty()) {
letters_[0].enabled = true;
}
first_active_letter_ = 0;
last_active_letter_ = 0;
}
@@ -224,9 +228,9 @@ void Title::renderMarquee() {
const auto& letter = letters_[i];
if (letter.enabled) {
marquee_text_->writeColored(
static_cast<int>(letter.x), // Conversión explícita float→int
static_cast<int>(MARQUEE_Y), // Usar constante
std::string(1, letter.letter), // Convertir char a string
static_cast<int>(letter.x), // Conversión explícita float→int
static_cast<int>(MARQUEE_Y), // Usar constante
Text::codepointToUtf8(letter.codepoint), // Convertir codepoint a string UTF-8
static_cast<Uint8>(PaletteColor::MAGENTA));
}
}

View File

@@ -25,10 +25,10 @@ class Title {
private:
// --- Estructuras y enumeraciones ---
struct Glyph {
char letter; // Letra a escribir (char es más eficiente que std::string)
float x; // Posición en el eje x (float para precisión con delta time)
float width; // Ancho pre-calculado del carácter
bool enabled; // Solo se escriben y mueven si estan habilitadas
uint32_t codepoint{0}; // Codepoint Unicode del carácter
float x{0.0F}; // Posición en el eje x (float para precisión con delta time)
float width{0.0F}; // Ancho pre-calculado del carácter
bool enabled{false}; // Solo se escriben y mueven si estan habilitadas
};
enum class State {

242
tools/font_gen/font_gen.py Normal file
View File

@@ -0,0 +1,242 @@
#!/usr/bin/env python3
"""Generador de fuentes bitmap para JailDoctor's Dilemma.
Convierte un archivo .ttf en un GIF indexado + fichero .fnt compatibles
con el sistema de texto del juego.
Dependencias: pip install Pillow
Uso:
python3 font_gen.py --ttf myfont.ttf --size 8 --output myfont
python3 font_gen.py --ttf myfont.ttf --size 8 --output myfont --dir data/font --box-width 8
Notas:
- Para fuentes bitmap (pixel fonts) en TTF, usa el tamaño exacto del bitmap strike.
- Los glifos se almacenan como índice de paleta 1 (blanco) sobre fondo transparente (índice 0).
- Esto es compatible con SurfaceSprite::render(1, color) del motor del juego.
- Los caracteres no incluidos en la fuente aparecerán como celdas vacías en el GIF.
"""
import argparse
import os
import sys
from math import ceil
try:
from PIL import Image, ImageDraw, ImageFont
except ImportError:
print("Error: Pillow no está instalado. Ejecuta: pip install Pillow", file=sys.stderr)
sys.exit(1)
# Conjunto de caracteres en el mismo orden que los ficheros .fnt del juego.
# ASCII 32-126 primero, luego extensiones para castellano, catalán y valenciano.
_ASCII_CHARS = [chr(i) for i in range(32, 127)] # 95 chars: espacio … ~
_EXTENDED_CHARS = list("ÀÁÈÉÍÏÒÓÚÜÑÇàáèéíïòóúüñç¡¿«»·") # 29 chars ES/CA/VA
ALL_CHARS = _ASCII_CHARS + _EXTENDED_CHARS # 124 total
def render_font(
ttf_path: str,
size: int,
output_name: str,
output_dir: str,
columns: int,
box_width_override: int | None,
box_height_override: int | None,
) -> None:
"""Genera el GIF indexado y el .fnt a partir de un archivo .ttf."""
if not os.path.isfile(ttf_path):
print(f"Error: No se encuentra el archivo TTF: {ttf_path}", file=sys.stderr)
sys.exit(1)
os.makedirs(output_dir, exist_ok=True)
output_gif = os.path.join(output_dir, f"{output_name}.gif")
output_fnt = os.path.join(output_dir, f"{output_name}.fnt")
# --- Cargar fuente ---
try:
font = ImageFont.truetype(ttf_path, size)
except OSError as e:
print(f"Error al cargar la fuente: {e}", file=sys.stderr)
sys.exit(1)
# --- Calcular dimensiones de la caja ---
# box_height = línea completa: ascent (sobre la línea base) + descent (bajo ella)
ascent, descent = font.getmetrics()
box_height = box_height_override if box_height_override is not None else (ascent + abs(descent))
# Filtrar chars: solo incluir los que el TTF soporta realmente.
# Un char se descarta si su advance es 0 (el TTF no lo tiene) y no es un
# espacio. Evita que chars sin glifo aparezcan con width=1 en el .fnt,
# lo que causaría solapamiento masivo al renderizar texto localizado.
chars_to_render = [
ch for ch in ALL_CHARS
if ch.isspace() or font.getlength(ch) >= 1.0
]
skipped = [ch for ch in ALL_CHARS if ch not in chars_to_render]
if skipped:
print(f"Omitidos : {len(skipped)} chars sin soporte en este TTF: {''.join(skipped)}")
# Medir advance width tipográfico: solo se usa para calcular box_width del canvas
# cuando el usuario no lo especifica. El ancho real del .fnt se mide desde píxeles.
char_widths: dict[str, int] = {}
for ch in chars_to_render:
char_widths[ch] = max(1, int(font.getlength(ch)))
# Calcular el offset vertical para eliminar el espacio en blanco en la parte
# superior de la celda. Muchas fuentes bitmap tienen un em-box más grande que
# los píxeles visibles (ascent incluye espacio interno). Usamos las letras
# mayúsculas como referencia de "cap height": su bbox[1] indica cuántos
# píxeles en blanco hay sobre los caracteres más altos, y restamos ese valor
# para que las mayúsculas queden alineadas al borde superior de la celda.
cap_tops = [font.getbbox(ch)[1] for ch in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" if font.getbbox(ch)]
y_offset = -round(sum(cap_tops) / len(cap_tops)) if cap_tops else 0
# box_width: anchura de cada celda en el bitmap.
# Si el usuario la especifica, se usa tal cual. Si no, se calcula como el
# advance máximo (sin padding extra, ya que getlength incluye el espaciado).
if box_width_override is not None:
box_width = box_width_override
else:
box_width = max(char_widths.values())
# --- Calcular dimensiones del bitmap completo ---
rows = ceil(len(chars_to_render) / columns)
img_width = columns * box_width
img_height = rows * box_height
print(f"Fuente : {os.path.basename(ttf_path)}, size={size}")
print(f"Caja : {box_width}×{box_height} px | {columns} columnas, {rows} filas")
print(f"Bitmap : {img_width}×{img_height} px | {len(chars_to_render)} caracteres")
# --- Buffer de píxeles: 0 = fondo transparente, 1 = glifo ---
pixels = bytearray(img_width * img_height)
# --- Renderizar cada carácter ---
for i, ch in enumerate(chars_to_render):
col = i % columns
row = i // columns
cell_x = col * box_width
cell_y = row * box_height
bbox = font.getbbox(ch)
if not bbox:
# Sin glifos visibles (ej. espacio): celda vacía, correcto.
continue
# Renderizar a imagen RGBA con fondo transparente, texto blanco.
#
# POSICIONAMIENTO VERTICAL: se usa y=0 para respetar la posición del
# carácter relativa a la línea base del em-box. draw.text((x, 0), ch)
# coloca la parte superior del em-box en y=0, de modo que la línea base
# queda en y=ascent y los signos de puntuación (que están cerca de la
# línea base) aparecen en la parte inferior de la celda, como es correcto.
#
# POSICIONAMIENTO HORIZONTAL: -bbox[0] alinea el borde izquierdo del
# glifo al inicio de la celda, compensando el bearing izquierdo.
char_img = Image.new("RGBA", (box_width, box_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(char_img)
draw.text((-bbox[0], y_offset), ch, font=font, fill=(255, 255, 255, 255))
# Umbralizar alpha y volcar al buffer de índices
char_bytes = char_img.tobytes()
for py in range(box_height):
for px in range(box_width):
src = (py * box_width + px) * 4
if char_bytes[src + 3] > 128:
pixels[(cell_y + py) * img_width + (cell_x + px)] = 1
# Medir ancho visual real: última columna con algún píxel opaco.
# Reemplaza el advance tipográfico de getlength() que incluye side-bearings.
for px in range(box_width - 1, -1, -1):
if any(char_bytes[(py * box_width + px) * 4 + 3] > 128 for py in range(box_height)):
char_widths[ch] = px + 1
break
# --- Crear imagen P (paleta indexada 8 bits) ---
img = Image.frombytes("P", (img_width, img_height), bytes(pixels))
# Paleta mínima: índice 0 = negro (transparente), índice 1 = blanco (glifo)
palette = [0] * 768
palette[3] = palette[4] = palette[5] = 255 # índice 1 → blanco
img.putpalette(palette)
# Guardar GIF: índice 0 = fondo, índice 1 = glifo.
# Pillow sin transparency escribe GIF87a. Luego magick reduce la paleta a
# 2 colores (lzw_min=2) para compatibilidad exacta con el parser gif.cpp.
img.save(output_gif, optimize=False)
try:
import subprocess
subprocess.run(
["magick", output_gif, "-colors", "2", f"GIF87:{output_gif}"],
check=True, capture_output=True,
)
except (subprocess.CalledProcessError, FileNotFoundError):
pass # si magick no está disponible se queda como GIF87a de 256 colores
print(f"GIF : {output_gif}")
# --- Generar fichero .fnt ---
ttf_name = os.path.basename(ttf_path)
with open(output_fnt, "w", encoding="utf-8") as f:
f.write(f"# Font: {output_name} — generado desde {ttf_name} size {size}\n")
f.write(f"# Generado con tools/font_gen/font_gen.py\n\n")
f.write(f"box_width {box_width}\n")
f.write(f"box_height {box_height}\n")
f.write(f"columns {columns}\n\n")
f.write("# codepoint_decimal ancho_visual\n")
for ch in chars_to_render:
cp = ord(ch)
w = char_widths[ch]
name = ch if ch.isprintable() and not ch.isspace() else f"U+{cp:04X}"
f.write(f"{cp} {w} # {name}\n")
print(f"FNT : {output_fnt}")
def main() -> None:
# Directorio del proyecto: dos niveles arriba de este script (tools/font_gen/)
script_dir = os.path.dirname(os.path.abspath(__file__))
project_dir = os.path.dirname(os.path.dirname(script_dir))
default_dir = os.path.join(project_dir, "data", "font")
parser = argparse.ArgumentParser(
description="Genera fuentes bitmap (.gif + .fnt) desde un archivo .ttf.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Ejemplos:
python3 font_gen.py --ttf myfont.ttf --size 8 --output myfont
python3 font_gen.py --ttf myfont.ttf --size 8 --output myfont --box-width 8
python3 font_gen.py --ttf myfont.ttf --size 16 --output myfont --dir data/font
Notas:
- Para pixel fonts (.ttf bitmap), usa el tamaño exacto del bitmap strike.
- El GIF resultante usa índice 1 = glifo, índice 0 = transparente.
- Se generan 124 caracteres: ASCII 32-126 + extensiones ES/CA/VA.
- Usa --box-width para forzar una anchura de celda fija (útil para fuentes
cuadradas donde la celda coincide con box_height).
""",
)
parser.add_argument("--ttf", required=True, help="Ruta al archivo .ttf")
parser.add_argument("--size", required=True, type=int, help="Tamaño en píxeles")
parser.add_argument("--output", required=True, help="Nombre base de salida (sin extensión)")
parser.add_argument("--dir", default=default_dir, help="Directorio de salida (default: data/font/)")
parser.add_argument("--columns", default=15, type=int, help="Columnas en el bitmap (default: 15)")
parser.add_argument("--box-width", default=None, type=int, help="Anchura fija de celda en px (default: auto)")
parser.add_argument("--box-height", default=None, type=int, help="Altura fija de celda en px (default: ascent + abs(descent))")
args = parser.parse_args()
render_font(
ttf_path = args.ttf,
size = args.size,
output_name = args.output,
output_dir = args.dir,
columns = args.columns,
box_width_override = args.box_width,
box_height_override = args.box_height,
)
if __name__ == "__main__":
main()