New upstream version 2.2.0
Markus Koschany
5 years ago
6 | 6 | winicon.res |
7 | 7 | nbproject |
8 | 8 | .* |
9 | -.travis.yml | |
10 | -.gitignore | |
9 | !.travis.yml | |
10 | !.gitignore | |
11 | 11 | !.editorconfig |
12 | 12 | !.dockerignore |
13 | 13 | *.patch |
2 | 2 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${blockattack_SOURCE_DIR}/source/misc/cmake") |
3 | 3 | set(BIN_DIR ${blockattack_SOURCE_DIR}/Game) |
4 | 4 | |
5 | SET(CPACK_PACKAGE_VERSION "2.1.2") | |
5 | SET(CPACK_PACKAGE_VERSION "2.2.0") | |
6 | 6 | SET(CPACK_PACKAGE_VERSION_MAJOR "2") |
7 | SET(CPACK_PACKAGE_VERSION_MINOR "1") | |
8 | SET(CPACK_PACKAGE_VERSION_PATCH "2") | |
7 | SET(CPACK_PACKAGE_VERSION_MINOR "2") | |
8 | SET(CPACK_PACKAGE_VERSION_PATCH "0") | |
9 | 9 | SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Block Attack - Rise of the Blocks ${CPACK_PACKAGE_VERSION}") |
10 | 10 | SET(CPACK_PACKAGE_VENDOR "Poul Sander") |
11 | 11 | INCLUDE(CPack) |
34 | 34 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=i686") |
35 | 35 | endif() |
36 | 36 | |
37 | if (NOT WIN32) | |
37 | if (NOT WIN32 AND NOT STANDALONE) | |
38 | 38 | #The path to the data dir must be compiled into the binary |
39 | 39 | add_definitions(-DSHAREDIR=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_DATA_DIR}\") |
40 | 40 | add_definitions(-DLOCALEDIR=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_LOCALE_DIR}\") |
44 | 44 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -std=c++11") |
45 | 45 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG} -g -DDEBUG") |
46 | 46 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} -O2") |
47 | ||
48 | #Newer versions of gcc fails for Cereal. | |
49 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-implicit-fallthrough") | |
47 | 50 | |
48 | 51 | #Setup SDL2 |
49 | 52 | find_package(SDL2 REQUIRED) |
0 | ../../../source/code/AUTHORS⏎ |
529 | 529 | "width" : 50, |
530 | 530 | "number_of_frames" : 1, |
531 | 531 | "frame_time" : 1 |
532 | }, | |
533 | "help_controller" : { | |
534 | "texture" : "help_controller", | |
535 | "topx" : 0, | |
536 | "topy" : 0, | |
537 | "height" : 310, | |
538 | "width" : 479 | |
532 | 539 | } |
533 | 540 | |
534 | 541 | } |
Binary diff not shown
0 | # blockattack-game [![Build Status](https://travis-ci.org/blockattack/blockattack-game.svg?branch=master)](https://travis-ci.org/blockattack/blockattack-game) [![Coverity Status](https://scan.coverity.com/projects/8278/badge.svg)](https://scan.coverity.com/projects/8278) [![license](https://img.shields.io/github/license/blockattack/blockattack-game.svg)]() | |
0 | # blockattack-game [![Build Status](https://travis-ci.org/blockattack/blockattack-game.svg?branch=master)](https://travis-ci.org/blockattack/blockattack-game) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/0dfba60f2ce9484a828bd7c112f50089)](https://www.codacy.com/app/github_43/blockattack-game?utm_source=github.com&utm_medium=referral&utm_content=blockattack/blockattack-game&utm_campaign=Badge_Grade) [![Coverity Status](https://scan.coverity.com/projects/8278/badge.svg)](https://scan.coverity.com/projects/8278) [![license](https://img.shields.io/github/license/blockattack/blockattack-game.svg)]() | |
1 | 1 | Block Attack - Rise of the Blocks - the game<br/> |
2 | 2 | A Tetris Attack Clone under the GPL. |
3 | 3 | |
71 | 71 | * Game - The output is placed here |
72 | 72 | * man - The manual file and the script to generate it. May be moved to source/misc at some point |
73 | 73 | * source/code/ - The source code |
74 | * source/code/Libs - External libs that are compiled into the project because they are either header only (Cereal) or not designed for use as a shared library (NFont). | |
74 | * source/code/Libs - External libs that are compiled into the project because they are either header only (Cereal) or not designed for use as a shared library. | |
75 | 75 | * source/code/sago - Source code. Not designed to be specific to Block Attack - Rise of the Blocks |
76 | 76 | * source/assets - Source for the assets if relevant. For instance svg source for the graphics. |
77 | 77 | * source/misc - Misc stuff. Code related tool that are used for development but not part of the final product. |
0 | This is Block Attack - Rise of the Blocks - version 2.1.1 | |
1 | ||
2 | This has the first gameplay change in a long time. In the previous version the rise would stop | |
3 | while clearing. It could stop for several seconds. This is no longer the case. The time is now | |
4 | bound and will lower as the game progresses. | |
5 | ||
6 | The update of the model has also been changed. This should make it more smooth in some of the | |
7 | high speed Stage Clear levels. | |
8 | ||
9 | Changes since 2.0.1: | |
10 | * There is now an upper limit on how long the rise will stop | |
11 | * The internal updated of the game has been updated from 20 times a second to 100 times a second | |
12 | * Menu fonts have been changed | |
13 | * Window is now resizeable by default | |
14 | * jsoncpp removed as a dependency | |
15 | * Other code cleanup | |
16 | * Docker files are now provided to make compiling even easier | |
17 | * Translation strings changed and are now more | |
18 | * Updated libraries | |
19 | ||
20 | About the resize of the window: | |
21 | ||
22 | The normal resize will simply resize the whole screen. This unfortunately makes the fonts hard to read. | |
23 | The size of the screen is not saved! | |
24 | You can use the command line argument "--no-auto-scale" to not scale the whole image. Screen size will | |
25 | also be saved in this mode and remembered for the next startup. | |
26 | ||
27 | Known issues: | |
28 | * Windows 10 with HighDPI: Fonts becomes unreadable. https://github.com/blockattack/blockattack-game/issues/5 | |
29 | * The translation does not work with the shortcuts created by the Windows installer due to a wrong startup path. | |
30 | I have not been able to figure out why | |
31 | ||
32 | ||
33 | Thank you | |
34 | Thanks to the people that have provided patches or feedback for this release: Paul Wise, mstraube and scootergrisen | |
35 | Especially mstraube that discovered a fatal problem just hours after the release of 2.1.0. | |
36 | ||
37 | ||
38 | Feedback can be given on http://www.blockattack.net or https://github.com/blockattack/blockattack-game |
0 | This is Block Attack - Rise of the Blocks - version 2.1.2 | |
1 | ||
2 | This version only fixes one single bug. The Windows version would not save correctly if "My Games" did not already exist in the Documents folder. | |
3 | No changes to anything else |
0 | This is Block Attack - Rise of the Blocks - version 2.2.0 | |
1 | ||
2 | The primary change in this version is the font handling but I have also added a few help pages. | |
3 | ||
4 | While an updated font handling may at first glance not seem so interesting the effect is actually quite bug. Most importantly the scaling is now a lot better. This is important as computer screens has gotten smaller and smaller in the past years. Another change is that the game now runs out of the box on Windows machines with high resolution displays. | |
5 | ||
6 | Changes since 2.1.2: | |
7 | * New font system | |
8 | * Better scaling | |
9 | * Works out of the box on Windows with high resolution displays | |
10 | * New help menu with rule explanations | |
11 | ||
12 | Known issues: | |
13 | * Some string like "winner", "loser" and "draw" are not translatable. | |
14 | * Keyboard key names are not translatable either | |
15 | * A few screens like the "name" screen does not have gamepad support | |
16 | * Only gamepads connected on startup can be used | |
17 | ||
18 | Feedback can be given on https://blockattack.net or https://github.com/blockattack/blockattack-game |
0 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. | |
1 | .TH BLOCKATTACK "6" "July 2017" "blockattack 2.1.2" "Games" | |
0 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. | |
1 | .TH BLOCKATTACK "6" "May 2018" "blockattack 2.2.0" "Games" | |
2 | 2 | .SH NAME |
3 | 3 | blockattack \- a puzzle game inspired by Tetris Attack |
4 | 4 | .SH SYNOPSIS |
5 | 5 | .B blockattack |
6 | 6 | [\fI\,OPTION\/\fR]... |
7 | 7 | .SH DESCRIPTION |
8 | Block Attack \- Rise of the blocks 2.1.2 | |
8 | Block Attack \- Rise of the blocks 2.2.0 | |
9 | 9 | .PP |
10 | 10 | Block Attack \- Rise of the Blocks is a puzzle/blockfall game inspired by Tetris Attack for the SNES. |
11 | 11 | .PP |
26 | 26 | .TP |
27 | 27 | \fB\-\-priority\fR |
28 | 28 | Causes the game to not sleep between frames. |
29 | .TP | |
30 | \fB\-\-software\-renderer\fR | |
31 | Asks SDL2 to use software renderer | |
29 | 32 | .TP |
30 | 33 | \fB\-\-verbose\-basic\fR |
31 | 34 | Enables basic verbose messages |
57 | 60 | .TP |
58 | 61 | blockattack \-\-nosound |
59 | 62 | Start the game without sound. Can be used if sound problems prevents the game from starting |
63 | .TP | |
64 | blockattack \-\-puzzle\-level\-file puzzle.levels \-\-puzzle\-single\-level 3 | |
65 | Start the game with the default puzzles in level 3 | |
66 | .TP | |
67 | blockattack \-\-bind\-text\-domain /dev/null | |
68 | Disables translations | |
60 | 69 | .SH "REPORTING BUGS" |
61 | 70 | Report bugs to the issue tracker here: <https://github.com/blockattack/blockattack\-game/issues> |
62 | 71 | .SH COPYRIGHT |
0 | 0 | #!/bin/bash |
1 | 1 | set -e |
2 | zip -9rj Game/blockattack.data source/AUTH | |
2 | zip -9rjX Game/blockattack.data source/AUTH | |
3 | 3 | cd Game/data |
4 | zip -9r ../blockattack.data * -x \*svn* | |
4 | zip -9rX ../blockattack.data * -x \*svn* | |
5 | 5 | cd ../../man |
6 | 6 | gzip -9 -c blockattack.man > blockattack.6.gz |
7 | 7 | cd ../source/misc/translation/ |
59 | 59 | garbage_tr.png qubodup |
60 | 60 | green.png qubodup |
61 | 61 | grey.png qubodup |
62 | help_controller.png nicefrog (https://opengameart.org/content/generic-gamepad-template) | |
62 | 63 | i_check_box_area.png Poul Sander |
63 | 64 | icon.png Poul Sander |
64 | 65 | i_draw.png Poul Sander |
0 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | |
1 | <!-- Created with Inkscape (http://www.inkscape.org/) --> | |
2 | ||
3 | <svg | |
4 | xmlns:dc="http://purl.org/dc/elements/1.1/" | |
5 | xmlns:cc="http://creativecommons.org/ns#" | |
6 | xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |
7 | xmlns:svg="http://www.w3.org/2000/svg" | |
8 | xmlns="http://www.w3.org/2000/svg" | |
9 | xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |
10 | xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |
11 | width="546.73724" | |
12 | height="367.63187" | |
13 | id="svg2" | |
14 | version="1.1" | |
15 | inkscape:version="0.48.4 r9939" | |
16 | sodipodi:docname="controller.svg"> | |
17 | <defs | |
18 | id="defs4" /> | |
19 | <sodipodi:namedview | |
20 | id="base" | |
21 | pagecolor="#ffffff" | |
22 | bordercolor="#666666" | |
23 | borderopacity="1.0" | |
24 | inkscape:pageopacity="0.0" | |
25 | inkscape:pageshadow="2" | |
26 | inkscape:zoom="1.28" | |
27 | inkscape:cx="571.3369" | |
28 | inkscape:cy="104.84448" | |
29 | inkscape:document-units="px" | |
30 | inkscape:current-layer="g3802" | |
31 | showgrid="false" | |
32 | inkscape:window-width="1920" | |
33 | inkscape:window-height="975" | |
34 | inkscape:window-x="-8" | |
35 | inkscape:window-y="-8" | |
36 | inkscape:window-maximized="1" | |
37 | fit-margin-top="20" | |
38 | fit-margin-left="20" | |
39 | fit-margin-right="20" | |
40 | fit-margin-bottom="20" /> | |
41 | <metadata | |
42 | id="metadata7"> | |
43 | <rdf:RDF> | |
44 | <cc:Work | |
45 | rdf:about=""> | |
46 | <dc:format>image/svg+xml</dc:format> | |
47 | <dc:type | |
48 | rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |
49 | <dc:title /> | |
50 | </cc:Work> | |
51 | </rdf:RDF> | |
52 | </metadata> | |
53 | <g | |
54 | inkscape:label="Layer 1" | |
55 | inkscape:groupmode="layer" | |
56 | id="layer1" | |
57 | style="display:inline" | |
58 | transform="translate(-154.99045,-304.71875)" /> | |
59 | <g | |
60 | inkscape:groupmode="layer" | |
61 | id="layer2" | |
62 | inkscape:label="Layer" | |
63 | style="display:inline" | |
64 | transform="translate(-154.99045,-304.71875)"> | |
65 | <g | |
66 | id="g3793" | |
67 | transform="translate(58.719552,141.85595)" | |
68 | style="display:inline"> | |
69 | <g | |
70 | id="g3788"> | |
71 | <path | |
72 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
73 | d="m 263.57143,419.50504 c -28.03572,56.42858 -80.49928,92.08847 -100.73276,90.44683 -20.23348,-1.64165 -48.23687,-25.58484 -45.92365,-67.64816 4.55975,-82.91401 73.58785,-221.40908 83.79926,-229.94153 10.21141,-8.53244 18.78619,-14.07754 71.22999,-14.52433 41.022,-0.34948 55.19859,-2.26138 52.34145,25.95291" | |
74 | id="path2985" | |
75 | inkscape:connector-curvature="0" | |
76 | sodipodi:nodetypes="csszsc" /> | |
77 | <path | |
78 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
79 | d="m 473.50901,365.07965 c 0,0 0.73545,8.10393 -6.7986,8.0934 -7.53405,-0.0105 -70.34884,0.0366 -77.88289,0.0261 -7.53405,-0.0105 -5.72717,-8.24589 -5.72717,-8.24589" | |
80 | id="path3838" | |
81 | inkscape:connector-curvature="0" | |
82 | transform="translate(-58.719552,-141.85595)" | |
83 | sodipodi:nodetypes="czzc" /> | |
84 | </g> | |
85 | <path | |
86 | transform="matrix(0.87614083,0,0,0.87046633,35.919161,50.685326)" | |
87 | d="m 344.28571,391.29074 c 0,30.17842 -24.30454,54.64286 -54.28571,54.64286 -29.98117,0 -54.28571,-24.46444 -54.28571,-54.64286 0,-30.17842 24.30454,-54.64286 54.28571,-54.64286 29.98117,0 54.28571,24.46444 54.28571,54.64286 z" | |
88 | sodipodi:ry="54.642857" | |
89 | sodipodi:rx="54.285713" | |
90 | sodipodi:cy="391.29074" | |
91 | sodipodi:cx="290" | |
92 | id="path2994" | |
93 | style="fill:#ffffff;stroke:#000000" | |
94 | sodipodi:type="arc" /> | |
95 | <path | |
96 | transform="matrix(1.1409547,0,0,1.1717896,-48.580202,-61.679814)" | |
97 | d="m 331.42858,309.14789 c 0,36.49024 -30.38067,66.07142 -67.85714,66.07142 -37.47646,0 -67.85714,-29.58118 -67.85714,-66.07142 0,-36.49024 30.38068,-66.07143 67.85714,-66.07143 37.47647,0 67.85714,29.58119 67.85714,66.07143 z" | |
98 | sodipodi:ry="66.071426" | |
99 | sodipodi:rx="67.85714" | |
100 | sodipodi:cy="309.14789" | |
101 | sodipodi:cx="263.57144" | |
102 | id="path3766" | |
103 | style="fill:#ffffff;stroke:#000000" | |
104 | sodipodi:type="arc" /> | |
105 | <path | |
106 | inkscape:connector-curvature="0" | |
107 | id="path3776" | |
108 | d="m 312.76848,350.62183 c -3.15673,-1.86247 -3.15673,-1.86247 -3.15673,-1.86247 l 0,0" | |
109 | style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> | |
110 | <path | |
111 | inkscape:connector-curvature="0" | |
112 | id="path3778" | |
113 | d="m 245.06697,378.85772 0.40178,-2.36608" | |
114 | style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> | |
115 | </g> | |
116 | <rect | |
117 | style="fill:#ffffff;stroke:#ffffff" | |
118 | id="rect3834" | |
119 | width="65.535683" | |
120 | height="34.46431" | |
121 | x="304.64285" | |
122 | y="492.36218" /> | |
123 | </g> | |
124 | <g | |
125 | style="display:inline" | |
126 | inkscape:label="Layer copy" | |
127 | id="g3802" | |
128 | inkscape:groupmode="layer" | |
129 | transform="translate(-154.99045,-304.71875)"> | |
130 | <g | |
131 | id="g3804" | |
132 | transform="matrix(-1,0,0,1,797.99858,141.85595)"> | |
133 | <g | |
134 | id="g3806"> | |
135 | <path | |
136 | sodipodi:nodetypes="csszsc" | |
137 | inkscape:connector-curvature="0" | |
138 | id="path3808" | |
139 | d="m 263.57143,419.50504 c -28.03572,56.42858 -80.49928,92.08847 -100.73276,90.44683 -20.23348,-1.64165 -48.23687,-25.58484 -45.92365,-67.64816 4.55975,-82.91401 73.58785,-221.40908 83.79926,-229.94153 10.21141,-8.53244 18.78619,-14.07754 71.22999,-14.52433 41.022,-0.34948 55.19859,-2.26138 52.34145,25.95291" | |
140 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> | |
141 | </g> | |
142 | <path | |
143 | sodipodi:type="arc" | |
144 | style="fill:#ffffff;stroke:#000000" | |
145 | id="path3810" | |
146 | sodipodi:cx="290" | |
147 | sodipodi:cy="391.29074" | |
148 | sodipodi:rx="54.285713" | |
149 | sodipodi:ry="54.642857" | |
150 | d="m 344.28571,391.29074 c 0,30.17842 -24.30454,54.64286 -54.28571,54.64286 -29.98117,0 -54.28571,-24.46444 -54.28571,-54.64286 0,-30.17842 24.30454,-54.64286 54.28571,-54.64286 29.98117,0 54.28571,24.46444 54.28571,54.64286 z" | |
151 | transform="matrix(0.87614083,0,0,0.87046633,35.919161,50.685326)" /> | |
152 | <path | |
153 | sodipodi:type="arc" | |
154 | style="fill:#ffffff;stroke:#000000" | |
155 | id="path3812" | |
156 | sodipodi:cx="263.57144" | |
157 | sodipodi:cy="309.14789" | |
158 | sodipodi:rx="67.85714" | |
159 | sodipodi:ry="66.071426" | |
160 | d="m 331.42858,309.14789 c 0,36.49024 -30.38067,66.07142 -67.85714,66.07142 -37.47646,0 -67.85714,-29.58118 -67.85714,-66.07142 0,-36.49024 30.38068,-66.07143 67.85714,-66.07143 37.47647,0 67.85714,29.58119 67.85714,66.07143 z" | |
161 | transform="matrix(1.1409547,0,0,1.1717896,-48.580202,-61.679814)" /> | |
162 | <path | |
163 | style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
164 | d="m 312.76848,350.62183 c -3.15673,-1.86247 -3.15673,-1.86247 -3.15673,-1.86247 l 0,0" | |
165 | id="path3814" | |
166 | inkscape:connector-curvature="0" /> | |
167 | <path | |
168 | style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
169 | d="m 245.06697,378.85772 0.40178,-2.36608" | |
170 | id="path3816" | |
171 | inkscape:connector-curvature="0" /> | |
172 | <rect | |
173 | style="fill:#ffffff;stroke:#ffffff;stroke-width:0.98766255" | |
174 | id="rect3830" | |
175 | width="65.695984" | |
176 | height="33.636066" | |
177 | x="-311.72412" | |
178 | y="350.62634" | |
179 | transform="scale(-1,1)" /> | |
180 | <path | |
181 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
182 | d="m 395.71429,539.50504 64.82142,0.0893" | |
183 | id="path3836" | |
184 | inkscape:connector-curvature="0" | |
185 | transform="matrix(-1,0,0,1,797.99858,-141.85595)" | |
186 | sodipodi:nodetypes="cc" /> | |
187 | </g> | |
188 | <text | |
189 | xml:space="preserve" | |
190 | style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Induction;-inkscape-font-specification:Induction" | |
191 | x="541.78571" | |
192 | y="465.93362" | |
193 | id="text4345" | |
194 | sodipodi:linespacing="125%"><tspan | |
195 | sodipodi:role="line" | |
196 | id="tspan4347" | |
197 | x="541.78571" | |
198 | y="465.93362" /></text> | |
199 | <text | |
200 | xml:space="preserve" | |
201 | style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#338000;fill-opacity:1;stroke:none;font-family:Induction;-inkscape-font-specification:Induction" | |
202 | x="542.85712" | |
203 | y="468.07648" | |
204 | id="text4349" | |
205 | sodipodi:linespacing="125%"><tspan | |
206 | sodipodi:role="line" | |
207 | id="tspan4351" | |
208 | x="542.85712" | |
209 | y="468.07648" /></text> | |
210 | <text | |
211 | xml:space="preserve" | |
212 | style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Induction;-inkscape-font-specification:Induction" | |
213 | x="540" | |
214 | y="474.50504" | |
215 | id="text4353" | |
216 | sodipodi:linespacing="125%"><tspan | |
217 | sodipodi:role="line" | |
218 | id="tspan4355" | |
219 | x="540" | |
220 | y="474.50504" /></text> | |
221 | <text | |
222 | xml:space="preserve" | |
223 | style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Induction;-inkscape-font-specification:Induction" | |
224 | x="547.3764" | |
225 | y="453.65738" | |
226 | id="text4357" | |
227 | sodipodi:linespacing="125%"><tspan | |
228 | sodipodi:role="line" | |
229 | id="tspan4359" | |
230 | x="547.3764" | |
231 | y="453.65738" /></text> | |
232 | </g> | |
233 | <g | |
234 | inkscape:groupmode="layer" | |
235 | id="layer5" | |
236 | inkscape:label="dpad" | |
237 | transform="translate(-154.99045,-304.71875)"> | |
238 | <path | |
239 | sodipodi:type="arc" | |
240 | style="fill:#ffffff;stroke:#000000" | |
241 | id="path3845" | |
242 | sodipodi:cx="309.10669" | |
243 | sodipodi:cy="438.94705" | |
244 | sodipodi:rx="36.36549" | |
245 | sodipodi:ry="46.214478" | |
246 | d="m 345.47218,438.94705 c 0,25.52355 -16.28139,46.21448 -36.36549,46.21448 -20.08411,0 -36.36549,-20.69093 -36.36549,-46.21448 0,-25.52355 16.28138,-46.21448 36.36549,-46.21448 20.0841,0 36.36549,20.69093 36.36549,46.21448 z" | |
247 | transform="matrix(1.3583623,0,0,1.0688757,-109.76203,-28.21247)" /> | |
248 | <path | |
249 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
250 | d="m 300.13896,392.88521 c 0,0 0.21752,6.21345 0.31578,9.66211 -0.55737,11.95566 2.83194,22.26427 -5.30375,27.18421 -5.89285,3.44866 -19.27082,1.58089 -33.02082,1.58089" | |
251 | id="path3847" | |
252 | inkscape:connector-curvature="0" | |
253 | sodipodi:nodetypes="cccc" /> | |
254 | <path | |
255 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
256 | d="m 357.8672,430.52556 c 0,0 -6.21345,0.21752 -9.66211,0.31578 -11.95566,-0.55737 -22.26427,2.83194 -27.18421,-5.30375 -3.44866,-5.89285 -1.58089,-19.27082 -1.58089,-33.02082" | |
257 | id="path3847-1" | |
258 | inkscape:connector-curvature="0" | |
259 | sodipodi:nodetypes="cccc" /> | |
260 | <path | |
261 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
262 | d="m 299.92758,489.2228 c 0,0 0.21752,-6.21345 0.31578,-9.66211 -0.55737,-11.95566 2.83194,-22.26427 -5.30375,-27.18421 -5.89285,-3.44866 -19.27082,-1.58089 -33.02082,-1.58089" | |
263 | id="path3847-7" | |
264 | inkscape:connector-curvature="0" | |
265 | sodipodi:nodetypes="cccc" /> | |
266 | <path | |
267 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
268 | d="m 357.9195,451.1566 c 0,0 -6.21345,-0.21752 -9.66211,-0.31578 -11.95566,0.55737 -22.26427,-2.83194 -27.18421,5.30375 -3.44866,5.89285 -1.58089,19.27082 -1.58089,33.02082" | |
269 | id="path3847-4" | |
270 | inkscape:connector-curvature="0" | |
271 | sodipodi:nodetypes="cccc" /> | |
272 | <path | |
273 | sodipodi:type="arc" | |
274 | style="fill:#ffffff;stroke:#000000" | |
275 | id="path3892" | |
276 | sodipodi:cx="349.76532" | |
277 | sodipodi:cy="530.61841" | |
278 | sodipodi:rx="19.950512" | |
279 | sodipodi:ry="25.75889" | |
280 | d="m 369.71583,530.61841 c 0,14.22624 -8.93215,25.75889 -19.95051,25.75889 -11.01836,0 -19.95051,-11.53265 -19.95051,-25.75889 0,-14.22624 8.93215,-25.75889 19.95051,-25.75889 11.01836,0 19.95051,11.53265 19.95051,25.75889 z" | |
281 | transform="matrix(1.8336949,0,0,1.4280878,-291.53629,-223.11064)" /> | |
282 | <path | |
283 | sodipodi:type="arc" | |
284 | style="fill:#ffffff;stroke:#000000" | |
285 | id="path3892-9" | |
286 | sodipodi:cx="349.76532" | |
287 | sodipodi:cy="530.61841" | |
288 | sodipodi:rx="19.950512" | |
289 | sodipodi:ry="25.75889" | |
290 | d="m 369.71583,530.61841 c 0,14.22624 -8.93215,25.75889 -19.95051,25.75889 -11.01836,0 -19.95051,-11.53265 -19.95051,-25.75889 0,-14.22624 8.93215,-25.75889 19.95051,-25.75889 11.01836,0 19.95051,11.53265 19.95051,25.75889 z" | |
291 | transform="matrix(1.8336949,0,0,1.4280878,-133.86288,-223.62178)" /> | |
292 | </g> | |
293 | <g | |
294 | inkscape:groupmode="layer" | |
295 | id="layer6" | |
296 | inkscape:label="buttons" | |
297 | transform="translate(-154.99045,-304.71875)"> | |
298 | <rect | |
299 | style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
300 | id="rect3922" | |
301 | width="20.355255" | |
302 | height="11.251501" | |
303 | x="441.53503" | |
304 | y="418.69357" | |
305 | ry="5.2401662" /> | |
306 | <path | |
307 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" | |
308 | d="m 446.67683,421.20625 -3e-5,6.43972 10.6066,-3.21987 z" | |
309 | id="path4104" | |
310 | inkscape:connector-curvature="0" | |
311 | sodipodi:nodetypes="cccc" /> | |
312 | <rect | |
313 | style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
314 | id="rect3922-8" | |
315 | width="20.355255" | |
316 | height="11.251501" | |
317 | x="395.04974" | |
318 | y="418.94559" | |
319 | ry="5.2401662" /> | |
320 | <path | |
321 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" | |
322 | d="m 409.99564,421.45878 3e-5,6.43972 -10.6066,-3.21987 z" | |
323 | id="path4104-8" | |
324 | inkscape:connector-curvature="0" | |
325 | sodipodi:nodetypes="cccc" /> | |
326 | <path | |
327 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
328 | d="m 497.41072,339.41575 0,-8.30357 5.08928,-5.89286 55.00001,0.17857 4.82143,5.71429 -1e-5,9.91071" | |
329 | id="path4171" | |
330 | inkscape:connector-curvature="0" | |
331 | sodipodi:nodetypes="cccccc" /> | |
332 | <path | |
333 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
334 | d="m 362.40015,339.55448 0,-8.30357 -5.08928,-5.89286 -55.00001,0.17857 -4.82143,5.71429 10e-6,9.91071" | |
335 | id="path4171-2" | |
336 | inkscape:connector-curvature="0" | |
337 | sodipodi:nodetypes="cccccc" /> | |
338 | </g> | |
339 | <g | |
340 | inkscape:groupmode="layer" | |
341 | id="layer7" | |
342 | inkscape:label="face_buttons" | |
343 | transform="translate(-154.99045,-304.71875)"> | |
344 | <path | |
345 | sodipodi:type="arc" | |
346 | style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
347 | id="path4212-1" | |
348 | sodipodi:cx="512.90497" | |
349 | sodipodi:cy="442.10379" | |
350 | sodipodi:rx="12.879445" | |
351 | sodipodi:ry="8.7125654" | |
352 | d="m 525.78441,442.10379 a 12.879445,8.7125654 0 1 1 -25.75889,0 12.879445,8.7125654 0 1 1 25.75889,0 z" | |
353 | transform="matrix(1.3351132,0,0,1.9720704,-96.78891,-432.50229)" /> | |
354 | <path | |
355 | sodipodi:type="arc" | |
356 | style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
357 | id="path4212-5-4" | |
358 | sodipodi:cx="512.90497" | |
359 | sodipodi:cy="442.10379" | |
360 | sodipodi:rx="12.879445" | |
361 | sodipodi:ry="8.7125654" | |
362 | d="m 525.78441,442.10379 a 12.879445,8.7125654 0 1 1 -25.75889,0 12.879445,8.7125654 0 1 1 25.75889,0 z" | |
363 | transform="matrix(1.3351132,0,0,1.9720704,-96.70624,-434.05825)" /> | |
364 | <path | |
365 | sodipodi:type="arc" | |
366 | style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
367 | id="path4212-2" | |
368 | sodipodi:cx="512.90497" | |
369 | sodipodi:cy="442.10379" | |
370 | sodipodi:rx="12.879445" | |
371 | sodipodi:ry="8.7125654" | |
372 | d="m 525.78441,442.10379 a 12.879445,8.7125654 0 1 1 -25.75889,0 12.879445,8.7125654 0 1 1 25.75889,0 z" | |
373 | transform="matrix(1.3351132,0,0,1.9720704,-136.51444,-397.50229)" /> | |
374 | <path | |
375 | sodipodi:type="arc" | |
376 | style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
377 | id="path4212-5-3" | |
378 | sodipodi:cx="512.90497" | |
379 | sodipodi:cy="442.10379" | |
380 | sodipodi:rx="12.879445" | |
381 | sodipodi:ry="8.7125654" | |
382 | d="m 525.78441,442.10379 a 12.879445,8.7125654 0 1 1 -25.75889,0 12.879445,8.7125654 0 1 1 25.75889,0 z" | |
383 | transform="matrix(1.3351132,0,0,1.9720704,-136.78891,-399.05825)" /> | |
384 | <path | |
385 | sodipodi:type="arc" | |
386 | style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
387 | id="path4212-22" | |
388 | sodipodi:cx="512.90497" | |
389 | sodipodi:cy="442.10379" | |
390 | sodipodi:rx="12.879445" | |
391 | sodipodi:ry="8.7125654" | |
392 | d="m 525.78441,442.10379 a 12.879445,8.7125654 0 1 1 -25.75889,0 12.879445,8.7125654 0 1 1 25.75889,0 z" | |
393 | transform="matrix(1.3351132,0,0,1.9720704,-136.78891,-472.50229)" /> | |
394 | <path | |
395 | sodipodi:type="arc" | |
396 | style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
397 | id="path4212-5-1" | |
398 | sodipodi:cx="512.90497" | |
399 | sodipodi:cy="442.10379" | |
400 | sodipodi:rx="12.879445" | |
401 | sodipodi:ry="8.7125654" | |
402 | d="m 525.78441,442.10379 a 12.879445,8.7125654 0 1 1 -25.75889,0 12.879445,8.7125654 0 1 1 25.75889,0 z" | |
403 | transform="matrix(1.3351132,0,0,1.9720704,-136.70624,-474.05825)" /> | |
404 | <path | |
405 | sodipodi:type="arc" | |
406 | style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
407 | id="path4212" | |
408 | sodipodi:cx="512.90497" | |
409 | sodipodi:cy="442.10379" | |
410 | sodipodi:rx="12.879445" | |
411 | sodipodi:ry="8.7125654" | |
412 | d="m 525.78441,442.10379 a 12.879445,8.7125654 0 1 1 -25.75889,0 12.879445,8.7125654 0 1 1 25.75889,0 z" | |
413 | transform="matrix(1.3351132,0,0,1.9720704,-176.78891,-432.50229)" /> | |
414 | <path | |
415 | sodipodi:type="arc" | |
416 | style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
417 | id="path4212-5" | |
418 | sodipodi:cx="512.90497" | |
419 | sodipodi:cy="442.10379" | |
420 | sodipodi:rx="12.879445" | |
421 | sodipodi:ry="8.7125654" | |
422 | d="m 525.78441,442.10379 a 12.879445,8.7125654 0 1 1 -25.75889,0 12.879445,8.7125654 0 1 1 25.75889,0 z" | |
423 | transform="matrix(1.3351132,0,0,1.9720704,-176.70624,-434.05825)" /> | |
424 | <path | |
425 | style="fill:#00ff66;stroke:#000000;stroke-width:0.29228577;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
426 | d="m 547.26452,454.96682 c -8.99466,0.14886 -17.15195,8.1631 -17.09419,17.40605 -0.4705,8.75652 6.52899,16.7924 14.94723,18.03231 8.1379,1.44809 16.88935,-3.49526 19.67598,-11.4751 2.5451,-6.81932 0.42098,-15.13904 -5.24513,-19.69002 -3.37564,-2.93548 -7.85236,-4.46284 -12.28389,-4.27324 z" | |
427 | id="path4325" | |
428 | inkscape:connector-curvature="0" /> | |
429 | <path | |
430 | style="fill:#00ccff;stroke:#000000;stroke-width:0.29228571;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
431 | d="m 507.34719,419.96682 c -8.99466,0.14886 -17.15195,8.1631 -17.09419,17.40605 -0.47048,8.75651 6.52898,16.7924 14.94722,18.03231 8.13791,1.44809 16.88936,-3.49528 19.67598,-11.47511 2.54511,-6.81931 0.42099,-15.13903 -5.24512,-19.69003 -3.37566,-2.93548 -7.85238,-4.46282 -12.28389,-4.27322 z" | |
432 | id="path4333" | |
433 | inkscape:connector-curvature="0" /> | |
434 | <path | |
435 | style="fill:#ff2a2a;stroke:#000000;stroke-width:0.29228565;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
436 | d="m 587.34719,419.96682 c -8.99466,0.14886 -17.15194,8.16312 -17.09419,17.40606 -0.47048,8.75651 6.529,16.7924 14.94722,18.03231 8.13791,1.44808 16.88936,-3.49528 19.67598,-11.47511 2.54511,-6.81931 0.42099,-15.13903 -5.24512,-19.69002 -3.37564,-2.93548 -7.85238,-4.46282 -12.28389,-4.27324 z" | |
437 | id="path4337" | |
438 | inkscape:connector-curvature="0" /> | |
439 | <path | |
440 | style="fill:#ff7f2a;stroke:#000000;stroke-width:0.29228565;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" | |
441 | d="m 547.34719,379.96682 c -8.99465,0.14886 -17.15194,8.1631 -17.09418,17.40606 -0.47048,8.75651 6.52898,16.7924 14.94722,18.03231 8.1379,1.44808 16.88936,-3.49528 19.67598,-11.47511 2.54511,-6.81931 0.42099,-15.13903 -5.24512,-19.69002 -3.37566,-2.93548 -7.85238,-4.46282 -12.2839,-4.27324 z" | |
442 | id="path4341" | |
443 | inkscape:connector-curvature="0" /> | |
444 | </g> | |
445 | <g | |
446 | inkscape:groupmode="layer" | |
447 | id="layer8" | |
448 | inkscape:label="face_buttons_text_layer" | |
449 | transform="translate(-154.99045,-304.71875)"> | |
450 | <text | |
451 | xml:space="preserve" | |
452 | style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Induction;-inkscape-font-specification:Induction" | |
453 | x="549.27045" | |
454 | y="452.33157" | |
455 | id="text4362" | |
456 | sodipodi:linespacing="125%"><tspan | |
457 | sodipodi:role="line" | |
458 | id="tspan4364" | |
459 | x="549.27045" | |
460 | y="452.33157" /></text> | |
461 | <text | |
462 | xml:space="preserve" | |
463 | style="font-size:12px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Mycalc;-inkscape-font-specification:Sans Bold" | |
464 | x="542.42029" | |
465 | y="480.00809" | |
466 | id="text4370" | |
467 | sodipodi:linespacing="125%"><tspan | |
468 | sodipodi:role="line" | |
469 | id="tspan4372" | |
470 | x="542.42029" | |
471 | y="480.00809" | |
472 | style="font-size:18px;font-weight:normal;fill:#225500;-inkscape-font-specification:Mycalc">A</tspan></text> | |
473 | <text | |
474 | xml:space="preserve" | |
475 | style="font-size:12px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#550000;fill-opacity:1;stroke:none;font-family:Mycalc;-inkscape-font-specification:Sans Bold" | |
476 | x="582.77081" | |
477 | y="445.09735" | |
478 | id="text4374" | |
479 | sodipodi:linespacing="125%"><tspan | |
480 | sodipodi:role="line" | |
481 | id="tspan4376" | |
482 | x="582.77081" | |
483 | y="445.09735" | |
484 | style="font-size:18px">B</tspan></text> | |
485 | <text | |
486 | xml:space="preserve" | |
487 | style="font-size:12px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#164450;fill-opacity:1;stroke:none;font-family:Mycalc;-inkscape-font-specification:Sans Bold" | |
488 | x="503.0386" | |
489 | y="444.56168" | |
490 | id="text4378" | |
491 | sodipodi:linespacing="125%"><tspan | |
492 | sodipodi:role="line" | |
493 | id="tspan4380" | |
494 | x="503.0386" | |
495 | y="444.56168" | |
496 | style="font-size:18px">X</tspan></text> | |
497 | <text | |
498 | xml:space="preserve" | |
499 | style="font-size:12px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#552200;fill-opacity:1;stroke:none;font-family:Mycalc;-inkscape-font-specification:Sans Bold" | |
500 | x="543.57434" | |
501 | y="404.47235" | |
502 | id="text4382" | |
503 | sodipodi:linespacing="125%"><tspan | |
504 | sodipodi:role="line" | |
505 | id="tspan4384" | |
506 | x="543.57434" | |
507 | y="404.47235" | |
508 | style="font-size:18px">Y</tspan></text> | |
509 | <path | |
510 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
511 | d="m 562.27679,331.06754 21.5625,-0.0447 3.83928,3.30357 -0.0446,13.12499" | |
512 | id="path3057" | |
513 | inkscape:connector-curvature="0" | |
514 | sodipodi:nodetypes="cccc" /> | |
515 | <path | |
516 | style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
517 | d="m 297.58938,331.24662 -21.5625,-0.0447 -3.83928,3.30357 0.0446,11.60713" | |
518 | id="path3057-1" | |
519 | inkscape:connector-curvature="0" | |
520 | sodipodi:nodetypes="cccc" /> | |
521 | </g> | |
522 | </svg> |
0 | 0 | Block Attack - Rise of the Blocks by |
1 | 1 | Poul Sander <blockattack@poulsander.com> |
2 | http://blockattack.net | |
2 | https://blockattack.net | |
3 | 3 | |
4 | 4 | Gonéri Le Bouder |
5 | 5 | Done some work for making it work with SHAREDIR and all Scons scripts |
6 | 6 | |
7 | Jonathan Dearborn | |
8 | NFont library | |
9 | ||
10 | Jordà Polo | |
11 | Two patches | |
12 | ||
13 | 7 | A lot of the 1.4.0 graphics is by |
14 | 8 | Iwan Gabovitch aka qubodup |
9 | ||
10 | Also: | |
11 | Paul Wise, Jordà Polo, mstraube and scootergrisen for patches and bug reports | |
15 | 12 | |
16 | 13 | This does not cover non-embedded libraries. |
17 | 14 |
29 | 29 | #define BLOCKFALL 10000 |
30 | 30 | #define GARBAGE 1000000 |
31 | 31 | #define CHAINPLACE 10000000 |
32 | ||
33 | // The game uses a very special base-10 pack system | |
34 | // int 999999999999 | |
35 | // YYYYYGWBHTTC | |
36 | // YYYY = Chain | |
37 | // G = Garbage, 1= NORMAL, 2=GRAY | |
38 | // W = Waiting (A bomb is on it) | |
39 | // B = 1 if the block is falling | |
40 | // H = Block is hanging after garbage (Get Ready text on it) | |
41 | // TT = Time (in steps) until something happens | |
42 | // C = color | |
43 | ||
44 | static bool block_isFalling(int block) { | |
45 | return (block/BLOCKFALL)%10; | |
46 | } | |
47 | ||
48 | static void block_setFalling(int& block, bool value) { | |
49 | if (value) { | |
50 | if (!block_isFalling(block)) { | |
51 | block += BLOCKFALL; | |
52 | } | |
53 | } | |
54 | else { | |
55 | if (block_isFalling(block)) { | |
56 | block -= BLOCKFALL; | |
57 | } | |
58 | } | |
59 | } | |
32 | 60 | |
33 | 61 | #include "BlockGame.hpp" |
34 | 62 | #include "puzzlehandler.hpp" |
723 | 751 | bool faaling = false; |
724 | 752 | for (int j=0; j<30; j++) { |
725 | 753 | if ((faaling)&&(board[i][j]>-1)&&(board[i][j]%10000000<7)) { |
726 | board[i][j]+=BLOCKFALL; | |
754 | block_setFalling(board[i][j], true); | |
727 | 755 | } |
728 | 756 | if ((!faaling)&&((board[i][j]/BLOCKFALL)%10==1)) { |
729 | board[i][j]-=BLOCKFALL; | |
757 | block_setFalling(board[i][j], false); | |
730 | 758 | } |
731 | 759 | if (!((board[i][j]>-1)&&(board[i][j]%10000000<7))) { |
732 | 760 | faaling=true; |
904 | 932 | if (toBeCleared[j][i]) { |
905 | 933 | if (!dead) { |
906 | 934 | dead=true; |
907 | string tempS = std::to_string(chainSize[chain]); | |
908 | 935 | if (chainSize[chain]>1) { |
909 | AddText(j, i, tempS, 1000); | |
936 | AddText(j, i, chainSize[chain], 1000); | |
910 | 937 | } |
911 | 938 | } |
912 | 939 | } |
984 | 1011 | bool faaling = false; //In the beginning we are NOT falling |
985 | 1012 | for (int j=0; j<30; j++) { |
986 | 1013 | if ((faaling)&&(board[i][j]>-1)&&(board[i][j]<7)) { |
987 | board[i][j]+=BLOCKFALL; | |
988 | } | |
989 | if ((!faaling)&&((board[i][j]/BLOCKFALL)%10==1)) { | |
990 | board[i][j]-=BLOCKFALL; | |
1014 | block_setFalling(board[i][j], true); | |
1015 | } | |
1016 | if (!faaling) { | |
1017 | block_setFalling(board[i][j], false); | |
991 | 1018 | } |
992 | 1019 | if ((!faaling)&&(board[i][j]>0)&&(board[i][j]/10000000!=0)&&((board[i][j]/BLOCKWAIT)%10!=1)&&((board[i][j]/BLOCKHANG)%10!=1)) { |
993 | 1020 | if (chainSize[board[i][j]/10000000]>chainSize[chain]) { |
1279 | 1306 | int BlockGame::horiInLine(int line) { |
1280 | 1307 | //cout << "Start_ hori in line" << "\n"; |
1281 | 1308 | int nrOfType[7] = {0, 0, 0, 0, 0, 0, 0}; |
1282 | int iTemp; | |
1283 | 1309 | int max = 0; |
1284 | 1310 | for (int i=0; i<6; i++) { |
1285 | iTemp = board[i][line]; | |
1311 | int iTemp = board[i][line]; | |
1286 | 1312 | if ((iTemp>-1)&&(iTemp<7)) { |
1287 | 1313 | nrOfType[iTemp]++; |
1288 | 1314 | } |
145 | 145 | int nrPushedPixel = 0; |
146 | 146 | int nrFellDown = 0; |
147 | 147 | unsigned int nrStops = 0; |
148 | bool garbageToBeCleared[7][30]; | |
148 | bool garbageToBeCleared[7][30] = {}; | |
149 | 149 | unsigned int lastAImove = 0; |
150 | 150 | |
151 | 151 | int AI_LineOffset = 0; //how many lines have changed since command |
218 | 218 | |
219 | 219 | int getAIlevel() const; |
220 | 220 | |
221 | virtual void AddText(int, int, const std::string&, int) const {} | |
221 | virtual void AddText(int, int, unsigned int, int) const {} | |
222 | 222 | virtual void AddBall(int, int, bool, int) const {} |
223 | 223 | virtual void AddExplosion(int, int) const {} |
224 | 224 | virtual void PlayerWonEvent() const {} |
16 | 16 | along with this program. If not, see http://www.gnu.org/licenses/ |
17 | 17 | |
18 | 18 | Source information and contacts persons can be found at |
19 | http://www.blockattack.net | |
19 | https://blockattack.net | |
20 | 20 | =========================================================================== |
21 | 21 | */ |
22 | 22 | |
23 | 23 | #include "BlockGame.hpp" |
24 | 24 | #include "global.hpp" |
25 | ||
25 | #include "sago/SagoTextField.hpp" | |
26 | ||
27 | static void setScoreboardFont(const sago::SagoDataHolder* holder, sago::SagoTextField& field, const char* text){ | |
28 | field.SetHolder(holder); | |
29 | field.SetFont("penguinattack"); | |
30 | field.SetColor({255,255,255,255}); | |
31 | field.SetFontSize(20); | |
32 | field.SetText(text); | |
33 | } | |
34 | ||
35 | static void setButtonFont(const sago::SagoDataHolder* holder, sago::SagoTextField& field, const char* text){ | |
36 | field.SetHolder(holder); | |
37 | field.SetFont("freeserif"); | |
38 | field.SetColor({255,255,255,255}); | |
39 | field.SetFontSize(24); | |
40 | field.SetOutline(1, {64,64,64,255}); | |
41 | field.SetText(text); | |
42 | } | |
26 | 43 | |
27 | 44 | class BlockGameSdl : public BlockGame { |
28 | 45 | public: |
29 | BlockGameSdl(int tx, int ty) { | |
46 | BlockGameSdl(int tx, int ty, const sago::SagoDataHolder* holder) { | |
30 | 47 | topx = tx; |
31 | 48 | topy = ty; |
49 | setScoreboardFont(holder, scoreLabel, _("Score:")); | |
50 | setScoreboardFont(holder, timeLabel, _("Time:")); | |
51 | setScoreboardFont(holder, chainLabel, _("Chain:")); | |
52 | setScoreboardFont(holder, speedLabel, _("Speed:")); | |
53 | setButtonFont(holder, buttonNext, _("Next")); | |
54 | setButtonFont(holder, buttonRetry, _("Retry")); | |
55 | setButtonFont(holder, buttonSkip, _("Skip")); | |
56 | setButtonFont(holder, stopIntField, ""); | |
32 | 57 | } |
33 | 58 | |
34 | 59 | |
40 | 65 | DrawIMG_Bounded(img, globalData.screen, x+topx, y+topy, topx, topy, topx + backBoard.GetWidth(), topy + backBoard.GetHeight()); |
41 | 66 | } |
42 | 67 | |
43 | void PrintTextCenteredBoard(int x, int y, const char* text) { | |
44 | globalData.nf_button_font.draw(globalData.screen, x+topx+60, y+topy+10, NFont::CENTER, "%s", text); | |
45 | } | |
46 | ||
47 | void PrintIntRightAlignedBoard(int x, int y, int number) { | |
48 | globalData.nf_button_font.draw(globalData.screen, x+topx+60, y+topy+10, NFont::RIGHT, "%d", number); | |
68 | void PrintTextCenteredBoard(int x, int y, sago::SagoTextField& field) { | |
69 | field.Draw(globalData.screen, x+topx+60, y+topy+20, | |
70 | sago::SagoTextField::Alignment::center, sago::SagoTextField::VerticalAlignment::center); | |
49 | 71 | } |
50 | 72 | |
51 | 73 | void SetTopXY(int tx, int ty) { |
92 | 114 | return true; |
93 | 115 | } |
94 | 116 | |
95 | void AddText(int x, int y, const std::string& text, int time) const override { | |
117 | void AddText(int x, int y, unsigned int text, int time) const override { | |
96 | 118 | globalData.theTextManager.addText(topx-10+x*bsize, topy+12*bsize-y*bsize, text, time); |
97 | 119 | } |
98 | 120 | |
108 | 130 | if (!globalData.SoundEnabled) { |
109 | 131 | return; |
110 | 132 | } |
111 | Mix_PlayChannel(1, applause, 0); | |
133 | Mix_PlayChannel(1, applause.get(), 0); | |
112 | 134 | } |
113 | 135 | |
114 | 136 | void DrawEvent() const override { |
119 | 141 | if (!globalData.SoundEnabled) { |
120 | 142 | return; |
121 | 143 | } |
122 | Mix_PlayChannel(0, boing, 0); | |
144 | Mix_PlayChannel(0, boing.get(), 0); | |
123 | 145 | } |
124 | 146 | |
125 | 147 | void LongChainDoneEvent() const override { |
126 | 148 | if (!globalData.SoundEnabled) { |
127 | 149 | return; |
128 | 150 | } |
129 | Mix_PlayChannel(1, applause, 0); | |
151 | Mix_PlayChannel(1, applause.get(), 0); | |
130 | 152 | } |
131 | 153 | |
132 | 154 | void TimeTrialEndEvent() const override { |
133 | 155 | if (!globalData.NoSound && globalData.SoundEnabled) { |
134 | Mix_PlayChannel(1,counterFinalChunk,0); | |
156 | Mix_PlayChannel(1,counterFinalChunk.get(),0); | |
135 | 157 | } |
136 | 158 | } |
137 | 159 | |
139 | 161 | if (!globalData.SoundEnabled) { |
140 | 162 | return; |
141 | 163 | } |
142 | Mix_PlayChannel(1, applause, 0); | |
164 | Mix_PlayChannel(1, applause.get(), 0); | |
143 | 165 | } |
144 | 166 | private: |
145 | 167 | //Draws all the bricks to the board (including garbage) |
289 | 311 | void DoPaintJob() { |
290 | 312 | DrawIMG(boardBackBack,globalData.screen,this->GetTopX()-60,this->GetTopY()-68); |
291 | 313 | |
292 | globalData.nf_scoreboard_font.draw(globalData.screen, this->GetTopX()+310,this->GetTopY()-68+148,_("Score:") ); | |
293 | globalData.nf_scoreboard_font.draw(globalData.screen, this->GetTopX()+310,this->GetTopY()-68+197,_("Time:") ); | |
294 | globalData.nf_scoreboard_font.draw(globalData.screen, this->GetTopX()+310,this->GetTopY()-68+246,_("Chain:") ); | |
295 | globalData.nf_scoreboard_font.draw(globalData.screen, this->GetTopX()+310,this->GetTopY()-68+295,_("Speed:") ); | |
314 | this->scoreLabel.Draw(globalData.screen, this->GetTopX()+310,this->GetTopY()-68+148); | |
315 | this->timeLabel.Draw(globalData.screen, this->GetTopX()+310,this->GetTopY()-68+197); | |
316 | this->chainLabel.Draw(globalData.screen, this->GetTopX()+310,this->GetTopY()-68+246); | |
317 | this->speedLabel.Draw(globalData.screen, this->GetTopX()+310,this->GetTopY()-68+295); | |
296 | 318 | DrawImgBoard(backBoard, 0, 0); |
297 | 319 | |
298 | 320 | PaintBricks(); |
302 | 324 | if (puzzleMode&&(!bGameOver)) { |
303 | 325 | //We need to write nr. of moves left! |
304 | 326 | strHolder = _("Moves left: ") + std::to_string(MovesLeft); |
305 | globalData.nf_standard_blue_font.draw(globalData.screen, topx+5, topy+5, "%s", strHolder.c_str()); | |
327 | static sago::SagoTextField movesPuzzleLabel; | |
328 | sagoTextSetHelpFont(movesPuzzleLabel); | |
329 | movesPuzzleLabel.SetText(strHolder); | |
330 | movesPuzzleLabel.Draw(globalData.screen, topx+5, topy+5); | |
306 | 331 | |
307 | 332 | } |
308 | 333 | if (puzzleMode && stageButtonStatus == SBpuzzleMode) { |
309 | 334 | DrawImgBoard(bRetry, cordRetryButton.x, cordRetryButton.y); |
310 | PrintTextCenteredBoard(cordRetryButton.x, cordRetryButton.y, _("Retry")); | |
335 | PrintTextCenteredBoard(cordRetryButton.x, cordRetryButton.y, buttonRetry); | |
311 | 336 | if (getLevel()<PuzzleGetNumberOfPuzzles()-1) { |
312 | 337 | if (hasWonTheGame) { |
313 | 338 | DrawImgBoard(globalData.bNext, cordNextButton.x, cordNextButton.y); |
314 | PrintTextCenteredBoard(cordNextButton.x, cordNextButton.y, _("Next")); | |
339 | PrintTextCenteredBoard(cordNextButton.x, cordNextButton.y, buttonNext); | |
315 | 340 | } |
316 | 341 | else { |
317 | 342 | DrawImgBoard(bSkip,cordNextButton.x, cordNextButton.y); |
318 | PrintTextCenteredBoard(cordNextButton.x, cordNextButton.y, _("Skip")); | |
343 | PrintTextCenteredBoard(cordNextButton.x, cordNextButton.y, buttonSkip); | |
319 | 344 | } |
320 | 345 | } |
321 | 346 | else { |
322 | strHolder = "Last puzzle"; | |
323 | globalData.nf_standard_blue_font.draw(globalData.screen, topx+5, topy+5, "%s",strHolder.c_str()); | |
347 | static sago::SagoTextField lastPuzzleLabel; | |
348 | sagoTextSetHelpFont(lastPuzzleLabel); | |
349 | lastPuzzleLabel.SetText(_("Last puzzle")); | |
350 | lastPuzzleLabel.Draw(globalData.screen, topx+5, topy+5); | |
324 | 351 | } |
325 | 352 | } |
326 | 353 | if (stageClear && stageButtonStatus == SBstageClear) { |
327 | 354 | DrawImgBoard(bRetry, cordRetryButton.x, cordRetryButton.y); |
328 | PrintTextCenteredBoard(cordRetryButton.x, cordRetryButton.y, _("Retry")); | |
355 | PrintTextCenteredBoard(cordRetryButton.x, cordRetryButton.y, buttonRetry); | |
329 | 356 | if (getLevel()<50-1) { |
330 | 357 | if (hasWonTheGame) { |
331 | 358 | DrawImgBoard(globalData.bNext, cordNextButton.x, cordNextButton.y); |
332 | PrintTextCenteredBoard(cordNextButton.x, cordNextButton.y, _("Next")); | |
359 | PrintTextCenteredBoard(cordNextButton.x, cordNextButton.y, buttonNext); | |
333 | 360 | } |
334 | 361 | else { |
335 | 362 | DrawImgBoard(bSkip,cordNextButton.x, cordNextButton.y); |
336 | PrintTextCenteredBoard(cordNextButton.x, cordNextButton.y, _("Skip")); | |
363 | PrintTextCenteredBoard(cordNextButton.x, cordNextButton.y, buttonSkip); | |
337 | 364 | } |
338 | 365 | } |
339 | 366 | else { |
340 | strHolder = "Last stage"; | |
341 | globalData.nf_standard_blue_font.draw(globalData.screen, topx+5, topy+5, "%s",strHolder.c_str()); | |
367 | static sago::SagoTextField lastStageLabel; | |
368 | sagoTextSetHelpFont(lastStageLabel); | |
369 | lastStageLabel.SetText(_("Last stage")); | |
370 | lastStageLabel.Draw(globalData.screen, topx+5, topy+5); | |
342 | 371 | } |
343 | 372 | } |
344 | 373 | if (!bGameOver && stop > 20) { |
345 | PrintIntRightAlignedBoard(240, -40, stop/10); | |
374 | stopIntField.SetText(std::to_string(stop/10)); | |
375 | stopIntField.Draw(globalData.screen, 240+topx, -40+topy); | |
346 | 376 | } |
347 | 377 | |
348 | 378 | #if DEBUG |
349 | 379 | if (AI_Enabled&&(!bGameOver)) { |
380 | sagoTextSetBlueFont(aiStatusField); | |
350 | 381 | strHolder = "AI_status: " + std::to_string(AIstatus)+ ", "+ std::to_string(AIlineToClear); |
351 | //NFont_Write( 5, 5, strHolder.c_str()); | |
352 | globalData.nf_standard_blue_font.draw(globalData.screen, topx+5, topy+5, "%s",strHolder.c_str()); | |
382 | aiStatusField.SetText(strHolder); | |
383 | aiStatusField.Draw(globalData.screen, topx+5, topy+5); | |
353 | 384 | } |
354 | 385 | #endif |
355 | 386 | if (!bGameOver) { |
361 | 392 | DrawImgBoard(globalData.spriteHolder->GetSprite("touchcursor"),mx*bsize, 11*bsize-my*bsize-pixels); |
362 | 393 | } |
363 | 394 | else { |
364 | DrawImgBoard(cursor,cursorx*bsize-4,11*bsize-cursory*bsize-pixels-4); | |
395 | DrawImgBoard(cursor,cursorx*bsize,11*bsize-cursory*bsize-pixels); | |
365 | 396 | } |
366 | 397 | } |
367 | 398 | if (ticks<gameStartedAt) { |
368 | 399 | int currentCounter = abs((int)ticks-(int)gameStartedAt)/1000; |
369 | 400 | if ( (currentCounter!=lastCounter) && (globalData.SoundEnabled)&&(!globalData.NoSound)) { |
370 | Mix_PlayChannel(1,counterChunk,0); | |
401 | Mix_PlayChannel(1, counterChunk.get(), 0); | |
371 | 402 | } |
372 | 403 | lastCounter = currentCounter; |
373 | 404 | switch (currentCounter) { |
389 | 420 | int currentCounter = (ticks-(int)gameStartedAt)/1000; |
390 | 421 | if (currentCounter!=lastCounter) { |
391 | 422 | if (currentCounter>115 && currentCounter<120) { |
392 | Mix_PlayChannel(1,counterChunk,0); | |
423 | Mix_PlayChannel(1, counterChunk.get(), 0); | |
393 | 424 | } |
394 | 425 | } |
395 | 426 | lastCounter = currentCounter; |
396 | 427 | } |
397 | 428 | else { |
398 | 429 | if ( (0==lastCounter) && (globalData.SoundEnabled)&&(!globalData.NoSound)) { |
399 | Mix_PlayChannel(1, counterFinalChunk, 0); | |
430 | Mix_PlayChannel(1, counterFinalChunk.get(), 0); | |
400 | 431 | } |
401 | 432 | lastCounter = -1; |
402 | 433 | } |
403 | 434 | } |
404 | 435 | |
405 | if ((bGameOver)&&(!editorMode)) { | |
436 | if (bGameOver) { | |
406 | 437 | if (hasWonTheGame) { |
407 | 438 | DrawImgBoard(iWinner, 0, 5*bsize); |
408 | 439 | } |
419 | 450 | |
420 | 451 | private: |
421 | 452 | int topx, topy; |
453 | sago::SagoTextField scoreLabel; | |
454 | sago::SagoTextField timeLabel; | |
455 | sago::SagoTextField chainLabel; | |
456 | sago::SagoTextField speedLabel; | |
457 | sago::SagoTextField buttonSkip; | |
458 | sago::SagoTextField buttonRetry; | |
459 | sago::SagoTextField buttonNext; | |
460 | sago::SagoTextField stopIntField; | |
461 | #if DEBUG | |
462 | sago::SagoTextField aiStatusField; | |
463 | #endif | |
422 | 464 | }; |
423 | 465 | |
424 | 466 |
25 | 25 | #include "common.h" |
26 | 26 | #include "ReadKeyboard.h" |
27 | 27 | |
28 | static void NFont_Write(SDL_Renderer* target, int x, int y, const std::string& text) { | |
29 | globalData.nf_standard_blue_font.draw(target, x, y, "%s", text.c_str()); | |
28 | static void setButtonFont(const sago::SagoDataHolder* holder, sago::SagoTextField& field, const char* text) { | |
29 | field.SetHolder(holder); | |
30 | field.SetFont("freeserif"); | |
31 | field.SetColor({255,255,255,255}); | |
32 | field.SetFontSize(24); | |
33 | field.SetOutline(1, {0,0,0,255}); | |
34 | field.SetText(text); | |
30 | 35 | } |
31 | 36 | |
32 | 37 | static void DrawRect(SDL_Renderer* target, int topx, int topy, int height, int width, const std::string& name) { |
63 | 68 | DrawRect(target, topx, topy, height, width, name); |
64 | 69 | } |
65 | 70 | |
66 | static void DrawRectYellow(SDL_Renderer* target, int topx, int topy, int height, int width) { | |
71 | void DrawRectYellow(SDL_Renderer* target, int topx, int topy, int height, int width) { | |
67 | 72 | std::string name = "ui_rect_yellow_"; |
68 | 73 | DrawRect(target, topx, topy, height, width, name); |
69 | 74 | } |
82 | 87 | this->x = x; |
83 | 88 | this->y = y; |
84 | 89 | SetName(name); |
90 | setButtonFont(&globalData.spriteHolder->GetDataHolder(), headerLabel, header.c_str()); | |
91 | setButtonFont(&globalData.spriteHolder->GetDataHolder(), enterLabel, _("Enter to accept")); | |
92 | setButtonFont(&globalData.spriteHolder->GetDataHolder(), cancelLabel, _("Esc to cancel")); | |
93 | sagoTextSetBlueFont(textField); | |
94 | sagoTextSetBlueFont(cursorLabel); | |
95 | cursorLabel.SetText("|"); | |
85 | 96 | } |
86 | 97 | |
87 | 98 | |
98 | 109 | this->x = globalData.xsize/2-300; |
99 | 110 | this->y = globalData.ysize/2-100; |
100 | 111 | DrawRectYellow(target, x, y, 200, 600); |
101 | globalData.nf_button_font.draw(target, x+300, y+20, NFont::CENTER, "%s", header.c_str()); | |
102 | globalData.nf_button_font.draw(target, x+150, y+140, NFont::CENTER, _("Enter to accept")); | |
103 | globalData.nf_button_font.draw(target, x+450, y+140, NFont::CENTER, _("Esc to cancel")); | |
112 | headerLabel.Draw(target, x+300, y+20, sago::SagoTextField::Alignment::center); | |
113 | enterLabel.Draw(target, x+150, y+140, sago::SagoTextField::Alignment::center); | |
114 | cancelLabel.Draw(target, x+450, y+140, sago::SagoTextField::Alignment::center); | |
104 | 115 | DrawRectWhite(target, x+26, y+64, 54, 600-2*26); |
105 | NFont_Write(target, x+40, y+76,rk->GetString()); | |
116 | textField.SetText(rk->GetString()); | |
117 | textField.Draw(target, x+40, y+76); | |
106 | 118 | std::string strHolder = rk->GetString(); |
107 | 119 | strHolder.erase((int)rk->CharsBeforeCursor()); |
108 | 120 | |
109 | 121 | if (((SDL_GetTicks()/600)%2)==1) { |
110 | NFont_Write(target, x+40+globalData.nf_standard_blue_font.getWidth( "%s", strHolder.c_str()),y+76,"|"); | |
122 | int width = 0; | |
123 | textField.GetRenderedSize( strHolder.c_str(), &width); | |
124 | width -= 2; | |
125 | cursorLabel.Draw(target, x+40+width,y+76); | |
111 | 126 | } |
112 | 127 | } |
113 | 128 | |
114 | 129 | void DialogBox::ProcessInput(const SDL_Event& event, bool& processed) { |
115 | 130 | if (event.type == SDL_TEXTINPUT) { |
116 | 131 | if ((rk->ReadKey(event))&&(globalData.SoundEnabled)&&(!globalData.NoSound)) { |
117 | Mix_PlayChannel(1, globalData.typingChunk, 0); | |
132 | Mix_PlayChannel(1, globalData.typingChunk.get(), 0); | |
118 | 133 | } |
119 | 134 | } |
120 | 135 | |
129 | 144 | } |
130 | 145 | else { |
131 | 146 | if ((rk->ReadKey(event))&&(globalData.SoundEnabled)&&(!globalData.NoSound)) { |
132 | Mix_PlayChannel(1, globalData.typingChunk, 0); | |
147 | Mix_PlayChannel(1, globalData.typingChunk.get(), 0); | |
133 | 148 | } |
134 | 149 | } |
135 | 150 | } |
51 | 51 | std::string header; |
52 | 52 | bool updated = false; |
53 | 53 | SDL_TextInput textInputScope; |
54 | sago::SagoTextField headerLabel; | |
55 | sago::SagoTextField enterLabel; | |
56 | sago::SagoTextField cancelLabel; | |
57 | sago::SagoTextField textField; | |
58 | sago::SagoTextField cursorLabel; | |
54 | 59 | }; |
55 | 60 | |
56 | 61 | #endif /* DIALOGBOX_HPP */ |
0 | /* | |
1 | =========================================================================== | |
2 | blockattack - Block Attack - Rise of the Blocks | |
3 | Copyright (C) 2005-2018 Poul Sander | |
4 | ||
5 | This program is free software: you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation, either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see http://www.gnu.org/licenses/ | |
17 | ||
18 | Source information and contacts persons can be found at | |
19 | https://blockattack.net | |
20 | =========================================================================== | |
21 | */ | |
22 | ||
23 | #include "HelpAboutState.hpp" | |
24 | #include "global.hpp" | |
25 | #include "common.h" | |
26 | #include "MenuSystem.h" | |
27 | #include "sstream" | |
28 | #include "version.h" | |
29 | ||
30 | const int xsize = 1024; | |
31 | const int ysize = 768; | |
32 | const int buttonOffset = 160; | |
33 | extern sago::SagoSprite bExit; | |
34 | ||
35 | ||
36 | ||
37 | static void setHelpGamepadFont(const sago::SagoDataHolder* holder, sago::SagoTextField& field, const char* text) { | |
38 | field.SetHolder(holder); | |
39 | field.SetFont("freeserif"); | |
40 | field.SetColor({255,255,255,255}); | |
41 | field.SetFontSize(30); | |
42 | field.SetOutline(1, {128,128,128,255}); | |
43 | field.SetText(text); | |
44 | } | |
45 | ||
46 | static void setHelpGamepadFont(const sago::SagoDataHolder* holder, sago::SagoTextBox& field, const char* text) { | |
47 | field.SetHolder(holder); | |
48 | field.SetFont("freeserif"); | |
49 | field.SetColor({255,255,255,255}); | |
50 | field.SetColor({0,0,0,255}); | |
51 | field.SetFontSize(20); | |
52 | field.SetOutline(0, {0,0,0,255}); | |
53 | field.SetText(text); | |
54 | } | |
55 | ||
56 | static SDL_RendererInfo renderInfo; | |
57 | ||
58 | HelpAboutState::HelpAboutState() { | |
59 | SDL_GetRendererInfo(globalData.screen, &renderInfo); | |
60 | std::stringstream infoStream; | |
61 | infoStream << _("Name:") << " " << _("Block Attack - Rise of the Blocks") << "\n"; | |
62 | infoStream << _("Original name:") << " Block Attack - Rise of the Blocks" << "\n"; | |
63 | infoStream << _("Version:") << " " << VERSION_NUMBER << "\n"; | |
64 | infoStream << _("Homepage:") << " " << "https://blockattack.net\n"; | |
65 | infoStream << _("Github page:") << " " << "https://github.com/blockattack/blockattack-game\n"; | |
66 | infoStream << _("SDL render:") << " " << renderInfo.name << "\n"; | |
67 | infoStream << _("Save folder:") << " " << PHYSFS_getWriteDir() << "\n"; | |
68 | infoStream << _("Locale:") << " " << setlocale( LC_CTYPE, nullptr ) << "\n"; | |
69 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), titleField, _("About")); | |
70 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), infoBox, infoStream.str().c_str()); | |
71 | } | |
72 | ||
73 | HelpAboutState::~HelpAboutState() { | |
74 | } | |
75 | ||
76 | bool HelpAboutState::IsActive() { | |
77 | return isActive; | |
78 | } | |
79 | ||
80 | void HelpAboutState::ProcessInput(const SDL_Event& event, bool& processed) { | |
81 | ||
82 | UpdateMouseCoordinates(event, globalData.mousex, globalData.mousey); | |
83 | ||
84 | if (isConfirmEvent(event) || isEscapeEvent(event)) { | |
85 | isActive = false; | |
86 | processed = true; | |
87 | } | |
88 | } | |
89 | ||
90 | extern void DrawRectYellow(SDL_Renderer* target, int topx, int topy, int height, int width); | |
91 | ||
92 | void HelpAboutState::Draw(SDL_Renderer* target) { | |
93 | DrawBackground(target); | |
94 | titleField.Draw(target, 50, 50); | |
95 | DrawRectYellow(target, 40, 90, 600, 900); | |
96 | infoBox.SetMaxWidth(850); | |
97 | infoBox.Draw(target, 50, 100); | |
98 | bExit.Draw(globalData.screen, SDL_GetTicks(), xsize-buttonOffset, ysize-buttonOffset); | |
99 | #if DEBUG | |
100 | static sago::SagoTextField mousePos; | |
101 | mousePos.SetHolder(&globalData.spriteHolder->GetDataHolder()); | |
102 | mousePos.SetFontSize(16); | |
103 | mousePos.SetOutline(1, {128,128,128,255}); | |
104 | mousePos.SetText(std::string("Mouse position: ")+std::to_string(globalData.mousex)+std::string(", ")+std::to_string(globalData.mousey)); | |
105 | mousePos.Draw(target, 0,0); | |
106 | #endif | |
107 | } | |
108 | ||
109 | void HelpAboutState::Update() { | |
110 | // If the mouse button is released, make bMouseUp equal true | |
111 | if ( !(SDL_GetMouseState(nullptr, nullptr)&SDL_BUTTON(1)) ) { | |
112 | bMouseUp=true; | |
113 | } | |
114 | ||
115 | if (SDL_GetMouseState(nullptr,nullptr)&SDL_BUTTON(1) && bMouseUp) { | |
116 | bMouseUp = false; | |
117 | ||
118 | //The Score button: | |
119 | if ((globalData.mousex>xsize-buttonOffset) && (globalData.mousex<xsize-buttonOffset+bExit.GetWidth()) | |
120 | && (globalData.mousey>ysize-buttonOffset) && (globalData.mousey<ysize-buttonOffset+bExit.GetHeight())) { | |
121 | isActive = false; | |
122 | } | |
123 | ||
124 | } | |
125 | }⏎ |
0 | /* | |
1 | =========================================================================== | |
2 | blockattack - Block Attack - Rise of the Blocks | |
3 | Copyright (C) 2005-2018 Poul Sander | |
4 | ||
5 | This program is free software: you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation, either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see http://www.gnu.org/licenses/ | |
17 | ||
18 | Source information and contacts persons can be found at | |
19 | https://blockattack.net | |
20 | =========================================================================== | |
21 | */ | |
22 | ||
23 | #ifndef HELPABOUT_HPP | |
24 | #define HELPABOUT_HPP | |
25 | ||
26 | #include "sago/GameStateInterface.hpp" | |
27 | #include "sago/SagoTextBox.hpp" | |
28 | #include "sago/SagoTextField.hpp" | |
29 | ||
30 | class HelpAboutState : public sago::GameStateInterface { | |
31 | public: | |
32 | HelpAboutState(); | |
33 | HelpAboutState(const HelpAboutState& orig) = delete; | |
34 | virtual ~HelpAboutState(); | |
35 | ||
36 | bool IsActive() override; | |
37 | void Draw(SDL_Renderer* target) override; | |
38 | void ProcessInput(const SDL_Event& event, bool &processed) override; | |
39 | void Update() override; | |
40 | ||
41 | private: | |
42 | bool isActive = true; | |
43 | bool bMouseUp = true; | |
44 | sago::SagoTextField titleField; | |
45 | sago::SagoTextBox infoBox; | |
46 | }; | |
47 | ||
48 | #endif /* HELPABOUT_HPP */ | |
49 |
0 | /* | |
1 | =========================================================================== | |
2 | blockattack - Block Attack - Rise of the Blocks | |
3 | Copyright (C) 2005-2018 Poul Sander | |
4 | ||
5 | This program is free software: you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation, either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see http://www.gnu.org/licenses/ | |
17 | ||
18 | Source information and contacts persons can be found at | |
19 | https://blockattack.net | |
20 | =========================================================================== | |
21 | */ | |
22 | ||
23 | #include "HelpGamepadState.hpp" | |
24 | #include "global.hpp" | |
25 | #include "common.h" | |
26 | #include "MenuSystem.h" | |
27 | #include "gamecontroller.h" | |
28 | ||
29 | const int xsize = 1024; | |
30 | const int ysize = 768; | |
31 | const int buttonOffset = 160; | |
32 | ||
33 | static void setHelpGamepadFont(const sago::SagoDataHolder* holder, sago::SagoTextField& field, const char* text) { | |
34 | field.SetHolder(holder); | |
35 | field.SetFont("freeserif"); | |
36 | field.SetColor({255,255,255,255}); | |
37 | field.SetFontSize(30); | |
38 | field.SetOutline(2, {0,0,0,255}); | |
39 | field.SetText(text); | |
40 | } | |
41 | ||
42 | static void setHelpGamepadFont(const sago::SagoDataHolder* holder, sago::SagoTextBox& field, const char* text) { | |
43 | field.SetHolder(holder); | |
44 | field.SetFont("freeserif"); | |
45 | field.SetColor({255,255,255,255}); | |
46 | field.SetFontSize(30); | |
47 | field.SetOutline(2, {0,0,0,255}); | |
48 | field.SetText(text); | |
49 | } | |
50 | ||
51 | ||
52 | HelpGamepadState::HelpGamepadState() { | |
53 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), moveLabel, _("Move cursor")); | |
54 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), pushLabel, _("Push line")); | |
55 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), backLabel, _("Back (Menu)")); | |
56 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), switchLabel, _("Switch")); | |
57 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), confirmLabel, _("Confirm")); | |
58 | std::string s = _("Only SDL2 compatible controllers are supported!\nSupported controllers: "); | |
59 | for (size_t i = 0 ; i<GetSupportedControllerNames().size(); ++i ) { | |
60 | if (i != 0) { | |
61 | s+= ", "; | |
62 | } | |
63 | s+= GetSupportedControllerNames().at(i); | |
64 | } | |
65 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), supportedControllers, s.c_str()); | |
66 | supportedControllers.SetMaxWidth(740); | |
67 | } | |
68 | ||
69 | HelpGamepadState::~HelpGamepadState() { | |
70 | } | |
71 | ||
72 | bool HelpGamepadState::IsActive() { | |
73 | return isActive; | |
74 | } | |
75 | ||
76 | void HelpGamepadState::ProcessInput(const SDL_Event& event, bool& processed) { | |
77 | UpdateMouseCoordinates(event, globalData.mousex, globalData.mousey); | |
78 | ||
79 | if (isConfirmEvent(event) || isEscapeEvent(event)) { | |
80 | isActive = false; | |
81 | processed = true; | |
82 | } | |
83 | } | |
84 | ||
85 | extern sago::SagoSprite bExit; | |
86 | ||
87 | void HelpGamepadState::Draw(SDL_Renderer* target) { | |
88 | DrawBackground(target); | |
89 | const sago::SagoSprite& gamepad = globalData.spriteHolder->GetSprite("help_controller"); | |
90 | gamepad.Draw(target, SDL_GetTicks(), globalData.xsize/2-480/2, 100); | |
91 | SDL_RenderDrawLine(target, 100, 210, globalData.xsize/2-480/2+130, 210); | |
92 | SDL_RenderDrawLine(target, 100, 298, globalData.xsize/2-480/2+158, 298); | |
93 | SDL_RenderDrawLine(target, 100, 210, 100, 400); | |
94 | moveLabel.Draw(target, 100, 404, sago::SagoTextField::Alignment::center); | |
95 | SDL_RenderDrawLine(target, globalData.xsize/2-480/2+140, 90, globalData.xsize/2-480/2+140, 105); | |
96 | SDL_RenderDrawLine(target, globalData.xsize/2+480/2-140, 90, globalData.xsize/2+480/2-140, 105); | |
97 | SDL_RenderDrawLine(target, globalData.xsize/2-480/2+140, 90, globalData.xsize/2+480/2-140, 90); | |
98 | SDL_RenderDrawLine(target, globalData.xsize/2, 80, globalData.xsize/2, 90); | |
99 | pushLabel.Draw(target, globalData.xsize/2, 80, sago::SagoTextField::Alignment::center, sago::SagoTextField::VerticalAlignment::bottom); | |
100 | SDL_RenderDrawLine(target, 625, 168, 800, 168); | |
101 | SDL_RenderDrawLine(target, 800, 158, 800, 168); | |
102 | backLabel.Draw(target, 800, 156, sago::SagoTextField::Alignment::center, sago::SagoTextField::VerticalAlignment::bottom); | |
103 | SDL_RenderDrawLine(target, 625, 241, 900, 241); | |
104 | SDL_RenderDrawLine(target, 663, 207, 900, 207); | |
105 | SDL_RenderDrawLine(target, 900, 207, 900, 400); | |
106 | switchLabel.Draw(target, 900, 404, sago::SagoTextField::Alignment::center); | |
107 | confirmLabel.Draw(target, 900, 404+30, sago::SagoTextField::Alignment::center); | |
108 | bExit.Draw(globalData.screen, SDL_GetTicks(), xsize-buttonOffset, ysize-buttonOffset); | |
109 | supportedControllers.Draw(target, 10, 600); | |
110 | ||
111 | #if DEBUG | |
112 | static sago::SagoTextField mousePos; | |
113 | mousePos.SetHolder(&globalData.spriteHolder->GetDataHolder()); | |
114 | mousePos.SetFontSize(16); | |
115 | mousePos.SetOutline(1, {128,128,128,255}); | |
116 | mousePos.SetText(std::string("Mouse position: ")+std::to_string(globalData.mousex)+std::string(", ")+std::to_string(globalData.mousey)); | |
117 | mousePos.Draw(target, 0,0); | |
118 | #endif | |
119 | } | |
120 | ||
121 | void HelpGamepadState::Update() { | |
122 | // If the mouse button is released, make bMouseUp equal true | |
123 | if ( !(SDL_GetMouseState(nullptr, nullptr)&SDL_BUTTON(1)) ) { | |
124 | bMouseUp=true; | |
125 | } | |
126 | ||
127 | if (SDL_GetMouseState(nullptr,nullptr)&SDL_BUTTON(1) && bMouseUp) { | |
128 | bMouseUp = false; | |
129 | ||
130 | //The Score button: | |
131 | if ((globalData.mousex>xsize-buttonOffset) && (globalData.mousex<xsize-buttonOffset+bExit.GetWidth()) | |
132 | && (globalData.mousey>ysize-buttonOffset) && (globalData.mousey<ysize-buttonOffset+bExit.GetHeight())) { | |
133 | isActive = false; | |
134 | } | |
135 | ||
136 | } | |
137 | }⏎ |
0 | /* | |
1 | =========================================================================== | |
2 | blockattack - Block Attack - Rise of the Blocks | |
3 | Copyright (C) 2005-2018 Poul Sander | |
4 | ||
5 | This program is free software: you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation, either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see http://www.gnu.org/licenses/ | |
17 | ||
18 | Source information and contacts persons can be found at | |
19 | https://blockattack.net | |
20 | =========================================================================== | |
21 | */ | |
22 | ||
23 | #ifndef HELPGAMEPADSTATE_HPP | |
24 | #define HELPGAMEPADSTATE_HPP | |
25 | ||
26 | #include "sago/GameStateInterface.hpp" | |
27 | #include "sago/SagoTextField.hpp" | |
28 | #include "sago/SagoTextBox.hpp" | |
29 | ||
30 | class HelpGamepadState : public sago::GameStateInterface { | |
31 | public: | |
32 | HelpGamepadState(); | |
33 | HelpGamepadState(const HelpGamepadState& orig) = delete; | |
34 | virtual ~HelpGamepadState(); | |
35 | ||
36 | bool IsActive() override; | |
37 | void Draw(SDL_Renderer* target) override; | |
38 | void ProcessInput(const SDL_Event& event, bool &processed) override; | |
39 | void Update() override; | |
40 | ||
41 | private: | |
42 | bool isActive = true; | |
43 | bool bMouseUp = true; | |
44 | sago::SagoTextField moveLabel; | |
45 | sago::SagoTextField pushLabel; | |
46 | sago::SagoTextField backLabel; | |
47 | sago::SagoTextField switchLabel; | |
48 | sago::SagoTextField confirmLabel; | |
49 | sago::SagoTextBox supportedControllers; | |
50 | }; | |
51 | ||
52 | #endif /* HELPGAMEPADSTATE_HPP */ | |
53 |
0 | /* | |
1 | =========================================================================== | |
2 | blockattack - Block Attack - Rise of the Blocks | |
3 | Copyright (C) 2005-2018 Poul Sander | |
4 | ||
5 | This program is free software: you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation, either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see http://www.gnu.org/licenses/ | |
17 | ||
18 | Source information and contacts persons can be found at | |
19 | https://blockattack.net | |
20 | =========================================================================== | |
21 | */ | |
22 | ||
23 | #include "HelpHowtoState.hpp" | |
24 | #include "global.hpp" | |
25 | #include "common.h" | |
26 | #include "MenuSystem.h" | |
27 | #include <cmath> | |
28 | ||
29 | const int xsize = 1024; | |
30 | const int ysize = 768; | |
31 | const int buttonOffset = 160; | |
32 | extern sago::SagoSprite bExit; | |
33 | extern sago::SagoSprite bricks[7]; | |
34 | ||
35 | /** | |
36 | * Draws bricks with a string like: | |
37 | * "aab" for two identical one and another | |
38 | * "aaB" the third one will have a bomb | |
39 | * The any char not in 'a' to 'g' or 'A' to 'G' the behavior is undefined. | |
40 | * @param target Target to draw to | |
41 | * @param bricks description on what to draw as a string | |
42 | * @param x | |
43 | * @param y | |
44 | */ | |
45 | static void RenderRowOfBricks(SDL_Renderer* target, const std::string& brickStr, int x, int y) { | |
46 | Uint32 tick = SDL_GetTicks(); | |
47 | for (size_t i = 0; i < brickStr.size(); ++i) { | |
48 | bool bomb = false; | |
49 | char brickChar = brickStr[i]; | |
50 | if (brickChar >= 'A' && brickChar <= 'G') { | |
51 | bomb = true; | |
52 | brickChar = brickChar + 'a' - 'A'; | |
53 | } | |
54 | if (brickChar >= 'a' && brickChar <= 'g') { | |
55 | bricks[brickChar - 'a'].Draw(target, tick, x+i*50, y); | |
56 | if (bomb) { | |
57 | globalData.spriteHolder->GetSprite("block_bomb").Draw(target, tick, x+i*50, y); | |
58 | } | |
59 | } | |
60 | } | |
61 | } | |
62 | ||
63 | class HorizontalSwitchAnimation { | |
64 | public: | |
65 | std::string brickStr = "abc"; | |
66 | int cursorPos = 0; | |
67 | int state = 0; //0=move left, 1 = switch, 2 = move right, 3 = switch | |
68 | Uint32 lastTick = 0; | |
69 | Uint32 animationSpeed = 2000; | |
70 | ||
71 | void Update (Uint32 tick) { | |
72 | if (tick > lastTick + animationSpeed) { | |
73 | lastTick = tick; | |
74 | switch (state) { | |
75 | case 0: | |
76 | cursorPos = 1; | |
77 | break; | |
78 | case 1: //fallthough | |
79 | case 3: | |
80 | std::swap(brickStr[cursorPos], brickStr[cursorPos + 1]); | |
81 | break; | |
82 | case 2: | |
83 | cursorPos = 0; | |
84 | break; | |
85 | } | |
86 | ++state; | |
87 | if (state > 3) { | |
88 | state = 0; | |
89 | } | |
90 | } | |
91 | } | |
92 | }; | |
93 | ||
94 | class MultiLineBlocks { | |
95 | private: | |
96 | std::vector<std::string> lines; | |
97 | public: | |
98 | MultiLineBlocks& addLine(const std::string& line) { | |
99 | lines.push_back(line); | |
100 | return *this; | |
101 | } | |
102 | ||
103 | void Render(SDL_Renderer* target, int x, int y) { | |
104 | for (size_t i = 0; i < lines.size(); ++i) { | |
105 | RenderRowOfBricks(target, lines[i], x, y+i*50); | |
106 | } | |
107 | } | |
108 | ||
109 | }; | |
110 | ||
111 | HorizontalSwitchAnimation switchAnimation; | |
112 | sago::SagoTextField switchAnimationField; | |
113 | sago::SagoTextField clearRowfield; | |
114 | sago::SagoTextField comboField; | |
115 | sago::SagoTextField dropField; | |
116 | sago::SagoTextField chainField; | |
117 | ||
118 | static void InitTextField(sago::SagoTextField& field, const char* text) { | |
119 | field.SetHolder(&globalData.spriteHolder->GetDataHolder()); | |
120 | field.SetFontSize(30); | |
121 | field.SetOutline(2, {0,0,0,255}); | |
122 | field.SetText(text); | |
123 | } | |
124 | ||
125 | HelpHowtoState::HelpHowtoState() { | |
126 | InitTextField(switchAnimationField, _("Switch block horizontally")); | |
127 | InitTextField(clearRowfield, _("Match 3 to clear")); | |
128 | InitTextField(comboField, _("Create combos!")); | |
129 | InitTextField(dropField, _("Drop blocks!")); | |
130 | InitTextField(chainField, _("Create a chain effect")); | |
131 | } | |
132 | ||
133 | HelpHowtoState::~HelpHowtoState() { | |
134 | } | |
135 | ||
136 | bool HelpHowtoState::IsActive() { | |
137 | return isActive; | |
138 | } | |
139 | ||
140 | void HelpHowtoState::ProcessInput(const SDL_Event& event, bool& processed) { | |
141 | ||
142 | UpdateMouseCoordinates(event, globalData.mousex, globalData.mousey); | |
143 | ||
144 | if (isConfirmEvent(event) || isEscapeEvent(event)) { | |
145 | isActive = false; | |
146 | processed = true; | |
147 | } | |
148 | } | |
149 | ||
150 | const double PI =3.141592653589793238463; | |
151 | ||
152 | static void DrawArrow(SDL_Renderer* target, int x1, int y1, int x2, int y2) { | |
153 | double dx = x1-x2; | |
154 | double dy = y1-y2; | |
155 | double distance = std::sqrt((dx*dx)+(dy*dy)); | |
156 | dx = dx * 10.0 / distance; | |
157 | dy = dy * 10.0 / distance; | |
158 | double angle= PI/4.0; | |
159 | double nx1 = dx * std::cos(angle) - dy * std::sin(angle) + x2; | |
160 | double ny1 = dx * std::sin(angle) + dy * std::cos(angle) + y2; | |
161 | SDL_RenderDrawLine(target, x1, y1, x2, y2); | |
162 | SDL_RenderDrawLine(target, nx1, ny1, x2, y2); | |
163 | nx1 = dx * std::cos(-angle) - dy * std::sin(-angle) + x2; | |
164 | ny1 = dx * std::sin(-angle) + dy * std::cos(-angle) + y2; | |
165 | SDL_RenderDrawLine(target, nx1, ny1, x2, y2); | |
166 | } | |
167 | ||
168 | void HelpHowtoState::Draw(SDL_Renderer* target) { | |
169 | DrawBackground(target); | |
170 | RenderRowOfBricks(target, switchAnimation.brickStr, 50, 50); | |
171 | globalData.spriteHolder->GetSprite("cursor").Draw(target, SDL_GetTicks(), 50+switchAnimation.cursorPos*50, 50); | |
172 | switchAnimationField.Draw(target, 50 +150+30, 50+25, sago::SagoTextField::Alignment::left, sago::SagoTextField::VerticalAlignment::center); | |
173 | RenderRowOfBricks(target, "adaa", 50, 150); | |
174 | globalData.spriteHolder->GetSprite("cursor").Draw(target, SDL_GetTicks(), 50, 150); | |
175 | RenderRowOfBricks(target, "dAAA", 50+300, 150); | |
176 | DrawArrow(target, 50+200+25, 150+25, 50+300-25, 150+25); | |
177 | clearRowfield.Draw(target, 600, 150+25, sago::SagoTextField::Alignment::left, sago::SagoTextField::VerticalAlignment::center); | |
178 | comboField.Draw(target, 50+175, 410, sago::SagoTextField::Alignment::center); | |
179 | MultiLineBlocks().addLine("ab").addLine("ba").addLine("ab").Render(target, 50, 250); | |
180 | globalData.spriteHolder->GetSprite("cursor").Draw(target, SDL_GetTicks(), 50, 250+50); | |
181 | MultiLineBlocks().addLine("AB").addLine("AB").addLine("AB").Render(target, 50+200, 250); | |
182 | DrawArrow(target, 175, 325, 225, 325); | |
183 | MultiLineBlocks().addLine("a").addLine("b").addLine("e").Render(target, 50+400, 250); | |
184 | globalData.spriteHolder->GetSprite("cursor").Draw(target, SDL_GetTicks(), 50+400, 250); | |
185 | MultiLineBlocks().addLine(" ").addLine("b").addLine("ea").Render(target, 50+400+200, 250); | |
186 | dropField.Draw(target, 50+400+150, 410, sago::SagoTextField::Alignment::center); | |
187 | DrawArrow(target, 575, 325, 625, 325); | |
188 | DrawArrow(target, 475, 275, 525, 275); | |
189 | DrawArrow(target, 525, 275, 525, 375); | |
190 | DrawArrow(target, 675, 275, 725, 275); | |
191 | DrawArrow(target, 725, 275, 725, 375); | |
192 | MultiLineBlocks().addLine(" d").addLine(" f").addLine(" f").addLine("fdd").Render(target, 50, 500); | |
193 | MultiLineBlocks().addLine(" d").addLine(" F").addLine(" F").addLine("dFd").Render(target, 50+200, 500); | |
194 | MultiLineBlocks().addLine(" d").addLine(" ").addLine(" ").addLine("d d").Render(target, 50+200*2, 500); | |
195 | MultiLineBlocks().addLine(" ").addLine(" ").addLine(" ").addLine("DDD").Render(target, 50+200*3, 500); | |
196 | globalData.spriteHolder->GetSprite("cursor").Draw(target, SDL_GetTicks(), 50, 650); | |
197 | DrawArrow(target, 200, 600, 250, 600); | |
198 | DrawArrow(target, 400, 600, 450, 600); | |
199 | DrawArrow(target, 600, 600, 650, 600); | |
200 | DrawArrow(target, 525, 525, 525, 675); | |
201 | chainField.Draw(target, 400, 710, sago::SagoTextField::Alignment::center); | |
202 | bExit.Draw(globalData.screen, SDL_GetTicks(), xsize-buttonOffset, ysize-buttonOffset); | |
203 | #if DEBUG | |
204 | static sago::SagoTextField mousePos; | |
205 | mousePos.SetHolder(&globalData.spriteHolder->GetDataHolder()); | |
206 | mousePos.SetFontSize(16); | |
207 | mousePos.SetOutline(1, {128,128,128,255}); | |
208 | mousePos.SetText(std::string("Mouse position: ")+std::to_string(globalData.mousex)+std::string(", ")+std::to_string(globalData.mousey)); | |
209 | mousePos.Draw(target, 0,0); | |
210 | #endif | |
211 | } | |
212 | ||
213 | void HelpHowtoState::Update() { | |
214 | // If the mouse button is released, make bMouseUp equal true | |
215 | if ( !(SDL_GetMouseState(nullptr, nullptr)&SDL_BUTTON(1)) ) { | |
216 | bMouseUp=true; | |
217 | } | |
218 | ||
219 | if (SDL_GetMouseState(nullptr,nullptr)&SDL_BUTTON(1) && bMouseUp) { | |
220 | bMouseUp = false; | |
221 | ||
222 | //The Score button: | |
223 | if ((globalData.mousex>xsize-buttonOffset) && (globalData.mousex<xsize-buttonOffset+bExit.GetWidth()) | |
224 | && (globalData.mousey>ysize-buttonOffset) && (globalData.mousey<ysize-buttonOffset+bExit.GetHeight())) { | |
225 | isActive = false; | |
226 | } | |
227 | ||
228 | } | |
229 | switchAnimation.Update(SDL_GetTicks()); | |
230 | }⏎ |
0 | /* | |
1 | =========================================================================== | |
2 | blockattack - Block Attack - Rise of the Blocks | |
3 | Copyright (C) 2005-2018 Poul Sander | |
4 | ||
5 | This program is free software: you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation, either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see http://www.gnu.org/licenses/ | |
17 | ||
18 | Source information and contacts persons can be found at | |
19 | https://blockattack.net | |
20 | =========================================================================== | |
21 | */ | |
22 | ||
23 | #ifndef HELPHOWTOSTATE_HPP | |
24 | #define HELPHOWTOSTATE_HPP | |
25 | ||
26 | #include "sago/GameStateInterface.hpp" | |
27 | #include "sago/SagoTextField.hpp" | |
28 | #include "sago/SagoTextBox.hpp" | |
29 | ||
30 | class HelpHowtoState : public sago::GameStateInterface { | |
31 | public: | |
32 | HelpHowtoState(); | |
33 | HelpHowtoState(const HelpHowtoState& orig) = delete; | |
34 | virtual ~HelpHowtoState(); | |
35 | ||
36 | bool IsActive() override; | |
37 | void Draw(SDL_Renderer* target) override; | |
38 | void ProcessInput(const SDL_Event& event, bool &processed) override; | |
39 | void Update() override; | |
40 | ||
41 | private: | |
42 | bool isActive = true; | |
43 | bool bMouseUp = true; | |
44 | }; | |
45 | ||
46 | #endif /* HELPHOWTOSTATE_HPP */ | |
47 |
0 | /* | |
1 | NFont: A font class for SDL and SDL_Renderer | |
2 | by Jonathan Dearborn | |
3 | ||
4 | See NFont.h for license info. | |
5 | */ | |
6 | ||
7 | #include "NFont.h" | |
8 | #include "SDL_FontCache.h" | |
9 | ||
10 | #include <cmath> | |
11 | #include <cstdio> | |
12 | #ifndef M_PI | |
13 | #define M_PI 3.14159265358979323846 | |
14 | #endif | |
15 | ||
16 | #include <string> | |
17 | #include <cstring> | |
18 | #include <list> | |
19 | using std::string; | |
20 | using std::list; | |
21 | ||
22 | #ifdef NFONT_USE_SDL_GPU | |
23 | #define NFont_Target GPU_Target | |
24 | #define NFont_Image GPU_Image | |
25 | #define NFont_Log GPU_LogError | |
26 | #else | |
27 | #define NFont_Target SDL_Renderer | |
28 | #define NFont_Image SDL_Texture | |
29 | #define NFont_Log SDL_Log | |
30 | #endif | |
31 | ||
32 | #define MIN(a,b) ((a) < (b)? (a) : (b)) | |
33 | #define MAX(a,b) ((a) > (b)? (a) : (b)) | |
34 | ||
35 | #define NFONT_BUFFER_SIZE 1024 | |
36 | ||
37 | // vsnprintf replacement adapted from Valentin Milea: | |
38 | // http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 | |
39 | #if defined(_MSC_VER) && _MSC_VER < 1900 | |
40 | ||
41 | #define vsnprintf c99_vsnprintf | |
42 | ||
43 | static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) | |
44 | { | |
45 | int count = -1; | |
46 | ||
47 | if (size != 0) | |
48 | count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); | |
49 | if (count == -1) | |
50 | count = _vscprintf(format, ap); | |
51 | ||
52 | return count; | |
53 | } | |
54 | ||
55 | #endif | |
56 | ||
57 | ||
58 | ||
59 | static inline SDL_Surface* createSurface24(Uint32 width, Uint32 height) | |
60 | { | |
61 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN | |
62 | return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, 0xFF0000, 0x00FF00, 0x0000FF, 0); | |
63 | #else | |
64 | return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0); | |
65 | #endif | |
66 | } | |
67 | ||
68 | static inline SDL_Surface* createSurface32(Uint32 width, Uint32 height) | |
69 | { | |
70 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN | |
71 | return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); | |
72 | #else | |
73 | return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); | |
74 | #endif | |
75 | } | |
76 | ||
77 | static inline char* copyString(const char* c) | |
78 | { | |
79 | if(c == NULL) | |
80 | return NULL; | |
81 | ||
82 | char* result = new char[strlen(c)+1]; | |
83 | strcpy(result, c); | |
84 | ||
85 | return result; | |
86 | } | |
87 | ||
88 | static inline Uint32 getPixel(SDL_Surface *Surface, int x, int y) | |
89 | { | |
90 | Uint8* bits; | |
91 | Uint32 bpp; | |
92 | ||
93 | if(x < 0 || x >= Surface->w) | |
94 | return 0; // Best I could do for errors | |
95 | ||
96 | bpp = Surface->format->BytesPerPixel; | |
97 | bits = ((Uint8*)Surface->pixels) + y*Surface->pitch + x*bpp; | |
98 | ||
99 | switch (bpp) | |
100 | { | |
101 | case 1: | |
102 | return *((Uint8*)Surface->pixels + y * Surface->pitch + x); | |
103 | break; | |
104 | case 2: | |
105 | return *((Uint16*)Surface->pixels + y * Surface->pitch/2 + x); | |
106 | break; | |
107 | case 3: | |
108 | // Endian-correct, but slower | |
109 | Uint8 r, g, b; | |
110 | r = *((bits)+Surface->format->Rshift/8); | |
111 | g = *((bits)+Surface->format->Gshift/8); | |
112 | b = *((bits)+Surface->format->Bshift/8); | |
113 | return SDL_MapRGB(Surface->format, r, g, b); | |
114 | break; | |
115 | case 4: | |
116 | return *((Uint32*)Surface->pixels + y * Surface->pitch/4 + x); | |
117 | break; | |
118 | } | |
119 | ||
120 | return 0; // FIXME: Handle errors better | |
121 | } | |
122 | ||
123 | static inline void setPixel(SDL_Surface* surface, int x, int y, Uint32 color) | |
124 | { | |
125 | int bpp = surface->format->BytesPerPixel; | |
126 | Uint8* bits = ((Uint8 *)surface->pixels) + y*surface->pitch + x*bpp; | |
127 | ||
128 | /* Set the pixel */ | |
129 | switch(bpp) | |
130 | { | |
131 | case 1: | |
132 | *((Uint8 *)(bits)) = (Uint8)color; | |
133 | break; | |
134 | case 2: | |
135 | *((Uint16 *)(bits)) = (Uint16)color; | |
136 | break; | |
137 | case 3: { /* Format/endian independent */ | |
138 | Uint8 r,g,b; | |
139 | r = (color >> surface->format->Rshift) & 0xFF; | |
140 | g = (color >> surface->format->Gshift) & 0xFF; | |
141 | b = (color >> surface->format->Bshift) & 0xFF; | |
142 | *((bits)+surface->format->Rshift/8) = r; | |
143 | *((bits)+surface->format->Gshift/8) = g; | |
144 | *((bits)+surface->format->Bshift/8) = b; | |
145 | } | |
146 | break; | |
147 | case 4: | |
148 | *((Uint32 *)(bits)) = (Uint32)color; | |
149 | break; | |
150 | } | |
151 | } | |
152 | ||
153 | static inline void drawPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha) | |
154 | { | |
155 | if(x > surface->clip_rect.x + surface->clip_rect.w || x < surface->clip_rect.x || y > surface->clip_rect.y + surface->clip_rect.h || y < surface->clip_rect.y) | |
156 | return; | |
157 | ||
158 | switch (surface->format->BytesPerPixel) | |
159 | { | |
160 | case 1: { /* Assuming 8-bpp */ | |
161 | ||
162 | Uint8 *pixel = (Uint8 *)surface->pixels + y*surface->pitch + x; | |
163 | ||
164 | Uint8 dR = surface->format->palette->colors[*pixel].r; | |
165 | Uint8 dG = surface->format->palette->colors[*pixel].g; | |
166 | Uint8 dB = surface->format->palette->colors[*pixel].b; | |
167 | Uint8 sR = surface->format->palette->colors[color].r; | |
168 | Uint8 sG = surface->format->palette->colors[color].g; | |
169 | Uint8 sB = surface->format->palette->colors[color].b; | |
170 | ||
171 | dR = dR + ((sR-dR)*alpha >> 8); | |
172 | dG = dG + ((sG-dG)*alpha >> 8); | |
173 | dB = dB + ((sB-dB)*alpha >> 8); | |
174 | ||
175 | *pixel = SDL_MapRGB(surface->format, dR, dG, dB); | |
176 | ||
177 | } | |
178 | break; | |
179 | ||
180 | case 2: { /* Probably 15-bpp or 16-bpp */ | |
181 | ||
182 | Uint32 Rmask = surface->format->Rmask, Gmask = surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask; | |
183 | Uint16 *pixel = (Uint16 *)surface->pixels + y*surface->pitch/2 + x; | |
184 | Uint32 dc = *pixel; | |
185 | Uint32 R,G,B,A=0; | |
186 | ||
187 | R = ((dc & Rmask) + (( (color & Rmask) - (dc & Rmask) ) * alpha >> 8)) & Rmask; | |
188 | G = ((dc & Gmask) + (( (color & Gmask) - (dc & Gmask) ) * alpha >> 8)) & Gmask; | |
189 | B = ((dc & Bmask) + (( (color & Bmask) - (dc & Bmask) ) * alpha >> 8)) & Bmask; | |
190 | if( Amask ) | |
191 | A = ((dc & Amask) + (( (color & Amask) - (dc & Amask) ) * alpha >> 8)) & Amask; | |
192 | ||
193 | *pixel= R | G | B | A; | |
194 | ||
195 | } | |
196 | break; | |
197 | ||
198 | case 3: { /* Slow 24-bpp mode, usually not used */ | |
199 | Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3; | |
200 | Uint8 rshift8=surface->format->Rshift/8; | |
201 | Uint8 gshift8=surface->format->Gshift/8; | |
202 | Uint8 bshift8=surface->format->Bshift/8; | |
203 | Uint8 ashift8=surface->format->Ashift/8; | |
204 | ||
205 | ||
206 | ||
207 | Uint8 dR, dG, dB, dA; | |
208 | Uint8 sR, sG, sB, sA; | |
209 | ||
210 | pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3; | |
211 | ||
212 | dR = *((pix)+rshift8); | |
213 | dG = *((pix)+gshift8); | |
214 | dB = *((pix)+bshift8); | |
215 | dA = *((pix)+ashift8); | |
216 | ||
217 | sR = (color>>surface->format->Rshift)&0xff; | |
218 | sG = (color>>surface->format->Gshift)&0xff; | |
219 | sB = (color>>surface->format->Bshift)&0xff; | |
220 | sA = (color>>surface->format->Ashift)&0xff; | |
221 | ||
222 | dR = dR + ((sR-dR)*alpha >> 8); | |
223 | dG = dG + ((sG-dG)*alpha >> 8); | |
224 | dB = dB + ((sB-dB)*alpha >> 8); | |
225 | dA = dA + ((sA-dA)*alpha >> 8); | |
226 | ||
227 | *((pix)+rshift8) = dR; | |
228 | *((pix)+gshift8) = dG; | |
229 | *((pix)+bshift8) = dB; | |
230 | *((pix)+ashift8) = dA; | |
231 | ||
232 | } | |
233 | break; | |
234 | ||
235 | case 4: { /* Probably 32-bpp */ | |
236 | Uint32 Rmask = surface->format->Rmask, Gmask = surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask; | |
237 | Uint32* pixel = (Uint32*)surface->pixels + y*surface->pitch/4 + x; | |
238 | Uint32 source = *pixel; | |
239 | Uint32 R,G,B,A; | |
240 | R = color & Rmask; | |
241 | G = color & Gmask; | |
242 | B = color & Bmask; | |
243 | A = 0; // keep this as 0 to avoid corruption of non-alpha surfaces | |
244 | ||
245 | // Blend and keep dest alpha | |
246 | if( alpha != SDL_ALPHA_OPAQUE ){ | |
247 | R = ((source & Rmask) + (( R - (source & Rmask) ) * alpha >> 8)) & Rmask; | |
248 | G = ((source & Gmask) + (( G - (source & Gmask) ) * alpha >> 8)) & Gmask; | |
249 | B = ((source & Bmask) + (( B - (source & Bmask) ) * alpha >> 8)) & Bmask; | |
250 | } | |
251 | if(Amask) | |
252 | A = (source & Amask); | |
253 | ||
254 | *pixel = R | G | B | A; | |
255 | } | |
256 | break; | |
257 | } | |
258 | } | |
259 | ||
260 | static inline NFont::Rectf rectUnion(const NFont::Rectf& A, const NFont::Rectf& B) | |
261 | { | |
262 | float x,x2,y,y2; | |
263 | x = MIN(A.x, B.x); | |
264 | y = MIN(A.y, B.y); | |
265 | x2 = MAX(A.x+A.w, B.x+B.w); | |
266 | y2 = MAX(A.y+A.h, B.y+B.h); | |
267 | NFont::Rectf result(x, y, MAX(0, x2 - x), MAX(0, y2 - y)); | |
268 | return result; | |
269 | } | |
270 | ||
271 | // Adapted from SDL_IntersectRect | |
272 | static inline NFont::Rectf rectIntersect(const NFont::Rectf& A, const NFont::Rectf& B) | |
273 | { | |
274 | NFont::Rectf result; | |
275 | float Amin, Amax, Bmin, Bmax; | |
276 | ||
277 | // Horizontal intersection | |
278 | Amin = A.x; | |
279 | Amax = Amin + A.w; | |
280 | Bmin = B.x; | |
281 | Bmax = Bmin + B.w; | |
282 | if(Bmin > Amin) | |
283 | Amin = Bmin; | |
284 | result.x = Amin; | |
285 | if(Bmax < Amax) | |
286 | Amax = Bmax; | |
287 | result.w = Amax - Amin > 0 ? Amax - Amin : 0; | |
288 | ||
289 | // Vertical intersection | |
290 | Amin = A.y; | |
291 | Amax = Amin + A.h; | |
292 | Bmin = B.y; | |
293 | Bmax = Bmin + B.h; | |
294 | if(Bmin > Amin) | |
295 | Amin = Bmin; | |
296 | result.y = Amin; | |
297 | if(Bmax < Amax) | |
298 | Amax = Bmax; | |
299 | result.h = Amax - Amin > 0 ? Amax - Amin : 0; | |
300 | ||
301 | return result; | |
302 | } | |
303 | ||
304 | static inline SDL_Surface* copySurface(SDL_Surface *Surface) | |
305 | { | |
306 | return SDL_ConvertSurface(Surface, Surface->format, Surface->flags); | |
307 | } | |
308 | ||
309 | ||
310 | ||
311 | ||
312 | ||
313 | ||
314 | ||
315 | ||
316 | ||
317 | NFont::Color::Color() | |
318 | : r(0), g(0), b(0), a(255) | |
319 | {} | |
320 | NFont::Color::Color(Uint8 r, Uint8 g, Uint8 b) | |
321 | : r(r), g(g), b(b), a(255) | |
322 | {} | |
323 | NFont::Color::Color(Uint8 r, Uint8 g, Uint8 b, Uint8 a) | |
324 | : r(r), g(g), b(b), a(a) | |
325 | {} | |
326 | NFont::Color::Color(const SDL_Color& color) | |
327 | : r(color.r), g(color.g), b(color.b), a(color.a) | |
328 | {} | |
329 | ||
330 | NFont::Color& NFont::Color::rgb(Uint8 R, Uint8 G, Uint8 B) | |
331 | { | |
332 | r = R; | |
333 | g = G; | |
334 | b = B; | |
335 | ||
336 | return *this; | |
337 | } | |
338 | ||
339 | NFont::Color& NFont::Color::rgba(Uint8 R, Uint8 G, Uint8 B, Uint8 A) | |
340 | { | |
341 | r = R; | |
342 | g = G; | |
343 | b = B; | |
344 | a = A; | |
345 | ||
346 | return *this; | |
347 | } | |
348 | ||
349 | NFont::Color& NFont::Color::color(const SDL_Color& color) | |
350 | { | |
351 | r = color.r; | |
352 | g = color.g; | |
353 | b = color.b; | |
354 | a = color.a; | |
355 | ||
356 | return *this; | |
357 | } | |
358 | ||
359 | SDL_Color NFont::Color::to_SDL_Color() const | |
360 | { | |
361 | SDL_Color c = {r, g, b, a}; | |
362 | return c; | |
363 | } | |
364 | ||
365 | ||
366 | ||
367 | ||
368 | NFont::Rectf::Rectf() | |
369 | : x(0), y(0), w(0), h(0) | |
370 | {} | |
371 | ||
372 | NFont::Rectf::Rectf(float x, float y) | |
373 | : x(x), y(y), w(0), h(0) | |
374 | {} | |
375 | ||
376 | NFont::Rectf::Rectf(float x, float y, float w, float h) | |
377 | : x(x), y(y), w(w), h(h) | |
378 | {} | |
379 | ||
380 | NFont::Rectf::Rectf(const SDL_Rect& rect) | |
381 | : x(rect.x), y(rect.y), w(rect.w), h(rect.h) | |
382 | {} | |
383 | ||
384 | #ifdef NFONT_USE_SDL_GPU | |
385 | NFont::Rectf::Rectf(const GPU_Rect& rect) | |
386 | : x(rect.x), y(rect.y), w(rect.w), h(rect.h) | |
387 | {} | |
388 | #endif | |
389 | ||
390 | SDL_Rect NFont::Rectf::to_SDL_Rect() const | |
391 | { | |
392 | SDL_Rect r = {int(x), int(y), int(w), int(h)}; | |
393 | return r; | |
394 | } | |
395 | ||
396 | #ifdef NFONT_USE_SDL_GPU | |
397 | GPU_Rect NFont::Rectf::to_GPU_Rect() const | |
398 | { | |
399 | return GPU_MakeRect(x, y, w, h); | |
400 | } | |
401 | #endif | |
402 | ||
403 | ||
404 | ||
405 | ||
406 | ||
407 | ||
408 | ||
409 | char* NFont::buffer = NULL; // Shared buffer for efficient drawing | |
410 | ||
411 | ||
412 | // Constructors | |
413 | NFont::NFont() | |
414 | { | |
415 | init(); | |
416 | } | |
417 | ||
418 | ||
419 | #ifdef NFONT_USE_SDL_GPU | |
420 | NFont::NFont(TTF_Font* ttf) | |
421 | { | |
422 | init(); | |
423 | load(ttf, FC_GetDefaultColor(font)); | |
424 | } | |
425 | NFont::NFont(TTF_Font* ttf, const NFont::Color& color) | |
426 | { | |
427 | init(); | |
428 | load(ttf, color); | |
429 | } | |
430 | NFont::NFont(const char* filename_ttf, Uint32 pointSize) | |
431 | { | |
432 | init(); | |
433 | load(filename_ttf, pointSize); | |
434 | } | |
435 | NFont::NFont(const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style) | |
436 | { | |
437 | init(); | |
438 | load(filename_ttf, pointSize, color, style); | |
439 | } | |
440 | NFont::NFont(SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style) | |
441 | { | |
442 | init(); | |
443 | load(file_rwops_ttf, own_rwops, pointSize, color, style); | |
444 | } | |
445 | ||
446 | #else | |
447 | ||
448 | NFont::NFont(NFont_Target* renderer, TTF_Font* ttf) | |
449 | { | |
450 | init(); | |
451 | load(renderer, ttf, FC_GetDefaultColor(font)); | |
452 | } | |
453 | NFont::NFont(NFont_Target* renderer, TTF_Font* ttf, const NFont::Color& color) | |
454 | { | |
455 | init(); | |
456 | load(renderer, ttf, color); | |
457 | } | |
458 | NFont::NFont(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize) | |
459 | { | |
460 | init(); | |
461 | load(renderer, filename_ttf, pointSize); | |
462 | } | |
463 | NFont::NFont(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style) | |
464 | { | |
465 | init(); | |
466 | load(renderer, filename_ttf, pointSize, color, style); | |
467 | } | |
468 | NFont::NFont(NFont_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style) | |
469 | { | |
470 | init(); | |
471 | load(renderer, file_rwops_ttf, own_rwops, pointSize, color, style); | |
472 | } | |
473 | #endif | |
474 | ||
475 | ||
476 | NFont::~NFont() | |
477 | { | |
478 | FC_FreeFont(font); | |
479 | } | |
480 | ||
481 | void NFont::init() | |
482 | { | |
483 | font = FC_CreateFont(); | |
484 | ||
485 | if(buffer == NULL) | |
486 | buffer = new char[NFONT_BUFFER_SIZE]; | |
487 | } | |
488 | ||
489 | ||
490 | ||
491 | ||
492 | ||
493 | ||
494 | void NFont::setLoadingString(const char* str) | |
495 | { | |
496 | FC_SetLoadingString(font, str); | |
497 | } | |
498 | ||
499 | #ifdef NFONT_USE_SDL_GPU | |
500 | bool NFont::load(TTF_Font* ttf) | |
501 | #else | |
502 | bool NFont::load(NFont_Target* renderer, TTF_Font* ttf) | |
503 | #endif | |
504 | { | |
505 | #ifdef NFONT_USE_SDL_GPU | |
506 | return load(ttf, FC_GetDefaultColor(font)); | |
507 | #else | |
508 | return load(renderer, ttf, Color(0,0,0,255)); | |
509 | #endif | |
510 | } | |
511 | ||
512 | #ifdef NFONT_USE_SDL_GPU | |
513 | bool NFont::load(TTF_Font* ttf, const NFont::Color& color) | |
514 | #else | |
515 | bool NFont::load(NFont_Target* renderer, TTF_Font* ttf, const NFont::Color& color) | |
516 | #endif | |
517 | { | |
518 | if(ttf == NULL) | |
519 | return false; | |
520 | ||
521 | #ifndef NFONT_USE_SDL_GPU | |
522 | if(renderer == NULL) | |
523 | return false; | |
524 | #endif | |
525 | ||
526 | FC_ClearFont(font); | |
527 | #ifdef NFONT_USE_SDL_GPU | |
528 | return FC_LoadFontFromTTF(font, ttf, color.to_SDL_Color()); | |
529 | #else | |
530 | return FC_LoadFontFromTTF(font, renderer, ttf, color.to_SDL_Color()); | |
531 | #endif | |
532 | } | |
533 | ||
534 | #ifdef NFONT_USE_SDL_GPU | |
535 | bool NFont::load(const char* filename_ttf, Uint32 pointSize) | |
536 | #else | |
537 | bool NFont::load(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize) | |
538 | #endif | |
539 | { | |
540 | #ifdef NFONT_USE_SDL_GPU | |
541 | return load(filename_ttf, pointSize, Color(0,0,0,255)); | |
542 | #else | |
543 | return load(renderer, filename_ttf, pointSize, Color(0,0,0,255)); | |
544 | #endif | |
545 | } | |
546 | ||
547 | #ifdef NFONT_USE_SDL_GPU | |
548 | bool NFont::load(const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style) | |
549 | #else | |
550 | bool NFont::load(NFont_Target* renderer, const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style) | |
551 | #endif | |
552 | { | |
553 | FC_ClearFont(font); | |
554 | #ifdef NFONT_USE_SDL_GPU | |
555 | return FC_LoadFont(font, filename_ttf, pointSize, color.to_SDL_Color(), style); | |
556 | #else | |
557 | return FC_LoadFont(font, renderer, filename_ttf, pointSize, color.to_SDL_Color(), style); | |
558 | #endif | |
559 | } | |
560 | ||
561 | #ifdef NFONT_USE_SDL_GPU | |
562 | bool NFont::load(SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style) | |
563 | #else | |
564 | bool NFont::load(NFont_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style) | |
565 | #endif | |
566 | { | |
567 | FC_ClearFont(font); | |
568 | #ifdef NFONT_USE_SDL_GPU | |
569 | return FC_LoadFont_RW(font, file_rwops_ttf, own_rwops, pointSize, color.to_SDL_Color(), style); | |
570 | #else | |
571 | return FC_LoadFont_RW(font, renderer, file_rwops_ttf, own_rwops, pointSize, color.to_SDL_Color(), style); | |
572 | #endif | |
573 | } | |
574 | ||
575 | ||
576 | ||
577 | void NFont::free() | |
578 | { | |
579 | FC_ClearFont(font); | |
580 | } | |
581 | ||
582 | ||
583 | ||
584 | NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const char* formatted_text, ...) | |
585 | { | |
586 | if(formatted_text == NULL) | |
587 | return Rectf(x, y, 0, 0); | |
588 | ||
589 | va_list lst; | |
590 | va_start(lst, formatted_text); | |
591 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
592 | va_end(lst); | |
593 | ||
594 | return FC_Draw(font, dest, x, y, "%s", buffer); | |
595 | } | |
596 | ||
597 | /*static int getIndexPastWidth(const char* text, int width, const int* charWidth) | |
598 | { | |
599 | int charnum; | |
600 | int len = strlen(text); | |
601 | ||
602 | for (int index = 0; index < len; index++) | |
603 | { | |
604 | char c = text[index]; | |
605 | charnum = (unsigned char)(c) - 33; | |
606 | ||
607 | // spaces and nonprintable characters | |
608 | if (c == ' ' || charnum > 222) | |
609 | { | |
610 | width -= charWidth[0]; | |
611 | } | |
612 | else | |
613 | width -= charWidth[charnum]; | |
614 | ||
615 | if(width <= 0) | |
616 | return index; | |
617 | } | |
618 | return 0; | |
619 | }*/ | |
620 | ||
621 | ||
622 | ||
623 | /*static list<string> explode(const string& str, char delimiter) | |
624 | { | |
625 | list<string> result; | |
626 | ||
627 | size_t oldPos = 0; | |
628 | size_t pos = str.find_first_of(delimiter); | |
629 | while(pos != string::npos) | |
630 | { | |
631 | result.push_back(str.substr(oldPos, pos - oldPos)); | |
632 | oldPos = pos+1; | |
633 | pos = str.find_first_of(delimiter, oldPos); | |
634 | } | |
635 | ||
636 | result.push_back(str.substr(oldPos, string::npos)); | |
637 | ||
638 | return result; | |
639 | }*/ | |
640 | ||
641 | NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const char* formatted_text, ...) | |
642 | { | |
643 | if(formatted_text == NULL) | |
644 | return Rectf(box.x, box.y, 0, 0); | |
645 | ||
646 | va_list lst; | |
647 | va_start(lst, formatted_text); | |
648 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
649 | va_end(lst); | |
650 | ||
651 | #ifdef NFONT_USE_SDL_GPU | |
652 | return FC_DrawBox(font, dest, box.to_GPU_Rect(), "%s", buffer); | |
653 | #else | |
654 | return FC_DrawBox(font, dest, box.to_SDL_Rect(), "%s", buffer); | |
655 | #endif | |
656 | } | |
657 | ||
658 | static FC_AlignEnum translate_enum_NFont_to_FC(NFont::AlignEnum align) | |
659 | { | |
660 | switch(align) | |
661 | { | |
662 | case NFont::LEFT: | |
663 | return FC_ALIGN_LEFT; | |
664 | case NFont::CENTER: | |
665 | return FC_ALIGN_CENTER; | |
666 | case NFont::RIGHT: | |
667 | return FC_ALIGN_RIGHT; | |
668 | default: | |
669 | return FC_ALIGN_LEFT; | |
670 | } | |
671 | } | |
672 | ||
673 | NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, AlignEnum align, const char* formatted_text, ...) | |
674 | { | |
675 | if(formatted_text == NULL) | |
676 | return Rectf(box.x, box.y, 0, 0); | |
677 | ||
678 | va_list lst; | |
679 | va_start(lst, formatted_text); | |
680 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
681 | va_end(lst); | |
682 | ||
683 | #ifdef NFONT_USE_SDL_GPU | |
684 | return FC_DrawBoxAlign(font, dest, box.to_GPU_Rect(), translate_enum_NFont_to_FC(align), "%s", buffer); | |
685 | #else | |
686 | return FC_DrawBoxAlign(font, dest, box.to_SDL_Rect(), translate_enum_NFont_to_FC(align), "%s", buffer); | |
687 | #endif | |
688 | } | |
689 | ||
690 | NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const Scale& scale, const char* formatted_text, ...) | |
691 | { | |
692 | if(formatted_text == NULL) | |
693 | return Rectf(box.x, box.y, 0, 0); | |
694 | ||
695 | va_list lst; | |
696 | va_start(lst, formatted_text); | |
697 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
698 | va_end(lst); | |
699 | ||
700 | #ifdef NFONT_USE_SDL_GPU | |
701 | return FC_DrawBoxScale(font, dest, box.to_GPU_Rect(), FC_MakeScale(scale.x, scale.y), "%s", buffer); | |
702 | #else | |
703 | return FC_DrawBoxScale(font, dest, box.to_SDL_Rect(), FC_MakeScale(scale.x, scale.y), "%s", buffer); | |
704 | #endif | |
705 | } | |
706 | ||
707 | NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const Color& color, const char* formatted_text, ...) | |
708 | { | |
709 | if(formatted_text == NULL) | |
710 | return Rectf(box.x, box.y, 0, 0); | |
711 | ||
712 | va_list lst; | |
713 | va_start(lst, formatted_text); | |
714 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
715 | va_end(lst); | |
716 | ||
717 | #ifdef NFONT_USE_SDL_GPU | |
718 | return FC_DrawBoxColor(font, dest, box.to_GPU_Rect(), color.to_SDL_Color(), "%s", buffer); | |
719 | #else | |
720 | return FC_DrawBoxColor(font, dest, box.to_SDL_Rect(), color.to_SDL_Color(), "%s", buffer); | |
721 | #endif | |
722 | } | |
723 | ||
724 | NFont::Rectf NFont::drawBox(NFont_Target* dest, const Rectf& box, const Effect& effect, const char* formatted_text, ...) | |
725 | { | |
726 | if(formatted_text == NULL) | |
727 | return Rectf(box.x, box.y, 0, 0); | |
728 | ||
729 | va_list lst; | |
730 | va_start(lst, formatted_text); | |
731 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
732 | va_end(lst); | |
733 | ||
734 | #ifdef NFONT_USE_SDL_GPU | |
735 | return FC_DrawBoxEffect(font, dest, box.to_GPU_Rect(), FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer); | |
736 | #else | |
737 | return FC_DrawBoxEffect(font, dest, box.to_SDL_Rect(), FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer); | |
738 | #endif | |
739 | } | |
740 | ||
741 | NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...) | |
742 | { | |
743 | if(formatted_text == NULL) | |
744 | return Rectf(x, y, 0, 0); | |
745 | ||
746 | va_list lst; | |
747 | va_start(lst, formatted_text); | |
748 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
749 | va_end(lst); | |
750 | ||
751 | return FC_DrawColumn(font, dest, x, y, width, "%s", buffer); | |
752 | } | |
753 | ||
754 | NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, AlignEnum align, const char* formatted_text, ...) | |
755 | { | |
756 | if(formatted_text == NULL) | |
757 | return Rectf(x, y, 0, 0); | |
758 | ||
759 | va_list lst; | |
760 | va_start(lst, formatted_text); | |
761 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
762 | va_end(lst); | |
763 | ||
764 | return FC_DrawColumnAlign(font, dest, x, y, width, translate_enum_NFont_to_FC(align), "%s", buffer); | |
765 | } | |
766 | ||
767 | NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const Scale& scale, const char* formatted_text, ...) | |
768 | { | |
769 | if(formatted_text == NULL) | |
770 | return Rectf(x, y, 0, 0); | |
771 | ||
772 | va_list lst; | |
773 | va_start(lst, formatted_text); | |
774 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
775 | va_end(lst); | |
776 | ||
777 | return FC_DrawColumnScale(font, dest, x, y, width, FC_MakeScale(scale.x, scale.y), "%s", buffer); | |
778 | } | |
779 | ||
780 | NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const Color& color, const char* formatted_text, ...) | |
781 | { | |
782 | if(formatted_text == NULL) | |
783 | return Rectf(x, y, 0, 0); | |
784 | ||
785 | va_list lst; | |
786 | va_start(lst, formatted_text); | |
787 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
788 | va_end(lst); | |
789 | ||
790 | return FC_DrawColumnColor(font, dest, x, y, width, color.to_SDL_Color(), "%s", buffer); | |
791 | } | |
792 | ||
793 | NFont::Rectf NFont::drawColumn(NFont_Target* dest, float x, float y, Uint16 width, const Effect& effect, const char* formatted_text, ...) | |
794 | { | |
795 | if(formatted_text == NULL) | |
796 | return Rectf(x, y, 0, 0); | |
797 | ||
798 | va_list lst; | |
799 | va_start(lst, formatted_text); | |
800 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
801 | va_end(lst); | |
802 | ||
803 | #ifdef NFONT_USE_SDL_GPU | |
804 | return FC_DrawColumnEffect(font, dest, x, y, width, FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer); | |
805 | #else | |
806 | return FC_DrawColumnEffect(font, dest, x, y, width, FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer); | |
807 | #endif | |
808 | } | |
809 | ||
810 | ||
811 | ||
812 | NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const Scale& scale, const char* formatted_text, ...) | |
813 | { | |
814 | if(formatted_text == NULL) | |
815 | return Rectf(x, y, 0, 0); | |
816 | ||
817 | va_list lst; | |
818 | va_start(lst, formatted_text); | |
819 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
820 | va_end(lst); | |
821 | ||
822 | return FC_DrawScale(font, dest, x, y, FC_MakeScale(scale.x, scale.y), "%s", buffer); | |
823 | } | |
824 | ||
825 | NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, AlignEnum align, const char* formatted_text, ...) | |
826 | { | |
827 | if(formatted_text == NULL) | |
828 | return Rectf(x, y, 0, 0); | |
829 | ||
830 | va_list lst; | |
831 | va_start(lst, formatted_text); | |
832 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
833 | va_end(lst); | |
834 | ||
835 | return FC_DrawAlign(font, dest, x, y, translate_enum_NFont_to_FC(align), "%s", buffer); | |
836 | } | |
837 | ||
838 | NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const Color& color, const char* formatted_text, ...) | |
839 | { | |
840 | if(formatted_text == NULL) | |
841 | return Rectf(x, y, 0, 0); | |
842 | ||
843 | va_list lst; | |
844 | va_start(lst, formatted_text); | |
845 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
846 | va_end(lst); | |
847 | ||
848 | return FC_DrawColor(font, dest, x, y, color.to_SDL_Color(), "%s", buffer); | |
849 | } | |
850 | ||
851 | ||
852 | NFont::Rectf NFont::draw(NFont_Target* dest, float x, float y, const Effect& effect, const char* formatted_text, ...) | |
853 | { | |
854 | if(formatted_text == NULL) | |
855 | return Rectf(x, y, 0, 0); | |
856 | ||
857 | va_list lst; | |
858 | va_start(lst, formatted_text); | |
859 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
860 | va_end(lst); | |
861 | ||
862 | return FC_DrawEffect(font, dest, x, y, FC_MakeEffect(translate_enum_NFont_to_FC(effect.alignment), FC_MakeScale(effect.scale.x, effect.scale.y), effect.color.to_SDL_Color()), "%s", buffer); | |
863 | } | |
864 | ||
865 | ||
866 | ||
867 | ||
868 | // Getters | |
869 | ||
870 | ||
871 | NFont::FilterEnum NFont::getFilterMode() const | |
872 | { | |
873 | FC_FilterEnum f = FC_GetFilterMode(font); | |
874 | if(f == FC_FILTER_LINEAR) | |
875 | return NFont::LINEAR; | |
876 | return NFont::NEAREST; | |
877 | } | |
878 | ||
879 | Uint16 NFont::getHeight() const | |
880 | { | |
881 | return FC_GetLineHeight(font); | |
882 | } | |
883 | ||
884 | Uint16 NFont::getHeight(const char* formatted_text, ...) const | |
885 | { | |
886 | if(formatted_text == NULL) | |
887 | return 0; | |
888 | ||
889 | va_list lst; | |
890 | va_start(lst, formatted_text); | |
891 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
892 | va_end(lst); | |
893 | ||
894 | return FC_GetHeight(font, "%s", buffer); | |
895 | } | |
896 | ||
897 | Uint16 NFont::getWidth(const char* formatted_text, ...) | |
898 | { | |
899 | if (formatted_text == NULL) | |
900 | return 0; | |
901 | ||
902 | va_list lst; | |
903 | va_start(lst, formatted_text); | |
904 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
905 | va_end(lst); | |
906 | ||
907 | return FC_GetWidth(font, "%s", buffer); | |
908 | } | |
909 | ||
910 | ||
911 | NFont::Rectf NFont::getCharacterOffset(Uint16 position_index, int column_width, const char* formatted_text, ...) | |
912 | { | |
913 | if(formatted_text == NULL) | |
914 | return Rectf(0,0,0,0); | |
915 | ||
916 | va_list lst; | |
917 | va_start(lst, formatted_text); | |
918 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
919 | va_end(lst); | |
920 | ||
921 | return FC_GetCharacterOffset(font, position_index, column_width, "%s", buffer); | |
922 | } | |
923 | ||
924 | // Given an offset (x,y) from the text draw position (the upper-left corner), returns the character position (UTF-8 index) | |
925 | Uint16 NFont::getPositionFromOffset(float x, float y, int column_width, NFont::AlignEnum align, const char* formatted_text, ...) | |
926 | { | |
927 | if(formatted_text == NULL) | |
928 | return 0; | |
929 | ||
930 | va_list lst; | |
931 | va_start(lst, formatted_text); | |
932 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
933 | va_end(lst); | |
934 | ||
935 | return FC_GetPositionFromOffset(font, x, y, column_width, translate_enum_NFont_to_FC(align), "%s", buffer); | |
936 | } | |
937 | ||
938 | ||
939 | Uint16 NFont::getColumnHeight(Uint16 width, const char* formatted_text, ...) | |
940 | { | |
941 | if(formatted_text == NULL || width == 0) | |
942 | return 0; | |
943 | ||
944 | va_list lst; | |
945 | va_start(lst, formatted_text); | |
946 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
947 | va_end(lst); | |
948 | ||
949 | return FC_GetColumnHeight(font, width, "%s", buffer); | |
950 | } | |
951 | ||
952 | int NFont::getAscent(const char character) | |
953 | { | |
954 | return FC_GetAscent(font, "%c", character); | |
955 | } | |
956 | ||
957 | int NFont::getAscent() const | |
958 | { | |
959 | return FC_GetAscent(font, NULL); | |
960 | } | |
961 | ||
962 | int NFont::getAscent(const char* formatted_text, ...) | |
963 | { | |
964 | if(formatted_text == NULL) | |
965 | return FC_GetAscent(font, NULL); | |
966 | ||
967 | va_list lst; | |
968 | va_start(lst, formatted_text); | |
969 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
970 | va_end(lst); | |
971 | ||
972 | return FC_GetAscent(font, "%s", buffer); | |
973 | } | |
974 | ||
975 | int NFont::getDescent(const char character) | |
976 | { | |
977 | return FC_GetDescent(font, "%c", character); | |
978 | } | |
979 | ||
980 | int NFont::getDescent() const | |
981 | { | |
982 | return FC_GetDescent(font, NULL); | |
983 | } | |
984 | ||
985 | int NFont::getDescent(const char* formatted_text, ...) | |
986 | { | |
987 | if(formatted_text == NULL) | |
988 | return FC_GetDescent(font, NULL); | |
989 | ||
990 | va_list lst; | |
991 | va_start(lst, formatted_text); | |
992 | vsnprintf(buffer, NFONT_BUFFER_SIZE, formatted_text, lst); | |
993 | va_end(lst); | |
994 | ||
995 | return FC_GetDescent(font, "%s", buffer); | |
996 | } | |
997 | ||
998 | int NFont::getSpacing() const | |
999 | { | |
1000 | return FC_GetSpacing(font); | |
1001 | } | |
1002 | ||
1003 | int NFont::getLineSpacing() const | |
1004 | { | |
1005 | return FC_GetLineSpacing(font); | |
1006 | } | |
1007 | ||
1008 | Uint16 NFont::getBaseline() const | |
1009 | { | |
1010 | return FC_GetBaseline(font); | |
1011 | } | |
1012 | ||
1013 | Uint16 NFont::getMaxWidth() const | |
1014 | { | |
1015 | return FC_GetMaxWidth(font); | |
1016 | } | |
1017 | ||
1018 | NFont::Color NFont::getDefaultColor() const | |
1019 | { | |
1020 | return FC_GetDefaultColor(font); | |
1021 | } | |
1022 | ||
1023 | ||
1024 | int NFont::getNumCacheLevels() const | |
1025 | { | |
1026 | return FC_GetNumCacheLevels(font); | |
1027 | } | |
1028 | ||
1029 | NFont_Image* NFont::getCacheLevel(int level) const | |
1030 | { | |
1031 | return FC_GetGlyphCacheLevel(font, level); | |
1032 | } | |
1033 | ||
1034 | ||
1035 | ||
1036 | ||
1037 | ||
1038 | // Setters | |
1039 | ||
1040 | void NFont::setFilterMode(NFont::FilterEnum filter) | |
1041 | { | |
1042 | if(filter == NFont::LINEAR) | |
1043 | FC_SetFilterMode(font, FC_FILTER_LINEAR); | |
1044 | else | |
1045 | FC_SetFilterMode(font, FC_FILTER_NEAREST); | |
1046 | } | |
1047 | ||
1048 | void NFont::setSpacing(int LetterSpacing) | |
1049 | { | |
1050 | FC_SetSpacing(font, LetterSpacing); | |
1051 | } | |
1052 | ||
1053 | void NFont::setLineSpacing(int LineSpacing) | |
1054 | { | |
1055 | FC_SetLineSpacing(font, LineSpacing); | |
1056 | } | |
1057 | ||
1058 | void NFont::setBaseline() | |
1059 | { | |
1060 | ||
1061 | } | |
1062 | ||
1063 | void NFont::setDefaultColor(const Color& color) | |
1064 | { | |
1065 | FC_SetDefaultColor(font, color.to_SDL_Color()); | |
1066 | } | |
1067 | ||
1068 | void NFont::enableTTFOwnership() | |
1069 | { | |
1070 | ||
1071 | } | |
1072 | ||
1073 | ||
1074 | ||
1075 |
0 | /* | |
1 | NFont v5.0.0: A font class for SDL and SDL_Renderer | |
2 | by Jonathan Dearborn | |
3 | Dedicated to the memory of Florian Hufsky | |
4 | ||
5 | License: | |
6 | The short: | |
7 | Use it however you'd like, but keep the copyright and license notice | |
8 | whenever these files or parts of them are distributed in uncompiled form. | |
9 | ||
10 | The long: | |
11 | Copyright (c) 2016 Jonathan Dearborn | |
12 | ||
13 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
14 | of this software and associated documentation files (the "Software"), to deal | |
15 | in the Software without restriction, including without limitation the rights | |
16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
17 | copies of the Software, and to permit persons to whom the Software is | |
18 | furnished to do so, subject to the following conditions: | |
19 | ||
20 | The above copyright notice and this permission notice shall be included in | |
21 | all copies or substantial portions of the Software. | |
22 | ||
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
29 | THE SOFTWARE. | |
30 | */ | |
31 | ||
32 | #ifndef _NFONT_H__ | |
33 | #define _NFONT_H__ | |
34 | ||
35 | #include "SDL.h" | |
36 | ||
37 | #if defined(FC_USE_SDL_GPU) && !defined(NFONT_USE_SDL_GPU) | |
38 | #define NFONT_USE_SDL_GPU | |
39 | #endif | |
40 | ||
41 | #ifdef NFONT_USE_SDL_GPU | |
42 | #include "SDL_gpu.h" | |
43 | #endif | |
44 | ||
45 | #ifndef NFONT_FORMAT | |
46 | ||
47 | #if ( (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) ) | |
48 | #define NFONT_FORMAT(X) __attribute__ ((format (printf, X, X+1))) | |
49 | #else | |
50 | #define NFONT_FORMAT(X) | |
51 | #endif | |
52 | ||
53 | #endif | |
54 | ||
55 | #include "stdarg.h" | |
56 | ||
57 | // Let's pretend this exists... | |
58 | #ifndef TTF_STYLE_OUTLINE | |
59 | #define TTF_STYLE_OUTLINE 16 | |
60 | #endif | |
61 | ||
62 | struct FC_Font; | |
63 | ||
64 | typedef struct _TTF_Font TTF_Font; | |
65 | ||
66 | // Differences between SDL_Renderer and SDL_gpu | |
67 | #ifdef NFONT_USE_SDL_GPU | |
68 | #define NFont_Image GPU_Image | |
69 | #else | |
70 | #define NFont_Image SDL_Texture | |
71 | #endif | |
72 | ||
73 | #if defined(NFONT_DLL) || defined(NFONT_DLL_EXPORT) | |
74 | #ifdef NFONT_DLL_EXPORT | |
75 | #define NFONT_EXPORT __declspec(dllexport) | |
76 | #else | |
77 | #define NFONT_EXPORT __declspec(dllimport) | |
78 | #endif | |
79 | #else | |
80 | #define NFONT_EXPORT | |
81 | #endif | |
82 | ||
83 | class NFONT_EXPORT NFont | |
84 | { | |
85 | public: | |
86 | ||
87 | class NFONT_EXPORT Color | |
88 | { | |
89 | public: | |
90 | ||
91 | Uint8 r, g, b, a; | |
92 | ||
93 | Color(); | |
94 | Color(Uint8 r, Uint8 g, Uint8 b); | |
95 | Color(Uint8 r, Uint8 g, Uint8 b, Uint8 a); | |
96 | Color(const SDL_Color& color); | |
97 | ||
98 | Color& rgb(Uint8 R, Uint8 G, Uint8 B); | |
99 | Color& rgba(Uint8 R, Uint8 G, Uint8 B, Uint8 A); | |
100 | Color& color(const SDL_Color& color); | |
101 | ||
102 | SDL_Color to_SDL_Color() const; | |
103 | }; | |
104 | ||
105 | class NFONT_EXPORT Rectf | |
106 | { | |
107 | public: | |
108 | float x, y; | |
109 | float w, h; | |
110 | ||
111 | Rectf(); | |
112 | Rectf(float x, float y); | |
113 | Rectf(float x, float y, float w, float h); | |
114 | Rectf(const SDL_Rect& rect); | |
115 | ||
116 | SDL_Rect to_SDL_Rect() const; | |
117 | ||
118 | #ifdef NFONT_USE_SDL_GPU | |
119 | Rectf(const GPU_Rect& rect); | |
120 | GPU_Rect to_GPU_Rect() const; | |
121 | #endif | |
122 | }; | |
123 | ||
124 | ||
125 | enum AlignEnum {LEFT, CENTER, RIGHT}; | |
126 | enum FilterEnum {NEAREST, LINEAR}; | |
127 | ||
128 | class NFONT_EXPORT Scale | |
129 | { | |
130 | public: | |
131 | ||
132 | float x; | |
133 | float y; | |
134 | ||
135 | enum ScaleTypeEnum {NEAREST}; | |
136 | ScaleTypeEnum type; | |
137 | ||
138 | Scale() | |
139 | : x(1.0f), y(1.0f), type(NEAREST) | |
140 | {} | |
141 | Scale(float xy) | |
142 | : x(xy), y(xy), type(NEAREST) | |
143 | {} | |
144 | Scale(float xy, ScaleTypeEnum type) | |
145 | : x(xy), y(xy), type(type) | |
146 | {} | |
147 | Scale(float x, float y) | |
148 | : x(x), y(y), type(NEAREST) | |
149 | {} | |
150 | Scale(float x, float y, ScaleTypeEnum type) | |
151 | : x(x), y(y), type(type) | |
152 | {} | |
153 | }; | |
154 | ||
155 | class NFONT_EXPORT Effect | |
156 | { | |
157 | public: | |
158 | AlignEnum alignment; | |
159 | Scale scale; | |
160 | bool use_color; | |
161 | Color color; | |
162 | ||
163 | Effect() | |
164 | : alignment(LEFT), use_color(false), color(255, 255, 255, 255) | |
165 | {} | |
166 | ||
167 | Effect(const Scale& scale) | |
168 | : alignment(LEFT), scale(scale), use_color(false), color(255, 255, 255, 255) | |
169 | {} | |
170 | Effect(AlignEnum alignment) | |
171 | : alignment(alignment), use_color(false), color(255, 255, 255, 255) | |
172 | {} | |
173 | Effect(const Color& color) | |
174 | : alignment(LEFT), use_color(true), color(color) | |
175 | {} | |
176 | ||
177 | Effect(AlignEnum alignment, const Scale& scale) | |
178 | : alignment(alignment), scale(scale), use_color(false), color(255, 255, 255, 255) | |
179 | {} | |
180 | Effect(AlignEnum alignment, const Color& color) | |
181 | : alignment(alignment), use_color(true), color(color) | |
182 | {} | |
183 | Effect(const Scale& scale, const Color& color) | |
184 | : alignment(LEFT), scale(scale), use_color(true), color(color) | |
185 | {} | |
186 | Effect(AlignEnum alignment, const Scale& scale, const Color& color) | |
187 | : alignment(alignment), scale(scale), use_color(true), color(color) | |
188 | {} | |
189 | }; | |
190 | ||
191 | ||
192 | // Constructors | |
193 | NFont(); | |
194 | NFont(const NFont& font); | |
195 | #ifdef NFONT_USE_SDL_GPU | |
196 | NFont(SDL_Surface* src); | |
197 | NFont(TTF_Font* ttf); | |
198 | NFont(TTF_Font* ttf, const NFont::Color& color); | |
199 | NFont(const char* filename_ttf, Uint32 pointSize); | |
200 | NFont(const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style = 0); | |
201 | NFont(SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style = 0); | |
202 | #else | |
203 | NFont(SDL_Renderer* renderer, SDL_Surface* src); | |
204 | NFont(SDL_Renderer* renderer, TTF_Font* ttf); | |
205 | NFont(SDL_Renderer* renderer, TTF_Font* ttf, const NFont::Color& color); | |
206 | NFont(SDL_Renderer* renderer, const char* filename_ttf, Uint32 pointSize); | |
207 | NFont(SDL_Renderer* renderer, const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style = 0); | |
208 | NFont(SDL_Renderer* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style = 0); | |
209 | #endif | |
210 | ||
211 | ~NFont(); | |
212 | ||
213 | NFont& operator=(const NFont& font); | |
214 | ||
215 | // Loading | |
216 | void setLoadingString(const char* str); | |
217 | ||
218 | #ifdef NFONT_USE_SDL_GPU | |
219 | bool load(SDL_Surface* FontSurface); | |
220 | bool load(TTF_Font* ttf); | |
221 | bool load(TTF_Font* ttf, const NFont::Color& color); | |
222 | bool load(const char* filename_ttf, Uint32 pointSize); | |
223 | bool load(const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style = 0); | |
224 | bool load(SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style = 0); | |
225 | #else | |
226 | bool load(SDL_Renderer* renderer, SDL_Surface* FontSurface); | |
227 | bool load(SDL_Renderer* renderer, TTF_Font* ttf); | |
228 | bool load(SDL_Renderer* renderer, TTF_Font* ttf, const NFont::Color& color); | |
229 | bool load(SDL_Renderer* renderer, const char* filename_ttf, Uint32 pointSize); | |
230 | bool load(SDL_Renderer* renderer, const char* filename_ttf, Uint32 pointSize, const NFont::Color& color, int style = 0); | |
231 | bool load(SDL_Renderer* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, const NFont::Color& color, int style = 0); | |
232 | #endif | |
233 | ||
234 | void free(); | |
235 | ||
236 | // Drawing | |
237 | #ifdef NFONT_USE_SDL_GPU | |
238 | Rectf draw(GPU_Target* dest, float x, float y, const char* formatted_text, ...) NFONT_FORMAT(5); | |
239 | Rectf draw(GPU_Target* dest, float x, float y, AlignEnum align, const char* formatted_text, ...) NFONT_FORMAT(6); | |
240 | Rectf draw(GPU_Target* dest, float x, float y, const Scale& scale, const char* formatted_text, ...) NFONT_FORMAT(6); | |
241 | Rectf draw(GPU_Target* dest, float x, float y, const Color& color, const char* formatted_text, ...) NFONT_FORMAT(6); | |
242 | Rectf draw(GPU_Target* dest, float x, float y, const Effect& effect, const char* formatted_text, ...) NFONT_FORMAT(6); | |
243 | ||
244 | Rectf drawBox(GPU_Target* dest, const Rectf& box, const char* formatted_text, ...) NFONT_FORMAT(4); | |
245 | Rectf drawBox(GPU_Target* dest, const Rectf& box, AlignEnum align, const char* formatted_text, ...) NFONT_FORMAT(5); | |
246 | Rectf drawBox(GPU_Target* dest, const Rectf& box, const Scale& scale, const char* formatted_text, ...) NFONT_FORMAT(5); | |
247 | Rectf drawBox(GPU_Target* dest, const Rectf& box, const Color& color, const char* formatted_text, ...) NFONT_FORMAT(5); | |
248 | Rectf drawBox(GPU_Target* dest, const Rectf& box, const Effect& effect, const char* formatted_text, ...) NFONT_FORMAT(5); | |
249 | ||
250 | Rectf drawColumn(GPU_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...) NFONT_FORMAT(6); | |
251 | Rectf drawColumn(GPU_Target* dest, float x, float y, Uint16 width, AlignEnum align, const char* formatted_text, ...) NFONT_FORMAT(7); | |
252 | Rectf drawColumn(GPU_Target* dest, float x, float y, Uint16 width, const Scale& scale, const char* formatted_text, ...) NFONT_FORMAT(7); | |
253 | Rectf drawColumn(GPU_Target* dest, float x, float y, Uint16 width, const Color& color, const char* formatted_text, ...) NFONT_FORMAT(7); | |
254 | Rectf drawColumn(GPU_Target* dest, float x, float y, Uint16 width, const Effect& effect, const char* formatted_text, ...) NFONT_FORMAT(7); | |
255 | #else | |
256 | Rectf draw(SDL_Renderer* dest, float x, float y, const char* formatted_text, ...) NFONT_FORMAT(5); | |
257 | Rectf draw(SDL_Renderer* dest, float x, float y, AlignEnum align, const char* formatted_text, ...) NFONT_FORMAT(6); | |
258 | Rectf draw(SDL_Renderer* dest, float x, float y, const Scale& scale, const char* formatted_text, ...) NFONT_FORMAT(6); | |
259 | Rectf draw(SDL_Renderer* dest, float x, float y, const Color& color, const char* formatted_text, ...) NFONT_FORMAT(6); | |
260 | Rectf draw(SDL_Renderer* dest, float x, float y, const Effect& effect, const char* formatted_text, ...) NFONT_FORMAT(6); | |
261 | ||
262 | Rectf drawBox(SDL_Renderer* dest, const Rectf& box, const char* formatted_text, ...) NFONT_FORMAT(4); | |
263 | Rectf drawBox(SDL_Renderer* dest, const Rectf& box, AlignEnum align, const char* formatted_text, ...) NFONT_FORMAT(5); | |
264 | Rectf drawBox(SDL_Renderer* dest, const Rectf& box, const Scale& scale, const char* formatted_text, ...) NFONT_FORMAT(5); | |
265 | Rectf drawBox(SDL_Renderer* dest, const Rectf& box, const Color& color, const char* formatted_text, ...) NFONT_FORMAT(5); | |
266 | Rectf drawBox(SDL_Renderer* dest, const Rectf& box, const Effect& effect, const char* formatted_text, ...) NFONT_FORMAT(5); | |
267 | ||
268 | Rectf drawColumn(SDL_Renderer* dest, float x, float y, Uint16 width, const char* formatted_text, ...) NFONT_FORMAT(6); | |
269 | Rectf drawColumn(SDL_Renderer* dest, float x, float y, Uint16 width, AlignEnum align, const char* formatted_text, ...) NFONT_FORMAT(7); | |
270 | Rectf drawColumn(SDL_Renderer* dest, float x, float y, Uint16 width, const Scale& scale, const char* formatted_text, ...) NFONT_FORMAT(7); | |
271 | Rectf drawColumn(SDL_Renderer* dest, float x, float y, Uint16 width, const Color& color, const char* formatted_text, ...) NFONT_FORMAT(7); | |
272 | Rectf drawColumn(SDL_Renderer* dest, float x, float y, Uint16 width, const Effect& effect, const char* formatted_text, ...) NFONT_FORMAT(7); | |
273 | #endif | |
274 | ||
275 | // Getters | |
276 | FilterEnum getFilterMode() const; | |
277 | Uint16 getHeight() const; | |
278 | Uint16 getHeight(const char* formatted_text, ...) const NFONT_FORMAT(2); | |
279 | Uint16 getWidth(const char* formatted_text, ...) NFONT_FORMAT(2); | |
280 | Rectf getCharacterOffset(Uint16 position_index, int column_width, const char* formatted_text, ...) NFONT_FORMAT(4); | |
281 | Uint16 getPositionFromOffset(float x, float y, int column_width, NFont::AlignEnum align, const char* formatted_text, ...) NFONT_FORMAT(6); | |
282 | Uint16 getColumnHeight(Uint16 width, const char* formatted_text, ...) NFONT_FORMAT(3); | |
283 | int getSpacing() const; | |
284 | int getLineSpacing() const; | |
285 | Uint16 getBaseline() const; | |
286 | int getAscent() const; | |
287 | int getAscent(const char character); | |
288 | int getAscent(const char* formatted_text, ...) NFONT_FORMAT(2); | |
289 | int getDescent() const; | |
290 | int getDescent(const char character); | |
291 | int getDescent(const char* formatted_text, ...) NFONT_FORMAT(2); | |
292 | Uint16 getMaxWidth() const; | |
293 | Color getDefaultColor() const; | |
294 | ||
295 | int getNumCacheLevels() const; | |
296 | NFont_Image* getCacheLevel(int level) const; | |
297 | ||
298 | // Setters | |
299 | void setFilterMode(FilterEnum filter); | |
300 | void setSpacing(int LetterSpacing); | |
301 | void setLineSpacing(int LineSpacing); | |
302 | void setBaseline(); | |
303 | void setBaseline(Uint16 Baseline); | |
304 | void setDefaultColor(const Color& color); | |
305 | ||
306 | void enableTTFOwnership(); | |
307 | ||
308 | private: | |
309 | ||
310 | static char* buffer; | |
311 | FC_Font* font; | |
312 | ||
313 | void init(); // Common constructor | |
314 | ||
315 | }; | |
316 | ||
317 | ||
318 | ||
319 | #endif // _NFONT_H__ |
0 | /* | |
1 | SDL_FontCache: A font cache for SDL and SDL_ttf | |
2 | by Jonathan Dearborn | |
3 | ||
4 | See SDL_FontCache.h for license info. | |
5 | */ | |
6 | ||
7 | #include "SDL_FontCache.h" | |
8 | ||
9 | #include <stdio.h> | |
10 | #include <stdlib.h> | |
11 | #include <string.h> | |
12 | ||
13 | // Visual C does not support static inline | |
14 | #ifndef static_inline | |
15 | #ifdef _MSC_VER | |
16 | #define static_inline static | |
17 | #else | |
18 | #define static_inline static inline | |
19 | #endif | |
20 | #endif | |
21 | ||
22 | #if SDL_VERSION_ATLEAST(2,0,0) | |
23 | #define FC_GET_ALPHA(sdl_color) ((sdl_color).a) | |
24 | #else | |
25 | #define FC_GET_ALPHA(sdl_color) ((sdl_color).unused) | |
26 | #endif | |
27 | ||
28 | // Need SDL_RenderIsClipEnabled() for proper clipping support | |
29 | #if SDL_VERSION_ATLEAST(2,0,4) | |
30 | #define ENABLE_SDL_CLIPPING | |
31 | #endif | |
32 | ||
33 | #define FC_MIN(a,b) ((a) < (b)? (a) : (b)) | |
34 | #define FC_MAX(a,b) ((a) > (b)? (a) : (b)) | |
35 | ||
36 | ||
37 | // vsnprintf replacement from Valentin Milea: | |
38 | // http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 | |
39 | #if defined(_MSC_VER) && _MSC_VER < 1900 | |
40 | ||
41 | #define snprintf c99_snprintf | |
42 | #define vsnprintf c99_vsnprintf | |
43 | ||
44 | __inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) | |
45 | { | |
46 | int count = -1; | |
47 | ||
48 | if (size != 0) | |
49 | count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); | |
50 | if (count == -1) | |
51 | count = _vscprintf(format, ap); | |
52 | ||
53 | return count; | |
54 | } | |
55 | ||
56 | __inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...) | |
57 | { | |
58 | int count; | |
59 | va_list ap; | |
60 | ||
61 | va_start(ap, format); | |
62 | count = c99_vsnprintf(outBuf, size, format, ap); | |
63 | va_end(ap); | |
64 | ||
65 | return count; | |
66 | } | |
67 | ||
68 | #endif | |
69 | ||
70 | ||
71 | #define FC_EXTRACT_VARARGS(buffer, start_args) \ | |
72 | { \ | |
73 | va_list lst; \ | |
74 | va_start(lst, start_args); \ | |
75 | vsnprintf(buffer, fc_buffer_size, start_args, lst); \ | |
76 | va_end(lst); \ | |
77 | } | |
78 | ||
79 | // Extra pixels of padding around each glyph to avoid linear filtering artifacts | |
80 | #define FC_CACHE_PADDING 1 | |
81 | ||
82 | ||
83 | ||
84 | static Uint8 has_clip(FC_Target* dest) | |
85 | { | |
86 | #ifdef FC_USE_SDL_GPU | |
87 | return dest->use_clip_rect; | |
88 | #elif defined(ENABLE_SDL_CLIPPING) | |
89 | return SDL_RenderIsClipEnabled(dest); | |
90 | #else | |
91 | return 0; | |
92 | #endif | |
93 | } | |
94 | ||
95 | static FC_Rect get_clip(FC_Target* dest) | |
96 | { | |
97 | #ifdef FC_USE_SDL_GPU | |
98 | return dest->clip_rect; | |
99 | #elif defined(ENABLE_SDL_CLIPPING) | |
100 | SDL_Rect r; | |
101 | SDL_RenderGetClipRect(dest, &r); | |
102 | return r; | |
103 | #else | |
104 | SDL_Rect r = {0, 0, 0, 0}; | |
105 | return r; | |
106 | #endif | |
107 | } | |
108 | ||
109 | static void set_clip(FC_Target* dest, FC_Rect* rect) | |
110 | { | |
111 | #ifdef FC_USE_SDL_GPU | |
112 | if(rect != NULL) | |
113 | GPU_SetClipRect(dest, *rect); | |
114 | else | |
115 | GPU_UnsetClip(dest); | |
116 | #elif defined(ENABLE_SDL_CLIPPING) | |
117 | SDL_RenderSetClipRect(dest, rect); | |
118 | #endif | |
119 | } | |
120 | ||
121 | static void set_color(FC_Image* src, Uint8 r, Uint8 g, Uint8 b, Uint8 a) | |
122 | { | |
123 | #ifdef FC_USE_SDL_GPU | |
124 | GPU_SetRGBA(src, r, g, b, a); | |
125 | #else | |
126 | SDL_SetTextureColorMod(src, r, g, b); | |
127 | SDL_SetTextureAlphaMod(src, a); | |
128 | #endif | |
129 | } | |
130 | ||
131 | ||
132 | ||
133 | static char* new_concat(const char* a, const char* b) | |
134 | { | |
135 | // Create new buffer | |
136 | unsigned int size = strlen(a) + strlen(b); | |
137 | char* new_string = (char*)malloc(size+1); | |
138 | ||
139 | // Concatenate strings in the new buffer | |
140 | strcpy(new_string, a); | |
141 | strcat(new_string, b); | |
142 | ||
143 | return new_string; | |
144 | } | |
145 | ||
146 | static char* replace_concat(char** a, const char* b) | |
147 | { | |
148 | char* new_string = new_concat(*a, b); | |
149 | free(*a); | |
150 | *a = new_string; | |
151 | return *a; | |
152 | } | |
153 | ||
154 | ||
155 | ||
156 | ||
157 | ||
158 | // Shared buffer for variadic text | |
159 | static char* fc_buffer = NULL; | |
160 | static unsigned int fc_buffer_size = 1024; | |
161 | ||
162 | static Uint8 fc_has_render_target_support = 0; | |
163 | ||
164 | const char* FC_GetStringASCII(void) | |
165 | { | |
166 | static char* buffer = NULL; | |
167 | if(buffer == NULL) | |
168 | { | |
169 | int i; | |
170 | char c; | |
171 | buffer = (char*)malloc(512); | |
172 | memset(buffer, 0, 512); | |
173 | i = 0; | |
174 | c = 32; | |
175 | while(1) | |
176 | { | |
177 | buffer[i] = c; | |
178 | if(c == 126) | |
179 | break; | |
180 | ++i; | |
181 | ++c; | |
182 | } | |
183 | } | |
184 | return buffer; | |
185 | } | |
186 | ||
187 | const char* FC_GetStringLatin1(void) | |
188 | { | |
189 | static char* buffer = NULL; | |
190 | if(buffer == NULL) | |
191 | { | |
192 | int i; | |
193 | unsigned char c; | |
194 | buffer = (char*)malloc(512); | |
195 | memset(buffer, 0, 512); | |
196 | i = 0; | |
197 | c = 0xA0; | |
198 | while(1) | |
199 | { | |
200 | buffer[i] = 0xC2; | |
201 | buffer[i+1] = c; | |
202 | if(c == 0xBF) | |
203 | break; | |
204 | i += 2; | |
205 | ++c; | |
206 | } | |
207 | i += 2; | |
208 | c = 0x80; | |
209 | while(1) | |
210 | { | |
211 | buffer[i] = 0xC3; | |
212 | buffer[i+1] = c; | |
213 | if(c == 0xBF) | |
214 | break; | |
215 | i += 2; | |
216 | ++c; | |
217 | } | |
218 | } | |
219 | return buffer; | |
220 | } | |
221 | ||
222 | const char* FC_GetStringASCII_Latin1(void) | |
223 | { | |
224 | static char* buffer = NULL; | |
225 | if(buffer == NULL) | |
226 | buffer = new_concat(FC_GetStringASCII(), FC_GetStringLatin1()); | |
227 | ||
228 | return buffer; | |
229 | } | |
230 | ||
231 | FC_Rect FC_MakeRect(float x, float y, float w, float h) | |
232 | { | |
233 | FC_Rect r = {x, y, w, h}; | |
234 | return r; | |
235 | } | |
236 | ||
237 | FC_Scale FC_MakeScale(float x, float y) | |
238 | { | |
239 | FC_Scale s = {x, y}; | |
240 | ||
241 | return s; | |
242 | } | |
243 | ||
244 | SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a) | |
245 | { | |
246 | SDL_Color c = {r, g, b, a}; | |
247 | ||
248 | return c; | |
249 | } | |
250 | ||
251 | FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color) | |
252 | { | |
253 | FC_Effect e; | |
254 | ||
255 | e.alignment = alignment; | |
256 | e.scale = scale; | |
257 | e.color = color; | |
258 | ||
259 | return e; | |
260 | } | |
261 | ||
262 | FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h) | |
263 | { | |
264 | FC_GlyphData gd; | |
265 | ||
266 | gd.rect.x = x; | |
267 | gd.rect.y = y; | |
268 | gd.rect.w = w; | |
269 | gd.rect.h = h; | |
270 | gd.cache_level = cache_level; | |
271 | ||
272 | return gd; | |
273 | } | |
274 | ||
275 | // Enough to hold all of the ascii characters and some. | |
276 | #define FC_DEFAULT_NUM_BUCKETS 300 | |
277 | ||
278 | typedef struct FC_MapNode | |
279 | { | |
280 | Uint32 key; | |
281 | FC_GlyphData value; | |
282 | struct FC_MapNode* next; | |
283 | ||
284 | } FC_MapNode; | |
285 | ||
286 | typedef struct FC_Map | |
287 | { | |
288 | int num_buckets; | |
289 | FC_MapNode** buckets; | |
290 | } FC_Map; | |
291 | ||
292 | ||
293 | ||
294 | static FC_Map* FC_MapCreate(int num_buckets) | |
295 | { | |
296 | int i; | |
297 | FC_Map* map = (FC_Map*)malloc(sizeof(FC_Map)); | |
298 | ||
299 | map->num_buckets = num_buckets; | |
300 | map->buckets = (FC_MapNode**)malloc(num_buckets * sizeof(FC_MapNode*)); | |
301 | ||
302 | for(i = 0; i < num_buckets; ++i) | |
303 | { | |
304 | map->buckets[i] = NULL; | |
305 | } | |
306 | ||
307 | return map; | |
308 | } | |
309 | ||
310 | /*static void FC_MapClear(FC_Map* map) | |
311 | { | |
312 | int i; | |
313 | if(map == NULL) | |
314 | return; | |
315 | ||
316 | // Go through each bucket | |
317 | for(i = 0; i < map->num_buckets; ++i) | |
318 | { | |
319 | // Delete the nodes in order | |
320 | FC_MapNode* node = map->buckets[i]; | |
321 | while(node != NULL) | |
322 | { | |
323 | FC_MapNode* last = node; | |
324 | node = node->next; | |
325 | free(last); | |
326 | } | |
327 | // Set the bucket to empty | |
328 | map->buckets[i] = NULL; | |
329 | } | |
330 | }*/ | |
331 | ||
332 | static void FC_MapFree(FC_Map* map) | |
333 | { | |
334 | int i; | |
335 | if(map == NULL) | |
336 | return; | |
337 | ||
338 | // Go through each bucket | |
339 | for(i = 0; i < map->num_buckets; ++i) | |
340 | { | |
341 | // Delete the nodes in order | |
342 | FC_MapNode* node = map->buckets[i]; | |
343 | while(node != NULL) | |
344 | { | |
345 | FC_MapNode* last = node; | |
346 | node = node->next; | |
347 | free(last); | |
348 | } | |
349 | } | |
350 | ||
351 | free(map->buckets); | |
352 | free(map); | |
353 | } | |
354 | ||
355 | // Note: Does not handle duplicates in any special way. | |
356 | static FC_GlyphData* FC_MapInsert(FC_Map* map, Uint32 codepoint, FC_GlyphData glyph) | |
357 | { | |
358 | Uint32 index; | |
359 | FC_MapNode* node; | |
360 | if(map == NULL) | |
361 | return NULL; | |
362 | ||
363 | // Get index for bucket | |
364 | index = codepoint % map->num_buckets; | |
365 | ||
366 | // If this bucket is empty, create a node and return its value | |
367 | if(map->buckets[index] == NULL) | |
368 | { | |
369 | node = map->buckets[index] = (FC_MapNode*)malloc(sizeof(FC_MapNode)); | |
370 | node->key = codepoint; | |
371 | node->value = glyph; | |
372 | node->next = NULL; | |
373 | return &node->value; | |
374 | } | |
375 | ||
376 | for(node = map->buckets[index]; node != NULL; node = node->next) | |
377 | { | |
378 | // Find empty node and add a new one on. | |
379 | if(node->next == NULL) | |
380 | { | |
381 | node->next = (FC_MapNode*)malloc(sizeof(FC_MapNode)); | |
382 | node = node->next; | |
383 | ||
384 | node->key = codepoint; | |
385 | node->value = glyph; | |
386 | node->next = NULL; | |
387 | return &node->value; | |
388 | } | |
389 | } | |
390 | ||
391 | return NULL; | |
392 | } | |
393 | ||
394 | static FC_GlyphData* FC_MapFind(FC_Map* map, Uint32 codepoint) | |
395 | { | |
396 | Uint32 index; | |
397 | FC_MapNode* node; | |
398 | if(map == NULL) | |
399 | return NULL; | |
400 | ||
401 | // Get index for bucket | |
402 | index = codepoint % map->num_buckets; | |
403 | ||
404 | // Go through list until we find a match | |
405 | for(node = map->buckets[index]; node != NULL; node = node->next) | |
406 | { | |
407 | if(node->key == codepoint) | |
408 | return &node->value; | |
409 | } | |
410 | ||
411 | return NULL; | |
412 | } | |
413 | ||
414 | ||
415 | ||
416 | struct FC_Font | |
417 | { | |
418 | #ifndef FC_USE_SDL_GPU | |
419 | SDL_Renderer* renderer; | |
420 | #endif | |
421 | ||
422 | TTF_Font* ttf_source; // TTF_Font source of characters | |
423 | Uint8 owns_ttf_source; // Can we delete the TTF_Font ourselves? | |
424 | ||
425 | FC_FilterEnum filter; | |
426 | ||
427 | SDL_Color default_color; | |
428 | Uint16 height; | |
429 | ||
430 | Uint16 maxWidth; | |
431 | Uint16 baseline; | |
432 | int ascent; | |
433 | int descent; | |
434 | ||
435 | int lineSpacing; | |
436 | int letterSpacing; | |
437 | ||
438 | // Uses 32-bit (4-byte) Unicode codepoints to refer to each glyph | |
439 | // Codepoints are little endian (reversed from UTF-8) so that something like 0x00000005 is ASCII 5 and the map can be indexed by ASCII values | |
440 | FC_Map* glyphs; | |
441 | ||
442 | FC_GlyphData last_glyph; // Texture packing cursor | |
443 | int glyph_cache_size; | |
444 | int glyph_cache_count; | |
445 | FC_Image** glyph_cache; | |
446 | ||
447 | char* loading_string; | |
448 | ||
449 | }; | |
450 | ||
451 | // Private | |
452 | static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight); | |
453 | ||
454 | ||
455 | static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); | |
456 | static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); | |
457 | static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); | |
458 | ||
459 | ||
460 | static_inline SDL_Surface* FC_CreateSurface32(Uint32 width, Uint32 height) | |
461 | { | |
462 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN | |
463 | return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); | |
464 | #else | |
465 | return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); | |
466 | #endif | |
467 | } | |
468 | ||
469 | ||
470 | char* U8_alloc(unsigned int size) | |
471 | { | |
472 | char* result; | |
473 | if(size == 0) | |
474 | return NULL; | |
475 | ||
476 | result = (char*)malloc(size); | |
477 | result[0] = '\0'; | |
478 | ||
479 | return result; | |
480 | } | |
481 | ||
482 | void U8_free(char* string) | |
483 | { | |
484 | free(string); | |
485 | } | |
486 | ||
487 | char* U8_strdup(const char* string) | |
488 | { | |
489 | char* result; | |
490 | if(string == NULL) | |
491 | return NULL; | |
492 | ||
493 | result = (char*)malloc(strlen(string)+1); | |
494 | strcpy(result, string); | |
495 | ||
496 | return result; | |
497 | } | |
498 | ||
499 | int U8_strlen(const char* string) | |
500 | { | |
501 | int length = 0; | |
502 | if(string == NULL) | |
503 | return 0; | |
504 | ||
505 | while(*string != '\0') | |
506 | { | |
507 | string = U8_next(string); | |
508 | ++length; | |
509 | } | |
510 | ||
511 | return length; | |
512 | } | |
513 | ||
514 | int U8_charsize(const char* character) | |
515 | { | |
516 | if(character == NULL) | |
517 | return 0; | |
518 | ||
519 | if((unsigned char)*character <= 0x7F) | |
520 | return 1; | |
521 | else if((unsigned char)*character < 0xE0) | |
522 | return 2; | |
523 | else if((unsigned char)*character < 0xF0) | |
524 | return 3; | |
525 | else | |
526 | return 4; | |
527 | return 1; | |
528 | } | |
529 | ||
530 | int U8_charcpy(char* buffer, const char* source, int buffer_size) | |
531 | { | |
532 | int charsize; | |
533 | if(buffer == NULL || source == NULL || buffer_size < 1) | |
534 | return 0; | |
535 | ||
536 | charsize = U8_charsize(source); | |
537 | if(charsize > buffer_size) | |
538 | return 0; | |
539 | ||
540 | memcpy(buffer, source, charsize); | |
541 | return charsize; | |
542 | } | |
543 | ||
544 | const char* U8_next(const char* string) | |
545 | { | |
546 | return string + U8_charsize(string); | |
547 | } | |
548 | ||
549 | int U8_strinsert(char* string, int position, const char* source, int max_bytes) | |
550 | { | |
551 | int pos_bytes; | |
552 | int len; | |
553 | int add_len; | |
554 | int ulen; | |
555 | ||
556 | if(string == NULL || source == NULL) | |
557 | return 0; | |
558 | ||
559 | len = strlen(string); | |
560 | add_len = strlen(source); | |
561 | ulen = U8_strlen(string); | |
562 | ||
563 | if(position == -1) | |
564 | position = ulen; | |
565 | ||
566 | if(position < 0 || position > ulen || len + add_len + 1 > max_bytes) | |
567 | return 0; | |
568 | ||
569 | // Move string pointer to the proper position | |
570 | pos_bytes = 0; | |
571 | while(*string != '\0' && pos_bytes < position) | |
572 | { | |
573 | string = (char*)U8_next(string); | |
574 | ++pos_bytes; | |
575 | } | |
576 | ||
577 | // Move the rest of the string out of the way | |
578 | memmove(string + add_len, string, len - pos_bytes + 1); | |
579 | ||
580 | // Copy in the new characters | |
581 | memcpy(string, source, add_len); | |
582 | ||
583 | return 1; | |
584 | } | |
585 | ||
586 | void U8_strdel(char* string, int position) | |
587 | { | |
588 | if(string == NULL || position < 0) | |
589 | return; | |
590 | ||
591 | while(*string != '\0') | |
592 | { | |
593 | if(position == 0) | |
594 | { | |
595 | int chars_to_erase = U8_charsize(string); | |
596 | int remaining_bytes = strlen(string) + 1; | |
597 | memmove(string, string + chars_to_erase, remaining_bytes); | |
598 | break; | |
599 | } | |
600 | ||
601 | string = (char*)U8_next(string); | |
602 | --position; | |
603 | } | |
604 | } | |
605 | ||
606 | ||
607 | ||
608 | ||
609 | ||
610 | static_inline FC_Rect FC_RectUnion(FC_Rect A, FC_Rect B) | |
611 | { | |
612 | float x,x2,y,y2; | |
613 | x = FC_MIN(A.x, B.x); | |
614 | y = FC_MIN(A.y, B.y); | |
615 | x2 = FC_MAX(A.x+A.w, B.x+B.w); | |
616 | y2 = FC_MAX(A.y+A.h, B.y+B.h); | |
617 | { | |
618 | FC_Rect result = {x, y, FC_MAX(0, x2 - x), FC_MAX(0, y2 - y)}; | |
619 | return result; | |
620 | } | |
621 | } | |
622 | ||
623 | // Adapted from SDL_IntersectRect | |
624 | static_inline FC_Rect FC_RectIntersect(FC_Rect A, FC_Rect B) | |
625 | { | |
626 | FC_Rect result; | |
627 | float Amin, Amax, Bmin, Bmax; | |
628 | ||
629 | // Horizontal intersection | |
630 | Amin = A.x; | |
631 | Amax = Amin + A.w; | |
632 | Bmin = B.x; | |
633 | Bmax = Bmin + B.w; | |
634 | if(Bmin > Amin) | |
635 | Amin = Bmin; | |
636 | result.x = Amin; | |
637 | if(Bmax < Amax) | |
638 | Amax = Bmax; | |
639 | result.w = Amax - Amin > 0 ? Amax - Amin : 0; | |
640 | ||
641 | // Vertical intersection | |
642 | Amin = A.y; | |
643 | Amax = Amin + A.h; | |
644 | Bmin = B.y; | |
645 | Bmax = Bmin + B.h; | |
646 | if(Bmin > Amin) | |
647 | Amin = Bmin; | |
648 | result.y = Amin; | |
649 | if(Bmax < Amax) | |
650 | Amax = Bmax; | |
651 | result.h = Amax - Amin > 0 ? Amax - Amin : 0; | |
652 | ||
653 | return result; | |
654 | } | |
655 | ||
656 | ||
657 | ||
658 | ||
659 | ||
660 | ||
661 | ||
662 | ||
663 | ||
664 | ||
665 | ||
666 | ||
667 | ||
668 | ||
669 | FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) | |
670 | { | |
671 | float w = srcrect->w * xscale; | |
672 | float h = srcrect->h * yscale; | |
673 | FC_Rect result; | |
674 | ||
675 | // FIXME: Why does the scaled offset look so wrong? | |
676 | #ifdef FC_USE_SDL_GPU | |
677 | { | |
678 | GPU_Rect r = *srcrect; | |
679 | GPU_BlitScale(src, &r, dest, x + xscale*r.w/2.0f, y + r.h/2.0f, xscale, yscale); | |
680 | } | |
681 | #else | |
682 | { | |
683 | SDL_RendererFlip flip = SDL_FLIP_NONE; | |
684 | if(xscale < 0) | |
685 | { | |
686 | xscale = -xscale; | |
687 | flip = (SDL_RendererFlip) ((int)flip | (int)SDL_FLIP_HORIZONTAL); | |
688 | } | |
689 | if(yscale < 0) | |
690 | { | |
691 | yscale = -yscale; | |
692 | flip = (SDL_RendererFlip) ((int)flip | (int)SDL_FLIP_VERTICAL); | |
693 | } | |
694 | ||
695 | SDL_Rect r = *srcrect; | |
696 | SDL_Rect dr = {(int)x, (int)y, (int)(xscale*r.w), (int)(yscale*r.h)}; | |
697 | SDL_RenderCopyEx(dest, src, &r, &dr, 0, NULL, flip); | |
698 | } | |
699 | #endif | |
700 | ||
701 | result.x = x; | |
702 | result.y = y; | |
703 | result.w = w; | |
704 | result.h = h; | |
705 | return result; | |
706 | } | |
707 | ||
708 | static FC_Rect (*fc_render_callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) = &FC_DefaultRenderCallback; | |
709 | ||
710 | void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale)) | |
711 | { | |
712 | if(callback == NULL) | |
713 | fc_render_callback = &FC_DefaultRenderCallback; | |
714 | else | |
715 | fc_render_callback = callback; | |
716 | } | |
717 | ||
718 | void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint) | |
719 | { | |
720 | char a, b, c, d; | |
721 | ||
722 | if(result == NULL) | |
723 | return; | |
724 | ||
725 | a = (codepoint >> 24) & 0xFF; | |
726 | b = (codepoint >> 16) & 0xFF; | |
727 | c = (codepoint >> 8) & 0xFF; | |
728 | d = codepoint & 0xFF; | |
729 | ||
730 | if(a == 0) | |
731 | { | |
732 | if(b == 0) | |
733 | { | |
734 | if(c == 0) | |
735 | { | |
736 | result[0] = d; | |
737 | result[1] = '\0'; | |
738 | } | |
739 | else | |
740 | { | |
741 | result[0] = c; | |
742 | result[1] = d; | |
743 | result[2] = '\0'; | |
744 | } | |
745 | } | |
746 | else | |
747 | { | |
748 | result[0] = b; | |
749 | result[1] = c; | |
750 | result[2] = d; | |
751 | result[3] = '\0'; | |
752 | } | |
753 | } | |
754 | else | |
755 | { | |
756 | result[0] = a; | |
757 | result[1] = b; | |
758 | result[2] = c; | |
759 | result[3] = d; | |
760 | result[4] = '\0'; | |
761 | } | |
762 | } | |
763 | ||
764 | Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer) | |
765 | { | |
766 | Uint32 result = 0; | |
767 | const char* str; | |
768 | if(c == NULL || *c == NULL) | |
769 | return 0; | |
770 | ||
771 | str = *c; | |
772 | if((unsigned char)*str <= 0x7F) | |
773 | result = *str; | |
774 | else if((unsigned char)*str < 0xE0) | |
775 | { | |
776 | result |= (unsigned char)(*str) << 8; | |
777 | result |= (unsigned char)(*(str+1)); | |
778 | if(advance_pointer) | |
779 | *c += 1; | |
780 | } | |
781 | else if((unsigned char)*str < 0xF0) | |
782 | { | |
783 | result |= (unsigned char)(*str) << 16; | |
784 | result |= (unsigned char)(*(str+1)) << 8; | |
785 | result |= (unsigned char)(*(str+2)); | |
786 | if(advance_pointer) | |
787 | *c += 2; | |
788 | } | |
789 | else | |
790 | { | |
791 | result |= (unsigned char)(*str) << 24; | |
792 | result |= (unsigned char)(*(str+1)) << 16; | |
793 | result |= (unsigned char)(*(str+2)) << 8; | |
794 | result |= (unsigned char)(*(str+3)); | |
795 | if(advance_pointer) | |
796 | *c += 3; | |
797 | } | |
798 | return result; | |
799 | } | |
800 | ||
801 | ||
802 | void FC_SetLoadingString(FC_Font* font, const char* string) | |
803 | { | |
804 | if(font == NULL) | |
805 | return; | |
806 | ||
807 | free(font->loading_string); | |
808 | font->loading_string = U8_strdup(string); | |
809 | } | |
810 | ||
811 | ||
812 | unsigned int FC_GetBufferSize(void) | |
813 | { | |
814 | return fc_buffer_size; | |
815 | } | |
816 | ||
817 | void FC_SetBufferSize(unsigned int size) | |
818 | { | |
819 | free(fc_buffer); | |
820 | if(size > 0) | |
821 | { | |
822 | fc_buffer_size = size; | |
823 | fc_buffer = (char*)malloc(fc_buffer_size); | |
824 | } | |
825 | else | |
826 | fc_buffer = (char*)malloc(fc_buffer_size); | |
827 | } | |
828 | ||
829 | ||
830 | ||
831 | ||
832 | ||
833 | // Constructors | |
834 | ||
835 | static void FC_Init(FC_Font* font) | |
836 | { | |
837 | if(font == NULL) | |
838 | return; | |
839 | ||
840 | #ifndef FC_USE_SDL_GPU | |
841 | font->renderer = NULL; | |
842 | #endif | |
843 | ||
844 | font->ttf_source = NULL; | |
845 | font->owns_ttf_source = 0; | |
846 | ||
847 | font->filter = FC_FILTER_NEAREST; | |
848 | ||
849 | font->default_color.r = 0; | |
850 | font->default_color.g = 0; | |
851 | font->default_color.b = 0; | |
852 | FC_GET_ALPHA(font->default_color) = 255; | |
853 | ||
854 | font->height = 0; // ascent+descent | |
855 | ||
856 | font->maxWidth = 0; | |
857 | font->baseline = 0; | |
858 | font->ascent = 0; | |
859 | font->descent = 0; | |
860 | ||
861 | font->lineSpacing = 0; | |
862 | font->letterSpacing = 0; | |
863 | ||
864 | // Give a little offset for when filtering/mipmaps are used. Depending on mipmap level, this will still not be enough. | |
865 | font->last_glyph.rect.x = FC_CACHE_PADDING; | |
866 | font->last_glyph.rect.y = FC_CACHE_PADDING; | |
867 | font->last_glyph.rect.w = 0; | |
868 | font->last_glyph.rect.h = 0; | |
869 | font->last_glyph.cache_level = 0; | |
870 | ||
871 | if(font->glyphs != NULL) | |
872 | FC_MapFree(font->glyphs); | |
873 | ||
874 | font->glyphs = FC_MapCreate(FC_DEFAULT_NUM_BUCKETS); | |
875 | ||
876 | font->glyph_cache_size = 3; | |
877 | font->glyph_cache_count = 0; | |
878 | ||
879 | ||
880 | font->glyph_cache = (FC_Image**)malloc(font->glyph_cache_size * sizeof(FC_Image*)); | |
881 | ||
882 | if(font->loading_string == NULL) | |
883 | font->loading_string = U8_strdup(FC_GetStringASCII()); | |
884 | ||
885 | if(fc_buffer == NULL) | |
886 | fc_buffer = (char*)malloc(fc_buffer_size); | |
887 | } | |
888 | ||
889 | static Uint8 FC_GrowGlyphCache(FC_Font* font) | |
890 | { | |
891 | if(font == NULL) | |
892 | return 0; | |
893 | #ifdef FC_USE_SDL_GPU | |
894 | GPU_Image* new_level = GPU_CreateImage(font->height * 12, font->height * 12, GPU_FORMAT_RGBA); | |
895 | #else | |
896 | SDL_Texture* new_level = SDL_CreateTexture(font->renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, font->height * 12, font->height * 12); | |
897 | #endif | |
898 | if(new_level == NULL || !FC_SetGlyphCacheLevel(font, font->glyph_cache_count, new_level)) | |
899 | { | |
900 | FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n"); | |
901 | #ifdef FC_USE_SDL_GPU | |
902 | GPU_FreeImage(new_level); | |
903 | #else | |
904 | SDL_DestroyTexture(new_level); | |
905 | #endif | |
906 | return 0; | |
907 | } | |
908 | return 1; | |
909 | } | |
910 | ||
911 | Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface) | |
912 | { | |
913 | if(font == NULL || data_surface == NULL) | |
914 | return 0; | |
915 | #ifdef FC_USE_SDL_GPU | |
916 | GPU_Image* new_level = GPU_CopyImageFromSurface(data_surface); | |
917 | if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) | |
918 | GPU_SetImageFilter(new_level, GPU_FILTER_LINEAR); | |
919 | else | |
920 | GPU_SetImageFilter(new_level, GPU_FILTER_NEAREST); | |
921 | #else | |
922 | SDL_Texture* new_level; | |
923 | if(!fc_has_render_target_support) | |
924 | new_level = SDL_CreateTextureFromSurface(font->renderer, data_surface); | |
925 | else | |
926 | { | |
927 | // Must upload with render target enabled so we can put more glyphs on later | |
928 | SDL_Renderer* renderer = font->renderer; | |
929 | ||
930 | // Set filter mode for new texture | |
931 | char old_filter_mode[16]; // Save it so we can change the hint value in the meantime | |
932 | snprintf(old_filter_mode, 16, "%s", SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY)); | |
933 | ||
934 | if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) | |
935 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); | |
936 | else | |
937 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); | |
938 | ||
939 | new_level = SDL_CreateTexture(renderer, data_surface->format->format, SDL_TEXTUREACCESS_TARGET, data_surface->w, data_surface->h); | |
940 | SDL_SetTextureBlendMode(new_level, SDL_BLENDMODE_BLEND); | |
941 | ||
942 | // Reset filter mode for the temp texture | |
943 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); | |
944 | ||
945 | { | |
946 | Uint8 r, g, b, a; | |
947 | SDL_Texture* temp = SDL_CreateTextureFromSurface(renderer, data_surface); | |
948 | SDL_SetTextureBlendMode(temp, SDL_BLENDMODE_NONE); | |
949 | SDL_SetRenderTarget(renderer, new_level); | |
950 | ||
951 | SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); | |
952 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); | |
953 | SDL_RenderClear(renderer); | |
954 | SDL_SetRenderDrawColor(renderer, r, g, b, a); | |
955 | ||
956 | SDL_RenderCopy(renderer, temp, NULL, NULL); | |
957 | SDL_SetRenderTarget(renderer, NULL); | |
958 | ||
959 | SDL_DestroyTexture(temp); | |
960 | } | |
961 | ||
962 | // Reset to the old filter value | |
963 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_filter_mode); | |
964 | ||
965 | } | |
966 | #endif | |
967 | if(new_level == NULL || !FC_SetGlyphCacheLevel(font, cache_level, new_level)) | |
968 | { | |
969 | FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n"); | |
970 | #ifdef FC_USE_SDL_GPU | |
971 | GPU_FreeImage(new_level); | |
972 | #else | |
973 | SDL_DestroyTexture(new_level); | |
974 | #endif | |
975 | return 0; | |
976 | } | |
977 | return 1; | |
978 | } | |
979 | ||
980 | static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight) | |
981 | { | |
982 | FC_Map* glyphs = font->glyphs; | |
983 | FC_GlyphData* last_glyph = &font->last_glyph; | |
984 | Uint16 height = font->height + FC_CACHE_PADDING; | |
985 | ||
986 | if(last_glyph->rect.x + last_glyph->rect.w + width >= maxWidth - FC_CACHE_PADDING) | |
987 | { | |
988 | if(last_glyph->rect.y + height + height >= maxHeight - FC_CACHE_PADDING) | |
989 | { | |
990 | // Get ready to pack on the next cache level when it is ready | |
991 | last_glyph->cache_level = font->glyph_cache_count; | |
992 | last_glyph->rect.x = FC_CACHE_PADDING; | |
993 | last_glyph->rect.y = FC_CACHE_PADDING; | |
994 | last_glyph->rect.w = 0; | |
995 | return NULL; | |
996 | } | |
997 | else | |
998 | { | |
999 | // Go to next row | |
1000 | last_glyph->rect.x = FC_CACHE_PADDING; | |
1001 | last_glyph->rect.y += height; | |
1002 | last_glyph->rect.w = 0; | |
1003 | } | |
1004 | } | |
1005 | ||
1006 | // Move to next space | |
1007 | last_glyph->rect.x += last_glyph->rect.w + 1 + FC_CACHE_PADDING; | |
1008 | last_glyph->rect.w = width; | |
1009 | ||
1010 | return FC_MapInsert(glyphs, codepoint, FC_MakeGlyphData(last_glyph->cache_level, last_glyph->rect.x, last_glyph->rect.y, last_glyph->rect.w, last_glyph->rect.h)); | |
1011 | } | |
1012 | ||
1013 | ||
1014 | FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level) | |
1015 | { | |
1016 | if(font == NULL || cache_level < 0 || cache_level > font->glyph_cache_count) | |
1017 | return NULL; | |
1018 | ||
1019 | return font->glyph_cache[cache_level]; | |
1020 | } | |
1021 | ||
1022 | Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture) | |
1023 | { | |
1024 | if(font == NULL || cache_level < 0) | |
1025 | return 0; | |
1026 | ||
1027 | // Must be sequentially added | |
1028 | if(cache_level > font->glyph_cache_count + 1) | |
1029 | return 0; | |
1030 | ||
1031 | if(cache_level == font->glyph_cache_count) | |
1032 | { | |
1033 | font->glyph_cache_count++; | |
1034 | ||
1035 | // Grow cache? | |
1036 | if(font->glyph_cache_count > font->glyph_cache_size) | |
1037 | { | |
1038 | // Copy old cache to new one | |
1039 | int i; | |
1040 | FC_Image** new_cache; | |
1041 | new_cache = (FC_Image**)malloc(font->glyph_cache_count * sizeof(FC_Image*)); | |
1042 | for(i = 0; i < font->glyph_cache_size; ++i) | |
1043 | new_cache[i] = font->glyph_cache[i]; | |
1044 | ||
1045 | // Save new cache | |
1046 | free(font->glyph_cache); | |
1047 | font->glyph_cache_size = font->glyph_cache_count; | |
1048 | font->glyph_cache = new_cache; | |
1049 | } | |
1050 | } | |
1051 | ||
1052 | font->glyph_cache[cache_level] = cache_texture; | |
1053 | return 1; | |
1054 | } | |
1055 | ||
1056 | ||
1057 | FC_Font* FC_CreateFont(void) | |
1058 | { | |
1059 | FC_Font* font; | |
1060 | ||
1061 | font = (FC_Font*)malloc(sizeof(FC_Font)); | |
1062 | memset(font, 0, sizeof(FC_Font)); | |
1063 | ||
1064 | FC_Init(font); | |
1065 | ||
1066 | return font; | |
1067 | } | |
1068 | ||
1069 | ||
1070 | // Assume this many will be enough... | |
1071 | #define FC_LOAD_MAX_SURFACES 10 | |
1072 | ||
1073 | #ifdef FC_USE_SDL_GPU | |
1074 | Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color) | |
1075 | #else | |
1076 | Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color) | |
1077 | #endif | |
1078 | { | |
1079 | if(font == NULL || ttf == NULL) | |
1080 | return 0; | |
1081 | #ifndef FC_USE_SDL_GPU | |
1082 | if(renderer == NULL) | |
1083 | return 0; | |
1084 | #endif | |
1085 | ||
1086 | FC_ClearFont(font); | |
1087 | ||
1088 | ||
1089 | // Might as well check render target support here | |
1090 | #ifdef FC_USE_SDL_GPU | |
1091 | fc_has_render_target_support = GPU_IsFeatureEnabled(GPU_FEATURE_RENDER_TARGETS); | |
1092 | #else | |
1093 | SDL_RendererInfo info; | |
1094 | SDL_GetRendererInfo(renderer, &info); | |
1095 | fc_has_render_target_support = (info.flags & SDL_RENDERER_TARGETTEXTURE); | |
1096 | ||
1097 | font->renderer = renderer; | |
1098 | #endif | |
1099 | ||
1100 | font->ttf_source = ttf; | |
1101 | ||
1102 | //font->line_height = TTF_FontLineSkip(ttf); | |
1103 | font->height = TTF_FontHeight(ttf); | |
1104 | font->ascent = TTF_FontAscent(ttf); | |
1105 | font->descent = -TTF_FontDescent(ttf); | |
1106 | ||
1107 | font->baseline = font->height - font->descent; | |
1108 | ||
1109 | font->default_color = color; | |
1110 | ||
1111 | { | |
1112 | SDL_Color white = {255, 255, 255, 255}; | |
1113 | SDL_Surface* glyph_surf; | |
1114 | char buff[5]; | |
1115 | const char* buff_ptr = buff; | |
1116 | const char* source_string; | |
1117 | Uint8 packed = 0; | |
1118 | ||
1119 | // Copy glyphs from the surface to the font texture and store the position data | |
1120 | // Pack row by row into a square texture | |
1121 | // Try figuring out dimensions that make sense for the font size. | |
1122 | unsigned int w = font->height*12; | |
1123 | unsigned int h = font->height*12; | |
1124 | SDL_Surface* surfaces[FC_LOAD_MAX_SURFACES]; | |
1125 | int num_surfaces = 1; | |
1126 | surfaces[0] = FC_CreateSurface32(w, h); | |
1127 | font->last_glyph.rect.x = FC_CACHE_PADDING; | |
1128 | font->last_glyph.rect.y = FC_CACHE_PADDING; | |
1129 | font->last_glyph.rect.w = 0; | |
1130 | font->last_glyph.rect.h = font->height; | |
1131 | ||
1132 | memset(buff, 0, 5); | |
1133 | source_string = font->loading_string; | |
1134 | for(; *source_string != '\0'; source_string = U8_next(source_string)) | |
1135 | { | |
1136 | if(!U8_charcpy(buff, source_string, 5)) | |
1137 | continue; | |
1138 | glyph_surf = TTF_RenderUTF8_Blended(ttf, buff, white); | |
1139 | if(glyph_surf == NULL) | |
1140 | continue; | |
1141 | ||
1142 | // Try packing. If it fails, create a new surface for the next cache level. | |
1143 | packed = (FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL); | |
1144 | if(!packed) | |
1145 | { | |
1146 | int i = num_surfaces-1; | |
1147 | if(num_surfaces >= FC_LOAD_MAX_SURFACES) | |
1148 | { | |
1149 | // Can't do any more! | |
1150 | FC_Log("SDL_FontCache error: Could not create enough cache surfaces to fit all of the loading string!\n"); | |
1151 | SDL_FreeSurface(glyph_surf); | |
1152 | break; | |
1153 | } | |
1154 | ||
1155 | // Upload the current surface to the glyph cache now so we can keep the cache level packing cursor up to date as we go. | |
1156 | FC_UploadGlyphCache(font, i, surfaces[i]); | |
1157 | SDL_FreeSurface(surfaces[i]); | |
1158 | #ifndef FC_USE_SDL_GPU | |
1159 | SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND); | |
1160 | #endif | |
1161 | // Update the glyph cursor to the new cache level. We need to do this here because the actual cache lags behind our use of the packing above. | |
1162 | font->last_glyph.cache_level = num_surfaces; | |
1163 | ||
1164 | ||
1165 | surfaces[num_surfaces] = FC_CreateSurface32(w, h); | |
1166 | num_surfaces++; | |
1167 | } | |
1168 | ||
1169 | // Try packing for the new surface, then blit onto it. | |
1170 | if(packed || FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL) | |
1171 | { | |
1172 | SDL_SetSurfaceBlendMode(glyph_surf, SDL_BLENDMODE_NONE); | |
1173 | SDL_Rect srcRect = {0, 0, glyph_surf->w, glyph_surf->h}; | |
1174 | SDL_Rect destrect = font->last_glyph.rect; | |
1175 | SDL_BlitSurface(glyph_surf, &srcRect, surfaces[num_surfaces-1], &destrect); | |
1176 | } | |
1177 | ||
1178 | SDL_FreeSurface(glyph_surf); | |
1179 | } | |
1180 | ||
1181 | { | |
1182 | int i = num_surfaces-1; | |
1183 | FC_UploadGlyphCache(font, i, surfaces[i]); | |
1184 | SDL_FreeSurface(surfaces[i]); | |
1185 | #ifndef FC_USE_SDL_GPU | |
1186 | SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND); | |
1187 | #endif | |
1188 | } | |
1189 | } | |
1190 | ||
1191 | return 1; | |
1192 | } | |
1193 | ||
1194 | ||
1195 | #ifdef FC_USE_SDL_GPU | |
1196 | Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style) | |
1197 | #else | |
1198 | Uint8 FC_LoadFont(FC_Font* font, FC_Target* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style) | |
1199 | #endif | |
1200 | { | |
1201 | SDL_RWops* rwops; | |
1202 | ||
1203 | if(font == NULL) | |
1204 | return 0; | |
1205 | ||
1206 | rwops = SDL_RWFromFile(filename_ttf, "rb"); | |
1207 | ||
1208 | if(rwops == NULL) | |
1209 | { | |
1210 | FC_Log("Unable to open file for reading: %s \n", SDL_GetError()); | |
1211 | return 0; | |
1212 | } | |
1213 | ||
1214 | #ifdef FC_USE_SDL_GPU | |
1215 | return FC_LoadFont_RW(font, rwops, 1, pointSize, color, style); | |
1216 | #else | |
1217 | return FC_LoadFont_RW(font, renderer, rwops, 1, pointSize, color, style); | |
1218 | #endif | |
1219 | } | |
1220 | ||
1221 | #ifdef FC_USE_SDL_GPU | |
1222 | Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style) | |
1223 | #else | |
1224 | Uint8 FC_LoadFont_RW(FC_Font* font, FC_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style) | |
1225 | #endif | |
1226 | { | |
1227 | Uint8 result; | |
1228 | TTF_Font* ttf; | |
1229 | Uint8 outline; | |
1230 | ||
1231 | if(font == NULL) | |
1232 | return 0; | |
1233 | ||
1234 | if(!TTF_WasInit() && TTF_Init() < 0) | |
1235 | { | |
1236 | FC_Log("Unable to initialize SDL_ttf: %s \n", TTF_GetError()); | |
1237 | if(own_rwops) | |
1238 | SDL_RWclose(file_rwops_ttf); | |
1239 | return 0; | |
1240 | } | |
1241 | ||
1242 | ttf = TTF_OpenFontRW(file_rwops_ttf, own_rwops, pointSize); | |
1243 | ||
1244 | if(ttf == NULL) | |
1245 | { | |
1246 | FC_Log("Unable to load TrueType font: %s \n", TTF_GetError()); | |
1247 | if(own_rwops) | |
1248 | SDL_RWclose(file_rwops_ttf); | |
1249 | return 0; | |
1250 | } | |
1251 | ||
1252 | outline = (style & TTF_STYLE_OUTLINE); | |
1253 | if(outline) | |
1254 | { | |
1255 | style &= ~TTF_STYLE_OUTLINE; | |
1256 | TTF_SetFontOutline(ttf, 1); | |
1257 | } | |
1258 | TTF_SetFontStyle(ttf, style); | |
1259 | ||
1260 | #ifdef FC_USE_SDL_GPU | |
1261 | result = FC_LoadFontFromTTF(font, ttf, color); | |
1262 | #else | |
1263 | result = FC_LoadFontFromTTF(font, renderer, ttf, color); | |
1264 | #endif | |
1265 | ||
1266 | // Can only load new (uncached) glyphs if we can keep the SDL_RWops open. | |
1267 | font->owns_ttf_source = own_rwops; | |
1268 | if(!own_rwops) | |
1269 | { | |
1270 | TTF_CloseFont(font->ttf_source); | |
1271 | font->ttf_source = NULL; | |
1272 | } | |
1273 | ||
1274 | return result; | |
1275 | } | |
1276 | ||
1277 | ||
1278 | void FC_ClearFont(FC_Font* font) | |
1279 | { | |
1280 | int i; | |
1281 | if(font == NULL) | |
1282 | return; | |
1283 | ||
1284 | // Release resources | |
1285 | if(font->owns_ttf_source) | |
1286 | TTF_CloseFont(font->ttf_source); | |
1287 | ||
1288 | font->owns_ttf_source = 0; | |
1289 | font->ttf_source = NULL; | |
1290 | ||
1291 | // Delete glyph map | |
1292 | FC_MapFree(font->glyphs); | |
1293 | font->glyphs = NULL; | |
1294 | ||
1295 | // Delete glyph cache | |
1296 | for(i = 0; i < font->glyph_cache_count; ++i) | |
1297 | { | |
1298 | #ifdef FC_USE_SDL_GPU | |
1299 | GPU_FreeImage(font->glyph_cache[i]); | |
1300 | #else | |
1301 | SDL_DestroyTexture(font->glyph_cache[i]); | |
1302 | #endif | |
1303 | } | |
1304 | free(font->glyph_cache); | |
1305 | font->glyph_cache = NULL; | |
1306 | ||
1307 | // Reset font | |
1308 | FC_Init(font); | |
1309 | } | |
1310 | ||
1311 | ||
1312 | void FC_FreeFont(FC_Font* font) | |
1313 | { | |
1314 | int i; | |
1315 | if(font == NULL) | |
1316 | return; | |
1317 | ||
1318 | // Release resources | |
1319 | if(font->owns_ttf_source) | |
1320 | TTF_CloseFont(font->ttf_source); | |
1321 | ||
1322 | // Delete glyph map | |
1323 | FC_MapFree(font->glyphs); | |
1324 | ||
1325 | // Delete glyph cache | |
1326 | for(i = 0; i < font->glyph_cache_count; ++i) | |
1327 | { | |
1328 | #ifdef FC_USE_SDL_GPU | |
1329 | GPU_FreeImage(font->glyph_cache[i]); | |
1330 | #else | |
1331 | SDL_DestroyTexture(font->glyph_cache[i]); | |
1332 | #endif | |
1333 | } | |
1334 | free(font->glyph_cache); | |
1335 | ||
1336 | free(font->loading_string); | |
1337 | ||
1338 | free(font); | |
1339 | } | |
1340 | ||
1341 | int FC_GetNumCacheLevels(FC_Font* font) | |
1342 | { | |
1343 | return font->glyph_cache_count; | |
1344 | } | |
1345 | ||
1346 | Uint8 FC_AddGlyphToCache(FC_Font* font, SDL_Surface* glyph_surface) | |
1347 | { | |
1348 | if(font == NULL || glyph_surface == NULL) | |
1349 | return 0; | |
1350 | ||
1351 | SDL_SetSurfaceBlendMode(glyph_surface, SDL_BLENDMODE_NONE); | |
1352 | FC_Image* dest = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level); | |
1353 | if(dest == NULL) | |
1354 | return 0; | |
1355 | ||
1356 | #ifdef FC_USE_SDL_GPU | |
1357 | { | |
1358 | GPU_Target* target = GPU_LoadTarget(dest); | |
1359 | if(target == NULL) | |
1360 | return 0; | |
1361 | GPU_Image* img = GPU_CopyImageFromSurface(glyph_surface); | |
1362 | GPU_SetImageFilter(img, GPU_FILTER_NEAREST); | |
1363 | GPU_SetBlendMode(img, GPU_BLEND_SET); | |
1364 | ||
1365 | SDL_Rect destrect = font->last_glyph.rect; | |
1366 | GPU_Blit(img, NULL, target, destrect.x + destrect.w/2, destrect.y + destrect.h/2); | |
1367 | ||
1368 | GPU_FreeImage(img); | |
1369 | GPU_FreeTarget(target); | |
1370 | } | |
1371 | #else | |
1372 | { | |
1373 | SDL_Renderer* renderer = font->renderer; | |
1374 | Uint8 use_clip; | |
1375 | FC_Rect clip_rect; | |
1376 | SDL_Texture* img; | |
1377 | SDL_Rect destrect; | |
1378 | ||
1379 | use_clip = has_clip(renderer); | |
1380 | if(use_clip) | |
1381 | { | |
1382 | clip_rect = get_clip(renderer); | |
1383 | set_clip(renderer, NULL); | |
1384 | } | |
1385 | ||
1386 | img = SDL_CreateTextureFromSurface(renderer, glyph_surface); | |
1387 | ||
1388 | destrect = font->last_glyph.rect; | |
1389 | SDL_SetRenderTarget(renderer, dest); | |
1390 | SDL_RenderCopy(renderer, img, NULL, &destrect); | |
1391 | SDL_SetRenderTarget(renderer, NULL); | |
1392 | SDL_DestroyTexture(img); | |
1393 | ||
1394 | if(use_clip) | |
1395 | set_clip(renderer, &clip_rect); | |
1396 | } | |
1397 | #endif | |
1398 | ||
1399 | return 1; | |
1400 | } | |
1401 | ||
1402 | ||
1403 | unsigned int FC_GetNumCodepoints(FC_Font* font) | |
1404 | { | |
1405 | FC_Map* glyphs; | |
1406 | int i; | |
1407 | unsigned int result = 0; | |
1408 | if(font == NULL || font->glyphs == NULL) | |
1409 | return 0; | |
1410 | ||
1411 | glyphs = font->glyphs; | |
1412 | ||
1413 | for(i = 0; i < glyphs->num_buckets; ++i) | |
1414 | { | |
1415 | FC_MapNode* node; | |
1416 | for(node = glyphs->buckets[i]; node != NULL; node = node->next) | |
1417 | { | |
1418 | result++; | |
1419 | } | |
1420 | } | |
1421 | ||
1422 | return result; | |
1423 | } | |
1424 | ||
1425 | void FC_GetCodepoints(FC_Font* font, Uint32* result) | |
1426 | { | |
1427 | FC_Map* glyphs; | |
1428 | int i; | |
1429 | unsigned int count = 0; | |
1430 | if(font == NULL || font->glyphs == NULL) | |
1431 | return; | |
1432 | ||
1433 | glyphs = font->glyphs; | |
1434 | ||
1435 | for(i = 0; i < glyphs->num_buckets; ++i) | |
1436 | { | |
1437 | FC_MapNode* node; | |
1438 | for(node = glyphs->buckets[i]; node != NULL; node = node->next) | |
1439 | { | |
1440 | result[count] = node->key; | |
1441 | count++; | |
1442 | } | |
1443 | } | |
1444 | } | |
1445 | ||
1446 | Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint) | |
1447 | { | |
1448 | FC_GlyphData* e = FC_MapFind(font->glyphs, codepoint); | |
1449 | if(e == NULL) | |
1450 | { | |
1451 | char buff[5]; | |
1452 | int w, h; | |
1453 | SDL_Color white = {255, 255, 255, 255}; | |
1454 | SDL_Surface* surf; | |
1455 | FC_Image* cache_image; | |
1456 | ||
1457 | if(font->ttf_source == NULL) | |
1458 | return 0; | |
1459 | ||
1460 | FC_GetUTF8FromCodepoint(buff, codepoint); | |
1461 | ||
1462 | cache_image = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level); | |
1463 | if(cache_image == NULL) | |
1464 | { | |
1465 | FC_Log("SDL_FontCache: Failed to load cache image, so cannot add new glyphs!\n"); | |
1466 | return 0; | |
1467 | } | |
1468 | ||
1469 | #ifdef FC_USE_SDL_GPU | |
1470 | w = cache_image->w; | |
1471 | h = cache_image->h; | |
1472 | #else | |
1473 | SDL_QueryTexture(cache_image, NULL, NULL, &w, &h); | |
1474 | #endif | |
1475 | ||
1476 | surf = TTF_RenderUTF8_Blended(font->ttf_source, buff, white); | |
1477 | if(surf == NULL) | |
1478 | { | |
1479 | return 0; | |
1480 | } | |
1481 | ||
1482 | e = FC_PackGlyphData(font, codepoint, surf->w, w, h); | |
1483 | if(e == NULL) | |
1484 | { | |
1485 | // Grow the cache | |
1486 | FC_GrowGlyphCache(font); | |
1487 | ||
1488 | // Try packing again | |
1489 | e = FC_PackGlyphData(font, codepoint, surf->w, w, h); | |
1490 | if(e == NULL) | |
1491 | { | |
1492 | SDL_FreeSurface(surf); | |
1493 | return 0; | |
1494 | } | |
1495 | } | |
1496 | ||
1497 | // Render onto the cache texture | |
1498 | FC_AddGlyphToCache(font, surf); | |
1499 | ||
1500 | SDL_FreeSurface(surf); | |
1501 | } | |
1502 | ||
1503 | if(result != NULL && e != NULL) | |
1504 | *result = *e; | |
1505 | ||
1506 | return 1; | |
1507 | } | |
1508 | ||
1509 | ||
1510 | FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data) | |
1511 | { | |
1512 | return FC_MapInsert(font->glyphs, codepoint, glyph_data); | |
1513 | } | |
1514 | ||
1515 | ||
1516 | ||
1517 | // Drawing | |
1518 | static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) | |
1519 | { | |
1520 | const char* c = text; | |
1521 | FC_Rect srcRect; | |
1522 | FC_Rect dstRect; | |
1523 | FC_Rect dirtyRect = FC_MakeRect(x, y, 0, 0); | |
1524 | ||
1525 | FC_GlyphData glyph; | |
1526 | Uint32 codepoint; | |
1527 | ||
1528 | float destX = x; | |
1529 | float destY = y; | |
1530 | float destH; | |
1531 | float destLineSpacing; | |
1532 | float destLetterSpacing; | |
1533 | ||
1534 | if(font == NULL) | |
1535 | return dirtyRect; | |
1536 | ||
1537 | destH = font->height * scale.y; | |
1538 | destLineSpacing = font->lineSpacing*scale.y; | |
1539 | destLetterSpacing = font->letterSpacing*scale.x; | |
1540 | ||
1541 | if(c == NULL || font->glyph_cache_count == 0 || dest == NULL) | |
1542 | return dirtyRect; | |
1543 | ||
1544 | int newlineX = x; | |
1545 | ||
1546 | for(; *c != '\0'; c++) | |
1547 | { | |
1548 | if(*c == '\n') | |
1549 | { | |
1550 | destX = newlineX; | |
1551 | destY += destH + destLineSpacing; | |
1552 | continue; | |
1553 | } | |
1554 | ||
1555 | codepoint = FC_GetCodepointFromUTF8(&c, 1); // Increments 'c' to skip the extra UTF-8 bytes | |
1556 | if(!FC_GetGlyphData(font, &glyph, codepoint)) | |
1557 | { | |
1558 | codepoint = ' '; | |
1559 | if(!FC_GetGlyphData(font, &glyph, codepoint)) | |
1560 | continue; // Skip bad characters | |
1561 | } | |
1562 | ||
1563 | if (codepoint == ' ') | |
1564 | { | |
1565 | destX += glyph.rect.w*scale.x + destLetterSpacing; | |
1566 | continue; | |
1567 | } | |
1568 | /*if(destX >= dest->w) | |
1569 | continue; | |
1570 | if(destY >= dest->h) | |
1571 | continue;*/ | |
1572 | ||
1573 | #ifdef FC_USE_SDL_GPU | |
1574 | srcRect.x = glyph.rect.x; | |
1575 | srcRect.y = glyph.rect.y; | |
1576 | srcRect.w = glyph.rect.w; | |
1577 | srcRect.h = glyph.rect.h; | |
1578 | #else | |
1579 | srcRect = glyph.rect; | |
1580 | #endif | |
1581 | dstRect = fc_render_callback(FC_GetGlyphCacheLevel(font, glyph.cache_level), &srcRect, dest, destX, destY, scale.x, scale.y); | |
1582 | if(dirtyRect.w == 0 || dirtyRect.h == 0) | |
1583 | dirtyRect = dstRect; | |
1584 | else | |
1585 | dirtyRect = FC_RectUnion(dirtyRect, dstRect); | |
1586 | ||
1587 | destX += glyph.rect.w*scale.x + destLetterSpacing; | |
1588 | } | |
1589 | ||
1590 | return dirtyRect; | |
1591 | } | |
1592 | ||
1593 | static void set_color_for_all_caches(FC_Font* font, SDL_Color color) | |
1594 | { | |
1595 | // TODO: How can I predict which glyph caches are to be used? | |
1596 | FC_Image* img; | |
1597 | int i; | |
1598 | int num_levels = FC_GetNumCacheLevels(font); | |
1599 | for(i = 0; i < num_levels; ++i) | |
1600 | { | |
1601 | img = FC_GetGlyphCacheLevel(font, i); | |
1602 | set_color(img, color.r, color.g, color.b, FC_GET_ALPHA(color)); | |
1603 | } | |
1604 | } | |
1605 | ||
1606 | FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...) | |
1607 | { | |
1608 | if(formatted_text == NULL || font == NULL) | |
1609 | return FC_MakeRect(x, y, 0, 0); | |
1610 | ||
1611 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
1612 | ||
1613 | set_color_for_all_caches(font, font->default_color); | |
1614 | ||
1615 | return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); | |
1616 | } | |
1617 | ||
1618 | ||
1619 | ||
1620 | typedef struct FC_StringList | |
1621 | { | |
1622 | char* value; | |
1623 | struct FC_StringList* next; | |
1624 | } FC_StringList; | |
1625 | ||
1626 | void FC_StringListFree(FC_StringList* node) | |
1627 | { | |
1628 | // Delete the nodes in order | |
1629 | while(node != NULL) | |
1630 | { | |
1631 | FC_StringList* last = node; | |
1632 | node = node->next; | |
1633 | ||
1634 | free(last->value); | |
1635 | free(last); | |
1636 | } | |
1637 | } | |
1638 | ||
1639 | FC_StringList** FC_StringListPushBack(FC_StringList** node, char* value, Uint8 copy) | |
1640 | { | |
1641 | // Get to the last node | |
1642 | while(node != NULL && *node != NULL) | |
1643 | { | |
1644 | node = &(*node)->next; | |
1645 | } | |
1646 | ||
1647 | *node = (FC_StringList*)malloc(sizeof(FC_StringList)); | |
1648 | ||
1649 | (*node)->value = (copy? U8_strdup(value) : value); | |
1650 | (*node)->next = NULL; | |
1651 | ||
1652 | return node; | |
1653 | } | |
1654 | ||
1655 | static FC_StringList* FC_Explode(const char* text, char delimiter) | |
1656 | { | |
1657 | FC_StringList* head; | |
1658 | FC_StringList* new_node; | |
1659 | FC_StringList** node; | |
1660 | const char* start; | |
1661 | const char* end; | |
1662 | unsigned int size; | |
1663 | if(text == NULL) | |
1664 | return NULL; | |
1665 | ||
1666 | head = NULL; | |
1667 | node = &head; | |
1668 | ||
1669 | // Doesn't technically support UTF-8, but it's probably fine, right? | |
1670 | size = 0; | |
1671 | start = end = text; | |
1672 | while(1) | |
1673 | { | |
1674 | if(*end == delimiter || *end == '\0') | |
1675 | { | |
1676 | *node = (FC_StringList*)malloc(sizeof(FC_StringList)); | |
1677 | new_node = *node; | |
1678 | ||
1679 | new_node->value = (char*)malloc(size + 1); | |
1680 | memcpy(new_node->value, start, size); | |
1681 | new_node->value[size] = '\0'; | |
1682 | ||
1683 | new_node->next = NULL; | |
1684 | ||
1685 | if(*end == '\0') | |
1686 | break; | |
1687 | ||
1688 | node = &((*node)->next); | |
1689 | start = end+1; | |
1690 | size = 0; | |
1691 | } | |
1692 | else | |
1693 | ++size; | |
1694 | ||
1695 | ++end; | |
1696 | } | |
1697 | ||
1698 | return head; | |
1699 | } | |
1700 | ||
1701 | static FC_StringList* FC_ExplodeAndKeep(const char* text, char delimiter) | |
1702 | { | |
1703 | FC_StringList* head; | |
1704 | FC_StringList* new_node; | |
1705 | FC_StringList** node; | |
1706 | const char* start; | |
1707 | const char* end; | |
1708 | unsigned int size; | |
1709 | if(text == NULL) | |
1710 | return NULL; | |
1711 | ||
1712 | head = NULL; | |
1713 | node = &head; | |
1714 | ||
1715 | // Doesn't technically support UTF-8, but it's probably fine, right? | |
1716 | size = 0; | |
1717 | start = end = text; | |
1718 | while(1) | |
1719 | { | |
1720 | if(*end == delimiter || *end == '\0') | |
1721 | { | |
1722 | *node = (FC_StringList*)malloc(sizeof(FC_StringList)); | |
1723 | new_node = *node; | |
1724 | ||
1725 | new_node->value = (char*)malloc(size + 1); | |
1726 | memcpy(new_node->value, start, size); | |
1727 | new_node->value[size] = '\0'; | |
1728 | ||
1729 | new_node->next = NULL; | |
1730 | ||
1731 | if(*end == '\0') | |
1732 | break; | |
1733 | ||
1734 | node = &((*node)->next); | |
1735 | start = end; | |
1736 | size = 1; | |
1737 | } | |
1738 | else | |
1739 | ++size; | |
1740 | ||
1741 | ++end; | |
1742 | } | |
1743 | ||
1744 | return head; | |
1745 | } | |
1746 | ||
1747 | static void FC_RenderAlign(FC_Font* font, FC_Target* dest, float x, float y, int width, FC_Scale scale, FC_AlignEnum align, const char* text) | |
1748 | { | |
1749 | switch(align) | |
1750 | { | |
1751 | case FC_ALIGN_LEFT: | |
1752 | FC_RenderLeft(font, dest, x, y, scale, text); | |
1753 | break; | |
1754 | case FC_ALIGN_CENTER: | |
1755 | FC_RenderCenter(font, dest, x + width/2, y, scale, text); | |
1756 | break; | |
1757 | case FC_ALIGN_RIGHT: | |
1758 | FC_RenderRight(font, dest, x + width, y, scale, text); | |
1759 | break; | |
1760 | } | |
1761 | } | |
1762 | ||
1763 | static FC_StringList* FC_GetBufferFitToColumn(FC_Font* font, int width, FC_Scale scale, Uint8 keep_newlines) | |
1764 | { | |
1765 | FC_StringList* result = NULL; | |
1766 | FC_StringList** current = &result; | |
1767 | ||
1768 | FC_StringList *ls, *iter; | |
1769 | ||
1770 | ls = (keep_newlines? FC_ExplodeAndKeep(fc_buffer, '\n') : FC_Explode(fc_buffer, '\n')); | |
1771 | for(iter = ls; iter != NULL; iter = iter->next) | |
1772 | { | |
1773 | char* line = iter->value; | |
1774 | ||
1775 | // If line is too long, then add words one at a time until we go over. | |
1776 | if(width > 0 && FC_GetWidth(font, "%s", line) > width) | |
1777 | { | |
1778 | FC_StringList *words, *word_iter; | |
1779 | ||
1780 | words = FC_Explode(line, ' '); | |
1781 | // Skip the first word for the iterator, so there will always be at least one word per line | |
1782 | line = new_concat(words->value, " "); | |
1783 | for(word_iter = words->next; word_iter != NULL; word_iter = word_iter->next) | |
1784 | { | |
1785 | char* line_plus_word = new_concat(line, word_iter->value); | |
1786 | char* word_plus_space = new_concat(word_iter->value, " "); | |
1787 | if(FC_GetWidth(font, "%s", line_plus_word) > width) | |
1788 | { | |
1789 | current = FC_StringListPushBack(current, line, 0); | |
1790 | ||
1791 | line = word_plus_space; | |
1792 | } | |
1793 | else | |
1794 | { | |
1795 | replace_concat(&line, word_plus_space); | |
1796 | free(word_plus_space); | |
1797 | } | |
1798 | free(line_plus_word); | |
1799 | } | |
1800 | current = FC_StringListPushBack(current, line, 0); | |
1801 | FC_StringListFree(words); | |
1802 | } | |
1803 | else | |
1804 | { | |
1805 | current = FC_StringListPushBack(current, line, 0); | |
1806 | iter->value = NULL; | |
1807 | } | |
1808 | } | |
1809 | FC_StringListFree(ls); | |
1810 | ||
1811 | return result; | |
1812 | } | |
1813 | ||
1814 | static void FC_DrawColumnFromBuffer(FC_Font* font, FC_Target* dest, FC_Rect box, int* total_height, FC_Scale scale, FC_AlignEnum align) | |
1815 | { | |
1816 | int y = box.y; | |
1817 | FC_StringList *ls, *iter; | |
1818 | ||
1819 | ls = FC_GetBufferFitToColumn(font, box.w, scale, 0); | |
1820 | for(iter = ls; iter != NULL; iter = iter->next) | |
1821 | { | |
1822 | FC_RenderAlign(font, dest, box.x, y, box.w, scale, align, iter->value); | |
1823 | y += FC_GetLineHeight(font); | |
1824 | } | |
1825 | FC_StringListFree(ls); | |
1826 | ||
1827 | if(total_height != NULL) | |
1828 | *total_height = y - box.y; | |
1829 | } | |
1830 | ||
1831 | FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...) | |
1832 | { | |
1833 | Uint8 useClip; | |
1834 | if(formatted_text == NULL || font == NULL) | |
1835 | return FC_MakeRect(box.x, box.y, 0, 0); | |
1836 | ||
1837 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
1838 | ||
1839 | useClip = has_clip(dest); | |
1840 | FC_Rect oldclip, newclip; | |
1841 | if(useClip) | |
1842 | { | |
1843 | oldclip = get_clip(dest); | |
1844 | newclip = FC_RectIntersect(oldclip, box); | |
1845 | } | |
1846 | else | |
1847 | newclip = box; | |
1848 | ||
1849 | set_clip(dest, &newclip); | |
1850 | ||
1851 | set_color_for_all_caches(font, font->default_color); | |
1852 | ||
1853 | FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT); | |
1854 | ||
1855 | if(useClip) | |
1856 | set_clip(dest, &oldclip); | |
1857 | else | |
1858 | set_clip(dest, NULL); | |
1859 | ||
1860 | return box; | |
1861 | } | |
1862 | ||
1863 | FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...) | |
1864 | { | |
1865 | Uint8 useClip; | |
1866 | if(formatted_text == NULL || font == NULL) | |
1867 | return FC_MakeRect(box.x, box.y, 0, 0); | |
1868 | ||
1869 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
1870 | ||
1871 | useClip = has_clip(dest); | |
1872 | FC_Rect oldclip, newclip; | |
1873 | if(useClip) | |
1874 | { | |
1875 | oldclip = get_clip(dest); | |
1876 | newclip = FC_RectIntersect(oldclip, box); | |
1877 | } | |
1878 | else | |
1879 | newclip = box; | |
1880 | set_clip(dest, &newclip); | |
1881 | ||
1882 | set_color_for_all_caches(font, font->default_color); | |
1883 | ||
1884 | FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), align); | |
1885 | ||
1886 | if(useClip) | |
1887 | set_clip(dest, &oldclip); | |
1888 | else | |
1889 | set_clip(dest, NULL); | |
1890 | ||
1891 | return box; | |
1892 | } | |
1893 | ||
1894 | FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...) | |
1895 | { | |
1896 | Uint8 useClip; | |
1897 | if(formatted_text == NULL || font == NULL) | |
1898 | return FC_MakeRect(box.x, box.y, 0, 0); | |
1899 | ||
1900 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
1901 | ||
1902 | useClip = has_clip(dest); | |
1903 | FC_Rect oldclip, newclip; | |
1904 | if(useClip) | |
1905 | { | |
1906 | oldclip = get_clip(dest); | |
1907 | newclip = FC_RectIntersect(oldclip, box); | |
1908 | } | |
1909 | else | |
1910 | newclip = box; | |
1911 | set_clip(dest, &newclip); | |
1912 | ||
1913 | set_color_for_all_caches(font, font->default_color); | |
1914 | ||
1915 | FC_DrawColumnFromBuffer(font, dest, box, NULL, scale, FC_ALIGN_LEFT); | |
1916 | ||
1917 | if(useClip) | |
1918 | set_clip(dest, &oldclip); | |
1919 | else | |
1920 | set_clip(dest, NULL); | |
1921 | ||
1922 | return box; | |
1923 | } | |
1924 | ||
1925 | FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...) | |
1926 | { | |
1927 | Uint8 useClip; | |
1928 | if(formatted_text == NULL || font == NULL) | |
1929 | return FC_MakeRect(box.x, box.y, 0, 0); | |
1930 | ||
1931 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
1932 | ||
1933 | useClip = has_clip(dest); | |
1934 | FC_Rect oldclip, newclip; | |
1935 | if(useClip) | |
1936 | { | |
1937 | oldclip = get_clip(dest); | |
1938 | newclip = FC_RectIntersect(oldclip, box); | |
1939 | } | |
1940 | else | |
1941 | newclip = box; | |
1942 | set_clip(dest, &newclip); | |
1943 | ||
1944 | set_color_for_all_caches(font, color); | |
1945 | ||
1946 | FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT); | |
1947 | ||
1948 | if(useClip) | |
1949 | set_clip(dest, &oldclip); | |
1950 | else | |
1951 | set_clip(dest, NULL); | |
1952 | ||
1953 | return box; | |
1954 | } | |
1955 | ||
1956 | FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...) | |
1957 | { | |
1958 | Uint8 useClip; | |
1959 | if(formatted_text == NULL || font == NULL) | |
1960 | return FC_MakeRect(box.x, box.y, 0, 0); | |
1961 | ||
1962 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
1963 | ||
1964 | useClip = has_clip(dest); | |
1965 | FC_Rect oldclip, newclip; | |
1966 | if(useClip) | |
1967 | { | |
1968 | oldclip = get_clip(dest); | |
1969 | newclip = FC_RectIntersect(oldclip, box); | |
1970 | } | |
1971 | else | |
1972 | newclip = box; | |
1973 | set_clip(dest, &newclip); | |
1974 | ||
1975 | set_color_for_all_caches(font, effect.color); | |
1976 | ||
1977 | FC_DrawColumnFromBuffer(font, dest, box, NULL, effect.scale, effect.alignment); | |
1978 | ||
1979 | if(useClip) | |
1980 | set_clip(dest, &oldclip); | |
1981 | else | |
1982 | set_clip(dest, NULL); | |
1983 | ||
1984 | return box; | |
1985 | } | |
1986 | ||
1987 | FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...) | |
1988 | { | |
1989 | FC_Rect box = {x, y, width, 0}; | |
1990 | int total_height; | |
1991 | ||
1992 | if(formatted_text == NULL || font == NULL) | |
1993 | return FC_MakeRect(x, y, 0, 0); | |
1994 | ||
1995 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
1996 | ||
1997 | set_color_for_all_caches(font, font->default_color); | |
1998 | ||
1999 | FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT); | |
2000 | ||
2001 | return FC_MakeRect(box.x, box.y, width, total_height); | |
2002 | } | |
2003 | ||
2004 | FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...) | |
2005 | { | |
2006 | FC_Rect box = {x, y, width, 0}; | |
2007 | int total_height; | |
2008 | ||
2009 | if(formatted_text == NULL || font == NULL) | |
2010 | return FC_MakeRect(x, y, 0, 0); | |
2011 | ||
2012 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2013 | ||
2014 | set_color_for_all_caches(font, font->default_color); | |
2015 | ||
2016 | switch(align) | |
2017 | { | |
2018 | case FC_ALIGN_CENTER: | |
2019 | box.x -= width/2; | |
2020 | break; | |
2021 | case FC_ALIGN_RIGHT: | |
2022 | box.x -= width; | |
2023 | break; | |
2024 | default: | |
2025 | break; | |
2026 | } | |
2027 | ||
2028 | FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), align); | |
2029 | ||
2030 | return FC_MakeRect(box.x, box.y, width, total_height); | |
2031 | } | |
2032 | ||
2033 | FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...) | |
2034 | { | |
2035 | FC_Rect box = {x, y, width, 0}; | |
2036 | int total_height; | |
2037 | ||
2038 | if(formatted_text == NULL || font == NULL) | |
2039 | return FC_MakeRect(x, y, 0, 0); | |
2040 | ||
2041 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2042 | ||
2043 | set_color_for_all_caches(font, font->default_color); | |
2044 | ||
2045 | FC_DrawColumnFromBuffer(font, dest, box, &total_height, scale, FC_ALIGN_LEFT); | |
2046 | ||
2047 | return FC_MakeRect(box.x, box.y, width, total_height); | |
2048 | } | |
2049 | ||
2050 | FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...) | |
2051 | { | |
2052 | FC_Rect box = {x, y, width, 0}; | |
2053 | int total_height; | |
2054 | ||
2055 | if(formatted_text == NULL || font == NULL) | |
2056 | return FC_MakeRect(x, y, 0, 0); | |
2057 | ||
2058 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2059 | ||
2060 | set_color_for_all_caches(font, color); | |
2061 | ||
2062 | FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT); | |
2063 | ||
2064 | return FC_MakeRect(box.x, box.y, width, total_height); | |
2065 | } | |
2066 | ||
2067 | FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...) | |
2068 | { | |
2069 | FC_Rect box = {x, y, width, 0}; | |
2070 | int total_height; | |
2071 | ||
2072 | if(formatted_text == NULL || font == NULL) | |
2073 | return FC_MakeRect(x, y, 0, 0); | |
2074 | ||
2075 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2076 | ||
2077 | set_color_for_all_caches(font, effect.color); | |
2078 | ||
2079 | switch(effect.alignment) | |
2080 | { | |
2081 | case FC_ALIGN_CENTER: | |
2082 | box.x -= width/2; | |
2083 | break; | |
2084 | case FC_ALIGN_RIGHT: | |
2085 | box.x -= width; | |
2086 | break; | |
2087 | default: | |
2088 | break; | |
2089 | } | |
2090 | ||
2091 | FC_DrawColumnFromBuffer(font, dest, box, &total_height, effect.scale, effect.alignment); | |
2092 | ||
2093 | return FC_MakeRect(box.x, box.y, width, total_height); | |
2094 | } | |
2095 | ||
2096 | static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) | |
2097 | { | |
2098 | FC_Rect result = {x, y, 0, 0}; | |
2099 | if(text == NULL || font == NULL) | |
2100 | return result; | |
2101 | ||
2102 | char* str = U8_strdup(text); | |
2103 | char* del = str; | |
2104 | char* c; | |
2105 | ||
2106 | // Go through str, when you find a \n, replace it with \0 and print it | |
2107 | // then move down, back, and continue. | |
2108 | for(c = str; *c != '\0';) | |
2109 | { | |
2110 | if(*c == '\n') | |
2111 | { | |
2112 | *c = '\0'; | |
2113 | result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result); | |
2114 | *c = '\n'; | |
2115 | c++; | |
2116 | str = c; | |
2117 | y += scale.y*font->height; | |
2118 | } | |
2119 | else | |
2120 | c++; | |
2121 | } | |
2122 | ||
2123 | result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result); | |
2124 | ||
2125 | free(del); | |
2126 | return result; | |
2127 | } | |
2128 | ||
2129 | static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) | |
2130 | { | |
2131 | FC_Rect result = {x, y, 0, 0}; | |
2132 | if(text == NULL || font == NULL) | |
2133 | return result; | |
2134 | ||
2135 | char* str = U8_strdup(text); | |
2136 | char* del = str; | |
2137 | char* c; | |
2138 | ||
2139 | for(c = str; *c != '\0';) | |
2140 | { | |
2141 | if(*c == '\n') | |
2142 | { | |
2143 | *c = '\0'; | |
2144 | result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result); | |
2145 | *c = '\n'; | |
2146 | c++; | |
2147 | str = c; | |
2148 | y += scale.y*font->height; | |
2149 | } | |
2150 | else | |
2151 | c++; | |
2152 | } | |
2153 | ||
2154 | result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result); | |
2155 | ||
2156 | free(del); | |
2157 | return result; | |
2158 | } | |
2159 | ||
2160 | ||
2161 | ||
2162 | FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...) | |
2163 | { | |
2164 | if(formatted_text == NULL || font == NULL) | |
2165 | return FC_MakeRect(x, y, 0, 0); | |
2166 | ||
2167 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2168 | ||
2169 | set_color_for_all_caches(font, font->default_color); | |
2170 | ||
2171 | return FC_RenderLeft(font, dest, x, y, scale, fc_buffer); | |
2172 | } | |
2173 | ||
2174 | FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...) | |
2175 | { | |
2176 | if(formatted_text == NULL || font == NULL) | |
2177 | return FC_MakeRect(x, y, 0, 0); | |
2178 | ||
2179 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2180 | ||
2181 | set_color_for_all_caches(font, font->default_color); | |
2182 | ||
2183 | FC_Rect result; | |
2184 | switch(align) | |
2185 | { | |
2186 | case FC_ALIGN_LEFT: | |
2187 | result = FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); | |
2188 | break; | |
2189 | case FC_ALIGN_CENTER: | |
2190 | result = FC_RenderCenter(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); | |
2191 | break; | |
2192 | case FC_ALIGN_RIGHT: | |
2193 | result = FC_RenderRight(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); | |
2194 | break; | |
2195 | default: | |
2196 | result = FC_MakeRect(x, y, 0, 0); | |
2197 | break; | |
2198 | } | |
2199 | ||
2200 | return result; | |
2201 | } | |
2202 | ||
2203 | FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...) | |
2204 | { | |
2205 | if(formatted_text == NULL || font == NULL) | |
2206 | return FC_MakeRect(x, y, 0, 0); | |
2207 | ||
2208 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2209 | ||
2210 | set_color_for_all_caches(font, color); | |
2211 | ||
2212 | return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); | |
2213 | } | |
2214 | ||
2215 | ||
2216 | FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...) | |
2217 | { | |
2218 | if(formatted_text == NULL || font == NULL) | |
2219 | return FC_MakeRect(x, y, 0, 0); | |
2220 | ||
2221 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2222 | ||
2223 | set_color_for_all_caches(font, effect.color); | |
2224 | ||
2225 | FC_Rect result; | |
2226 | switch(effect.alignment) | |
2227 | { | |
2228 | case FC_ALIGN_LEFT: | |
2229 | result = FC_RenderLeft(font, dest, x, y, effect.scale, fc_buffer); | |
2230 | break; | |
2231 | case FC_ALIGN_CENTER: | |
2232 | result = FC_RenderCenter(font, dest, x, y, effect.scale, fc_buffer); | |
2233 | break; | |
2234 | case FC_ALIGN_RIGHT: | |
2235 | result = FC_RenderRight(font, dest, x, y, effect.scale, fc_buffer); | |
2236 | break; | |
2237 | default: | |
2238 | result = FC_MakeRect(x, y, 0, 0); | |
2239 | break; | |
2240 | } | |
2241 | ||
2242 | return result; | |
2243 | } | |
2244 | ||
2245 | ||
2246 | ||
2247 | ||
2248 | // Getters | |
2249 | ||
2250 | ||
2251 | FC_FilterEnum FC_GetFilterMode(FC_Font* font) | |
2252 | { | |
2253 | if(font == NULL) | |
2254 | return FC_FILTER_NEAREST; | |
2255 | ||
2256 | return font->filter; | |
2257 | } | |
2258 | ||
2259 | Uint16 FC_GetLineHeight(FC_Font* font) | |
2260 | { | |
2261 | if(font == NULL) | |
2262 | return 0; | |
2263 | ||
2264 | return font->height; | |
2265 | } | |
2266 | ||
2267 | Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...) | |
2268 | { | |
2269 | if(formatted_text == NULL || font == NULL) | |
2270 | return 0; | |
2271 | ||
2272 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2273 | ||
2274 | Uint16 numLines = 1; | |
2275 | const char* c; | |
2276 | ||
2277 | for (c = fc_buffer; *c != '\0'; c++) | |
2278 | { | |
2279 | if(*c == '\n') | |
2280 | numLines++; | |
2281 | } | |
2282 | ||
2283 | // Actual height of letter region + line spacing | |
2284 | return font->height*numLines + font->lineSpacing*(numLines - 1); //height*numLines; | |
2285 | } | |
2286 | ||
2287 | Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...) | |
2288 | { | |
2289 | if(formatted_text == NULL || font == NULL) | |
2290 | return 0; | |
2291 | ||
2292 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2293 | ||
2294 | const char* c; | |
2295 | Uint16 width = 0; | |
2296 | Uint16 bigWidth = 0; // Allows for multi-line strings | |
2297 | ||
2298 | for (c = fc_buffer; *c != '\0'; c++) | |
2299 | { | |
2300 | if(*c == '\n') | |
2301 | { | |
2302 | bigWidth = bigWidth >= width? bigWidth : width; | |
2303 | width = 0; | |
2304 | continue; | |
2305 | } | |
2306 | ||
2307 | FC_GlyphData glyph; | |
2308 | Uint32 codepoint = FC_GetCodepointFromUTF8(&c, 1); | |
2309 | if(FC_GetGlyphData(font, &glyph, codepoint) || FC_GetGlyphData(font, &glyph, ' ')) | |
2310 | width += glyph.rect.w; | |
2311 | } | |
2312 | bigWidth = bigWidth >= width? bigWidth : width; | |
2313 | ||
2314 | return bigWidth; | |
2315 | } | |
2316 | ||
2317 | // If width == -1, use no width limit | |
2318 | FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...) | |
2319 | { | |
2320 | FC_Rect result = {0, 0, 1, FC_GetLineHeight(font)}; | |
2321 | FC_StringList *ls, *iter; | |
2322 | int num_lines = 0; | |
2323 | Uint8 done = 0; | |
2324 | ||
2325 | if(formatted_text == NULL || column_width == 0 || position_index == 0 || font == NULL) | |
2326 | return result; | |
2327 | ||
2328 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2329 | ||
2330 | ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1); | |
2331 | for(iter = ls; iter != NULL;) | |
2332 | { | |
2333 | char* line; | |
2334 | int i = 0; | |
2335 | FC_StringList* next_iter = iter->next; | |
2336 | ||
2337 | ++num_lines; | |
2338 | for(line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line)) | |
2339 | { | |
2340 | ++i; | |
2341 | --position_index; | |
2342 | if(position_index == 0) | |
2343 | { | |
2344 | // FIXME: Doesn't handle box-wrapped newlines correctly | |
2345 | line = (char*)U8_next(line); | |
2346 | line[0] = '\0'; | |
2347 | result.x = FC_GetWidth(font, "%s", iter->value); | |
2348 | done = 1; | |
2349 | break; | |
2350 | } | |
2351 | } | |
2352 | if(done) | |
2353 | break; | |
2354 | ||
2355 | // Prevent line wrapping if there are no more lines | |
2356 | if(next_iter == NULL && !done) | |
2357 | result.x = FC_GetWidth(font, "%s", iter->value); | |
2358 | iter = next_iter; | |
2359 | } | |
2360 | FC_StringListFree(ls); | |
2361 | ||
2362 | if(num_lines > 1) | |
2363 | { | |
2364 | result.y = (num_lines - 1) * FC_GetLineHeight(font); | |
2365 | } | |
2366 | ||
2367 | return result; | |
2368 | } | |
2369 | ||
2370 | ||
2371 | Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...) | |
2372 | { | |
2373 | int y = 0; | |
2374 | ||
2375 | FC_StringList *ls, *iter; | |
2376 | ||
2377 | if(font == NULL) | |
2378 | return 0; | |
2379 | ||
2380 | if(formatted_text == NULL || width == 0) | |
2381 | return font->height; | |
2382 | ||
2383 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2384 | ||
2385 | ls = FC_GetBufferFitToColumn(font, width, FC_MakeScale(1,1), 0); | |
2386 | for(iter = ls; iter != NULL; iter = iter->next) | |
2387 | { | |
2388 | y += FC_GetLineHeight(font); | |
2389 | } | |
2390 | FC_StringListFree(ls); | |
2391 | ||
2392 | return y; | |
2393 | } | |
2394 | ||
2395 | static int FC_GetAscentFromCodepoint(FC_Font* font, Uint32 codepoint) | |
2396 | { | |
2397 | FC_GlyphData glyph; | |
2398 | ||
2399 | if(font == NULL) | |
2400 | return 0; | |
2401 | ||
2402 | // FIXME: Store ascent so we can return it here | |
2403 | FC_GetGlyphData(font, &glyph, codepoint); | |
2404 | return glyph.rect.h; | |
2405 | } | |
2406 | ||
2407 | static int FC_GetDescentFromCodepoint(FC_Font* font, Uint32 codepoint) | |
2408 | { | |
2409 | FC_GlyphData glyph; | |
2410 | ||
2411 | if(font == NULL) | |
2412 | return 0; | |
2413 | ||
2414 | // FIXME: Store descent so we can return it here | |
2415 | FC_GetGlyphData(font, &glyph, codepoint); | |
2416 | return glyph.rect.h; | |
2417 | } | |
2418 | ||
2419 | int FC_GetAscent(FC_Font* font, const char* formatted_text, ...) | |
2420 | { | |
2421 | Uint32 codepoint; | |
2422 | int max, ascent; | |
2423 | const char* c; | |
2424 | ||
2425 | if(font == NULL) | |
2426 | return 0; | |
2427 | ||
2428 | if(formatted_text == NULL) | |
2429 | return font->ascent; | |
2430 | ||
2431 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2432 | ||
2433 | max = 0; | |
2434 | c = fc_buffer; | |
2435 | ||
2436 | while(*c != '\0') | |
2437 | { | |
2438 | codepoint = FC_GetCodepointFromUTF8(&c, 1); | |
2439 | if(codepoint != 0) | |
2440 | { | |
2441 | ascent = FC_GetAscentFromCodepoint(font, codepoint); | |
2442 | if(ascent > max) | |
2443 | max = ascent; | |
2444 | } | |
2445 | ++c; | |
2446 | } | |
2447 | return max; | |
2448 | } | |
2449 | ||
2450 | int FC_GetDescent(FC_Font* font, const char* formatted_text, ...) | |
2451 | { | |
2452 | Uint32 codepoint; | |
2453 | int max, descent; | |
2454 | const char* c; | |
2455 | ||
2456 | if(font == NULL) | |
2457 | return 0; | |
2458 | ||
2459 | if(formatted_text == NULL) | |
2460 | return font->descent; | |
2461 | ||
2462 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2463 | ||
2464 | max = 0; | |
2465 | c = fc_buffer; | |
2466 | ||
2467 | while(*c != '\0') | |
2468 | { | |
2469 | codepoint = FC_GetCodepointFromUTF8(&c, 1); | |
2470 | if(codepoint != 0) | |
2471 | { | |
2472 | descent = FC_GetDescentFromCodepoint(font, codepoint); | |
2473 | if(descent > max) | |
2474 | max = descent; | |
2475 | } | |
2476 | ++c; | |
2477 | } | |
2478 | return max; | |
2479 | } | |
2480 | ||
2481 | int FC_GetBaseline(FC_Font* font) | |
2482 | { | |
2483 | if(font == NULL) | |
2484 | return 0; | |
2485 | ||
2486 | return font->baseline; | |
2487 | } | |
2488 | ||
2489 | int FC_GetSpacing(FC_Font* font) | |
2490 | { | |
2491 | if(font == NULL) | |
2492 | return 0; | |
2493 | ||
2494 | return font->letterSpacing; | |
2495 | } | |
2496 | ||
2497 | int FC_GetLineSpacing(FC_Font* font) | |
2498 | { | |
2499 | if(font == NULL) | |
2500 | return 0; | |
2501 | ||
2502 | return font->lineSpacing; | |
2503 | } | |
2504 | ||
2505 | Uint16 FC_GetMaxWidth(FC_Font* font) | |
2506 | { | |
2507 | if(font == NULL) | |
2508 | return 0; | |
2509 | ||
2510 | return font->maxWidth; | |
2511 | } | |
2512 | ||
2513 | SDL_Color FC_GetDefaultColor(FC_Font* font) | |
2514 | { | |
2515 | if(font == NULL) | |
2516 | { | |
2517 | SDL_Color c = {0,0,0,255}; | |
2518 | return c; | |
2519 | } | |
2520 | ||
2521 | return font->default_color; | |
2522 | } | |
2523 | ||
2524 | ||
2525 | Uint8 FC_InRect(float x, float y, FC_Rect input_rect) | |
2526 | { | |
2527 | return (input_rect.x <= x && x <= input_rect.x + input_rect.w && input_rect.y <= y && y <= input_rect.y + input_rect.h); | |
2528 | } | |
2529 | ||
2530 | // TODO: Make it work with alignment | |
2531 | Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...) | |
2532 | { | |
2533 | FC_StringList *ls, *iter; | |
2534 | Uint8 done = 0; | |
2535 | int height = FC_GetLineHeight(font); | |
2536 | Uint16 position = 0; | |
2537 | int current_x = 0; | |
2538 | int current_y = 0; | |
2539 | FC_GlyphData glyph_data; | |
2540 | ||
2541 | if(formatted_text == NULL || column_width == 0 || font == NULL) | |
2542 | return 0; | |
2543 | ||
2544 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); | |
2545 | ||
2546 | ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1); | |
2547 | for(iter = ls; iter != NULL; iter = iter->next) | |
2548 | { | |
2549 | char* line; | |
2550 | ||
2551 | for(line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line)) | |
2552 | { | |
2553 | if(FC_GetGlyphData(font, &glyph_data, FC_GetCodepointFromUTF8((const char**)&line, 0))) | |
2554 | { | |
2555 | if(FC_InRect(x, y, FC_MakeRect(current_x, current_y, glyph_data.rect.w, glyph_data.rect.h))) | |
2556 | { | |
2557 | done = 1; | |
2558 | break; | |
2559 | } | |
2560 | ||
2561 | current_x += glyph_data.rect.w; | |
2562 | } | |
2563 | position++; | |
2564 | } | |
2565 | if(done) | |
2566 | break; | |
2567 | ||
2568 | current_x = 0; | |
2569 | current_y += height; | |
2570 | if(y < current_y) | |
2571 | break; | |
2572 | } | |
2573 | FC_StringListFree(ls); | |
2574 | ||
2575 | return position; | |
2576 | } | |
2577 | ||
2578 | ||
2579 | ||
2580 | ||
2581 | // Setters | |
2582 | ||
2583 | ||
2584 | void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter) | |
2585 | { | |
2586 | if(font == NULL) | |
2587 | return; | |
2588 | ||
2589 | if(font->filter != filter) | |
2590 | { | |
2591 | font->filter = filter; | |
2592 | ||
2593 | #ifdef FC_USE_SDL_GPU | |
2594 | // Update each texture to use this filter mode | |
2595 | { | |
2596 | int i; | |
2597 | GPU_FilterEnum gpu_filter = GPU_FILTER_NEAREST; | |
2598 | if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) | |
2599 | gpu_filter = GPU_FILTER_LINEAR; | |
2600 | ||
2601 | for(i = 0; i < font->glyph_cache_count; ++i) | |
2602 | { | |
2603 | GPU_SetImageFilter(font->glyph_cache[i], gpu_filter); | |
2604 | } | |
2605 | } | |
2606 | #endif | |
2607 | } | |
2608 | } | |
2609 | ||
2610 | ||
2611 | void FC_SetSpacing(FC_Font* font, int LetterSpacing) | |
2612 | { | |
2613 | if(font == NULL) | |
2614 | return; | |
2615 | ||
2616 | font->letterSpacing = LetterSpacing; | |
2617 | } | |
2618 | ||
2619 | void FC_SetLineSpacing(FC_Font* font, int LineSpacing) | |
2620 | { | |
2621 | if(font == NULL) | |
2622 | return; | |
2623 | ||
2624 | font->lineSpacing = LineSpacing; | |
2625 | } | |
2626 | ||
2627 | void FC_SetDefaultColor(FC_Font* font, SDL_Color color) | |
2628 | { | |
2629 | if(font == NULL) | |
2630 | return; | |
2631 | ||
2632 | font->default_color = color; | |
2633 | } | |
2634 | ||
2635 | ||
2636 | ||
2637 | ||
2638 | ||
2639 |
0 | /* | |
1 | SDL_FontCache v0.9.0: A font cache for SDL and SDL_ttf | |
2 | by Jonathan Dearborn | |
3 | Dedicated to the memory of Florian Hufsky | |
4 | ||
5 | License: | |
6 | The short: | |
7 | Use it however you'd like, but keep the copyright and license notice | |
8 | whenever these files or parts of them are distributed in uncompiled form. | |
9 | ||
10 | The long: | |
11 | Copyright (c) 2016 Jonathan Dearborn | |
12 | ||
13 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
14 | of this software and associated documentation files (the "Software"), to deal | |
15 | in the Software without restriction, including without limitation the rights | |
16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
17 | copies of the Software, and to permit persons to whom the Software is | |
18 | furnished to do so, subject to the following conditions: | |
19 | ||
20 | The above copyright notice and this permission notice shall be included in | |
21 | all copies or substantial portions of the Software. | |
22 | ||
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
29 | THE SOFTWARE. | |
30 | */ | |
31 | ||
32 | #ifndef _SDL_FONTCACHE_H__ | |
33 | #define _SDL_FONTCACHE_H__ | |
34 | ||
35 | #include "SDL.h" | |
36 | #include "SDL_ttf.h" | |
37 | ||
38 | #ifdef FC_USE_SDL_GPU | |
39 | #include "SDL_gpu.h" | |
40 | #endif | |
41 | ||
42 | ||
43 | #include <stdarg.h> | |
44 | ||
45 | #ifdef __cplusplus | |
46 | extern "C" { | |
47 | #endif | |
48 | ||
49 | ||
50 | // Let's pretend this exists... | |
51 | #define TTF_STYLE_OUTLINE 16 | |
52 | ||
53 | ||
54 | ||
55 | // Differences between SDL_Renderer and SDL_gpu | |
56 | #ifdef FC_USE_SDL_GPU | |
57 | #define FC_Rect GPU_Rect | |
58 | #define FC_Target GPU_Target | |
59 | #define FC_Image GPU_Image | |
60 | #define FC_Log GPU_LogError | |
61 | #else | |
62 | #define FC_Rect SDL_Rect | |
63 | #define FC_Target SDL_Renderer | |
64 | #define FC_Image SDL_Texture | |
65 | #define FC_Log SDL_Log | |
66 | #endif | |
67 | ||
68 | ||
69 | // SDL_FontCache types | |
70 | ||
71 | typedef enum | |
72 | { | |
73 | FC_ALIGN_LEFT, | |
74 | FC_ALIGN_CENTER, | |
75 | FC_ALIGN_RIGHT | |
76 | } FC_AlignEnum; | |
77 | ||
78 | typedef enum | |
79 | { | |
80 | FC_FILTER_NEAREST, | |
81 | FC_FILTER_LINEAR | |
82 | } FC_FilterEnum; | |
83 | ||
84 | typedef struct FC_Scale | |
85 | { | |
86 | float x; | |
87 | float y; | |
88 | ||
89 | } FC_Scale; | |
90 | ||
91 | typedef struct FC_Effect | |
92 | { | |
93 | FC_AlignEnum alignment; | |
94 | FC_Scale scale; | |
95 | SDL_Color color; | |
96 | ||
97 | } FC_Effect; | |
98 | ||
99 | // Opaque type | |
100 | typedef struct FC_Font FC_Font; | |
101 | ||
102 | ||
103 | typedef struct FC_GlyphData | |
104 | { | |
105 | SDL_Rect rect; | |
106 | int cache_level; | |
107 | ||
108 | } FC_GlyphData; | |
109 | ||
110 | ||
111 | ||
112 | ||
113 | // Object creation | |
114 | ||
115 | FC_Rect FC_MakeRect(float x, float y, float w, float h); | |
116 | ||
117 | FC_Scale FC_MakeScale(float x, float y); | |
118 | ||
119 | SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a); | |
120 | ||
121 | FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color); | |
122 | ||
123 | FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h); | |
124 | ||
125 | ||
126 | ||
127 | // Font object | |
128 | ||
129 | FC_Font* FC_CreateFont(void); | |
130 | ||
131 | #ifdef FC_USE_SDL_GPU | |
132 | Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style); | |
133 | ||
134 | Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color); | |
135 | ||
136 | Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style); | |
137 | #else | |
138 | Uint8 FC_LoadFont(FC_Font* font, SDL_Renderer* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style); | |
139 | ||
140 | Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color); | |
141 | ||
142 | Uint8 FC_LoadFont_RW(FC_Font* font, SDL_Renderer* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style); | |
143 | #endif | |
144 | ||
145 | void FC_ClearFont(FC_Font* font); | |
146 | ||
147 | void FC_FreeFont(FC_Font* font); | |
148 | ||
149 | ||
150 | ||
151 | // Built-in loading strings | |
152 | ||
153 | const char* FC_GetStringASCII(void); | |
154 | ||
155 | const char* FC_GetStringLatin1(void); | |
156 | ||
157 | const char* FC_GetStringASCII_Latin1(void); | |
158 | ||
159 | ||
160 | // UTF-8 to SDL_FontCache codepoint conversion | |
161 | ||
162 | /*! | |
163 | Returns the Uint32 codepoint (not UTF-32) parsed from the given UTF-8 string. | |
164 | \param c A pointer to a string of proper UTF-8 character values. | |
165 | \param advance_pointer If true, the source pointer will be incremented to skip the extra bytes from multibyte codepoints. | |
166 | */ | |
167 | Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer); | |
168 | ||
169 | /*! | |
170 | Parses the given codepoint and stores the UTF-8 bytes in 'result'. The result is NULL terminated. | |
171 | \param result A memory buffer for the UTF-8 values. Must be at least 5 bytes long. | |
172 | \param codepoint The Uint32 codepoint to parse (not UTF-32). | |
173 | */ | |
174 | void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint); | |
175 | ||
176 | ||
177 | // UTF-8 string operations | |
178 | ||
179 | /*! Allocates a new string of 'size' bytes that is already NULL-terminated. The NULL byte counts toward the size limit, as usual. Returns NULL if size is 0. */ | |
180 | char* U8_alloc(unsigned int size); | |
181 | ||
182 | /*! Deallocates the given string. */ | |
183 | void U8_free(char* string); | |
184 | ||
185 | /*! Allocates a copy of the given string. */ | |
186 | char* U8_strdup(const char* string); | |
187 | ||
188 | /*! Returns the number of UTF-8 characters in the given string. */ | |
189 | int U8_strlen(const char* string); | |
190 | ||
191 | /*! Returns the number of bytes in the UTF-8 multibyte character pointed at by 'character'. */ | |
192 | int U8_charsize(const char* character); | |
193 | ||
194 | /*! Copies the source multibyte character into the given buffer without overrunning it. Returns 0 on failure. */ | |
195 | int U8_charcpy(char* buffer, const char* source, int buffer_size); | |
196 | ||
197 | /*! Returns a pointer to the next UTF-8 character. */ | |
198 | const char* U8_next(const char* string); | |
199 | ||
200 | /*! Inserts a UTF-8 string into 'string' at the given position. Use a position of -1 to append. Returns 0 when unable to insert the string. */ | |
201 | int U8_strinsert(char* string, int position, const char* source, int max_bytes); | |
202 | ||
203 | /*! Erases the UTF-8 character at the given position, moving the subsequent characters down. */ | |
204 | void U8_strdel(char* string, int position); | |
205 | ||
206 | ||
207 | // Internal settings | |
208 | ||
209 | /*! Sets the string from which to load the initial glyphs. Use this if you need upfront loading for any reason (such as lack of render-target support). */ | |
210 | void FC_SetLoadingString(FC_Font* font, const char* string); | |
211 | ||
212 | /*! Returns the size of the internal buffer which is used for unpacking variadic text data. This buffer is shared by all FC_Fonts. */ | |
213 | unsigned int FC_GetBufferSize(void); | |
214 | ||
215 | /*! Changes the size of the internal buffer which is used for unpacking variadic text data. This buffer is shared by all FC_Fonts. */ | |
216 | void FC_SetBufferSize(unsigned int size); | |
217 | ||
218 | void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale)); | |
219 | ||
220 | FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale); | |
221 | ||
222 | ||
223 | // Custom caching | |
224 | ||
225 | /*! Returns the number of cache levels that are active. */ | |
226 | int FC_GetNumCacheLevels(FC_Font* font); | |
227 | ||
228 | /*! Returns the cache source texture at the given cache level. */ | |
229 | FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level); | |
230 | ||
231 | // TODO: Specify ownership of the texture (should be shareable) | |
232 | /*! Sets a cache source texture for rendering. New cache levels must be sequential. */ | |
233 | Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture); | |
234 | ||
235 | /*! Copies the given surface to the given cache level as a texture. New cache levels must be sequential. */ | |
236 | Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface); | |
237 | ||
238 | ||
239 | /*! Returns the number of codepoints that are stored in the font's glyph data map. */ | |
240 | unsigned int FC_GetNumCodepoints(FC_Font* font); | |
241 | ||
242 | /*! Copies the stored codepoints into the given array. */ | |
243 | void FC_GetCodepoints(FC_Font* font, Uint32* result); | |
244 | ||
245 | /*! Stores the glyph data for the given codepoint in 'result'. Returns 0 if the codepoint was not found in the cache. */ | |
246 | Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint); | |
247 | ||
248 | /*! Sets the glyph data for the given codepoint. Duplicates are not checked. Returns a pointer to the stored data. */ | |
249 | FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data); | |
250 | ||
251 | ||
252 | // Rendering | |
253 | ||
254 | FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...); | |
255 | FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...); | |
256 | FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...); | |
257 | FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...); | |
258 | FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...); | |
259 | ||
260 | FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...); | |
261 | FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...); | |
262 | FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...); | |
263 | FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...); | |
264 | FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...); | |
265 | ||
266 | FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...); | |
267 | FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...); | |
268 | FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...); | |
269 | FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...); | |
270 | FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...); | |
271 | ||
272 | ||
273 | // Getters | |
274 | ||
275 | FC_FilterEnum FC_GetFilterMode(FC_Font* font); | |
276 | Uint16 FC_GetLineHeight(FC_Font* font); | |
277 | Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...); | |
278 | Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...); | |
279 | ||
280 | // Returns a 1-pixel wide box in front of the character in the given position (index) | |
281 | FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...); | |
282 | Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...); | |
283 | ||
284 | int FC_GetAscent(FC_Font* font, const char* formatted_text, ...); | |
285 | int FC_GetDescent(FC_Font* font, const char* formatted_text, ...); | |
286 | int FC_GetBaseline(FC_Font* font); | |
287 | int FC_GetSpacing(FC_Font* font); | |
288 | int FC_GetLineSpacing(FC_Font* font); | |
289 | Uint16 FC_GetMaxWidth(FC_Font* font); | |
290 | SDL_Color FC_GetDefaultColor(FC_Font* font); | |
291 | ||
292 | Uint8 FC_InRect(float x, float y, FC_Rect input_rect); | |
293 | // Given an offset (x,y) from the text draw position (the upper-left corner), returns the character position (UTF-8 index) | |
294 | Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...); | |
295 | ||
296 | // Setters | |
297 | ||
298 | void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter); | |
299 | void FC_SetSpacing(FC_Font* font, int LetterSpacing); | |
300 | void FC_SetLineSpacing(FC_Font* font, int LineSpacing); | |
301 | void FC_SetDefaultColor(FC_Font* font, SDL_Color color); | |
302 | ||
303 | ||
304 | #ifdef __cplusplus | |
305 | } | |
306 | #endif | |
307 | ||
308 | ||
309 | ||
310 | #endif |
48 | 48 | } |
49 | 49 | } |
50 | 50 | |
51 | sago::SagoTextField* ButtonGfx::getLabel(const std::string& text) { | |
52 | const auto& theLabel = labels.find(text); | |
53 | if (theLabel != labels.end()) { | |
54 | return labels[text].get(); | |
55 | } | |
56 | std::shared_ptr<sago::SagoTextField> newField = std::make_shared<sago::SagoTextField>(); | |
57 | newField->SetHolder(&globalData.spriteHolder->GetDataHolder()); | |
58 | newField->SetFont("freeserif"); | |
59 | newField->SetFontSize(30); | |
60 | newField->SetOutline(1, {64,64,64,255}); | |
61 | newField->SetText(text); | |
62 | labels[text] = newField; | |
63 | return labels[text].get(); | |
64 | } | |
65 | ||
51 | 66 | Button::Button() { |
52 | 67 | label = ""; |
53 | 68 | marked = false; |
97 | 112 | globalData.spriteHolder->GetSprite(menu_unmarked).Draw(globalData.screen, SDL_GetTicks(), b.x, b.y); |
98 | 113 | } |
99 | 114 | |
100 | standardButton.thefont->draw(globalData.screen, b.x+standardButton.xsize/2,b.y+standardButton.ysize/2-standardButton.thefont->getHeight("%s", b.label.c_str())/2, NFont::CENTER, "%s", b.label.c_str()); | |
115 | standardButton.getLabel(b.label)->Draw(globalData.screen, b.x+standardButton.xsize/2,b.y+standardButton.ysize/2, | |
116 | sago::SagoTextField::Alignment::center, sago::SagoTextField::VerticalAlignment::center); | |
117 | //standardButton.thefont->drawCenterAlsoHeight(globalData.screen, b.x+standardButton.xsize/2,b.y+standardButton.ysize/2, b.label); | |
101 | 118 | } |
102 | 119 | |
103 | 120 | |
114 | 131 | drawToScreen(*b); |
115 | 132 | } |
116 | 133 | drawToScreen(exit); |
117 | standardButton.thefont->draw(target, 50, 50, "%s", title.c_str()); | |
134 | standardButton.getLabel(title)->Draw(target, 50, 50); | |
118 | 135 | } |
119 | 136 | |
120 | 137 | |
300 | 317 | void Menu::Draw(SDL_Renderer* target) { |
301 | 318 | placeButtons(); |
302 | 319 | drawSelf(target); |
320 | #if DEBUG | |
321 | static unsigned long int Frames; | |
322 | static unsigned long int Ticks; | |
323 | static char FPS[10]; | |
324 | static sago::SagoTextField fpsField; | |
325 | sagoTextSetBlueFont(fpsField); | |
326 | Frames++; | |
327 | if (SDL_GetTicks() >= Ticks + 1000) { | |
328 | if (Frames > 999) { | |
329 | Frames=999; | |
330 | } | |
331 | snprintf(FPS, sizeof(FPS), "%lu fps", Frames); | |
332 | Frames = 0; | |
333 | Ticks = SDL_GetTicks(); | |
334 | } | |
335 | fpsField.SetText(FPS); | |
336 | fpsField.Draw(globalData.screen, 800, 4); | |
337 | #endif | |
303 | 338 | } |
304 | 339 | void Menu::ProcessInput(const SDL_Event& event, bool& processed) { |
305 | 340 | if (isUpEvent(event)) { |
370 | 405 | if (buttons.at(i)->isPopOnRun()) { |
371 | 406 | running = false; |
372 | 407 | } |
373 | globalData.mousex = 0; | |
408 | //Quit here to ensure that we do not continue checking buttons after we have done the action. | |
409 | return; | |
374 | 410 | } |
375 | 411 | } |
376 | 412 | if (isClicked(exit, globalData.mousex, globalData.mousey)) { |
26 | 26 | |
27 | 27 | #include <string> |
28 | 28 | #include "SDL.h" |
29 | #include <map> | |
29 | 30 | #include <vector> |
30 | #include "Libs/NFont.h" | |
31 | 31 | #include "sago/SagoSprite.hpp" |
32 | 32 | #include "sago/GameStateInterface.hpp" |
33 | 33 | #include <memory> |
34 | #include "sago/SagoTextField.hpp" | |
34 | 35 | |
35 | 36 | //The ButtonGfx object hold common media for all buttons, so we can reskin them by only changeing one pointer |
36 | 37 | struct ButtonGfx |
38 | 39 | //The size of the buttons, so we don't have to ask w and h from the SDL Surfaces each time |
39 | 40 | int xsize = 0; |
40 | 41 | int ysize = 0; |
41 | //A TTFont used for writing the label on the buttons | |
42 | NFont* thefont = nullptr; | |
42 | sago::SagoTextField* getLabel(const std::string& text); | |
43 | 43 | void setSurfaces(); |
44 | private: | |
45 | std::map<std::string, std::shared_ptr<sago::SagoTextField> > labels; | |
44 | 46 | }; |
45 | 47 | |
46 | 48 | extern ButtonGfx standardButton; |
29 | 29 | |
30 | 30 | |
31 | 31 | /*static void MoveBlockGameSdls( BlockGameSdl& game1, BlockGameSdl& game2 ) { |
32 | game1.SetTopXY(50, globalData.ysize/2-284); | |
33 | game2.SetTopXY(globalData.xsize-500, globalData.ysize/2-284); | |
32 | game1.SetTopXY(50, globalData.ysize/2-284); | |
33 | game2.SetTopXY(globalData.xsize-500, globalData.ysize/2-284); | |
34 | 34 | }*/ |
35 | 35 | |
36 | 36 |
30 | 30 | using std::cerr; |
31 | 31 | using std::vector; |
32 | 32 | |
33 | static void NFont_Write(SDL_Renderer* target, int x, int y, const char* text) { | |
34 | globalData.nf_standard_blue_font.draw(target, x, y, "%s", text); | |
33 | static void setButtonFont(const sago::SagoDataHolder* holder, sago::SagoTextField& field, const char* text) { | |
34 | field.SetHolder(holder); | |
35 | field.SetFont("freeserif"); | |
36 | field.SetColor({255,255,255,255}); | |
37 | field.SetFontSize(24); | |
38 | field.SetOutline(1, {128,128,128,255}); | |
39 | field.SetText(text); | |
40 | } | |
41 | ||
42 | sago::SagoTextField* ScoresDisplay::getCachedText(const std::string& text) { | |
43 | std::shared_ptr<sago::SagoTextField> ptr = fieldCache[text]; | |
44 | if (!ptr) { | |
45 | std::shared_ptr<sago::SagoTextField> newText = std::make_shared<sago::SagoTextField>(); | |
46 | sagoTextSetBlueFont(*newText.get()); | |
47 | newText->SetText(text); | |
48 | fieldCache[text] = newText; | |
49 | } | |
50 | return fieldCache[text].get(); | |
51 | } | |
52 | ||
53 | void ScoresDisplay::Write(SDL_Renderer* target, int x, int y, const char* text) { | |
54 | getCachedText(text)->Draw(target, x, y); | |
35 | 55 | } |
36 | 56 | |
37 | 57 | const int numberOfPages = 3; |
47 | 67 | void ScoresDisplay::DrawHighscores(int x, int y, bool endless) { |
48 | 68 | DrawBackgroundAndCalcPlacements(); |
49 | 69 | if (endless) { |
50 | globalData.nf_standard_blue_font.draw(globalData.screen, x+100,y+100, "%s",_("Endless:") ); | |
70 | Write(globalData.screen, x+100,y+100, _("Endless:") ); | |
51 | 71 | } |
52 | 72 | else { |
53 | globalData.nf_standard_blue_font.draw(globalData.screen, x+100,y+100, "%s",_("Time Trial:") ); | |
73 | Write(globalData.screen, x+100,y+100, _("Time Trial:") ); | |
54 | 74 | } |
55 | 75 | for (int i =0; i<10; i++) { |
56 | 76 | record r; |
64 | 84 | char playerName[32]; |
65 | 85 | snprintf(playerScore, sizeof(playerScore), "%i", r.score); |
66 | 86 | snprintf(playerName, sizeof(playerName), "%s", r.name.c_str()); |
67 | globalData.nf_standard_blue_font.draw(globalData.screen, x+420,y+150+i*35, "%s",playerScore); | |
68 | globalData.nf_standard_blue_font.draw(globalData.screen, x+60,y+150+i*35, "%s",playerName); | |
87 | Write(globalData.screen, x+420,y+150+i*35, playerScore); | |
88 | Write(globalData.screen, x+60,y+150+i*35, playerName); | |
69 | 89 | } |
70 | 90 | } |
71 | 91 | |
73 | 93 | DrawBackgroundAndCalcPlacements(); |
74 | 94 | int y = 5; |
75 | 95 | const int y_spacing = 30; |
76 | NFont_Write(globalData.screen, 10,y,_("Stats") ); | |
96 | Write(globalData.screen, 10,y,_("Stats") ); | |
77 | 97 | y+=y_spacing*2; |
78 | NFont_Write(globalData.screen, 10,y,_("Chains") ); | |
98 | Write(globalData.screen, 10,y,_("Chains") ); | |
79 | 99 | for (int i=2; i<13; i++) { |
80 | 100 | y+=y_spacing; |
81 | NFont_Write(globalData.screen, 10,y,(std::to_string(i)+"X").c_str()); | |
101 | Write(globalData.screen, 10,y,(std::to_string(i)+"X").c_str()); | |
82 | 102 | string numberAsString = std::to_string(Stats::getInstance()->getNumberOf("chainX"+std::to_string(i))); |
83 | NFont_Write(globalData.screen, 300,y,numberAsString.c_str()); | |
103 | Write(globalData.screen, 300,y,numberAsString.c_str()); | |
84 | 104 | } |
85 | 105 | y+=y_spacing*2; |
86 | NFont_Write(globalData.screen, 10,y,_("Lines Pushed: ") ); | |
106 | Write(globalData.screen, 10,y,_("Lines Pushed: ") ); | |
87 | 107 | string numberAsString = std::to_string(Stats::getInstance()->getNumberOf("linesPushed")); |
88 | NFont_Write(globalData.screen, 300,y,numberAsString.c_str()); | |
89 | ||
90 | y+=y_spacing; | |
91 | NFont_Write(globalData.screen, 10,y, _("Puzzles solved: ") ); | |
108 | Write(globalData.screen, 300,y,numberAsString.c_str()); | |
109 | ||
110 | y+=y_spacing; | |
111 | Write(globalData.screen, 10,y, _("Puzzles solved: ") ); | |
92 | 112 | numberAsString = std::to_string(Stats::getInstance()->getNumberOf("puzzlesSolved")); |
93 | NFont_Write(globalData.screen, 300,y,numberAsString.c_str()); | |
113 | Write(globalData.screen, 300,y,numberAsString.c_str()); | |
94 | 114 | |
95 | 115 | y+=y_spacing*2; |
96 | NFont_Write(globalData.screen, 10,y, _("Run time: ") ); | |
116 | Write(globalData.screen, 10,y, _("Run time: ") ); | |
97 | 117 | commonTime ct = TimeHandler::peekTime("totalTime",TimeHandler::ms2ct(SDL_GetTicks())); |
98 | 118 | y+=y_spacing; |
99 | NFont_Write(globalData.screen, 10, y, SPrintCF( _("Days: %i"), ct.days) ); | |
100 | y+=y_spacing; | |
101 | NFont_Write(globalData.screen, 10, y, SPrintCF( _("Hours: %i"), ct.hours) ); | |
102 | y+=y_spacing; | |
103 | NFont_Write(globalData.screen, 10, y, SPrintCF( _("Minutes: %i"), ct.minutes) ); | |
104 | y+=y_spacing; | |
105 | NFont_Write(globalData.screen, 10, y, SPrintCF( _("Seconds: %i"), ct.seconds) ); | |
119 | Write(globalData.screen, 10, y, SPrintCF( _("Days: %i"), ct.days) ); | |
120 | y+=y_spacing; | |
121 | Write(globalData.screen, 10, y, SPrintCF( _("Hours: %i"), ct.hours) ); | |
122 | y+=y_spacing; | |
123 | Write(globalData.screen, 10, y, SPrintCF( _("Minutes: %i"), ct.minutes) ); | |
124 | y+=y_spacing; | |
125 | Write(globalData.screen, 10, y, SPrintCF( _("Seconds: %i"), ct.seconds) ); | |
106 | 126 | |
107 | 127 | y-=y_spacing*4; //Four rows back |
108 | 128 | const int x_offset3 = globalData.xsize/3+10; //Ofset for three rows |
109 | NFont_Write(globalData.screen, x_offset3,y, _("Play time: ") ); | |
129 | Write(globalData.screen, x_offset3,y, _("Play time: ") ); | |
110 | 130 | ct = TimeHandler::getTime("playTime"); |
111 | 131 | y+=y_spacing; |
112 | NFont_Write(globalData.screen, x_offset3, y, SPrintCF( _("Days: %i"), ct.days) ); | |
113 | y+=y_spacing; | |
114 | NFont_Write(globalData.screen, x_offset3, y, SPrintCF( _("Hours: %i"), ct.hours) ); | |
115 | y+=y_spacing; | |
116 | NFont_Write(globalData.screen, x_offset3, y, SPrintCF( _("Minutes: %i"), ct.minutes) ); | |
117 | y+=y_spacing; | |
118 | NFont_Write(globalData.screen, x_offset3, y, SPrintCF( _("Seconds: %i"), ct.seconds) ); | |
132 | Write(globalData.screen, x_offset3, y, SPrintCF( _("Days: %i"), ct.days) ); | |
133 | y+=y_spacing; | |
134 | Write(globalData.screen, x_offset3, y, SPrintCF( _("Hours: %i"), ct.hours) ); | |
135 | y+=y_spacing; | |
136 | Write(globalData.screen, x_offset3, y, SPrintCF( _("Minutes: %i"), ct.minutes) ); | |
137 | y+=y_spacing; | |
138 | Write(globalData.screen, x_offset3, y, SPrintCF( _("Seconds: %i"), ct.seconds) ); | |
119 | 139 | |
120 | 140 | const int x_offset = globalData.xsize/2+10; |
121 | 141 | y = 5+y_spacing*2; |
122 | NFont_Write(globalData.screen, x_offset,y, _("VS CPU (win/loss)") ); | |
142 | Write(globalData.screen, x_offset,y, _("VS CPU (win/loss)") ); | |
123 | 143 | for (int i=0; i<7; i++) { |
124 | 144 | y += y_spacing; |
125 | NFont_Write(globalData.screen, x_offset,y,string("AI "+std::to_string(i+1)).c_str()); | |
145 | Write(globalData.screen, x_offset,y,string("AI "+std::to_string(i+1)).c_str()); | |
126 | 146 | numberAsString = std::to_string(Stats::getInstance()->getNumberOf("defeatedAI"+std::to_string(i))); |
127 | 147 | string numberAsString2 = std::to_string(Stats::getInstance()->getNumberOf("defeatedByAI"+std::to_string(i))); |
128 | 148 | string toPrint = numberAsString + "/" + numberAsString2; |
129 | NFont_Write(globalData.screen, x_offset+230,y,toPrint.c_str()); | |
149 | Write(globalData.screen, x_offset+230,y,toPrint.c_str()); | |
130 | 150 | } |
131 | 151 | } |
132 | 152 | |
155 | 175 | DrawStats(); |
156 | 176 | }; |
157 | 177 | |
178 | const sago::SagoDataHolder* holder = &globalData.spriteHolder->GetDataHolder(); | |
158 | 179 | //Draw buttons: |
159 | 180 | globalData.bHighScore.Draw(globalData.screen, 0, scoreX,scoreY); |
160 | 181 | globalData.bBack.Draw(globalData.screen, 0, backX, backY); |
161 | globalData.nf_button_font.draw(globalData.screen, backX+60,backY+10, NFont::CENTER ,_("Back")); | |
182 | static sago::SagoTextField backLabel; | |
183 | setButtonFont(holder, backLabel, _("Back")); | |
184 | backLabel.Draw(globalData.screen, backX+60,backY+10, sago::SagoTextField::Alignment::center); | |
162 | 185 | globalData.bNext.Draw(globalData.screen, 0, nextX, nextY); |
163 | globalData.nf_button_font.draw(globalData.screen, nextX+60,nextY+10, NFont::CENTER,_("Next")); | |
186 | static sago::SagoTextField nextLabel; | |
187 | setButtonFont(holder, nextLabel, _("Next")); | |
188 | nextLabel.Draw(globalData.screen, nextX+60, nextY+10, sago::SagoTextField::Alignment::center); | |
164 | 189 | |
165 | 190 | //Draw page number |
166 | 191 | string pageXofY = SPrintStringF(_("Page %i of %i"), page+1, numberOfPages); |
167 | NFont_Write(globalData.screen, globalData.xsize/2-globalData.nf_standard_blue_font.getWidth( "%s", pageXofY.c_str())/2, globalData.ysize-60, pageXofY.c_str()); | |
192 | getCachedText(pageXofY)->Draw(globalData.screen, globalData.xsize/2, globalData.ysize-60, sago::SagoTextField::Alignment::center); | |
168 | 193 | } |
169 | 194 | |
170 | 195 | void ScoresDisplay::ProcessInput(const SDL_Event& event, bool& processed) { |
24 | 24 | #define SCORESDISPLAY_HPP |
25 | 25 | |
26 | 26 | #include "sago/GameStateInterface.hpp" |
27 | #include "sago/SagoTextField.hpp" | |
28 | #include <map> | |
29 | #include <memory> | |
27 | 30 | |
28 | 31 | class ScoresDisplay : public sago::GameStateInterface { |
29 | 32 | public: |
46 | 49 | void DrawHighscores(int x, int y, bool endless); |
47 | 50 | void DrawStats(); |
48 | 51 | void DrawBackgroundAndCalcPlacements(); |
52 | void Write(SDL_Renderer* target, int x, int y, const char* text); | |
53 | sago::SagoTextField* getCachedText(const std::string& text); | |
54 | std::map<std::string, std::shared_ptr<sago::SagoTextField> > fieldCache; | |
49 | 55 | bool isActive = true; |
50 | 56 | bool bMouseUp = false; |
51 | 57 | int backX = 20; |
0 | /* | |
1 | =========================================================================== | |
2 | blockattack - Block Attack - Rise of the Blocks | |
3 | Copyright (C) 2005-2018 Poul Sander | |
4 | This program is free software: you can redistribute it and/or modify | |
5 | it under the terms of the GNU General Public License as published by | |
6 | the Free Software Foundation, either version 2 of the License, or | |
7 | (at your option) any later version. | |
8 | This program is distributed in the hope that it will be useful, | |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | GNU General Public License for more details. | |
12 | You should have received a copy of the GNU General Public License | |
13 | along with this program. If not, see http://www.gnu.org/licenses/ | |
14 | Source information and contacts persons can be found at | |
15 | https://blockattack.net | |
16 | =========================================================================== | |
17 | */ | |
18 | ||
19 | #include "ShowFileState.hpp" | |
20 | #include "global.hpp" | |
21 | #include "common.h" | |
22 | #include "MenuSystem.h" | |
23 | ||
24 | const int xsize = 1024; | |
25 | const int ysize = 768; | |
26 | const int buttonOffset = 160; | |
27 | extern sago::SagoSprite bExit; | |
28 | ||
29 | static void setHelpGamepadFont(const sago::SagoDataHolder* holder, sago::SagoTextField& field, const char* text) { | |
30 | field.SetHolder(holder); | |
31 | field.SetFont("freeserif"); | |
32 | field.SetColor({255,255,255,255}); | |
33 | field.SetFontSize(30); | |
34 | field.SetOutline(1, {128,128,128,255}); | |
35 | field.SetText(text); | |
36 | } | |
37 | ||
38 | static void setHelpGamepadFont(const sago::SagoDataHolder* holder, sago::SagoTextBox& field, const char* text) { | |
39 | field.SetHolder(holder); | |
40 | field.SetFont("freeserif"); | |
41 | field.SetColor({255,255,255,255}); | |
42 | field.SetColor({0,0,0,255}); | |
43 | field.SetFontSize(20); | |
44 | field.SetOutline(0, {0,0,0,255}); | |
45 | field.SetText(text); | |
46 | } | |
47 | ||
48 | ShowFileState::ShowFileState() { | |
49 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), titleField, ""); | |
50 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), infoBox, ""); | |
51 | setHelpGamepadFont(&globalData.spriteHolder->GetDataHolder(), filenameField, ""); | |
52 | } | |
53 | ||
54 | ShowFileState::~ShowFileState() { | |
55 | } | |
56 | ||
57 | bool ShowFileState::IsActive() { | |
58 | return isActive; | |
59 | } | |
60 | ||
61 | void ShowFileState::ProcessInput(const SDL_Event& event, bool& processed) { | |
62 | ||
63 | UpdateMouseCoordinates(event, globalData.mousex, globalData.mousey); | |
64 | ||
65 | if (isConfirmEvent(event) || isEscapeEvent(event)) { | |
66 | isActive = false; | |
67 | processed = true; | |
68 | } | |
69 | } | |
70 | ||
71 | extern void DrawRectYellow(SDL_Renderer* target, int topx, int topy, int height, int width); | |
72 | ||
73 | void ShowFileState::Draw(SDL_Renderer* target) { | |
74 | DrawBackground(target); | |
75 | titleField.Draw(target, 50, 50); | |
76 | DrawRectYellow(target, 40, 90, 600, 900); | |
77 | infoBox.SetMaxWidth(850); | |
78 | infoBox.Draw(target, 50, 100); | |
79 | DrawRectYellow(target, 40, 700, 50, 900); | |
80 | filenameField.Draw(target, 50, 715); | |
81 | bExit.Draw(globalData.screen, SDL_GetTicks(), xsize-buttonOffset, ysize-buttonOffset); | |
82 | #if DEBUG | |
83 | static sago::SagoTextField mousePos; | |
84 | mousePos.SetHolder(&globalData.spriteHolder->GetDataHolder()); | |
85 | mousePos.SetFontSize(16); | |
86 | mousePos.SetOutline(1, {128,128,128,255}); | |
87 | mousePos.SetText(std::string("Mouse position: ")+std::to_string(globalData.mousex)+std::string(", ")+std::to_string(globalData.mousey)); | |
88 | mousePos.Draw(target, 0,0); | |
89 | #endif | |
90 | } | |
91 | ||
92 | void ShowFileState::Update() { | |
93 | // If the mouse button is released, make bMouseUp equal true | |
94 | if ( !(SDL_GetMouseState(nullptr, nullptr)&SDL_BUTTON(1)) ) { | |
95 | bMouseUp=true; | |
96 | } | |
97 | ||
98 | if (SDL_GetMouseState(nullptr,nullptr)&SDL_BUTTON(1) && bMouseUp) { | |
99 | bMouseUp = false; | |
100 | ||
101 | //The Score button: | |
102 | if ((globalData.mousex>xsize-buttonOffset) && (globalData.mousex<xsize-buttonOffset+bExit.GetWidth()) | |
103 | && (globalData.mousey>ysize-buttonOffset) && (globalData.mousey<ysize-buttonOffset+bExit.GetHeight())) { | |
104 | isActive = false; | |
105 | } | |
106 | ||
107 | } | |
108 | }⏎ |
0 | /* | |
1 | =========================================================================== | |
2 | blockattack - Block Attack - Rise of the Blocks | |
3 | Copyright (C) 2005-2018 Poul Sander | |
4 | This program is free software: you can redistribute it and/or modify | |
5 | it under the terms of the GNU General Public License as published by | |
6 | the Free Software Foundation, either version 2 of the License, or | |
7 | (at your option) any later version. | |
8 | This program is distributed in the hope that it will be useful, | |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | GNU General Public License for more details. | |
12 | You should have received a copy of the GNU General Public License | |
13 | along with this program. If not, see http://www.gnu.org/licenses/ | |
14 | Source information and contacts persons can be found at | |
15 | https://blockattack.net | |
16 | =========================================================================== | |
17 | */ | |
18 | ||
19 | #ifndef SHOWFILESTATE_HPP | |
20 | #define SHOWFILESTATE_HPP | |
21 | ||
22 | ||
23 | #include "sago/GameStateInterface.hpp" | |
24 | #include "sago/SagoTextField.hpp" | |
25 | #include "sago/SagoTextBox.hpp" | |
26 | #include "sago/SagoMisc.hpp" | |
27 | #include <string> | |
28 | #include "common.h" | |
29 | ||
30 | class ShowFileState : public sago::GameStateInterface { | |
31 | public: | |
32 | ShowFileState(); | |
33 | ShowFileState(const ShowFileState& orig) = delete; | |
34 | virtual ~ShowFileState(); | |
35 | ||
36 | bool IsActive() override; | |
37 | void Draw(SDL_Renderer* target) override; | |
38 | void ProcessInput(const SDL_Event& event, bool &processed) override; | |
39 | void Update() override; | |
40 | ||
41 | void SetData(const std::string& filename, const std::string& header) { | |
42 | titleField.SetText(header); | |
43 | infoBox.SetText(sago::GetFileContent(filename)); | |
44 | filenameField.SetText(SPrintStringF(_("Showing content of: %s"), filename.c_str())); | |
45 | } | |
46 | ||
47 | private: | |
48 | bool isActive = true; | |
49 | bool bMouseUp = true; | |
50 | sago::SagoTextField titleField; | |
51 | sago::SagoTextBox filenameField; | |
52 | sago::SagoTextBox infoBox; | |
53 | }; | |
54 | ||
55 | #endif /* SHOWFILESTATE_HPP */ | |
56 |
16 | 16 | along with this program. If not, see http://www.gnu.org/licenses/ |
17 | 17 | |
18 | 18 | Source information and contacts persons can be found at |
19 | http://www.blockattack.net | |
19 | https://blockattack.net | |
20 | 20 | =========================================================================== |
21 | 21 | */ |
22 | 22 | |
30 | 30 | private: |
31 | 31 | int x = 0; |
32 | 32 | int y = 0; |
33 | std::string textt; | |
33 | unsigned int textt = 0; | |
34 | 34 | unsigned long int time = 0; |
35 | 35 | unsigned long int placeTime = 0; //Then the text was placed |
36 | 36 | public: |
40 | 40 | } |
41 | 41 | |
42 | 42 | //constructor: |
43 | TextMessage(int X, int Y,const char* Text,unsigned int Time) { | |
43 | TextMessage(int X, int Y,unsigned int Text,unsigned int Time) { | |
44 | 44 | placeTime = SDL_GetTicks(); |
45 | 45 | x = X; |
46 | 46 | y = Y; |
61 | 61 | return y; |
62 | 62 | } |
63 | 63 | |
64 | const char* getText() { | |
65 | return textt.c_str(); | |
64 | unsigned int getText() { | |
65 | return textt; | |
66 | 66 | } |
67 | 67 | }; //text popup |
68 | 68 | |
74 | 74 | TextManager() { |
75 | 75 | } |
76 | 76 | |
77 | int addText(int x, int y, const std::string& Text,unsigned int Time) { | |
77 | int addText(int x, int y, unsigned int Text, unsigned int Time) { | |
78 | 78 | size_t textNumber = 0; |
79 | 79 | while (textNumber<textArray.size() && textArray[textNumber].inUse) { |
80 | 80 | textNumber++; |
82 | 82 | if (textNumber==textArray.size()) { |
83 | 83 | return -1; |
84 | 84 | } |
85 | textArray[textNumber] = TextMessage(x,y,Text.c_str(),Time); | |
85 | textArray[textNumber] = TextMessage(x,y,Text,Time); | |
86 | 86 | textArray[textNumber].inUse = true; |
87 | 87 | return 1; |
88 | 88 | } //addText |
156 | 156 | stringstream inFile(filecontent); |
157 | 157 | string key; |
158 | 158 | string previuskey; |
159 | char value[MAX_VAR_LENGTH]; | |
160 | 159 | if (inFile) { |
161 | 160 | while (!inFile.eof()) { |
162 | 161 | inFile >> key; |
165 | 164 | } |
166 | 165 | previuskey = key; |
167 | 166 | inFile.get(); //Read the space between the key and the content |
168 | inFile.getline(value,MAX_VAR_LENGTH); | |
167 | std::string value; | |
168 | std::getline(inFile, value); | |
169 | 169 | #if DEBUG |
170 | 170 | cerr << "Config read: " << key << " with:\"" << value << "\"" << "\n"; |
171 | 171 | #endif |
172 | configMap[key] = (string)value; | |
172 | configMap[key] = value; | |
173 | 173 | } |
174 | 174 | } |
175 | 175 | } |
185 | 185 | void Config::save() { |
186 | 186 | std::stringstream outFile; |
187 | 187 | map<string,string>::iterator iter; |
188 | for (iter = configMap.begin(); iter != configMap.end(); iter++) { | |
188 | for (iter = configMap.begin(); iter != configMap.end(); ++iter) { | |
189 | 189 | outFile << iter->first << " " << iter->second << "\n"; |
190 | 190 | } |
191 | 191 | outFile << "\n"; //The last entry in the file will be read double if a linebreak is missing |
47 | 47 | unsigned int seconds = 0; |
48 | 48 | }; |
49 | 49 | |
50 | //std::string itoa(int num) __attribute__((const)); | |
51 | ||
52 | 50 | bool strequals(const char* a, const char* b); |
53 | 51 | |
54 | 52 | /** |
36 | 36 | }; |
37 | 37 | |
38 | 38 | static std::map<SDL_JoystickID, ControllerStatus> controllerStatusMap; |
39 | static std::map<std::string, int> gamecontrollers_assigned; | |
40 | static std::vector<std::string> supportedControllers; | |
39 | 41 | |
40 | 42 | |
41 | 43 | void GameControllerSetVerbose(bool value) { |
60 | 62 | return ret; |
61 | 63 | } |
62 | 64 | |
63 | static std::map<std::string, int> gamecontrollers_assigned; | |
64 | 65 | |
65 | 66 | static int GetNextPlayerByGui(const SDL_JoystickGUID& guid) { |
66 | 67 | Config::getInstance()->setDefault("gc_AllToOnePlayer", "0"); |
99 | 100 | SDL_JoystickGUID guid = SDL_JoystickGetGUID(j); |
100 | 101 | int assingToPlayer = GetNextPlayerByGui(guid); |
101 | 102 | controllerStatusMap[instanceId].player = assingToPlayer; |
103 | supportedControllers.push_back(GameControllerGetName(controller)); | |
102 | 104 | if (verbose) { |
103 | 105 | std::cout << "Supported game controller detected: " << GameControllerGetName(controller) << ", mapping: " << SDL_GameControllerMapping(controller) << "\n"; |
104 | 106 | std::cout << "Assigned to player: " << controllerStatusMap[instanceId].player << "\n"; |
105 | 107 | } |
106 | 108 | } |
107 | 109 | } |
110 | } | |
111 | ||
112 | const std::vector<std::string>& GetSupportedControllerNames() { | |
113 | return supportedControllers; | |
108 | 114 | } |
109 | 115 | |
110 | 116 | void checkDeadZone(const SDL_Event& event) { |
21 | 21 | */ |
22 | 22 | |
23 | 23 | #include "SDL.h" |
24 | ||
24 | #include <vector> | |
25 | #include <string> | |
25 | 26 | |
26 | 27 | const int deadZoneLimit = 20000; |
27 | 28 | |
34 | 35 | bool isPlayerSwitchEvent(int playerNumber, const SDL_Event& event); |
35 | 36 | bool isPlayerPushEvent(int playerNumber, const SDL_Event& event); |
36 | 37 | void GameControllerSetVerbose(bool value); |
38 | const std::vector<std::string>& GetSupportedControllerNames(); | |
37 | 39 | |
38 | 40 | /** |
39 | 41 | * Checks that the given event is in the dead zone. |
23 | 23 | #ifndef _GLOBAL_HPP |
24 | 24 | #define _GLOBAL_HPP |
25 | 25 | |
26 | #include "Libs/NFont.h" | |
27 | 26 | #include <memory> |
28 | 27 | #include "sago/SagoSpriteHolder.hpp" |
29 | 28 | #include "highscore.h" |
30 | 29 | #include "sago/GameStateInterface.hpp" |
31 | 30 | #include "TextManager.hpp" |
32 | 31 | #include "ExplosionManager.hpp" |
32 | #include "sago/SagoTextField.hpp" | |
33 | 33 | |
34 | 34 | void MainMenu(); |
35 | 35 | void ResetFullscreen(); |
44 | 44 | void DrawIMG(const sago::SagoSprite& sprite, SDL_Renderer* target, int x, int y); |
45 | 45 | void DrawIMG_Bounded(const sago::SagoSprite& sprite, SDL_Renderer* target, int x, int y, int minx, int miny, int maxx, int maxy); |
46 | 46 | |
47 | void sagoTextSetHelpFont(sago::SagoTextField& field); | |
48 | void sagoTextSetBlueFont(sago::SagoTextField& field); | |
49 | ||
47 | 50 | struct GlobalData { |
48 | 51 | sago::SagoSprite bHighScore; |
49 | 52 | sago::SagoSprite bBack; |
52 | 55 | sago::SagoSprite iLevelCheckBox; |
53 | 56 | sago::SagoSprite iLevelCheckBoxMarked; |
54 | 57 | sago::SagoSprite iCheckBoxArea; |
55 | NFont nf_scoreboard_font; | |
56 | NFont nf_standard_blue_font; | |
57 | NFont nf_button_font; | |
58 | 58 | bool MusicEnabled; //true if background music is enabled |
59 | 59 | bool SoundEnabled; //true if sound effects is enabled |
60 | 60 | bool bFullscreen; //true if game is running fullscreen |
62 | 62 | std::string player1name; |
63 | 63 | std::string player2name; |
64 | 64 | SDL_Renderer *screen = nullptr; //The whole screen; |
65 | Mix_Chunk *typingChunk; | |
65 | sago::SoundHandler typingChunk; | |
66 | 66 | sago::SagoSprite mouse; |
67 | 67 | bool highPriority = false; |
68 | 68 | bool NoSound = false; |
16 | 16 | along with this program. If not, see http://www.gnu.org/licenses/ |
17 | 17 | |
18 | 18 | Source information and contacts persons can be found at |
19 | http://www.blockattack.net | |
19 | https://blockattack.net | |
20 | 20 | =========================================================================== |
21 | 21 | */ |
22 | 22 |
16 | 16 | along with this program. If not, see http://www.gnu.org/licenses/ |
17 | 17 | |
18 | 18 | Source information and contacts persons can be found at |
19 | http://www.blockattack.net | |
19 | https://blockattack.net | |
20 | 20 | =========================================================================== |
21 | 21 | */ |
22 | 22 | |
37 | 37 | |
38 | 38 | static bool bMouseUp; //true if the mouse(1) is unpressed |
39 | 39 | |
40 | static void NFont_Write(SDL_Renderer* target, int x, int y, const std::string& text) { | |
41 | globalData.nf_standard_blue_font.draw(target, x, y, "%s", text.c_str()); | |
40 | static std::map<std::string, std::shared_ptr<sago::SagoTextField> > fieldCache; | |
41 | ||
42 | static sago::SagoTextField* getCachedText(const std::string& text) { | |
43 | std::shared_ptr<sago::SagoTextField> ptr = fieldCache[text]; | |
44 | if (!ptr) { | |
45 | std::shared_ptr<sago::SagoTextField> newText = std::make_shared<sago::SagoTextField>(); | |
46 | sagoTextSetBlueFont(*newText.get()); | |
47 | newText->SetText(text); | |
48 | fieldCache[text] = newText; | |
49 | } | |
50 | return fieldCache[text].get(); | |
51 | } | |
52 | ||
53 | static void Write(SDL_Renderer* target, int x, int y, const char* text) { | |
54 | getCachedText(text)->Draw(target, x, y); | |
42 | 55 | } |
43 | 56 | |
44 | 57 | //The function that allows the player to choose PuzzleLevel |
53 | 66 | Uint32 totalScore = 0; |
54 | 67 | Uint32 totalTime = 0; |
55 | 68 | int selected = 0; |
69 | fieldCache.clear(); | |
56 | 70 | |
57 | 71 | //Loads the levels, if they havn't been loaded: |
58 | 72 | if (Type == 0) { |
74 | 88 | DrawBackground(globalData.screen); |
75 | 89 | globalData.iCheckBoxArea.Draw(globalData.screen,ticks,xplace,yplace); |
76 | 90 | if (Type == 0) { |
77 | NFont_Write(globalData.screen, xplace+12,yplace+2,_("Select Puzzle") ); | |
91 | Write(globalData.screen, xplace+12,yplace+2,_("Select Puzzle") ); | |
78 | 92 | } |
79 | 93 | if (Type == 1) { |
80 | NFont_Write(globalData.screen, xplace+12,yplace+2, _("Stage Clear Level Select") ); | |
94 | Write(globalData.screen, xplace+12,yplace+2, _("Stage Clear Level Select") ); | |
81 | 95 | } |
82 | 96 | //Now drow the fields you click in (and a V if clicked): |
83 | 97 | for (int i = 0; i < nrOfLevels; i++) { |
190 | 204 | timeString = SPrintStringF(_("Time used: %d : %02d"), GetStageTime(selected)/1000/60, (GetStageTime(selected)/1000)%60); |
191 | 205 | } |
192 | 206 | |
193 | NFont_Write(globalData.screen, 200,200,scoreString.c_str()); | |
194 | NFont_Write(globalData.screen, 200,250,timeString.c_str()); | |
207 | Write(globalData.screen, 200,200,scoreString.c_str()); | |
208 | Write(globalData.screen, 200,250,timeString.c_str()); | |
195 | 209 | string totalString = SPrintStringF(_("Total score: %i in %i:%02i"), totalScore, totalTime/1000/60, ((totalTime/1000)%60) ); |
196 | NFont_Write(globalData.screen, 200,600,totalString.c_str()); | |
210 | Write(globalData.screen, 200,600,totalString.c_str()); | |
197 | 211 | } |
198 | 212 | |
199 | 213 | globalData.mouse.Draw(globalData.screen, SDL_GetTicks(), globalData.mousex, globalData.mousey); |
33 | 33 | #define WITH_SDL 1 |
34 | 34 | |
35 | 35 | #include "sago/SagoSpriteHolder.hpp" |
36 | #include "sago/SagoTextBox.hpp" | |
36 | 37 | #include <iostream> |
37 | 38 | #include <stdlib.h> |
38 | 39 | #include <time.h> //Used for srand() |
42 | 43 | #include <SDL_mixer.h> //Used for sound & music |
43 | 44 | #include <SDL_image.h> //To load PNG images! |
44 | 45 | #include <physfs.h> //Abstract file system. To use containers |
45 | #include "Libs/NFont.h" | |
46 | 46 | #include <vector> |
47 | 47 | #include "MenuSystem.h" |
48 | 48 | #include "puzzlehandler.hpp" |
180 | 180 | globalData.mouse = holder.GetSprite("mouse"); |
181 | 181 | backBoard = holder.GetSprite("back_board"); |
182 | 182 | |
183 | SDL_Color nf_button_color, nf_standard_blue_color, nf_standard_small_color; | |
184 | memset(&nf_button_color,0,sizeof(SDL_Color)); | |
185 | nf_button_color.b = 255; | |
186 | nf_button_color.g = 255; | |
187 | nf_button_color.r = 255; | |
188 | nf_button_color.a = 255; | |
189 | nf_standard_blue_color.b = 255; | |
190 | nf_standard_blue_color.g = 0; | |
191 | nf_standard_blue_color.r = 0; | |
192 | nf_standard_blue_color.a = 255; | |
193 | nf_standard_small_color.b = 0; | |
194 | nf_standard_small_color.g = 0; | |
195 | nf_standard_small_color.r = 200; | |
196 | nf_standard_small_color.a = 255; | |
197 | globalData.nf_button_font.load(globalData.screen, holder.GetDataHolder().getFontPtr("freeserif", 24), nf_button_color); | |
198 | globalData.nf_standard_blue_font.load(globalData.screen, holder.GetDataHolder().getFontPtr("freeserif", 30), nf_standard_blue_color); | |
199 | nf_standard_small_font.load(globalData.screen, holder.GetDataHolder().getFontPtr("freeserif", 16), nf_standard_small_color); | |
200 | globalData.nf_scoreboard_font.load(globalData.screen, holder.GetDataHolder().getFontPtr("penguinattack", 20), nf_button_color); | |
183 | sagoTextSetBlueFont(player1name); | |
184 | sagoTextSetBlueFont(player2name); | |
185 | sagoTextSetBlueFont(player1time); | |
186 | sagoTextSetBlueFont(player2time); | |
187 | sagoTextSetBlueFont(player1score); | |
188 | sagoTextSetBlueFont(player2score); | |
189 | sagoTextSetBlueFont(player1chain); | |
190 | sagoTextSetBlueFont(player2chain); | |
191 | sagoTextSetBlueFont(player1speed); | |
192 | sagoTextSetBlueFont(player2speed); | |
193 | ||
201 | 194 | |
202 | 195 | //Loads the sound if sound present |
203 | 196 | if (!globalData.NoSound) { |
204 | 197 | //And here the music: |
205 | bgMusic = holder.GetDataHolder().getMusicPtr("bgmusic"); | |
206 | highbeatMusic = holder.GetDataHolder().getMusicPtr("highbeat"); | |
198 | bgMusic = holder.GetDataHolder().getMusicHandler("bgmusic"); | |
199 | highbeatMusic = holder.GetDataHolder().getMusicHandler("highbeat"); | |
207 | 200 | //the music... we just hope it exists, else the user won't hear anything |
208 | 201 | //Same goes for the sounds |
209 | boing = holder.GetDataHolder().getSoundPtr("pop"); | |
210 | applause = holder.GetDataHolder().getSoundPtr("applause"); | |
211 | photoClick = holder.GetDataHolder().getSoundPtr("cameraclick"); | |
212 | globalData.typingChunk = holder.GetDataHolder().getSoundPtr("typing"); | |
213 | counterChunk = holder.GetDataHolder().getSoundPtr("counter"); | |
214 | counterFinalChunk = holder.GetDataHolder().getSoundPtr("counter_final"); | |
202 | boing = holder.GetDataHolder().getSoundHandler("pop"); | |
203 | applause = holder.GetDataHolder().getSoundHandler("applause"); | |
204 | photoClick = holder.GetDataHolder().getSoundHandler("cameraclick"); | |
205 | globalData.typingChunk = holder.GetDataHolder().getSoundHandler("typing"); | |
206 | counterChunk = holder.GetDataHolder().getSoundHandler("counter"); | |
207 | counterFinalChunk = holder.GetDataHolder().getSoundHandler("counter_final"); | |
215 | 208 | const int soundVolume = 84; //0-128 |
216 | Mix_VolumeChunk(boing, soundVolume); | |
217 | Mix_VolumeChunk(applause, soundVolume); | |
218 | Mix_VolumeChunk(photoClick, soundVolume); | |
219 | Mix_VolumeChunk(globalData.typingChunk, soundVolume); | |
220 | Mix_VolumeChunk(counterChunk, soundVolume); | |
221 | Mix_VolumeChunk(counterFinalChunk, soundVolume); | |
209 | Mix_VolumeChunk(boing.get(), soundVolume); | |
210 | Mix_VolumeChunk(applause.get(), soundVolume); | |
211 | Mix_VolumeChunk(photoClick.get(), soundVolume); | |
212 | Mix_VolumeChunk(globalData.typingChunk.get(), soundVolume); | |
213 | Mix_VolumeChunk(counterChunk.get(), soundVolume); | |
214 | Mix_VolumeChunk(counterFinalChunk.get(), soundVolume); | |
222 | 215 | } //All sound has been loaded or not |
223 | 216 | return 0; |
224 | 217 | } //InitImages() |
225 | 218 | |
226 | 219 | /*Draws a image from on a given Surface. Takes source image, destination surface and coordinates*/ |
227 | 220 | void DrawIMG(const sago::SagoSprite& sprite, SDL_Renderer* target, int x, int y) { |
228 | sprite.Draw(target, SDL_GetTicks() ,x,y); | |
221 | sprite.Draw(target, SDL_GetTicks(),x,y); | |
229 | 222 | } |
230 | 223 | |
231 | 224 | void DrawIMG_Bounded(const sago::SagoSprite& sprite, SDL_Renderer* target, int x, int y, int minx, int miny, int maxx, int maxy) { |
237 | 230 | sprite.DrawBounded(target, SDL_GetTicks(),x,y,bounds); |
238 | 231 | } |
239 | 232 | |
240 | ||
241 | static void NFont_Write(SDL_Renderer* target, int x, int y, const string& text) { | |
242 | globalData.nf_standard_blue_font.draw(target, x, y, "%s", text.c_str()); | |
243 | } | |
244 | ||
245 | static void NFont_Write(SDL_Renderer* target, int x, int y, const char* text) { | |
246 | globalData.nf_standard_blue_font.draw(target, x, y, "%s", text); | |
247 | } | |
248 | ||
249 | 233 | SDL_Window* sdlWindow; |
250 | 234 | |
251 | std::unique_ptr<sago::SagoDataHolder> dataHolder; | |
235 | sago::SagoDataHolder dataHolder; | |
252 | 236 | |
253 | 237 | void ResetFullscreen() { |
254 | 238 | Mix_HaltMusic(); //We need to reload all data in case the screen type changes. Music must be stopped before unload. |
258 | 242 | else { |
259 | 243 | SDL_SetWindowFullscreen(sdlWindow, 0); |
260 | 244 | } |
261 | dataHolder.reset(new sago::SagoDataHolder(globalData.screen)); | |
262 | globalData.spriteHolder.reset(new sago::SagoSpriteHolder( *(dataHolder.get()) ) ); | |
245 | dataHolder.invalidateAll(globalData.screen); | |
246 | globalData.spriteHolder.reset(new sago::SagoSpriteHolder( dataHolder ) ); | |
263 | 247 | InitImages(*(globalData.spriteHolder.get()) ); |
264 | 248 | SDL_ShowCursor(SDL_DISABLE); |
265 | 249 | } |
331 | 315 | SDL_FreeSurface(sreenshotSurface); |
332 | 316 | if (!globalData.NoSound) { |
333 | 317 | if (globalData.SoundEnabled) { |
334 | Mix_PlayChannel(1, photoClick, 0); | |
318 | Mix_PlayChannel(1, photoClick.get(), 0); | |
335 | 319 | } |
336 | 320 | } |
337 | 321 | } |
393 | 377 | } |
394 | 378 | |
395 | 379 | |
380 | static sago::SagoTextField* getSmallInt(size_t number) { | |
381 | static std::vector<std::shared_ptr<sago::SagoTextField> > smallFontCache; | |
382 | if (smallFontCache.size() < number+1) { | |
383 | smallFontCache.resize(number+1); | |
384 | } | |
385 | if (!smallFontCache[number]) { | |
386 | std::shared_ptr<sago::SagoTextField> newNumber = std::make_shared<sago::SagoTextField>(); | |
387 | newNumber->SetHolder(&globalData.spriteHolder->GetDataHolder()); | |
388 | newNumber->SetFont("freeserif"); | |
389 | newNumber->SetFontSize(16); | |
390 | newNumber->SetColor({255,0,0,255}); | |
391 | newNumber->SetText(std::to_string(number)); | |
392 | smallFontCache[number] = newNumber; | |
393 | } | |
394 | return smallFontCache[number].get(); | |
395 | } | |
396 | ||
396 | 397 | //Draws the balls and explosions |
397 | 398 | static void DrawBalls() { |
398 | 399 | for (size_t i = 0; i< theBallManager.ballArray.size(); i++) { |
411 | 412 | int y = globalData.theTextManager.textArray[i].getY()-12; |
412 | 413 | DrawIMG(iChainFrame,globalData.screen,x,y); |
413 | 414 | |
414 | nf_standard_small_font.draw(globalData.screen, x+12,y+7, NFont::CENTER, "%s", globalData.theTextManager.textArray[i].getText()); | |
415 | getSmallInt(globalData.theTextManager.textArray[i].getText())->Draw(globalData.screen, x+12, y+7, | |
416 | sago::SagoTextField::Alignment::center); | |
415 | 417 | } |
416 | 418 | } |
417 | 419 | } //DrawBalls |
418 | 420 | |
421 | template <class T> void sagoTextSetHelpFont(T& field) { | |
422 | field.SetHolder(&globalData.spriteHolder->GetDataHolder()); | |
423 | field.SetFont("freeserif"); | |
424 | field.SetFontSize(30); | |
425 | field.SetOutline(1, {128,128,128,255}); | |
426 | } | |
427 | ||
428 | void sagoTextSetHelpFont(sago::SagoTextField& gametypeNameField) { | |
429 | sagoTextSetHelpFont<sago::SagoTextField>(gametypeNameField); | |
430 | } | |
431 | ||
432 | void sagoTextSetBlueFont(sago::SagoTextField& field) { | |
433 | field.SetHolder(&globalData.spriteHolder->GetDataHolder()); | |
434 | field.SetFont("freeserif"); | |
435 | field.SetFontSize(30); | |
436 | field.SetColor({0,0,255,255}); | |
437 | field.SetOutline(1, {128,128,255,255}); | |
438 | } | |
419 | 439 | |
420 | 440 | //draws everything |
421 | 441 | void DrawEverything(int xsize, int ysize,BlockGameSdl* theGame, BlockGameSdl* theGame2) { |
425 | 445 | theGame2->DoPaintJob(); |
426 | 446 | string strHolder; |
427 | 447 | strHolder = std::to_string(theGame->GetScore()+theGame->GetHandicap()); |
428 | NFont_Write(globalData.screen, theGame->GetTopX()+310,theGame->GetTopY()+100,strHolder.c_str()); | |
448 | player1score.SetText(strHolder); | |
449 | player1score.Draw(globalData.screen, theGame->GetTopX()+310,theGame->GetTopY()+100); | |
429 | 450 | if (theGame->GetAIenabled()) { |
430 | NFont_Write(globalData.screen, theGame->GetTopX()+10,theGame->GetTopY()-34,_("AI") ); | |
431 | } | |
432 | else if (editorMode || singlePuzzle) { | |
433 | NFont_Write(globalData.screen, theGame->GetTopX()+10,theGame->GetTopY()-34,_("Playing field") ); | |
451 | player1name.SetText(_("AI")); | |
452 | } | |
453 | else if (singlePuzzle) { | |
454 | player1name.SetText(_("Playing field")); | |
434 | 455 | } |
435 | 456 | else { |
436 | NFont_Write(globalData.screen, theGame->GetTopX()+10,theGame->GetTopY()-34, globalData.player1name); | |
437 | } | |
457 | player1name.SetText(globalData.player1name); | |
458 | } | |
459 | player1name.Draw(globalData.screen, theGame->GetTopX()+10,theGame->GetTopY()-34); | |
438 | 460 | if (theGame->isTimeTrial()) { |
439 | 461 | int tid = (int)SDL_GetTicks()-theGame->GetGameStartedAt(); |
440 | 462 | int minutes; |
459 | 481 | else { |
460 | 482 | strHolder = std::to_string(minutes)+":0"+std::to_string(seconds); |
461 | 483 | } |
462 | //if ((SoundEnabled)&&(!NoSound)&&(tid>0)&&(seconds<5)&&(minutes == 0)&&(seconds>1)&&(!(Mix_Playing(6)))) Mix_PlayChannel(6,heartBeat,0); | |
463 | NFont_Write(globalData.screen, theGame->GetTopX()+310,theGame->GetTopY()+150,strHolder.c_str()); | |
484 | player1time.SetText(strHolder); | |
464 | 485 | } |
465 | 486 | else { |
466 | 487 | int minutes = ((abs((int)SDL_GetTicks()-(int)theGame->GetGameStartedAt())))/60/1000; |
477 | 498 | else { |
478 | 499 | strHolder = std::to_string(minutes)+":0"+std::to_string(seconds); |
479 | 500 | } |
480 | NFont_Write(globalData.screen, theGame->GetTopX()+310,theGame->GetTopY()+150,strHolder.c_str()); | |
481 | } | |
501 | player1time.SetText(strHolder); | |
502 | } | |
503 | player1time.Draw(globalData.screen, theGame->GetTopX()+310,theGame->GetTopY()+150); | |
482 | 504 | strHolder = std::to_string(theGame->GetChains()); |
483 | NFont_Write(globalData.screen, theGame->GetTopX()+310,theGame->GetTopY()+200,strHolder.c_str()); | |
505 | player1chain.SetText(strHolder); | |
506 | player1chain.Draw(globalData.screen, theGame->GetTopX()+310,theGame->GetTopY()+200); | |
484 | 507 | //drawspeedLevel: |
485 | 508 | strHolder = std::to_string(theGame->GetSpeedLevel()); |
486 | NFont_Write(globalData.screen, theGame->GetTopX()+310,theGame->GetTopY()+250,strHolder.c_str()); | |
509 | player1speed.SetText(strHolder); | |
510 | player1speed.Draw(globalData.screen, theGame->GetTopX()+310,theGame->GetTopY()+250); | |
487 | 511 | if ((theGame->isStageClear()) &&(theGame->GetTopY()+700+50*(theGame->GetStageClearLimit()-theGame->GetLinesCleared())-theGame->GetPixels()-1<600+theGame->GetTopY())) { |
488 | 512 | oldBubleX = theGame->GetTopX()+280; |
489 | 513 | oldBubleY = theGame->GetTopY()+650+50*(theGame->GetStageClearLimit()-theGame->GetLinesCleared())-theGame->GetPixels()-1; |
490 | 514 | DrawIMG(stageBobble,globalData.screen,theGame->GetTopX()+280,theGame->GetTopY()+650+50*(theGame->GetStageClearLimit()-theGame->GetLinesCleared())-theGame->GetPixels()-1); |
491 | 515 | } |
492 | 516 | //player1 finnish, player2 start |
493 | if (!editorMode /*&& !singlePuzzle*/ ) { | |
517 | if (true ) { | |
494 | 518 | /* |
495 | 519 | *If single player mode (and not VS) |
496 | 520 | */ |
518 | 542 | infostring = _("Score as much as possible. No time limit."); |
519 | 543 | } |
520 | 544 | if (infostring.length() > 0) { |
521 | NFont_Write(globalData.screen, theGame2->GetTopX()+7,theGame2->GetTopY()+10, gametypeName); | |
522 | NFont_Write(globalData.screen, theGame2->GetTopX()+7,theGame2->GetTopY()+160, _("Objective:")); | |
523 | globalData.nf_standard_blue_font.drawBox(globalData.screen, { static_cast<float>(theGame2->GetTopX()+7),static_cast<float>(theGame2->GetTopY()+160+32), 280, 200}, "%s", infostring.c_str()); | |
545 | static sago::SagoTextBox infoBox; | |
546 | static sago::SagoTextField objectiveField; | |
547 | static sago::SagoTextField gametypeNameField; | |
548 | sagoTextSetHelpFont(infoBox); | |
549 | infoBox.SetMaxWidth(290); | |
550 | infoBox.SetText(infostring); | |
551 | sagoTextSetHelpFont(objectiveField); | |
552 | objectiveField.SetText(_("Objective:")); | |
553 | sagoTextSetHelpFont(gametypeNameField); | |
554 | gametypeNameField.SetText(gametypeName); | |
555 | gametypeNameField.Draw(globalData.screen, theGame2->GetTopX()+7,theGame2->GetTopY()+10); | |
556 | objectiveField.Draw(globalData.screen, theGame2->GetTopX()+7, theGame2->GetTopY()+160); | |
557 | infoBox.Draw(globalData.screen, theGame2->GetTopX()+7, theGame2->GetTopY()+160+32); | |
524 | 558 | } |
525 | 559 | |
526 | 560 | //Write the keys that are in use |
527 | 561 | int y = theGame2->GetTopY()+400; |
528 | NFont_Write(globalData.screen, theGame2->GetTopX()+7,y,_("Movement keys:") ); | |
529 | NFont_Write(globalData.screen, theGame2->GetTopX()+7,y+40,(getKeyName(keySettings[0].left)+", "+getKeyName(keySettings[0].right)+"," ).c_str() ); | |
530 | NFont_Write(globalData.screen, theGame2->GetTopX()+7,y+76,(getKeyName(keySettings[0].up)+", "+getKeyName(keySettings[0].down)).c_str() ); | |
531 | NFont_Write(globalData.screen, theGame2->GetTopX()+7,y+120,( _("Switch: ")+getKeyName(keySettings[0].change) ).c_str() ); | |
562 | std::string controldBoxText = std::string(_("Movement keys:"))+"\n"+getKeyName(keySettings[0].left)+", "+getKeyName(keySettings[0].right)+",\n" | |
563 | + getKeyName(keySettings[0].up)+", "+getKeyName(keySettings[0].down)+"\n" | |
564 | + _("Switch: ") + getKeyName(keySettings[0].change); | |
532 | 565 | if (theGame->isPuzzleMode()) { |
533 | NFont_Write(globalData.screen, theGame2->GetTopX()+7,y+160,( _("Restart: ")+getKeyName(keySettings[0].push) ).c_str() ); | |
566 | controldBoxText += std::string("\n") + _("Restart: ")+getKeyName(keySettings[0].push); | |
534 | 567 | } |
535 | 568 | else { |
536 | NFont_Write(globalData.screen, theGame2->GetTopX()+7,y+160,( _("Push line: ")+getKeyName(keySettings[0].push) ).c_str() ); | |
537 | } | |
538 | ||
569 | controldBoxText += std::string("\n") + _("Push line: ")+getKeyName(keySettings[0].push); | |
570 | } | |
571 | static sago::SagoTextBox controldBox; | |
572 | controldBox.SetHolder(&globalData.spriteHolder->GetDataHolder()); | |
573 | sagoTextSetHelpFont(controldBox); | |
574 | controldBox.SetMaxWidth(290); | |
575 | controldBox.SetText(controldBoxText); | |
576 | controldBox.Draw(globalData.screen, theGame2->GetTopX()+7,y); | |
539 | 577 | } |
540 | 578 | strHolder = std::to_string(theGame2->GetScore()+theGame2->GetHandicap()); |
541 | NFont_Write(globalData.screen, theGame2->GetTopX()+310,theGame2->GetTopY()+100,strHolder.c_str()); | |
579 | player2score.SetText(strHolder); | |
580 | player2score.Draw(globalData.screen, theGame2->GetTopX()+310, theGame2->GetTopY()+100); | |
542 | 581 | if (theGame2->GetAIenabled()) { |
543 | NFont_Write(globalData.screen, theGame2->GetTopX()+10,theGame2->GetTopY()-34,_("AI") ); | |
582 | player2name.SetText(_("AI")); | |
544 | 583 | } |
545 | 584 | else { |
546 | NFont_Write(globalData.screen, theGame2->GetTopX()+10,theGame2->GetTopY()-34,theGame2->name); | |
547 | } | |
585 | player2name.SetText(theGame2->name); | |
586 | } | |
587 | player2name.Draw(globalData.screen, theGame2->GetTopX()+10,theGame2->GetTopY()-34); | |
548 | 588 | if (theGame2->isTimeTrial()) { |
549 | 589 | int tid = (int)SDL_GetTicks()-theGame2->GetGameStartedAt(); |
550 | 590 | int minutes; |
569 | 609 | else { |
570 | 610 | strHolder = std::to_string(minutes)+":0"+std::to_string(seconds); |
571 | 611 | } |
572 | //if ((SoundEnabled)&&(!NoSound)&&(tid>0)&&(seconds<5)&&(minutes == 0)&&(seconds>1)&&(!(Mix_Playing(6)))) Mix_PlayChannel(6,heartBeat,0); | |
573 | NFont_Write(globalData.screen, theGame2->GetTopX()+310,theGame2->GetTopY()+150,strHolder.c_str()); | |
574 | 612 | } |
575 | 613 | else { |
576 | 614 | int minutes = (abs((int)SDL_GetTicks()-(int)theGame2->GetGameStartedAt()))/60/1000; |
587 | 625 | else { |
588 | 626 | strHolder = std::to_string(minutes)+":0"+std::to_string(seconds); |
589 | 627 | } |
590 | NFont_Write(globalData.screen, theGame2->GetTopX()+310,theGame2->GetTopY()+150,strHolder.c_str()); | |
591 | } | |
628 | } | |
629 | player2time.SetText(strHolder); | |
630 | player2time.Draw(globalData.screen, theGame2->GetTopX()+310,theGame2->GetTopY()+150); | |
592 | 631 | strHolder = std::to_string(theGame2->GetChains()); |
593 | NFont_Write(globalData.screen, theGame2->GetTopX()+310,theGame2->GetTopY()+200,strHolder.c_str()); | |
632 | player2chain.SetText(strHolder); | |
633 | player2chain.Draw(globalData.screen, theGame2->GetTopX()+310,theGame2->GetTopY()+200); | |
594 | 634 | strHolder = std::to_string(theGame2->GetSpeedLevel()); |
595 | NFont_Write(globalData.screen, theGame2->GetTopX()+310,theGame2->GetTopY()+250,strHolder.c_str()); | |
635 | player2speed.SetText(strHolder); | |
636 | player2speed.Draw(globalData.screen, theGame2->GetTopX()+310,theGame2->GetTopY()+250); | |
596 | 637 | } |
597 | 638 | //player2 finnish |
598 | 639 | |
602 | 643 | DrawBalls(); |
603 | 644 | |
604 | 645 | #if DEBUG |
646 | static sago::SagoTextField fpsField; | |
647 | sagoTextSetBlueFont(fpsField); | |
605 | 648 | Frames++; |
606 | 649 | if (SDL_GetTicks() >= Ticks + 1000) { |
607 | 650 | if (Frames > 999) { |
611 | 654 | Frames = 0; |
612 | 655 | Ticks = SDL_GetTicks(); |
613 | 656 | } |
614 | ||
615 | globalData.nf_standard_blue_font.draw(globalData.screen, 800, 4, "%s", FPS); | |
657 | fpsField.SetText(FPS); | |
658 | fpsField.Draw(globalData.screen, 800, 4); | |
616 | 659 | #endif |
617 | 660 | } |
618 | 661 | |
745 | 788 | string puzzleName; |
746 | 789 | bool allowResize = true; |
747 | 790 | bool autoScale = true; |
791 | bool softwareRenderer = false; | |
748 | 792 | }; |
749 | 793 | |
750 | 794 | static void ParseArguments(int argc, char* argv[], globalConfig& conf) { |
765 | 809 | ("config,c", boost::program_options::value<vector<string> >(), "Read a config file with the values. Can be given multiple times") |
766 | 810 | ("nosound", "Disables the sound. Can be used if sound errors prevents you from starting") |
767 | 811 | ("priority", "Causes the game to not sleep between frames.") |
812 | ("software-renderer", "Asks SDL2 to use software renderer") | |
768 | 813 | ("verbose-basic", "Enables basic verbose messages") |
769 | 814 | ("verbose-game-controller", "Enables verbose messages regarding controllers") |
770 | 815 | ("print-search-path", "Prints the search path and quits") |
816 | 861 | cout << "Examples:" << "\n"; |
817 | 862 | cout << "\tblockattack \tStart the game normally" << "\n"; |
818 | 863 | cout << "\tblockattack --nosound\tStart the game without sound. Can be used if sound problems prevents the game from starting" << "\n"; |
864 | cout << "\tblockattack --puzzle-level-file puzzle.levels --puzzle-single-level 3\tStart the game with the default puzzles in level 3" << "\n"; | |
865 | cout << "\tblockattack --bind-text-domain /dev/null\t Disables translations" << "\n"; | |
819 | 866 | cout << "\n"; |
820 | 867 | cout << "Report bugs to the issue tracker here: <https://github.com/blockattack/blockattack-game/issues>" << "\n"; |
821 | 868 | exit(0); |
834 | 881 | } |
835 | 882 | if (vm.count("priority")) { |
836 | 883 | globalData.highPriority = true; |
884 | } | |
885 | if (vm.count("software-renderer")) { | |
886 | conf.softwareRenderer = true; | |
837 | 887 | } |
838 | 888 | if (vm.count("verbose-basic")) { |
839 | 889 | globalData.verboseLevel++; |
1024 | 1074 | globalData.xsize, globalData.ysize, |
1025 | 1075 | createWindowParams ); |
1026 | 1076 | dieOnNullptr(sdlWindow, "Unable to create window"); |
1027 | SDL_Renderer* renderer = SDL_CreateRenderer(sdlWindow, -1, 0); | |
1077 | int rendererFlags = 0; | |
1078 | if (config.softwareRenderer) { | |
1079 | rendererFlags |= SDL_RENDERER_SOFTWARE; | |
1080 | } | |
1081 | SDL_Renderer* renderer = SDL_CreateRenderer(sdlWindow, -1, rendererFlags); | |
1028 | 1082 | dieOnNullptr(renderer, "Unable to create render"); |
1029 | 1083 | if (config.autoScale) { |
1030 | 1084 | SDL_RenderSetLogicalSize(renderer, 1024, 768); |
1031 | 1085 | logicalRenderer = true; |
1032 | 1086 | } |
1087 | if (globalData.verboseLevel) { | |
1088 | SDL_RendererInfo info; | |
1089 | SDL_GetRendererInfo(renderer, &info); | |
1090 | cout << "Renderer: " << info.name << "\n"; | |
1091 | } | |
1033 | 1092 | globalData.screen = renderer; |
1034 | 1093 | ResetFullscreen(); |
1035 | 1094 | SetSDLIcon(sdlWindow); |
1038 | 1097 | cout << "Images loaded" << "\n"; |
1039 | 1098 | } |
1040 | 1099 | |
1041 | BlockGameSdl theGame = BlockGameSdl(50, 100); //creates game objects | |
1042 | BlockGameSdl theGame2 = BlockGameSdl(globalData.xsize-500, 100); | |
1100 | BlockGameSdl theGame = BlockGameSdl(50, 100, &globalData.spriteHolder->GetDataHolder()); //creates game objects | |
1101 | BlockGameSdl theGame2 = BlockGameSdl(globalData.xsize-500, 100, &globalData.spriteHolder->GetDataHolder()); | |
1043 | 1102 | player1 = &theGame; |
1044 | 1103 | player2 = &theGame2; |
1045 | 1104 | |
1080 | 1139 | |
1081 | 1140 | |
1082 | 1141 | //Saves options |
1083 | if (!editorMode) { | |
1142 | if (true) { | |
1084 | 1143 | configSettings->setInt("fullscreen",(int)globalData.bFullscreen); |
1085 | 1144 | configSettings->setInt("musicenabled",(int)globalData.MusicEnabled); |
1086 | 1145 | configSettings->setInt("soundenabled",(int)globalData.SoundEnabled); |
1138 | 1197 | globalData.theTopScoresTimeTrial = Highscore("timetrial"); |
1139 | 1198 | drawBalls = true; |
1140 | 1199 | puzzleLoaded = false; |
1141 | bool bNearDeath = false; //Play music faster or louder while tru | |
1200 | bool bNearDeath = false; //Play music faster or louder while tru | |
1142 | 1201 | |
1143 | 1202 | theBallManager = BallManager(); |
1144 | 1203 | theExplosionManager = ExplosionManager(); |
1145 | BlockGameSdl theGame = BlockGameSdl(50,100); //creates game objects | |
1146 | BlockGameSdl theGame2 = BlockGameSdl(globalData.xsize-500,100); | |
1204 | BlockGameSdl theGame = BlockGameSdl(50, 100, &globalData.spriteHolder->GetDataHolder()); //creates game objects | |
1205 | BlockGameSdl theGame2 = BlockGameSdl(globalData.xsize-500, 100, &globalData.spriteHolder->GetDataHolder()); | |
1147 | 1206 | player1 = &theGame; |
1148 | 1207 | player2 = &theGame2; |
1149 | theGame.DoPaintJob(); //Makes sure what there is something to paint | |
1208 | theGame.DoPaintJob(); //Makes sure what there is something to paint | |
1150 | 1209 | theGame2.DoPaintJob(); |
1151 | 1210 | BlockGameAction a; |
1152 | 1211 | a.action = BlockGameAction::Action::SET_GAME_OVER; |
1276 | 1335 | done=1; |
1277 | 1336 | DrawBackground(globalData.screen); |
1278 | 1337 | } |
1279 | if ((!editorMode)&&(!editorModeTest)&&(!theGame.GetAIenabled())) { | |
1338 | if (!theGame.GetAIenabled()) { | |
1280 | 1339 | //player1: |
1281 | 1340 | if ( event.key.keysym.sym == keySettings[player1keys].up ) { |
1282 | 1341 | a.action = BlockGameAction::Action::MOVE; |
1307 | 1366 | theGame.DoAction(a); |
1308 | 1367 | } |
1309 | 1368 | } |
1310 | if (!editorMode && !theGame2.GetAIenabled()) { | |
1369 | if (!theGame2.GetAIenabled()) { | |
1311 | 1370 | //player2: |
1312 | 1371 | if ( event.key.keysym.sym == keySettings[player2keys].up ) { |
1313 | 1372 | a.action = BlockGameAction::Action::MOVE; |
1339 | 1398 | } |
1340 | 1399 | } |
1341 | 1400 | //common: |
1342 | if ((!singlePuzzle)&&(!editorMode)) { | |
1401 | if (!singlePuzzle) { | |
1343 | 1402 | if ( event.key.keysym.sym == SDLK_F2 ) { |
1344 | 1403 | /*#if NETWORK |
1345 | 1404 | if ((!showOptions)&&(!networkActive)){ |
1525 | 1584 | bMouseUp2=true; |
1526 | 1585 | } |
1527 | 1586 | |
1528 | if (!editorMode ) { | |
1587 | if (true ) { | |
1529 | 1588 | //read mouse events |
1530 | 1589 | if (SDL_GetMouseState(nullptr,nullptr)&SDL_BUTTON(1) && bMouseUp) { |
1531 | 1590 | //This is the mouse events |
1565 | 1624 | //Sees if music is stopped and if music is enabled |
1566 | 1625 | if ((!globalData.NoSound)&&(!Mix_PlayingMusic())&&(globalData.MusicEnabled)&&(!bNearDeath)) { |
1567 | 1626 | // then starts playing it. |
1568 | Mix_PlayMusic(bgMusic, -1); //music loop | |
1627 | Mix_PlayMusic(bgMusic.get(), -1); //music loop | |
1569 | 1628 | Mix_VolumeMusic((MIX_MAX_VOLUME*3)/10); |
1570 | 1629 | } |
1571 | 1630 | |
1572 | 1631 | if (bNearDeath!=bNearDeathPrev) { |
1573 | 1632 | if (bNearDeath) { |
1574 | 1633 | if (!globalData.NoSound &&(globalData.MusicEnabled)) { |
1575 | Mix_PlayMusic(highbeatMusic, 1); | |
1634 | Mix_PlayMusic(highbeatMusic.get(), 1); | |
1576 | 1635 | Mix_VolumeMusic((MIX_MAX_VOLUME*5)/10); |
1577 | 1636 | } |
1578 | 1637 | } |
1579 | 1638 | else { |
1580 | 1639 | if (!globalData.NoSound &&(globalData.MusicEnabled)) { |
1581 | Mix_PlayMusic(bgMusic, -1); | |
1640 | Mix_PlayMusic(bgMusic.get(), -1); | |
1582 | 1641 | Mix_VolumeMusic((MIX_MAX_VOLUME*3)/10); |
1583 | 1642 | } |
1584 | 1643 | } |
26 | 26 | |
27 | 27 | #include "sago/SagoSprite.hpp" |
28 | 28 | #include "BlockGame.hpp" |
29 | #include "sago/SagoTextField.hpp" | |
29 | 30 | |
30 | 31 | |
31 | 32 | //main variables and constants |
42 | 43 | |
43 | 44 | const char sharedir[] = SHAREDIR; |
44 | 45 | |
45 | //All graphic in the game (as pointers): | |
46 | //All graphic in the game: | |
46 | 47 | sago::SagoSprite backgroundImage; //Stores the background image |
48 | sago::SagoSprite bExit; | |
47 | 49 | static sago::SagoSprite backBoard; //Stores the background to the board |
48 | 50 | static sago::SagoSprite bForward; //The "forward" button |
49 | 51 | #if NETWORK |
66 | 68 | static sago::SagoSprite explosion[4]; //Then a block explodes |
67 | 69 | //Animations end |
68 | 70 | static sago::SagoSprite counter[3]; //Counts down from 3 |
69 | static sago::SagoSprite bricks[7]; //The bricks, saved in an array of pointers | |
71 | sago::SagoSprite bricks[7]; //The bricks, saved in an array of pointers | |
70 | 72 | static sago::SagoSprite crossover; //Cross the bricks that will be cleared soon |
71 | 73 | static sago::SagoSprite balls[7]; //The balls (the small ones that jump around) |
72 | 74 | |
90 | 92 | static sago::SagoSprite transCover; //The transperant block, covers the upcomming |
91 | 93 | static sago::SagoSprite bSkip; |
92 | 94 | static sago::SagoSprite bRetry; |
93 | static sago::SagoSprite bExit; | |
94 | 95 | const int bExitSize = 100; //height and width of the exit button |
95 | 96 | const int bExitOffset = 140; //pixels from the buttom right corner to the top left of the exit button |
96 | 97 | |
97 | static NFont nf_standard_small_font; | |
98 | ||
99 | static Mix_Music *bgMusic; //backgroundMusic | |
100 | static Mix_Music *highbeatMusic; //Background music with higher beat | |
101 | static Mix_Chunk *boing; //boing sound when clearing | |
102 | static Mix_Chunk *applause; //Applause, then the player is good | |
103 | static Mix_Chunk *photoClick; //clickSound | |
104 | static Mix_Chunk *counterChunk; //When counting down | |
105 | static Mix_Chunk *counterFinalChunk; | |
98 | static sago::MusicHandler bgMusic; //backgroundMusic | |
99 | static sago::MusicHandler highbeatMusic; //Background music with higher beat | |
100 | static sago::SoundHandler boing; //boing sound when clearing | |
101 | static sago::SoundHandler applause; //Applause, then the player is good | |
102 | static sago::SoundHandler photoClick; //clickSound | |
103 | static sago::SoundHandler counterChunk; //When counting down | |
104 | static sago::SoundHandler counterFinalChunk; | |
106 | 105 | |
107 | 106 | static bool bMouseUp; //true if the mouse(1) is unpressed |
108 | 107 | static bool bMouseUp2; //true if the mouse(2) is unpressed |
112 | 111 | static bool bNearDeathPrev; //Near death status last time checked. |
113 | 112 | static bool puzzleLoaded; //true if the puzzle levels have been loaded |
114 | 113 | static bool drawBalls; //if true balls are drawed to the screen, this might lower framerate too much |
115 | ||
116 | static bool editorMode = false; | |
117 | static bool editorModeTest = false; | |
118 | 114 | |
119 | 115 | |
120 | 116 | |
196 | 192 | cordRetryButton.ysize = buttonYsize |
197 | 193 | }; |
198 | 194 | |
199 | #endif | |
195 | static sago::SagoTextField player1name; | |
196 | static sago::SagoTextField player2name; | |
197 | static sago::SagoTextField player1time; | |
198 | static sago::SagoTextField player2time; | |
199 | static sago::SagoTextField player1score; | |
200 | static sago::SagoTextField player2score; | |
201 | static sago::SagoTextField player1chain; | |
202 | static sago::SagoTextField player2chain; | |
203 | static sago::SagoTextField player1speed; | |
204 | static sago::SagoTextField player2speed; | |
205 | ||
206 | ||
207 | #endif |
24 | 24 | #include <stdlib.h> |
25 | 25 | #include "MenuSystem.h" |
26 | 26 | #include "common.h" |
27 | #include "HelpHowtoState.hpp" | |
28 | #include "HelpGamepadState.hpp" | |
29 | #include "HelpAboutState.hpp" | |
30 | #include "ShowFileState.hpp" | |
27 | 31 | |
28 | 32 | using std::string; |
29 | 33 | using std::cerr; |
95 | 99 | |
96 | 100 | void InitMenues() { |
97 | 101 | standardButton.setSurfaces(); |
98 | standardButton.thefont = &globalData.nf_button_font; | |
99 | 102 | } |
100 | 103 | |
101 | 104 | static void runSinglePlayerEndless() { |
266 | 269 | RunGameState(mm); |
267 | 270 | } |
268 | 271 | |
269 | void MainMenu() { | |
270 | InitMenues(); | |
271 | Menu m(globalData.screen,_("Block Attack - Rise of the blocks"),false); | |
272 | Button bHi,bTimetrial1, bStageClear, bPuzzle, bVs1, bMulti, bConfigure,bHighscore; | |
272 | static void runHowto() { | |
273 | HelpHowtoState howto; | |
274 | RunGameState(howto); | |
275 | } | |
276 | ||
277 | static void runHelpGamepad() { | |
278 | HelpGamepadState helpGamepad; | |
279 | RunGameState(helpGamepad); | |
280 | } | |
281 | ||
282 | static void runHelpAbout() { | |
283 | HelpAboutState helpAbout; | |
284 | RunGameState(helpAbout); | |
285 | } | |
286 | ||
287 | static void runCredits() { | |
288 | ShowFileState creditsFile; | |
289 | creditsFile.SetData("misc/AUTHORS", _("Credits")); | |
290 | RunGameState(creditsFile); | |
291 | } | |
292 | ||
293 | static void HelpMenu() { | |
294 | Menu m(globalData.screen, _("Help"), true); | |
295 | Button bHowto; | |
296 | bHowto.setLabel(_("How to")); | |
297 | bHowto.setAction(runHowto); | |
298 | m.addButton(&bHowto); | |
299 | Button bGamepad; | |
300 | bGamepad.setLabel(_("Gamepad")); | |
301 | bGamepad.setAction(runHelpGamepad); | |
302 | m.addButton(&bGamepad); | |
303 | Button bCredits; | |
304 | bCredits.setLabel(_("Credits")); | |
305 | bCredits.setAction(runCredits); | |
306 | m.addButton(&bCredits); | |
307 | Button bAbout; | |
308 | bAbout.setLabel(_("About")); | |
309 | bAbout.setAction(runHelpAbout); | |
310 | m.addButton(&bAbout); | |
311 | RunGameState(m); | |
312 | } | |
313 | ||
314 | static void SinglePlayerMenu() { | |
315 | Menu m(globalData.screen, _("Single player"), true); | |
316 | Button bHi,bTimetrial1, bStageClear, bPuzzle, bVs1; | |
273 | 317 | bHi.setLabel(_("Single player - endless") ); |
274 | 318 | bHi.setAction(runSinglePlayerEndless); |
275 | 319 | bTimetrial1.setLabel(_("Single player - time trial") ); |
280 | 324 | bPuzzle.setAction(runSinglePlayerPuzzle); |
281 | 325 | bVs1.setLabel(_("Single player - vs") ); |
282 | 326 | bVs1.setAction(SinglePlayerVsMenu); |
327 | m.addButton(&bHi); | |
328 | m.addButton(&bTimetrial1); | |
329 | m.addButton(&bStageClear); | |
330 | m.addButton(&bPuzzle); | |
331 | m.addButton(&bVs1); | |
332 | RunGameState(m); | |
333 | } | |
334 | ||
335 | void MainMenu() { | |
336 | InitMenues(); | |
337 | Menu m(globalData.screen,_("Block Attack - Rise of the blocks"),false); | |
338 | Button bHi, bMulti, bConfigure, bHighscore, bHelp; | |
339 | bHi.setLabel(_("Single player") ); | |
340 | bHi.setAction(SinglePlayerMenu); | |
283 | 341 | bMulti.setLabel(_("Multi player") ); |
284 | 342 | bMulti.setAction(MultiplayerMenu); |
285 | 343 | bConfigure.setLabel(_("Configure") ); |
286 | 344 | bConfigure.setAction(ConfigureMenu); |
287 | 345 | bHighscore.setLabel(_("Highscores") ); |
288 | 346 | bHighscore.setAction(buttonActionHighscores); |
347 | bHelp.setLabel(_("Help")); | |
348 | bHelp.setAction(HelpMenu); | |
289 | 349 | m.addButton(&bHi); |
290 | m.addButton(&bTimetrial1); | |
291 | m.addButton(&bStageClear); | |
292 | m.addButton(&bPuzzle); | |
293 | m.addButton(&bVs1); | |
294 | 350 | m.addButton(&bMulti); |
295 | 351 | m.addButton(&bConfigure); |
296 | 352 | m.addButton(&bHighscore); |
353 | m.addButton(&bHelp); | |
297 | 354 | RunGameState(m); |
298 | 355 | } |
32 | 32 | } |
33 | 33 | |
34 | 34 | static std::string CreateFileName(const std::tm& t ) { |
35 | std::string ret = "replays/blockattack_game_"; | |
36 | 35 | char buffer[200]; |
37 | 36 | snprintf(buffer, sizeof(buffer), "replays/blockattack_game_%i-%02i-%02iT%02i_%02i_%02i_AUTO.replay", t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); |
38 | ret = buffer; | |
37 | std::string ret = buffer; | |
39 | 38 | return ret; |
40 | 39 | } |
41 | 40 |
45 | 45 | virtual void ProcessInput(const SDL_Event& event, bool &processed) = 0; |
46 | 46 | |
47 | 47 | virtual void Update() {} |
48 | ||
49 | virtual ~GameStateInterface() {} | |
48 | 50 | }; |
49 | 51 | |
50 | 52 | } //sago |
41 | 41 | std::vector<SDL_RWops*> rwOpsToFree; |
42 | 42 | std::vector<std::unique_ptr<char[]>> dataToFree; |
43 | 43 | bool verbose = false; |
44 | Uint64 version = 0; | |
44 | 45 | SDL_Renderer* renderer = nullptr; |
45 | 46 | }; |
46 | 47 | |
47 | 48 | static void printFileWeLoad(const std::string& value) { |
48 | 49 | std::cout << "Loading " << value << "\n"; |
50 | } | |
51 | ||
52 | SagoDataHolder::SagoDataHolder() { | |
53 | data = new SagoDataHolderData(); | |
49 | 54 | } |
50 | 55 | |
51 | 56 | SagoDataHolder::SagoDataHolder(SDL_Renderer* renderer) { |
53 | 58 | data->renderer = renderer; |
54 | 59 | } |
55 | 60 | |
56 | SagoDataHolder::~SagoDataHolder() { | |
61 | void SagoDataHolder::invalidateAll(SDL_Renderer* renderer) { | |
62 | invalidateAll(); | |
63 | data->renderer = renderer; | |
64 | } | |
65 | ||
66 | void SagoDataHolder::invalidateAll() { | |
67 | data->version++; | |
57 | 68 | for (auto& item : data->textures) { |
58 | 69 | SDL_DestroyTexture(item.second); |
59 | 70 | } |
71 | data->textures.clear(); | |
60 | 72 | for (auto& item : data->music) { |
61 | 73 | Mix_FreeMusic(item.second); |
62 | 74 | } |
75 | data->music.clear(); | |
63 | 76 | for (auto& item : data->sounds) { |
64 | 77 | Mix_FreeChunk(item.second); |
65 | 78 | } |
79 | data->sounds.clear(); | |
66 | 80 | for (auto& item : data->fonts) { |
67 | 81 | for (auto& item2 : item.second) { |
68 | 82 | TTF_CloseFont(item2.second); |
69 | 83 | } |
70 | 84 | } |
85 | data->fonts.clear(); | |
71 | 86 | for (auto& item : data->rwOpsToFree) { |
72 | 87 | SDL_FreeRW(item); |
73 | 88 | } |
89 | data->rwOpsToFree.clear(); | |
90 | } | |
91 | ||
92 | SagoDataHolder::~SagoDataHolder() { | |
93 | invalidateAll(); | |
74 | 94 | delete data; |
75 | 95 | } |
76 | 96 | |
77 | 97 | SDL_Texture* SagoDataHolder::getTexturePtr(const std::string& textureName) const { |
98 | if (!data->renderer) { | |
99 | throw std::runtime_error("SagoDataHolder used before setting the renderer"); | |
100 | } | |
78 | 101 | SDL_Texture* ret = data->textures[textureName]; |
79 | 102 | if (ret) { |
80 | 103 | return ret; |
243 | 266 | data->verbose = value; |
244 | 267 | } |
245 | 268 | |
269 | Uint64 SagoDataHolder::getVersion() const { | |
270 | return data->version; | |
271 | } | |
272 | ||
273 | TextureHandler::TextureHandler(const SagoDataHolder* holder, const std::string &textureName) { | |
274 | this->holder = holder; | |
275 | this->version = this->holder->getVersion(); | |
276 | this->textureName = textureName; | |
277 | this->data = this->holder->getTexturePtr(this->textureName); | |
278 | } | |
279 | ||
280 | SDL_Texture* TextureHandler::get() { | |
281 | if (version != holder->getVersion()) { | |
282 | //The holder has been invalidated | |
283 | this->data = this->holder->getTexturePtr(textureName); | |
284 | } | |
285 | return data; | |
286 | } | |
287 | ||
288 | ||
289 | MusicHandler::MusicHandler(const SagoDataHolder* holder, const std::string& musicName) { | |
290 | this->holder = holder; | |
291 | this->version = this->holder->getVersion(); | |
292 | this->musicName = musicName; | |
293 | this->data = this->holder->getMusicPtr(this->musicName); | |
294 | } | |
295 | ||
296 | Mix_Music* MusicHandler::get() { | |
297 | if (version != holder->getVersion()) { | |
298 | //The holder has been invalidated | |
299 | this->data = this->holder->getMusicPtr(musicName); | |
300 | } | |
301 | return data; | |
302 | } | |
303 | ||
304 | SoundHandler::SoundHandler(const SagoDataHolder* holder, const std::string& soundName) { | |
305 | this->holder = holder; | |
306 | this->version = this->holder->getVersion(); | |
307 | this->soundName = soundName; | |
308 | this->data = this->holder->getSoundPtr(this->soundName); | |
309 | } | |
310 | ||
311 | Mix_Chunk* SoundHandler::get() { | |
312 | if (version != holder->getVersion()) { | |
313 | //The holder has been invalidated | |
314 | this->data = this->holder->getSoundPtr(soundName); | |
315 | } | |
316 | return data; | |
317 | } | |
318 | ||
319 | ||
320 | TextureHandler SagoDataHolder::getTextureHandler(const std::string &textureName) const { | |
321 | return TextureHandler(this, textureName); | |
322 | } | |
323 | ||
324 | MusicHandler SagoDataHolder::getMusicHandler(const std::string &musicName) const { | |
325 | return MusicHandler(this, musicName); | |
326 | } | |
327 | ||
328 | SoundHandler SagoDataHolder::getSoundHandler(const std::string &soundName) const { | |
329 | return SoundHandler(this, soundName); | |
330 | } | |
331 | ||
246 | 332 | } //name space sago |
33 | 33 | |
34 | 34 | namespace sago { |
35 | 35 | |
36 | class SagoDataHolder { | |
36 | class SagoDataHolder; | |
37 | ||
38 | class TextureHandler { | |
37 | 39 | public: |
38 | SagoDataHolder(SDL_Renderer* renderer); | |
40 | TextureHandler() {}; | |
41 | TextureHandler(const SagoDataHolder* holder, const std::string &textureName); | |
42 | SDL_Texture* get(); | |
43 | private: | |
44 | std::string textureName; | |
45 | const SagoDataHolder* holder = nullptr; | |
46 | SDL_Texture* data = nullptr; | |
47 | Uint64 version = 0; | |
48 | }; | |
49 | ||
50 | ||
51 | class MusicHandler final { | |
52 | public: | |
53 | MusicHandler() {}; | |
54 | MusicHandler(const SagoDataHolder* holder, const std::string &musicName); | |
55 | Mix_Music* get(); | |
56 | private: | |
57 | std::string musicName; | |
58 | const SagoDataHolder* holder = nullptr; | |
59 | Mix_Music* data = nullptr; | |
60 | Uint64 version = 0; | |
61 | }; | |
62 | ||
63 | ||
64 | class SoundHandler final { | |
65 | public: | |
66 | SoundHandler() {}; | |
67 | SoundHandler(const SagoDataHolder* holder, const std::string &soundName); | |
68 | Mix_Chunk* get(); | |
69 | private: | |
70 | std::string soundName; | |
71 | const SagoDataHolder* holder = nullptr; | |
72 | Mix_Chunk* data = nullptr; | |
73 | Uint64 version = 0; | |
74 | }; | |
75 | ||
76 | class SagoDataHolder final { | |
77 | public: | |
39 | 78 | /** |
40 | * Return a pointer to the given texture. The pointer is valid for the lifetime of SagoDataHolder object it was taken from. | |
79 | * The renderer must be set before requesting a texture. | |
80 | * If the constructor without elements is used then invalidateAll(SDL_Renderer*) must be called before getTexturePtr | |
81 | */ | |
82 | SagoDataHolder(); | |
83 | explicit SagoDataHolder(SDL_Renderer* renderer); | |
84 | /** | |
85 | * Return a pointer to the given texture. The pointer is valid for the lifetime of SagoDataHolder object it was taken from or until invalidateAll is called. | |
41 | 86 | * @param textureName Name of the texture |
42 | 87 | * @return Pointer to the loaded texture |
43 | 88 | */ |
44 | 89 | SDL_Texture* getTexturePtr(const std::string &textureName) const; |
90 | TextureHandler getTextureHandler(const std::string &textureName) const; | |
45 | 91 | TTF_Font* getFontPtr(const std::string &fontName, int ptsize) const; |
46 | 92 | Mix_Music* getMusicPtr(const std::string &musicName) const; |
93 | MusicHandler getMusicHandler(const std::string &musicName) const; | |
47 | 94 | Mix_Chunk* getSoundPtr(const std::string &soundName) const; |
95 | SoundHandler getSoundHandler(const std::string &soundName) const; | |
48 | 96 | void setVerbose(bool value); |
49 | virtual ~SagoDataHolder(); | |
97 | /** | |
98 | * Invalidates all pointers returned by any of the get variables | |
99 | */ | |
100 | void invalidateAll(); | |
101 | /** | |
102 | * Invalidates all pointers returned by any of the get variables. | |
103 | * Also sets a new renderer. | |
104 | * | |
105 | * Setting a new renderer might cause all old textures to no longer match the renderer format. | |
106 | */ | |
107 | void invalidateAll(SDL_Renderer* renderer); | |
108 | /** | |
109 | * The version number. Changes everytime the pointers are invalidated. | |
110 | * Can be used to determen if it is neccecary to get a new pointer. | |
111 | * @return A globally unique number. | |
112 | */ | |
113 | Uint64 getVersion() const; | |
114 | ~SagoDataHolder(); | |
50 | 115 | private: |
51 | 116 | SagoDataHolder(const SagoDataHolder& base) = delete; |
52 | 117 | SagoDataHolder& operator=(const SagoDataHolder& base) = delete; |
70 | 70 | return ret; |
71 | 71 | } |
72 | 72 | |
73 | static void CreatePathToFile(const std::string& path) { | |
74 | size_t end_of_path = path.find_last_of("/"); | |
75 | if (end_of_path == std::string::npos) { | |
76 | //No path | |
77 | return; | |
78 | } | |
79 | std::string path2dir = path.substr(0, end_of_path); | |
80 | PHYSFS_mkdir(path2dir.c_str()); | |
81 | } | |
82 | ||
73 | 83 | void WriteFileContent(const char* filename, const std::string& content) { |
84 | CreatePathToFile(filename); | |
74 | 85 | PHYSFS_file* myfile = PHYSFS_openWrite(filename); |
75 | 86 | if (!myfile) { |
76 | 87 | cerr << "Failed to open file for writing, " << PHYSFS_getLastError() << "\n"; |
27 | 27 | namespace sago { |
28 | 28 | |
29 | 29 | struct SagoSprite::SagoSpriteData { |
30 | SDL_Texture* tex = nullptr; | |
31 | SDL_Rect imgCord{}; | |
32 | SDL_Rect origin{}; | |
30 | TextureHandler tex; | |
31 | SDL_Rect imgCord = {}; | |
32 | SDL_Rect origin = {}; | |
33 | 33 | int aniFrames = 0; |
34 | 34 | int aniFrameTime = 0; |
35 | 35 | }; |
40 | 40 | |
41 | 41 | SagoSprite::SagoSprite(const SagoDataHolder& texHolder, const std::string& texture,const SDL_Rect& initImage,const int animationFrames, const int animationFrameLength) { |
42 | 42 | data = new SagoSpriteData(); |
43 | data->tex = texHolder.getTexturePtr(texture); | |
43 | data->tex = texHolder.getTextureHandler(texture); | |
44 | 44 | data->imgCord = initImage; |
45 | 45 | data->aniFrames = animationFrames; |
46 | 46 | data->aniFrameTime = animationFrameLength; |
64 | 64 | } |
65 | 65 | |
66 | 66 | void SagoSprite::DrawScaled(SDL_Renderer* target, Sint32 frameTime, int x, int y, int w, int h) const { |
67 | if (!data->tex) { | |
67 | if (!data->tex.get()) { | |
68 | 68 | std::cerr << "Texture is null!\n"; |
69 | 69 | } |
70 | 70 | SDL_Rect rect = data->imgCord; |
71 | 71 | rect.x+=rect.w*((frameTime/data->aniFrameTime)%data->aniFrames); |
72 | 72 | SDL_Rect pos = rect; |
73 | pos.x = x; | |
74 | pos.y = y; | |
73 | pos.x = x - this->data->origin.x; | |
74 | pos.y = y - this->data->origin.y; | |
75 | 75 | if (w > 0) { |
76 | 76 | pos.w = w; |
77 | 77 | } |
78 | 78 | if (h > 0) { |
79 | 79 | pos.h = h; |
80 | 80 | } |
81 | SDL_RenderCopy(target, data->tex, &rect, &pos); | |
81 | SDL_RenderCopy(target, data->tex.get(), &rect, &pos); | |
82 | 82 | } |
83 | 83 | |
84 | 84 | void SagoSprite::Draw(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& part) const { |
89 | 89 | rect.w = part.w; |
90 | 90 | rect.h = part.h; |
91 | 91 | SDL_Rect pos = rect; |
92 | pos.x = x; | |
93 | pos.y = y; | |
94 | SDL_RenderCopy(target, data->tex, &rect, &pos); | |
92 | pos.x = x - this->data->origin.x; | |
93 | pos.y = y - this->data->origin.y; | |
94 | SDL_RenderCopy(target, data->tex.get(), &rect, &pos); | |
95 | } | |
96 | ||
97 | void SagoSprite::DrawProgressive(SDL_Renderer* target, float progress, int x, int y) const { | |
98 | Sint32 frameNumber = progress*data->aniFrames; | |
99 | Sint32 frameTime = frameNumber*data->aniFrameTime; | |
100 | Draw(target, frameTime, x, y); | |
95 | 101 | } |
96 | 102 | |
97 | 103 | void SagoSprite::DrawBounded(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& bounds) const { |
137 | 143 | rect.h -= absDiff; |
138 | 144 | } |
139 | 145 | |
140 | SDL_RenderCopy(target, data->tex, &rect, &pos); | |
146 | SDL_RenderCopy(target, data->tex.get(), &rect, &pos); | |
141 | 147 | } |
142 | 148 | |
143 | 149 | void SagoSprite::SetOrigin(const SDL_Rect& newOrigin) { |
28 | 28 | |
29 | 29 | namespace sago { |
30 | 30 | |
31 | class SagoSprite { | |
31 | class SagoSprite final { | |
32 | 32 | public: |
33 | 33 | SagoSprite(); |
34 | 34 | SagoSprite(const SagoDataHolder &texHolder, const std::string &texture,const SDL_Rect& initImage,const int animationFrames, const int animationFrameLength); |
57 | 57 | * @param y Place to draw the sprite |
58 | 58 | * @param bounds A recagular area that we must not draw outside. |
59 | 59 | */ |
60 | void DrawBounded(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& bounds) const; | |
60 | void DrawBounded(SDL_Renderer* target, Sint32 frameTime, int x, int y, const SDL_Rect& bounds) const;/** | |
61 | * Draws the sprite to a given render window | |
62 | * @param target The render window to draw on | |
63 | * @param progress A float with value from 0.0f to 1.0f. Tells how far in the animation that we got | |
64 | * @param x Place to draw the sprite | |
65 | * @param y Place to draw the sprite | |
66 | */ | |
67 | void DrawProgressive(SDL_Renderer* target, float progress, int x, int y) const; | |
61 | 68 | void DrawScaled(SDL_Renderer* target, Sint32 frameTime, int x, int y, int w, int h) const; |
62 | 69 | /** |
63 | 70 | * Set a different origin. Normally it is the top left cornor. But in some cases you might want to center the origin or tranform it for other reasons |
68 | 75 | SagoSprite& operator=(const SagoSprite& base); |
69 | 76 | int GetWidth() const; |
70 | 77 | int GetHeight() const; |
71 | virtual ~SagoSprite(); | |
78 | ~SagoSprite(); | |
72 | 79 | private: |
73 | 80 | struct SagoSpriteData; |
74 | 81 | SagoSpriteData *data; |
63 | 63 | static int getDefaultValue(const rapidjson::Value& value, const char* name, int defaultValue) { |
64 | 64 | assert(value.IsObject()); |
65 | 65 | const auto& t = value.GetObject().FindMember(name); |
66 | if (t->value.IsInt()) { | |
66 | if (t != value.MemberEnd() && t->value.IsInt()) { | |
67 | 67 | return t->value.GetInt(); |
68 | 68 | } |
69 | 69 | return defaultValue; |
72 | 72 | static std::string getDefaultValue(const rapidjson::Value& value, const char* name, std::string defaultValue) { |
73 | 73 | assert(value.IsObject()); |
74 | 74 | const auto& t = value.GetObject().FindMember(name); |
75 | if (t->value.IsString()) { | |
75 | if (t != value.MemberEnd() && t->value.IsString()) { | |
76 | 76 | defaultValue = t->value.GetString(); |
77 | 77 | } |
78 | 78 | return defaultValue; |
102 | 102 | int width = getDefaultValue(m.value, "width",0); |
103 | 103 | int number_of_frames = getDefaultValue(m.value, "number_of_frames",1); |
104 | 104 | int frame_time = getDefaultValue(m.value, "frame_time",1); |
105 | int originx = getDefaultValue(m.value, "originx",0); | |
106 | int originy = getDefaultValue(m.value, "originy",0); | |
105 | SDL_Rect origin = {}; | |
106 | origin.x = getDefaultValue(m.value, "originx",0); | |
107 | origin.y = getDefaultValue(m.value, "originy",0); | |
107 | 108 | if (number_of_frames < 1) { |
108 | 109 | number_of_frames = 1; |
109 | 110 | } |
111 | 112 | frame_time = 1; |
112 | 113 | } |
113 | 114 | std::shared_ptr<sago::SagoSprite> ptr(new SagoSprite(*(data->tex),textureName, {topx,topy,width,height},number_of_frames,frame_time)); |
114 | ptr->SetOrigin({originx,originy, 0, 0}); | |
115 | ptr->SetOrigin(origin); | |
115 | 116 | this->data->sprites[std::string(spriteName)] = ptr; |
116 | 117 | } |
117 | 118 | } |
29 | 29 | |
30 | 30 | namespace sago { |
31 | 31 | |
32 | class SagoSpriteHolder { | |
32 | class SagoSpriteHolder final { | |
33 | 33 | public: |
34 | SagoSpriteHolder(const SagoDataHolder &texHolder); | |
35 | virtual ~SagoSpriteHolder(); | |
34 | explicit SagoSpriteHolder(const SagoDataHolder &texHolder); | |
35 | ~SagoSpriteHolder(); | |
36 | 36 | void ReadSprites(); |
37 | 37 | const sago::SagoSprite& GetSprite(const std::string &spritename) const; |
38 | 38 | const SagoDataHolder& GetDataHolder() const; |
0 | /* | |
1 | Copyright (c) 2018 Poul Sander | |
2 | ||
3 | Permission is hereby granted, free of charge, to any person | |
4 | obtaining a copy of this software and associated documentation files | |
5 | (the "Software"), to deal in the Software without restriction, | |
6 | including without limitation the rights to use, copy, modify, merge, | |
7 | publish, distribute, sublicense, and/or sell copies of the Software, | |
8 | and to permit persons to whom the Software is furnished to do so, | |
9 | subject to the following conditions: | |
10 | ||
11 | The above copyright notice and this permission notice shall be | |
12 | included in all copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
18 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
19 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
21 | SOFTWARE. | |
22 | */ | |
23 | ||
24 | #include "SagoTextBox.hpp" | |
25 | #include "SagoTextField.hpp" | |
26 | #include <vector> | |
27 | #include <iostream> | |
28 | #include "utf8.h" | |
29 | #include <algorithm> | |
30 | ||
31 | namespace sago { | |
32 | ||
33 | struct SagoTextBox::SagoTextBoxData { | |
34 | const sago::SagoDataHolder* tex = nullptr; | |
35 | std::string fontName = "freeserif"; | |
36 | SDL_Color color = { 255, 255, 255, 0 }; | |
37 | SDL_Color outlineColor = { 255, 255, 0, 0 }; | |
38 | int fontSize = 16; | |
39 | int outline = 0; | |
40 | std::string text = ""; | |
41 | std::string renderedText = ""; | |
42 | std::vector<SagoTextField> lines; | |
43 | int maxWidth = 0; | |
44 | }; | |
45 | ||
46 | SagoTextBox::SagoTextBox() { | |
47 | data = new SagoTextBoxData(); | |
48 | } | |
49 | ||
50 | SagoTextBox::~SagoTextBox() { | |
51 | delete data; | |
52 | } | |
53 | ||
54 | void SagoTextBox::SetHolder(const SagoDataHolder* holder) { | |
55 | data->tex = holder; | |
56 | } | |
57 | ||
58 | void SagoTextBox::SetText(const char* text) { | |
59 | data->text = text; | |
60 | } | |
61 | ||
62 | void SagoTextBox::SetText(const std::string& text) { | |
63 | data->text = text; | |
64 | } | |
65 | ||
66 | void SagoTextBox::SetColor(const SDL_Color& color) { | |
67 | data->color = color; | |
68 | } | |
69 | ||
70 | void SagoTextBox::SetFont(const char* fontName) { | |
71 | data->fontName = fontName; | |
72 | } | |
73 | ||
74 | void SagoTextBox::SetFontSize(int fontSize) { | |
75 | data->fontSize = fontSize; | |
76 | } | |
77 | ||
78 | void SagoTextBox::SetOutline(int outlineSize, const SDL_Color& color) { | |
79 | data->outline = outlineSize; | |
80 | data->outlineColor = color; | |
81 | } | |
82 | ||
83 | void SagoTextBox::SetMaxWidth(int width) { | |
84 | data->maxWidth = width; | |
85 | } | |
86 | ||
87 | const std::string& SagoTextBox::GetText() const { | |
88 | return data->text; | |
89 | } | |
90 | ||
91 | void SagoTextBox::AppendLineToCache(const std::string& text) { | |
92 | data->lines.resize(data->lines.size()+1); | |
93 | SagoTextField& tf = data->lines.back(); | |
94 | tf.SetHolder(data->tex); | |
95 | tf.SetFont(data->fontName.c_str()); | |
96 | tf.SetFontSize(data->fontSize); | |
97 | tf.SetColor(data->color); | |
98 | tf.SetOutline(data->outline, data->outlineColor); | |
99 | tf.SetText(text); | |
100 | } | |
101 | ||
102 | ||
103 | void SagoTextBox::SplitAndAppendLineToCache(TTF_Font* font, const std::string& text) { | |
104 | int width = data->maxWidth; | |
105 | TTF_SizeUTF8(font, text.c_str(),&width, nullptr); | |
106 | if (data->maxWidth <= 0 || width <= data->maxWidth || text.length() == 1) { | |
107 | AppendLineToCache(text); | |
108 | return; | |
109 | } | |
110 | std::string::const_iterator splitLocation = text.begin()+1; | |
111 | bool splitDone = false; | |
112 | while (!splitDone) { | |
113 | std::string::const_iterator nextSearchStart = splitLocation+1; | |
114 | if (nextSearchStart == text.end()) { | |
115 | splitDone = true; | |
116 | continue; | |
117 | } | |
118 | std::string::const_iterator nextSpace = std::find(nextSearchStart, text.end(), ' '); | |
119 | std::string attemptSubString(text.begin(), nextSpace); | |
120 | TTF_SizeUTF8(font, attemptSubString.c_str(),&width, nullptr); | |
121 | if (width <= data->maxWidth && nextSpace != text.end()) { | |
122 | splitLocation = nextSpace; | |
123 | } | |
124 | else { | |
125 | splitDone = true; | |
126 | } | |
127 | } | |
128 | if (splitLocation == text.begin()+1) { | |
129 | splitLocation = text.begin(); | |
130 | utf8::advance(splitLocation, 1, text.end()); | |
131 | splitDone = false; | |
132 | while (!splitDone && splitLocation != text.end()) { | |
133 | std::string::const_iterator nextSplit = splitLocation; | |
134 | utf8::advance(nextSplit, 1, text.end()); | |
135 | std::string attemptSubString(text.begin(), nextSplit); | |
136 | TTF_SizeUTF8(font, attemptSubString.c_str(), &width, nullptr); | |
137 | if (width <= data->maxWidth) { | |
138 | splitLocation = nextSplit; | |
139 | } | |
140 | else { | |
141 | splitDone = true; | |
142 | } | |
143 | } | |
144 | } | |
145 | std::string firstPart(text.begin(), splitLocation); | |
146 | AppendLineToCache(firstPart); | |
147 | while (splitLocation != text.end() && *splitLocation == ' ') { | |
148 | //Trim spaces after an automatic line break. | |
149 | ++splitLocation; | |
150 | } | |
151 | if (splitLocation == text.end()) { | |
152 | return; | |
153 | } | |
154 | std::string secondPart(splitLocation, text.end()); | |
155 | SplitAndAppendLineToCache(font, secondPart); | |
156 | } | |
157 | ||
158 | void SagoTextBox::UpdateCache() { | |
159 | if (!data->tex) { | |
160 | std::cerr << "FATAL: SagoTextBox::UpdateCache - DataHolder not set!\n"; | |
161 | abort(); | |
162 | } | |
163 | TTF_Font *font = data->tex->getFontPtr(data->fontName, data->fontSize); | |
164 | const char delim = '\n'; | |
165 | const std::string& s = data->text; | |
166 | auto start = 0U; | |
167 | auto end = s.find(delim); | |
168 | data->lines.clear(); | |
169 | while (end != std::string::npos) | |
170 | { | |
171 | const std::string& theSubString = s.substr(start, end - start); | |
172 | SplitAndAppendLineToCache(font, theSubString); | |
173 | start = end + 1; | |
174 | end = s.find(delim, start); | |
175 | } | |
176 | SplitAndAppendLineToCache(font, s.substr(start, end)); | |
177 | data->renderedText = data->text; | |
178 | } | |
179 | ||
180 | void SagoTextBox::Draw(SDL_Renderer* target, int x, int y) { | |
181 | if (data->text != data->renderedText) { | |
182 | UpdateCache(); | |
183 | } | |
184 | TTF_Font *font = data->tex->getFontPtr(data->fontName, data->fontSize); | |
185 | int lineSkip = TTF_FontLineSkip(font); | |
186 | for (size_t i = 0; i < data->lines.size(); ++i) { | |
187 | data->lines[i].Draw(target, x, y+i*lineSkip); | |
188 | } | |
189 | } | |
190 | ||
191 | } //namespace sago⏎ |
0 | /* | |
1 | Copyright (c) 2018 Poul Sander | |
2 | ||
3 | Permission is hereby granted, free of charge, to any person | |
4 | obtaining a copy of this software and associated documentation files | |
5 | (the "Software"), to deal in the Software without restriction, | |
6 | including without limitation the rights to use, copy, modify, merge, | |
7 | publish, distribute, sublicense, and/or sell copies of the Software, | |
8 | and to permit persons to whom the Software is furnished to do so, | |
9 | subject to the following conditions: | |
10 | ||
11 | The above copyright notice and this permission notice shall be | |
12 | included in all copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
18 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
19 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
21 | SOFTWARE. | |
22 | */ | |
23 | ||
24 | #ifndef SAGOTEXTBOX_HPP | |
25 | #define SAGOTEXTBOX_HPP | |
26 | ||
27 | #include "SagoDataHolder.hpp" | |
28 | #include "SagoTextField.hpp" | |
29 | ||
30 | namespace sago { | |
31 | ||
32 | class SagoTextBox final { | |
33 | public: | |
34 | SagoTextBox(); | |
35 | ~SagoTextBox(); | |
36 | void SetHolder(const SagoDataHolder* holder); | |
37 | void SetText(const char* text); | |
38 | void SetText(const std::string& text); | |
39 | void SetColor(const SDL_Color& color); | |
40 | ||
41 | /** | |
42 | * Set the name of the font. Must be known to the data holder. | |
43 | * The name could for instance be "freeserif". | |
44 | * @param fontName Name of the font as required by SagoDataHolder | |
45 | */ | |
46 | void SetFont(const char* fontName); | |
47 | void SetFontSize(int fontSize); | |
48 | void SetOutline(int outlineSize, const SDL_Color& color); | |
49 | /** | |
50 | * Sets the max width to generate. SagoTextBox will insert line breaks to keep the width below this number. | |
51 | * Outline is not included in the width: If you have a 2 pixels outline the rendere may go 2 pixels beyond. | |
52 | * You will always need to generate at least one char per line. If max width is too low one char will be drawn per line even if it goes above max width. | |
53 | * Setting this to 0 will disable the feature. | |
54 | * @param width The maximum width before forcing a line break | |
55 | */ | |
56 | void SetMaxWidth(int width); | |
57 | const std::string& GetText() const; | |
58 | void Draw(SDL_Renderer* target, int x, int y); | |
59 | void UpdateCache(); | |
60 | private: | |
61 | void AppendLineToCache(const std::string& text); | |
62 | void SplitAndAppendLineToCache(TTF_Font* font, const std::string& text); | |
63 | SagoTextBox(const SagoTextBox& orig) = delete; | |
64 | SagoTextBox& operator=(const SagoTextBox& base) = delete; | |
65 | struct SagoTextBoxData; | |
66 | SagoTextBoxData *data; | |
67 | }; | |
68 | ||
69 | } //namespace sago | |
70 | ||
71 | #endif /* SAGOTEXTBOX_HPP */ | |
72 |
0 | /* | |
1 | Copyright (c) 2018 Poul Sander | |
2 | ||
3 | Permission is hereby granted, free of charge, to any person | |
4 | obtaining a copy of this software and associated documentation files | |
5 | (the "Software"), to deal in the Software without restriction, | |
6 | including without limitation the rights to use, copy, modify, merge, | |
7 | publish, distribute, sublicense, and/or sell copies of the Software, | |
8 | and to permit persons to whom the Software is furnished to do so, | |
9 | subject to the following conditions: | |
10 | ||
11 | The above copyright notice and this permission notice shall be | |
12 | included in all copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
18 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
19 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
21 | SOFTWARE. | |
22 | */ | |
23 | ||
24 | #include "SagoTextField.hpp" | |
25 | #include <iostream> | |
26 | #include <SDL_ttf.h> | |
27 | ||
28 | namespace sago { | |
29 | ||
30 | class OutlineHandler { | |
31 | TTF_Font* font; | |
32 | int originalOutline = 0; | |
33 | int targetOutline; | |
34 | bool doChange = false; | |
35 | public: | |
36 | OutlineHandler(TTF_Font* font, int outline) : font{font}, targetOutline{outline} { | |
37 | originalOutline = TTF_GetFontOutline(font); | |
38 | if (originalOutline == targetOutline) { | |
39 | return; | |
40 | } | |
41 | doChange = true; | |
42 | TTF_SetFontOutline(font, targetOutline); | |
43 | }; | |
44 | ||
45 | void reset() { | |
46 | if (doChange) { | |
47 | TTF_SetFontOutline(font,originalOutline); | |
48 | doChange = false; | |
49 | } | |
50 | } | |
51 | ||
52 | ~OutlineHandler() { | |
53 | reset(); | |
54 | } | |
55 | private: | |
56 | OutlineHandler(const OutlineHandler& orig) = delete; | |
57 | OutlineHandler& operator=(const OutlineHandler& base) = delete; | |
58 | }; | |
59 | ||
60 | struct SagoTextField::SagoTextFieldData { | |
61 | const sago::SagoDataHolder* tex = nullptr; | |
62 | SDL_Surface* textSurface = nullptr; | |
63 | SDL_Texture* texture = nullptr; | |
64 | SDL_Surface* outlineTextSurface = nullptr; | |
65 | SDL_Texture* outlineTexture = nullptr; | |
66 | std::string fontName = "freeserif"; | |
67 | SDL_Color color = { 255, 255, 255, 0 }; | |
68 | SDL_Color outlineColor = { 255, 255, 0, 0 }; | |
69 | int fontSize = 16; | |
70 | int outline = 0; | |
71 | std::string text = ""; | |
72 | std::string renderedText = ""; | |
73 | Uint64 renderedVersion = 0; | |
74 | }; | |
75 | ||
76 | SagoTextField::SagoTextField() { | |
77 | data = new SagoTextFieldData(); | |
78 | } | |
79 | ||
80 | SagoTextField::SagoTextField(SagoTextField&& o) noexcept { | |
81 | data = o.data; | |
82 | o.data = nullptr; | |
83 | } | |
84 | ||
85 | SagoTextField& SagoTextField::CopyFrom(const SagoTextField& base) { | |
86 | ClearCache(); | |
87 | try { | |
88 | *data = *(base.data); | |
89 | //Copy all data but do not reuse the cache as it would result in a double free | |
90 | data->outlineTextSurface = nullptr; | |
91 | data->outlineTexture = nullptr; | |
92 | data->textSurface = nullptr; | |
93 | data->texture = nullptr; | |
94 | return *this; | |
95 | } catch (...) { | |
96 | delete data; | |
97 | throw; | |
98 | } | |
99 | } | |
100 | ||
101 | SagoTextField::~SagoTextField() { | |
102 | if(!data) { | |
103 | return; | |
104 | } | |
105 | ClearCache(); | |
106 | delete data; | |
107 | } | |
108 | ||
109 | void SagoTextField::SetHolder(const SagoDataHolder* holder) { | |
110 | data->tex = holder; | |
111 | } | |
112 | ||
113 | void SagoTextField::SetText(const char* text) { | |
114 | data->text = text; | |
115 | } | |
116 | ||
117 | void SagoTextField::SetText(const std::string& text) { | |
118 | data->text = text; | |
119 | } | |
120 | ||
121 | void SagoTextField::SetColor(const SDL_Color& color) { | |
122 | data->color = color; | |
123 | } | |
124 | ||
125 | void SagoTextField::SetFont(const char* fontName) { | |
126 | data->fontName = fontName; | |
127 | } | |
128 | ||
129 | void SagoTextField::SetFontSize(int fontSize) { | |
130 | data->fontSize = fontSize; | |
131 | } | |
132 | ||
133 | void SagoTextField::SetOutline(int outlineSize, const SDL_Color& color) { | |
134 | data->outline = outlineSize; | |
135 | data->outlineColor = color; | |
136 | } | |
137 | ||
138 | const std::string& SagoTextField::GetText() const { | |
139 | return data->text; | |
140 | } | |
141 | ||
142 | void SagoTextField::ClearCache() { | |
143 | if (data->texture) { | |
144 | SDL_DestroyTexture(data->texture); | |
145 | data->texture = nullptr; | |
146 | } | |
147 | if (data->textSurface) { | |
148 | SDL_FreeSurface(data->textSurface); | |
149 | data->textSurface = nullptr; | |
150 | } | |
151 | if (data->outlineTexture) { | |
152 | SDL_DestroyTexture(data->outlineTexture); | |
153 | data->outlineTexture = nullptr; | |
154 | } | |
155 | if (data->outlineTextSurface) { | |
156 | SDL_FreeSurface(data->outlineTextSurface); | |
157 | data->outlineTextSurface = nullptr; | |
158 | } | |
159 | } | |
160 | ||
161 | void SagoTextField::UpdateCache(SDL_Renderer* target) { | |
162 | if (!data->tex) { | |
163 | std::cerr << "FATAL: DataHolder not set!\n"; | |
164 | abort(); | |
165 | } | |
166 | ClearCache(); | |
167 | TTF_Font *font = data->tex->getFontPtr(data->fontName, data->fontSize); | |
168 | data->textSurface = TTF_RenderUTF8_Blended (font, data->text.c_str(), data->color); | |
169 | data->texture = SDL_CreateTextureFromSurface(target, data->textSurface); | |
170 | if (data->outline > 0) { | |
171 | OutlineHandler oh(font, data->outline); | |
172 | data->outlineTextSurface = TTF_RenderUTF8_Blended (font, data->text.c_str(), data->outlineColor); | |
173 | data->outlineTexture = SDL_CreateTextureFromSurface(target, data->outlineTextSurface); | |
174 | oh.reset(); | |
175 | } | |
176 | data->renderedText = data->text; | |
177 | data->renderedVersion = data->tex->getVersion(); | |
178 | } | |
179 | ||
180 | void SagoTextField::GetRenderedSize(const char* text, int* w, int* h) { | |
181 | TTF_Font *font = data->tex->getFontPtr(data->fontName, data->fontSize); | |
182 | int ret = TTF_SizeUTF8(font, text, w, h); | |
183 | if (ret) { | |
184 | if (w) { | |
185 | *w = 0; | |
186 | } | |
187 | if (h) { | |
188 | *h = 0; | |
189 | } | |
190 | std::cerr << "GetRenderedSize failed to find size of " << text << ". Error code: " << ret << "\n"; | |
191 | } | |
192 | } | |
193 | ||
194 | void SagoTextField::Draw(SDL_Renderer* target, int x, int y, Alignment alignment, VerticalAlignment verticalAlignment) { | |
195 | if (data->text.empty()) { | |
196 | return; | |
197 | } | |
198 | if (data->text != data->renderedText || data->renderedVersion != data->tex->getVersion()) { | |
199 | UpdateCache(target); | |
200 | } | |
201 | if (!data->texture) { | |
202 | return; | |
203 | } | |
204 | int texW = 0; | |
205 | int texH = 0; | |
206 | SDL_QueryTexture(data->texture, NULL, NULL, &texW, &texH); | |
207 | if (alignment == Alignment::center) { | |
208 | x -= texW/2; | |
209 | } | |
210 | if (alignment == Alignment::right) { | |
211 | y -= texW; | |
212 | } | |
213 | if (verticalAlignment == VerticalAlignment::center) { | |
214 | y -= texH/2; | |
215 | } | |
216 | if (verticalAlignment == VerticalAlignment::bottom) { | |
217 | y -= texH; | |
218 | } | |
219 | SDL_Rect dstrect = { x, y, texW, texH }; | |
220 | if (data->outlineTexture) { | |
221 | int outlineTexW = 0; | |
222 | int outlineTexH = 0; | |
223 | SDL_QueryTexture(data->outlineTexture, NULL, NULL, &outlineTexW, &outlineTexH); | |
224 | SDL_Rect dstrectOutline = { x-(data->outline), y-(data->outline), outlineTexW, outlineTexH }; | |
225 | SDL_RenderCopy(target, data->outlineTexture, NULL, &dstrectOutline); | |
226 | } | |
227 | SDL_RenderCopy(target, data->texture, NULL, &dstrect); | |
228 | } | |
229 | ||
230 | } //namespace sago⏎ |
0 | /* | |
1 | Copyright (c) 2018 Poul Sander | |
2 | ||
3 | Permission is hereby granted, free of charge, to any person | |
4 | obtaining a copy of this software and associated documentation files | |
5 | (the "Software"), to deal in the Software without restriction, | |
6 | including without limitation the rights to use, copy, modify, merge, | |
7 | publish, distribute, sublicense, and/or sell copies of the Software, | |
8 | and to permit persons to whom the Software is furnished to do so, | |
9 | subject to the following conditions: | |
10 | ||
11 | The above copyright notice and this permission notice shall be | |
12 | included in all copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
18 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
19 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
21 | SOFTWARE. | |
22 | */ | |
23 | ||
24 | #ifndef SAGOTEXTFIELD_HPP | |
25 | #define SAGOTEXTFIELD_HPP | |
26 | ||
27 | #include "SagoDataHolder.hpp" | |
28 | ||
29 | namespace sago { | |
30 | ||
31 | /** | |
32 | * This is a text field. | |
33 | * It represents a line of text to be drawn on screen. It is not possible to have line breaks. | |
34 | * If line breaks are needed use SagoTextBox instead. | |
35 | * | |
36 | * This object renderes to a texture and cahces the texture. The texture will be automatically refreshed if the text changes, the SagoDataHolder is invalidated or ClearCache is called. | |
37 | * Normally all values will be set at the beginning before text is drawn. | |
38 | * SetHolder MUST be called before the field is drawn! | |
39 | */ | |
40 | class SagoTextField final { | |
41 | public: | |
42 | SagoTextField(); | |
43 | SagoTextField(SagoTextField&& o) noexcept; | |
44 | SagoTextField& operator=(const SagoTextField&& base) = delete; | |
45 | SagoTextField& operator=(const SagoTextField& base) = delete; | |
46 | ~SagoTextField(); | |
47 | /** | |
48 | * This method creates a copy of a given font. | |
49 | * The cache will not be copied. | |
50 | * This is ALMOST like the "= operator" but given its own name to prevent implicit calling. | |
51 | * @param base The object to copy from | |
52 | * @return A reference to this object. | |
53 | */ | |
54 | SagoTextField& CopyFrom(const SagoTextField& base); | |
55 | /** | |
56 | * Sets the data holder. This is MANDATORY | |
57 | * @param holder The data holder to fetch the fonts from | |
58 | */ | |
59 | void SetHolder(const SagoDataHolder* holder); | |
60 | /** | |
61 | * Set the text to display. | |
62 | * @param text The actual UTF-8 encoded text | |
63 | */ | |
64 | void SetText(const char* text); | |
65 | /** | |
66 | * Set the text to display. | |
67 | * @param text The actual UTF-8 encoded text | |
68 | */ | |
69 | void SetText(const std::string& text); | |
70 | void SetColor(const SDL_Color& color); | |
71 | /** | |
72 | * Set the name of the font. Must be known to the data holder. | |
73 | * The name could for instance be "freeserif". | |
74 | * @param fontName Name of the font as required by SagoDataHolder | |
75 | */ | |
76 | void SetFont(const char* fontName); | |
77 | void SetFontSize(int fontSize); | |
78 | /** | |
79 | * Enable outline against the font. | |
80 | * @param outlineSize Number of pixels of outline. | |
81 | * @param color The color of the outline. | |
82 | */ | |
83 | void SetOutline(int outlineSize, const SDL_Color& color); | |
84 | /** | |
85 | * Get the text we are currently drawing | |
86 | * @return The text | |
87 | */ | |
88 | const std::string& GetText() const; | |
89 | /** | |
90 | * A Shorthand for calling TTF_SizeUTF8 on the right font | |
91 | * The size is measuered WITHOUT the outline! | |
92 | * Will fail silently on error (except writing to stderr) and set w and h to 0 if they are not null. | |
93 | * @param text The text to check the rendered size for | |
94 | * @param w Pointer to an int where the width of the text will be stored. Maybe null. | |
95 | * @param h Pointer to an int where the hight of the text will be stored. Maybe null. | |
96 | */ | |
97 | void GetRenderedSize(const char* text, int* w = nullptr, int* h = nullptr); | |
98 | enum class Alignment { left = 0, right=1, center = 2 }; | |
99 | enum class VerticalAlignment { top = 0, center = 1, bottom = 2}; | |
100 | void Draw(SDL_Renderer* target, int x, int y, Alignment alignment = Alignment::left, VerticalAlignment verticalAlignment = VerticalAlignment::top); | |
101 | /** | |
102 | * Updates the cache. | |
103 | * You normally do not want to call this from the outside as it is done just in time. | |
104 | * Unless you want to precache of course.... | |
105 | * @param target Target the the text will eventually be rendered to | |
106 | */ | |
107 | void UpdateCache(SDL_Renderer* target); | |
108 | /** | |
109 | * Clears the cache and forces the SagoTextField to render it again the next time it is drawn. | |
110 | * Can be used if you have changed font, color or sizes. | |
111 | * Changing the text implices a cache clear. | |
112 | */ | |
113 | void ClearCache(); | |
114 | private: | |
115 | SagoTextField(const SagoTextField& orig) = delete; | |
116 | struct SagoTextFieldData; | |
117 | SagoTextFieldData *data; | |
118 | }; | |
119 | ||
120 | } //namespace sago | |
121 | ||
122 | #endif /* SAGOTEXTFIELD_HPP */ | |
123 |
111 | 111 | } |
112 | 112 | |
113 | 113 | /** |
114 | * Retrives the effective user's home dir. | |
115 | * If the user is running as root we ignore the HOME environment. It works badly with sudo. | |
114 | * Retrives the effective user's home dir. | |
115 | * If the user is running as root we ignore the HOME environment. It works badly with sudo. | |
116 | 116 | * Writing to $HOME as root implies security concerns that a multiplatform program cannot be assumed to handle. |
117 | * @return The home directory. HOME environment is respected for non-root users if it exists. | |
117 | * @return The home directory. HOME environment is respected for non-root users if it exists. | |
118 | 118 | */ |
119 | 119 | static std::string getHome() { |
120 | 120 | std::string res; |
259 | 259 | folders["XDG_TEMPLATES_DIR"] = "$HOME/.Templates"; |
260 | 260 | folders["XDG_VIDEOS_DIR"] = "$HOME/Videos"; |
261 | 261 | PlatformFoldersAddFromFile( getConfigHome()+"/user-dirs.dirs", folders); |
262 | for (std::map<std::string, std::string>::iterator itr = folders.begin() ; itr != folders.end() ; itr ++ ) { | |
262 | for (std::map<std::string, std::string>::iterator itr = folders.begin() ; itr != folders.end() ; ++itr ) { | |
263 | 263 | std::string& value = itr->second; |
264 | 264 | if (value.compare(0, 5, "$HOME") == 0) { |
265 | 265 | value = getHome() + value.substr(5, std::string::npos); |
26 | 26 | */ |
27 | 27 | |
28 | 28 | #ifndef SAGO_PLATFORM_FOLDERS_H |
29 | #define SAGO_PLATFORM_FOLDERS_H | |
29 | #define SAGO_PLATFORM_FOLDERS_H | |
30 | 30 | |
31 | 31 | #include <vector> |
32 | 32 | #include <string> |
161 | 161 | private: |
162 | 162 | PlatformFolders(const PlatformFolders&); |
163 | 163 | PlatformFolders& operator=(const PlatformFolders&); |
164 | #if defined(_WIN32) | |
165 | #elif defined(__APPLE__) | |
166 | #else | |
164 | 167 | struct PlatformFoldersData; |
165 | 168 | PlatformFoldersData *data; |
169 | #endif | |
166 | 170 | }; |
167 | 171 | |
168 | 172 | } //namespace sago |
169 | 173 | |
170 | #endif /* PLATFORM_FOLDERS_H */ | |
171 | ||
174 | #endif /* PLATFORM_FOLDERS_H */ |
29 | 29 | class SDL_RendererHolder { |
30 | 30 | SDL_Renderer* ptr; |
31 | 31 | public: |
32 | SDL_RendererHolder(SDL_Renderer* input) { | |
32 | explicit SDL_RendererHolder(SDL_Renderer* input) { | |
33 | 33 | dieOnNullptr(input, "Failed to get render"); |
34 | 34 | ptr = input; |
35 | 35 | } |
29 | 29 | #include "cereal/types/vector.hpp" |
30 | 30 | #include "cereal/archives/json.hpp" |
31 | 31 | #include "sago/SagoMisc.hpp" |
32 | #include "Libs/include/cereal/details/helpers.hpp" | |
33 | 32 | |
34 | 33 | //paths |
35 | 34 | const char* const stageClearSaveName = "stageClear.json.SCsave"; |
67 | 67 | void Stats::save() { |
68 | 68 | std::stringstream outFile; |
69 | 69 | map<string,unsigned int>::iterator iter; |
70 | for (iter = statMap.begin(); iter != statMap.end(); iter++) { | |
70 | for (iter = statMap.begin(); iter != statMap.end(); ++iter) { | |
71 | 71 | outFile << iter->first << " " << iter->second << "\n"; |
72 | 72 | } |
73 | 73 | sago::WriteFileContent(statsFileName, outFile.str()); |
21 | 21 | */ |
22 | 22 | |
23 | 23 | #ifndef VERSION_NUMBER |
24 | #define VERSION_NUMBER "2.1.2" | |
24 | #define VERSION_NUMBER "2.2.0" | |
25 | 25 | #endif |
0 | FROM fedora:25 | |
1 | ||
2 | RUN dnf -y install cmake \ | |
3 | make \ | |
4 | gcc-c++ \ | |
5 | SDL2-devel \ | |
6 | boost-devel \ | |
7 | physfs-devel \ | |
8 | SDL2_image-devel SDL2_ttf-devel SDL2_mixer-devel \ | |
9 | utf8cpp-devel \ | |
10 | findutils \ | |
11 | gettext \ | |
12 | zip \ | |
13 | && dnf -y clean all | |
14 | ||
15 | COPY . /staging/blockattack-game | |
16 | ||
17 | ENV BLOCKATTACK_VERSION 2.2.0 | |
18 | ||
19 | RUN cd /staging/blockattack-game && \ | |
20 | ./packdata.sh && \ | |
21 | cmake . && \ | |
22 | make |
0 | FROM ubuntu:12.04 | |
1 | ||
2 | RUN apt-get update && apt-get install -y build-essential cmake pkg-config libfreetype6-dev libvorbis-dev libgl1-mesa-dev libpulse-dev libpng12-dev zip gettext | |
3 | ||
4 | RUN apt-get update && apt-get install -y python-software-properties && \ | |
5 | add-apt-repository ppa:ubuntu-toolchain-r/test && \ | |
6 | apt-get update && \ | |
7 | apt-get install -y gcc-4.8 g++-4.8 && \ | |
8 | update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 50 && \ | |
9 | update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 50 | |
10 | ||
11 | COPY source/misc/standalone/compile_requirements.sh / | |
12 | RUN /compile_requirements.sh | |
13 | ||
14 | ||
15 | ||
16 | COPY . /staging/blockattack-game | |
17 | ||
18 | ENV BLOCKATTACK_VERSION 2.2.0 | |
19 | ||
20 | RUN cd /staging/blockattack-game && \ | |
21 | ./packdata.sh && \ | |
22 | cp source/misc/travis_help/utf8_v2_3_4/source/utf8.h source/code/ && \ | |
23 | cp source/misc/travis_help/utf8_v2_3_4/source/utf8.h source/code/sago/ && \ | |
24 | cp -r source/misc/travis_help/utf8_v2_3_4/source/utf8 source/code/ && \ | |
25 | cp -r source/misc/travis_help/utf8_v2_3_4/source/utf8 source/code/sago/ && \ | |
26 | cmake -D Boost_USE_STATIC_LIBS=ON -D INSTALL_DATA_DIR=. -D CMAKE_INSTALL_PREFIX=. -D STANDALONE=1 . && make |
0 | 0 | FROM ubuntu:14.04 |
1 | 1 | |
2 | RUN apt-get update && apt-get install -y build-essential libphysfs-dev libboost-dev cmake libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev libboost-program-options-dev libutfcpp-dev zip gettext | |
2 | RUN apt-get update && apt-get install -yy build-essential libphysfs-dev libboost-dev cmake libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev libboost-program-options-dev libutfcpp-dev zip gettext | |
3 | 3 | |
4 | 4 | RUN mkdir -p /staging/blockattack-game |
5 | 5 | |
6 | 6 | COPY . /staging/blockattack-game |
7 | 7 | |
8 | ENV BLOCKATTACK_VERSION 2.1.0 | |
8 | ENV BLOCKATTACK_VERSION 2.2.0 | |
9 | 9 | |
10 | 10 | RUN cd /staging/blockattack-game && \ |
11 | 11 | ./packdata.sh && \ |
0 | FROM ubuntu:14.04 | |
1 | ||
2 | RUN apt-get update && apt-get install -y build-essential libboost-dev cmake pkg-config libboost-program-options-dev libfreetype6-dev libvorbis-dev libgl1-mesa-dev libpulse-dev libutfcpp-dev zip gettext | |
3 | ||
4 | COPY source/misc/standalone/compile_requirements.sh / | |
5 | RUN /compile_requirements.sh | |
6 | ||
7 | COPY . /staging/blockattack-game | |
8 | ||
9 | ENV BLOCKATTACK_VERSION 2.2.0 | |
10 | ||
11 | RUN cd /staging/blockattack-game && \ | |
12 | ./packdata.sh && \ | |
13 | cmake -D Boost_USE_STATIC_LIBS=ON -D INSTALL_DATA_DIR=. -D CMAKE_INSTALL_PREFIX=. -D STANDALONE=1 . && make |
0 | FROM ioft/i386-ubuntu:14.04 | |
1 | ||
2 | RUN apt-get update && apt-get install -y build-essential libboost-dev cmake pkg-config libboost-program-options-dev libfreetype6-dev libvorbis-dev libgl1-mesa-dev libpulse-dev libutfcpp-dev zip gettext | |
3 | ||
4 | COPY source/misc/standalone/compile_requirements.sh / | |
5 | RUN /compile_requirements.sh | |
6 | ||
7 | COPY . /staging/blockattack-game | |
8 | ||
9 | ENV BLOCKATTACK_VERSION 2.2.0 | |
10 | ||
11 | RUN cd /staging/blockattack-game && \ | |
12 | ./packdata.sh && \ | |
13 | cmake -D Boost_USE_STATIC_LIBS=ON -D INSTALL_DATA_DIR=. -D CMAKE_INSTALL_PREFIX=. -D STANDALONE=1 . && make |
7 | 7 | |
8 | 8 | COPY . /staging/blockattack-game |
9 | 9 | |
10 | ENV BLOCKATTACK_VERSION 2.1.2 | |
10 | ENV BLOCKATTACK_VERSION 2.2.0 | |
11 | 11 | |
12 | 12 | RUN cd /staging/blockattack-game && \ |
13 | 13 | ./packdata.sh && \ |
14 | cp source/misc/travis_help/utf8_v2_3_4/source/utf8.h source/code/ && \ | |
15 | cp -r source/misc/travis_help/utf8_v2_3_4/source/utf8 source/code/ && \ | |
14 | cp source/misc/travis_help/utf8_v2_3_4/source/utf8.h source/code/Libs/include/ && \ | |
15 | cp -r source/misc/travis_help/utf8_v2_3_4/source/utf8 source/code/Libs/include/ && \ | |
16 | 16 | i686-w64-mingw32.static-cmake . && \ |
17 | 17 | make && \ |
18 | 18 | cd windows\ installer/ && \ |
0 | #! /bin/bash | |
1 | set -e | |
2 | set +H | |
3 | if [ "$#" -lt 3 ]; then | |
4 | echo "Must be called with $0 <MAJOR> <MINOR> <PATCH> [>TAG>]" | |
5 | echo "Like: $0 2 2 10 SNAPSHOT" | |
6 | exit 1 | |
7 | fi | |
8 | MAJOR=$1 | |
9 | MINOR=$2 | |
10 | PATCH=$3 | |
11 | TAG=$4 | |
12 | ||
13 | FULLVERSION=$MAJOR.$MINOR.$PATCH | |
14 | ||
15 | if [ "$#" -gt 3 ]; then | |
16 | FULLVERSION="$FULLVERSION-$TAG" | |
17 | fi | |
18 | ||
19 | sed -i "/SET(CPACK_PACKAGE_VERSION /c\\SET(CPACK_PACKAGE_VERSION \"$FULLVERSION\")" CMakeLists.txt | |
20 | sed -i "/SET(CPACK_PACKAGE_VERSION_MAJOR /c\\SET(CPACK_PACKAGE_VERSION_MAJOR \"$MAJOR\")" CMakeLists.txt | |
21 | sed -i "/SET(CPACK_PACKAGE_VERSION_MINOR /c\\SET(CPACK_PACKAGE_VERSION_MINOR \"$MINOR\")" CMakeLists.txt | |
22 | sed -i "/SET(CPACK_PACKAGE_VERSION_PATCH /c\\SET(CPACK_PACKAGE_VERSION_PATCH \"$PATCH\")" CMakeLists.txt | |
23 | ||
24 | ||
25 | sed -i -E "s/#define VERSION_NUMBER.*$/#define VERSION_NUMBER \"$FULLVERSION\"/" source/code/version.h | |
26 | sed -i -E "s/!define PRODUCT_VERSION .*$/!define PRODUCT_VERSION \"$FULLVERSION\"/" "windows installer/install_script.nsi" | |
27 | sed -i -E "s/#ENV BLOCKATTACK_VERSION .*$/#ENV BLOCKATTACK_VERSION $FULLVERSION/" source/misc/docker/Dockerfile.WindoesBuild | |
28 | ||
29 | make | |
30 | pushd man | |
31 | COLUMNS=300 help2man --no-info --section=6 --name="a puzzle game inspired by Tetris Attack" ../Game/blockattack | sed 's@'"$HOME"'@$HOME@g' > blockattack.man | |
32 | popd |
0 | staging |
0 | Block Attack - Rise of the Blocks - Linux build | |
1 | ||
2 | This is the standalone version. It is precompiled and includes the necessary libraries to run. | |
3 | ||
4 | Both a 32 bit and 64 bit version is provided. | |
5 | It has been tested on Ubuntu 14.04 and Fedora 22 | |
6 | Some systems might require the game to be launched from the command line. | |
7 | PulseAudio is required for sound. | |
8 | ||
9 | Check www.blockattack.net for more info. |
0 | #!/bin/bash | |
1 | ||
2 | # This file is based on the guide from https://itch.io/docs/itch/integrating/platforms/linux.html | |
3 | ||
4 | # Move to script's directory | |
5 | cd "`dirname "$0"`" | |
6 | ||
7 | # Get the kernel/architecture information | |
8 | ARCH=`uname -m` | |
9 | ||
10 | # Set the libpath and pick the proper binary | |
11 | if [ "$ARCH" == "x86_64" ]; then | |
12 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./x86_64/ | |
13 | ./x86_64/blockattack "$@" | |
14 | else | |
15 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./x86/ | |
16 | ./x86/blockattack "$@" | |
17 | fi |
0 | #! /bin/bash | |
1 | set -e | |
2 | ||
3 | if [ "$#" -ne 1 ]; then | |
4 | echo "Must be called with $0 <archivename>" | |
5 | exit 1 | |
6 | fi | |
7 | ||
8 | ARCHIVENAME=$1 | |
9 | ||
10 | rm -rf staging | |
11 | mkdir -p staging/$ARCHIVENAME/x86_64 | |
12 | mkdir -p staging/$ARCHIVENAME/x86 | |
13 | mkdir -p staging/$ARCHIVENAME/docs | |
14 | cp blockattack_standalone_launcher staging/$ARCHIVENAME/blockattack | |
15 | cp README.txt staging/$ARCHIVENAME/README | |
16 | chmod +x staging/$ARCHIVENAME/blockattack | |
17 | ||
18 | cd ../../.. | |
19 | ||
20 | docker build -f source/misc/docker/Dockerfile.Ubuntu12.04build_Standalone . -t blockattack_test | |
21 | ||
22 | echo Copying to: $(pwd)/source/misc/standalone/staging/$ARCHIVENAME | |
23 | ||
24 | docker run -it --rm -v $(pwd)/source/misc/standalone/staging/$ARCHIVENAME/:/output blockattack_test /bin/bash -c "cp /staging/blockattack-game/Game/blockattack /output/x86_64/ && \ | |
25 | cp /usr/local/lib/libSDL2-2.0.so.0 /output/x86_64/ && \ | |
26 | cp /usr/local/lib/libphysfs.so.1 /output/x86_64/ && \ | |
27 | cp /usr/local/lib/libSDL2_mixer-2.0.so.0 /output/x86_64/ && \ | |
28 | cp /usr/local/lib/libSDL2_ttf-2.0.so.0 /output/x86_64/ && \ | |
29 | cp /usr/lib/x86_64-linux-gnu/libfreetype.so.6 /output/x86_64/ && \ | |
30 | cp /lib/x86_64-linux-gnu/libpng12.so.0 /output/x86_64/ && \ | |
31 | cp /usr/local/lib/libSDL2_image-2.0.so.0 /output/x86_64/" | |
32 | ||
33 | docker run -it --rm -v $(pwd)/source/misc/standalone/staging/$ARCHIVENAME/:/output blockattack_test /bin/bash -c "cp -r /staging/blockattack-game/source/misc/translation/locale /output/ && \ | |
34 | cp /staging/blockattack-game/Game/blockattack.data /output/ && \ | |
35 | cp -r /staging/blockattack-game/source/misc/icons /output/ && \ | |
36 | cp /staging/blockattack-game/COPYING /output/ && \ | |
37 | cp /staging/blockattack-game/man/blockattack.man /output/docs/ && \ | |
38 | cp /staging/blockattack-game/README.md /output/docs/README_ORG.md && \ | |
39 | chown -R 1000 /output" | |
40 | ||
41 | scp source/misc/standalone/compile_requirements.sh blockattack_build:/ | |
42 | scp source/misc/standalone/install_requirements.sh blockattack_build:/ | |
43 | ssh blockattack_build /install_requirements.sh | |
44 | ssh blockattack_build /compile_requirements.sh | |
45 | ssh blockattack_build rm -rf /staging/blockattack-game | |
46 | scp -r . blockattack_build:/staging/blockattack-game | |
47 | ssh blockattack_build rm -f /staging/blockattack-game/CMakeCache.txt | |
48 | ||
49 | ssh blockattack_build "BLOCKATTACK_VERSION=2.2.0 && cd /staging/blockattack-game && \ | |
50 | ./packdata.sh && \ | |
51 | cp source/misc/travis_help/utf8_v2_3_4/source/utf8.h source/code/ && \ | |
52 | cp source/misc/travis_help/utf8_v2_3_4/source/utf8.h source/code/sago/ && \ | |
53 | cp -r source/misc/travis_help/utf8_v2_3_4/source/utf8 source/code/ && \ | |
54 | cp -r source/misc/travis_help/utf8_v2_3_4/source/utf8 source/code/sago/ && \ | |
55 | cmake -D Boost_USE_STATIC_LIBS=ON -D INSTALL_DATA_DIR=. -D CMAKE_INSTALL_PREFIX=. -D STANDALONE=1 . && make clean && make" | |
56 | ||
57 | OUTPUT=$(pwd)/source/misc/standalone/staging/$ARCHIVENAME/ | |
58 | ||
59 | scp blockattack_build:/staging/blockattack-game/Game/blockattack $OUTPUT/x86/ && \ | |
60 | scp blockattack_build:/usr/local/lib/libSDL2-2.0.so.0 $OUTPUT/x86/ && \ | |
61 | scp blockattack_build:/usr/local/lib/libphysfs.so.1 $OUTPUT/x86/ && \ | |
62 | scp blockattack_build:/usr/local/lib/libSDL2_mixer-2.0.so.0 $OUTPUT/x86/ && \ | |
63 | scp blockattack_build:/usr/local/lib/libSDL2_ttf-2.0.so.0 $OUTPUT/x86/ && \ | |
64 | scp blockattack_build:/usr/lib/i386-linux-gnu/libfreetype.so.6 $OUTPUT/x86/ && \ | |
65 | scp blockattack_build:/lib/i386-linux-gnu/libpng12.so.0 $OUTPUT/x86/ && \ | |
66 | scp blockattack_build:/usr/local/lib/libSDL2_image-2.0.so.0 $OUTPUT/x86/ | |
67 | ||
68 | exit 0 | |
69 | ||
70 | docker build -f source/misc/docker/Dockerfile.Ubuntu12.04build_Standalone32 . -t blockattack_test | |
71 | ||
72 | docker run -it --rm -v $(pwd)/source/misc/standalone/staging/$ARCHIVENAME/:/output blockattack_test /bin/bash -c "cp /staging/blockattack-game/Game/blockattack /output/x86/ && \ | |
73 | cp /usr/local/lib/libSDL2-2.0.so.0 /output/x86/ && \ | |
74 | cp /usr/local/lib/libphysfs.so.1 /output/x86/ && \ | |
75 | cp /usr/local/lib/libSDL2_mixer-2.0.so.0 /output/x86/ && \ | |
76 | cp /usr/local/lib/libSDL2_ttf-2.0.so.0 /output/x86/ && \ | |
77 | cp /usr/lib/i386-linux-gnu/libfreetype.so.6 /output/x86/ && \ | |
78 | cp /lib/i386-linux-gnu/libpng12.so.0 /output/x86/ && \ | |
79 | cp /usr/local/lib/libSDL2_image-2.0.so.0 /output/x86/" | |
80 | ||
81 | cd source/misc/standalone/staging/ | |
82 | tar -cvjSf "$ARCHIVENAME.tar.bz2" "$ARCHIVENAME" |
0 | #! /bin/bash | |
1 | set -e | |
2 | set -x | |
3 | ||
4 | mkdir -p /staging/deps && cd /staging/deps && curl https://libsdl.org/release/SDL2-2.0.6.tar.gz | tar -zx && cd SDL2-2.0.6 && ls -lrt | |
5 | cd /staging/deps/SDL2-2.0.6 && ./configure --enable-shared --enable-static && make && make install | |
6 | ||
7 | #https://www.libsdl.org/projects/SDL_image/release/SDL2_image-2.0.1.tar.gz | |
8 | mkdir -p /staging/deps && cd /staging/deps && curl https://www.libsdl.org/projects/SDL_image/release/SDL2_image-2.0.1.tar.gz | tar -zx && cd SDL2_image-2.0.1 && ls -lrt | |
9 | cd /staging/deps/SDL2_image-2.0.1 && ./configure --enable-shared --enable-static && make && make install | |
10 | ||
11 | #https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-2.0.1.tar.gz | |
12 | mkdir -p /staging/deps && cd /staging/deps && curl https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-2.0.1.tar.gz | tar -zx && cd SDL2_mixer-2.0.1 && ls -lrt | |
13 | cd /staging/deps/SDL2_mixer-2.0.1 && ./configure --enable-shared --enable-static && make && make install | |
14 | ||
15 | #https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-2.0.14.tar.gz | |
16 | mkdir -p /staging/deps && cd /staging/deps && curl https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-2.0.14.tar.gz | tar -zx && cd SDL2_ttf-2.0.14 && ls -lrt | |
17 | cd /staging/deps/SDL2_ttf-2.0.14 && ./configure --enable-shared --enable-static && make && make install | |
18 | mkdir -p /staging/blockattack-game | |
19 | ||
20 | mkdir -p /staging/deps && cd /staging/deps && curl http://icculus.org/physfs/downloads/physfs-2.0.3.tar.bz2 | tar -jx && cd physfs-2.0.3 && ls -lrt | |
21 | cd /staging/deps/physfs-2.0.3 && cmake . && make && make install | |
22 | ||
23 | # boost | |
24 | cd ~ | |
25 | mkdir -p Downloads | |
26 | cd Downloads | |
27 | curl https://files.poulsander.com/~poul19/public_files/boost_1_65_1.tar.bz2 -O | |
28 | tar xvfj boost_1_65_1.tar.bz2 | |
29 | pushd boost_1_65_1 | |
30 | ./bootstrap.sh --with-libraries=program_options | |
31 | ./b2 install -j 2 --prefix=/usr link=static | |
32 | popd |
0 | #! /bin/bash | |
1 | set -e | |
2 | apt-get update && apt-get install -y build-essential cmake pkg-config libfreetype6-dev libvorbis-dev libgl1-mesa-dev libpulse-dev libpng12-dev zip gettext | |
3 | ||
4 | apt-get update && apt-get install -y python-software-properties | |
5 | add-apt-repository ppa:ubuntu-toolchain-r/test | |
6 | apt-get update | |
7 | apt-get install -y gcc-4.8 g++-4.8 | |
8 | update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 50 | |
9 | update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 50 |
4 | 4 | |
5 | 5 | POFILE=$1 |
6 | 6 | |
7 | LANGUAGE=${1##*/} | |
7 | LANGUAGE=${POFILE##*/} | |
8 | 8 | LANGUAGE=${LANGUAGE%%.*} |
9 | 9 | mkdir -p locale/$LANGUAGE/LC_MESSAGES |
10 | echo "Building $LANGUAGE from $1" | |
11 | msgfmt -o locale/$LANGUAGE/LC_MESSAGES/blockattack_roftb.mo $1 | |
10 | echo "Building $LANGUAGE from $POFILE" | |
11 | msgfmt -o locale/$LANGUAGE/LC_MESSAGES/blockattack_roftb.mo $POFILE |
0 | #! /bin/bash | |
0 | 1 | mkdir -p template |
1 | xgettext -k_ ../../code/*.cpp ../../code/*.inc --output=template/blockattack_roftb.pot | |
2 | xgettext -k_ ../../code/*.cpp ../../code/*.inc ../../code/*.hpp --output=template/blockattack_roftb.pot |
6 | 6 | msgstr "" |
7 | 7 | "Project-Id-Version: \n" |
8 | 8 | "Report-Msgid-Bugs-To: \n" |
9 | "POT-Creation-Date: 2017-03-29 19:57+0200\n" | |
10 | "PO-Revision-Date: 2017-03-29 19:57+0200\n" | |
9 | "POT-Creation-Date: 2018-05-19 18:29+0200\n" | |
10 | "PO-Revision-Date: 2018-05-19 18:30+0200\n" | |
11 | 11 | "Last-Translator: \n" |
12 | 12 | "Language-Team: \n" |
13 | 13 | "Language: da\n" |
14 | 14 | "MIME-Version: 1.0\n" |
15 | 15 | "Content-Type: text/plain; charset=UTF-8\n" |
16 | 16 | "Content-Transfer-Encoding: 8bit\n" |
17 | "X-Generator: Poedit 1.8.7.1\n" | |
17 | "X-Generator: Poedit 2.0.6\n" | |
18 | 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" |
19 | 19 | |
20 | #: ../../code/DialogBox.cpp:103 | |
20 | #: ../../code/DialogBox.cpp:92 | |
21 | 21 | msgid "Enter to accept" |
22 | 22 | msgstr "Bekræft med Enter" |
23 | 23 | |
24 | #: ../../code/DialogBox.cpp:104 | |
24 | #: ../../code/DialogBox.cpp:93 | |
25 | 25 | msgid "Esc to cancel" |
26 | 26 | msgstr "Annuller med Esc" |
27 | 27 | |
28 | #: ../../code/levelselect.cpp:78 | |
28 | #: ../../code/HelpAboutState.cpp:62 | |
29 | msgid "Name:" | |
30 | msgstr "Navn:" | |
31 | ||
32 | #: ../../code/HelpAboutState.cpp:62 | |
33 | msgid "Block Attack - Rise of the Blocks" | |
34 | msgstr "Block Attack - Blokkenes opstand" | |
35 | ||
36 | #: ../../code/HelpAboutState.cpp:63 | |
37 | msgid "Original name:" | |
38 | msgstr "Originalt navn:" | |
39 | ||
40 | #: ../../code/HelpAboutState.cpp:64 | |
41 | msgid "Version:" | |
42 | msgstr "Version:" | |
43 | ||
44 | #: ../../code/HelpAboutState.cpp:65 | |
45 | msgid "Homepage:" | |
46 | msgstr "Hjemmeside:" | |
47 | ||
48 | #: ../../code/HelpAboutState.cpp:66 | |
49 | msgid "Github page:" | |
50 | msgstr "Github side:" | |
51 | ||
52 | #: ../../code/HelpAboutState.cpp:67 | |
53 | msgid "SDL render:" | |
54 | msgstr "SDL render:" | |
55 | ||
56 | #: ../../code/HelpAboutState.cpp:68 | |
57 | msgid "Save folder:" | |
58 | msgstr "Mappe til gemte spil:" | |
59 | ||
60 | #: ../../code/HelpAboutState.cpp:69 | |
61 | msgid "Locale:" | |
62 | msgstr "Lokalitet:" | |
63 | ||
64 | #: ../../code/HelpAboutState.cpp:70 ../../code/menudef.cpp:309 | |
65 | msgid "About" | |
66 | msgstr "Om" | |
67 | ||
68 | #: ../../code/HelpGamepadState.cpp:54 | |
69 | msgid "Move cursor" | |
70 | msgstr "Flyt markøren" | |
71 | ||
72 | #: ../../code/HelpGamepadState.cpp:55 | |
73 | msgid "Push line" | |
74 | msgstr "Skub linje" | |
75 | ||
76 | #: ../../code/HelpGamepadState.cpp:56 | |
77 | msgid "Back (Menu)" | |
78 | msgstr "Tilbage (Menu)" | |
79 | ||
80 | #: ../../code/HelpGamepadState.cpp:57 | |
81 | msgid "Switch" | |
82 | msgstr "Skift" | |
83 | ||
84 | #: ../../code/HelpGamepadState.cpp:58 | |
85 | msgid "Confirm" | |
86 | msgstr "Bekræft" | |
87 | ||
88 | #: ../../code/HelpGamepadState.cpp:59 | |
89 | msgid "" | |
90 | "Only SDL2 compatible controllers are supported!\n" | |
91 | "Supported controllers: " | |
92 | msgstr "" | |
93 | "Kun SDL2 kompatible kontrollere er understøttet!\n" | |
94 | "Understøttede kontrollere: " | |
95 | ||
96 | #: ../../code/HelpHowtoState.cpp:127 | |
97 | msgid "Switch block horizontally" | |
98 | msgstr "Skift blokke horisontalt" | |
99 | ||
100 | #: ../../code/HelpHowtoState.cpp:128 | |
101 | msgid "Match 3 to clear" | |
102 | msgstr "Sammensæt 3 for at fjerne" | |
103 | ||
104 | #: ../../code/HelpHowtoState.cpp:129 | |
105 | msgid "Create combos!" | |
106 | msgstr "Sammensæt mange!" | |
107 | ||
108 | #: ../../code/HelpHowtoState.cpp:130 | |
109 | msgid "Drop blocks!" | |
110 | msgstr "Lad blokke falde!" | |
111 | ||
112 | #: ../../code/HelpHowtoState.cpp:131 | |
113 | msgid "Create a chain effect" | |
114 | msgstr "Lav en kæde effekt" | |
115 | ||
116 | #: ../../code/levelselect.cpp:92 | |
29 | 117 | msgid "Select Puzzle" |
30 | 118 | msgstr "Vælg gåde" |
31 | 119 | |
32 | #: ../../code/levelselect.cpp:81 | |
120 | #: ../../code/levelselect.cpp:95 | |
33 | 121 | msgid "Stage Clear Level Select" |
34 | 122 | msgstr "Vælg Stage Clear-bane" |
35 | 123 | |
36 | #: ../../code/levelselect.cpp:187 | |
124 | #: ../../code/levelselect.cpp:201 | |
37 | 125 | #, c-format |
38 | 126 | msgid "Best score: %i" |
39 | 127 | msgstr "Bedste resultat: %i" |
40 | 128 | |
41 | #: ../../code/levelselect.cpp:188 | |
129 | #: ../../code/levelselect.cpp:202 | |
42 | 130 | #, c-format |
43 | 131 | msgid "Time used: %s" |
44 | 132 | msgstr "Tid brugt: %s" |
45 | 133 | |
46 | #: ../../code/levelselect.cpp:191 | |
134 | #: ../../code/levelselect.cpp:205 | |
47 | 135 | #, c-format |
48 | 136 | msgid "Time used: %d : %02d" |
49 | 137 | msgstr "Tid brugt: %d : %02d" |
50 | 138 | |
51 | #: ../../code/levelselect.cpp:196 | |
139 | #: ../../code/levelselect.cpp:210 | |
52 | 140 | #, c-format |
53 | 141 | msgid "Total score: %i in %i:%02i" |
54 | 142 | msgstr "Sammenlagt: %i på %i:%02i" |
55 | 143 | |
56 | #: ../../code/main.cpp:431 ../../code/main.cpp:544 | |
144 | #: ../../code/main.cpp:452 ../../code/main.cpp:583 | |
57 | 145 | msgid "AI" |
58 | 146 | msgstr "Computer" |
59 | 147 | |
60 | #: ../../code/main.cpp:434 | |
148 | #: ../../code/main.cpp:455 | |
61 | 149 | msgid "Playing field" |
62 | 150 | msgstr "Spilleflade" |
63 | 151 | |
64 | #: ../../code/main.cpp:505 | |
152 | #: ../../code/main.cpp:529 | |
65 | 153 | msgid "Time Trial" |
66 | 154 | msgstr "På tid" |
67 | 155 | |
68 | #: ../../code/main.cpp:506 | |
156 | #: ../../code/main.cpp:530 | |
69 | 157 | msgid "Score as much as possible in 2 minutes" |
70 | 158 | msgstr "Se hvor mange point du kan opnå på 2 minutter" |
71 | 159 | |
72 | #: ../../code/main.cpp:510 | |
160 | #: ../../code/main.cpp:534 | |
73 | 161 | msgid "Stage Clear" |
74 | 162 | msgstr "Stage Clear" |
75 | 163 | |
76 | #: ../../code/main.cpp:511 | |
164 | #: ../../code/main.cpp:535 | |
77 | 165 | msgid "You must clear a number of lines. Speed is rapidly increased." |
78 | 166 | msgstr "Fjern et specifik antal linjer. Hastigheden stiger hurtigt." |
79 | 167 | |
80 | #: ../../code/main.cpp:514 | |
168 | #: ../../code/main.cpp:538 | |
81 | 169 | msgid "Puzzle" |
82 | 170 | msgstr "Gåde" |
83 | 171 | |
84 | #: ../../code/main.cpp:515 | |
172 | #: ../../code/main.cpp:539 | |
85 | 173 | msgid "Clear the entire board with a limited number of moves." |
86 | 174 | msgstr "Fjern alle blokke med et begrænset antal flyt." |
87 | 175 | |
88 | #: ../../code/main.cpp:518 | |
176 | #: ../../code/main.cpp:542 | |
89 | 177 | msgid "Endless" |
90 | 178 | msgstr "Evighed" |
91 | 179 | |
92 | #: ../../code/main.cpp:519 | |
180 | #: ../../code/main.cpp:543 | |
93 | 181 | msgid "Score as much as possible. No time limit." |
94 | 182 | msgstr "Opnå så mange point som muligt. Ingen tidsbegrænsning." |
95 | 183 | |
96 | #: ../../code/main.cpp:523 | |
184 | #: ../../code/main.cpp:553 | |
97 | 185 | msgid "Objective:" |
98 | 186 | msgstr "Formål:" |
99 | 187 | |
100 | #: ../../code/main.cpp:529 | |
188 | #: ../../code/main.cpp:563 | |
101 | 189 | msgid "Movement keys:" |
102 | 190 | msgstr "Bevægelsestaster:" |
103 | 191 | |
104 | #: ../../code/main.cpp:532 | |
192 | #: ../../code/main.cpp:565 | |
105 | 193 | msgid "Switch: " |
106 | 194 | msgstr "Skift: " |
107 | 195 | |
108 | #: ../../code/main.cpp:534 | |
196 | #: ../../code/main.cpp:567 | |
109 | 197 | msgid "Restart: " |
110 | 198 | msgstr "Genstart: " |
111 | 199 | |
112 | #: ../../code/main.cpp:537 | |
200 | #: ../../code/main.cpp:570 | |
113 | 201 | msgid "Push line: " |
114 | 202 | msgstr "Skub linje: " |
115 | 203 | |
116 | #: ../../code/main.cpp:945 | |
204 | #: ../../code/main.cpp:995 | |
117 | 205 | msgid "Player 1" |
118 | 206 | msgstr "Spiller 1" |
119 | 207 | |
120 | #: ../../code/main.cpp:946 | |
208 | #: ../../code/main.cpp:996 | |
121 | 209 | msgid "Player 2" |
122 | 210 | msgstr "Spiller 2" |
123 | 211 | |
124 | #: ../../code/menudef.cpp:135 ../../code/menudef.cpp:203 | |
212 | #: ../../code/menudef.cpp:138 ../../code/menudef.cpp:206 | |
125 | 213 | msgid "Music: On" |
126 | 214 | msgstr "Musik: Til" |
127 | 215 | |
128 | #: ../../code/menudef.cpp:135 ../../code/menudef.cpp:203 | |
216 | #: ../../code/menudef.cpp:138 ../../code/menudef.cpp:206 | |
129 | 217 | msgid "Music: Off" |
130 | 218 | msgstr "Musik: Fra" |
131 | 219 | |
132 | #: ../../code/menudef.cpp:143 ../../code/menudef.cpp:204 | |
220 | #: ../../code/menudef.cpp:146 ../../code/menudef.cpp:207 | |
133 | 221 | msgid "Sound: On" |
134 | 222 | msgstr "Lyd: Til" |
135 | 223 | |
136 | #: ../../code/menudef.cpp:143 ../../code/menudef.cpp:204 | |
224 | #: ../../code/menudef.cpp:146 ../../code/menudef.cpp:207 | |
137 | 225 | msgid "Sound: Off" |
138 | 226 | msgstr "Lyd: Fra" |
139 | 227 | |
140 | #: ../../code/menudef.cpp:150 ../../code/menudef.cpp:205 | |
228 | #: ../../code/menudef.cpp:153 ../../code/menudef.cpp:208 | |
141 | 229 | msgid "Fullscreen: On" |
142 | 230 | msgstr "Fuldskærm: Til" |
143 | 231 | |
144 | #: ../../code/menudef.cpp:150 ../../code/menudef.cpp:205 | |
232 | #: ../../code/menudef.cpp:153 ../../code/menudef.cpp:208 | |
145 | 233 | msgid "Fullscreen: Off" |
146 | 234 | msgstr "Fuldskærm: Fra" |
147 | 235 | |
148 | #: ../../code/menudef.cpp:156 | |
236 | #: ../../code/menudef.cpp:159 | |
149 | 237 | msgid "Enter player 1 name:" |
150 | 238 | msgstr "Indtast navnet på spiller 1:" |
151 | 239 | |
152 | #: ../../code/menudef.cpp:162 | |
240 | #: ../../code/menudef.cpp:165 | |
153 | 241 | msgid "Enter player 2 name:" |
154 | 242 | msgstr "Indtast navnet på spiller 2:" |
155 | 243 | |
156 | #: ../../code/menudef.cpp:172 | |
244 | #: ../../code/menudef.cpp:175 | |
157 | 245 | msgid "Change key bindings" |
158 | 246 | msgstr "Skift taster" |
159 | 247 | |
160 | #: ../../code/menudef.cpp:173 | |
248 | #: ../../code/menudef.cpp:176 | |
161 | 249 | msgid "Left" |
162 | 250 | msgstr "Venstre" |
163 | 251 | |
164 | #: ../../code/menudef.cpp:174 | |
252 | #: ../../code/menudef.cpp:177 | |
165 | 253 | msgid "Right" |
166 | 254 | msgstr "Højre" |
167 | 255 | |
168 | #: ../../code/menudef.cpp:175 | |
256 | #: ../../code/menudef.cpp:178 | |
169 | 257 | msgid "Up" |
170 | 258 | msgstr "Op" |
171 | 259 | |
172 | #: ../../code/menudef.cpp:176 | |
260 | #: ../../code/menudef.cpp:179 | |
173 | 261 | msgid "Down" |
174 | 262 | msgstr "Ned" |
175 | 263 | |
176 | #: ../../code/menudef.cpp:177 | |
264 | #: ../../code/menudef.cpp:180 | |
177 | 265 | msgid "Push" |
178 | 266 | msgstr "Skub" |
179 | 267 | |
180 | #: ../../code/menudef.cpp:178 | |
268 | #: ../../code/menudef.cpp:181 | |
181 | 269 | msgid "Change" |
182 | 270 | msgstr "Skift" |
183 | 271 | |
184 | #: ../../code/menudef.cpp:197 | |
272 | #: ../../code/menudef.cpp:200 | |
185 | 273 | msgid "Configuration" |
186 | 274 | msgstr "Konfiguration" |
187 | 275 | |
188 | #: ../../code/menudef.cpp:207 | |
276 | #: ../../code/menudef.cpp:210 | |
189 | 277 | msgid "Change player 1's name" |
190 | 278 | msgstr "Skift navn på spiller 1" |
191 | 279 | |
192 | #: ../../code/menudef.cpp:209 | |
280 | #: ../../code/menudef.cpp:212 | |
193 | 281 | msgid "Change player 2's name" |
194 | 282 | msgstr "Skift navn på spiller 2" |
195 | 283 | |
196 | #: ../../code/menudef.cpp:211 | |
284 | #: ../../code/menudef.cpp:214 | |
197 | 285 | msgid "Change player 1's keys" |
198 | 286 | msgstr "Tildel taster til spiller 1" |
199 | 287 | |
200 | #: ../../code/menudef.cpp:213 | |
288 | #: ../../code/menudef.cpp:216 | |
201 | 289 | msgid "Change player 2's keys" |
202 | 290 | msgstr "Tildel taster til spiller 2" |
203 | 291 | |
204 | #: ../../code/menudef.cpp:225 | |
292 | #: ../../code/menudef.cpp:228 | |
205 | 293 | msgid "Single player VS" |
206 | 294 | msgstr "En spiller - VS" |
207 | 295 | |
208 | #: ../../code/menudef.cpp:241 | |
296 | #: ../../code/menudef.cpp:244 | |
209 | 297 | msgid "Very easy" |
210 | 298 | msgstr "Meget let" |
211 | 299 | |
212 | #: ../../code/menudef.cpp:242 | |
300 | #: ../../code/menudef.cpp:245 | |
213 | 301 | msgid "Easy" |
214 | 302 | msgstr "Let" |
215 | 303 | |
216 | #: ../../code/menudef.cpp:243 | |
304 | #: ../../code/menudef.cpp:246 | |
217 | 305 | msgid "Below normal" |
218 | 306 | msgstr "Under normal" |
219 | 307 | |
220 | #: ../../code/menudef.cpp:244 | |
308 | #: ../../code/menudef.cpp:247 | |
221 | 309 | msgid "Normal" |
222 | 310 | msgstr "Normal" |
223 | 311 | |
224 | #: ../../code/menudef.cpp:245 | |
312 | #: ../../code/menudef.cpp:248 | |
225 | 313 | msgid "Above normal" |
226 | 314 | msgstr "Over normal" |
227 | 315 | |
228 | #: ../../code/menudef.cpp:246 | |
316 | #: ../../code/menudef.cpp:249 | |
229 | 317 | msgid "Hard" |
230 | 318 | msgstr "Svær" |
231 | 319 | |
232 | #: ../../code/menudef.cpp:247 | |
320 | #: ../../code/menudef.cpp:250 | |
233 | 321 | msgid "Hardest" |
234 | 322 | msgstr "Meget svær" |
235 | 323 | |
236 | #: ../../code/menudef.cpp:259 | |
324 | #: ../../code/menudef.cpp:262 | |
237 | 325 | msgid "Multiplayer" |
238 | 326 | msgstr "Flerspiller" |
239 | 327 | |
240 | #: ../../code/menudef.cpp:261 | |
328 | #: ../../code/menudef.cpp:264 | |
241 | 329 | msgid "Two player - time trial" |
242 | 330 | msgstr "To spillere - På tid" |
243 | 331 | |
244 | #: ../../code/menudef.cpp:263 | |
332 | #: ../../code/menudef.cpp:266 | |
245 | 333 | msgid "Two player - vs" |
246 | 334 | msgstr "To spillere - VS" |
247 | 335 | |
248 | #: ../../code/menudef.cpp:272 | |
249 | msgid "Block Attack - Rise of the blocks" | |
250 | msgstr "Block Attack - Rise of the blocks" | |
251 | ||
252 | #: ../../code/menudef.cpp:274 | |
336 | #: ../../code/menudef.cpp:290 ../../code/menudef.cpp:305 | |
337 | msgid "Credits" | |
338 | msgstr "Bidragydere" | |
339 | ||
340 | #: ../../code/menudef.cpp:295 ../../code/menudef.cpp:348 | |
341 | msgid "Help" | |
342 | msgstr "Hjælp" | |
343 | ||
344 | #: ../../code/menudef.cpp:297 | |
345 | msgid "How to" | |
346 | msgstr "Guide" | |
347 | ||
348 | #: ../../code/menudef.cpp:301 | |
349 | msgid "Gamepad" | |
350 | msgstr "Spilkontroller" | |
351 | ||
352 | #: ../../code/menudef.cpp:316 ../../code/menudef.cpp:340 | |
353 | msgid "Single player" | |
354 | msgstr "Enkeltspiller" | |
355 | ||
356 | #: ../../code/menudef.cpp:318 | |
253 | 357 | msgid "Single player - endless" |
254 | 358 | msgstr "En spiller - Evighed" |
255 | 359 | |
256 | #: ../../code/menudef.cpp:276 | |
360 | #: ../../code/menudef.cpp:320 | |
257 | 361 | msgid "Single player - time trial" |
258 | msgstr "En spiller - Time Trial" | |
259 | ||
260 | #: ../../code/menudef.cpp:278 | |
362 | msgstr "En spiller - På tid" | |
363 | ||
364 | #: ../../code/menudef.cpp:322 | |
261 | 365 | msgid "Single player - stage clear" |
262 | 366 | msgstr "En spiller - Stage Clear" |
263 | 367 | |
264 | #: ../../code/menudef.cpp:280 | |
368 | #: ../../code/menudef.cpp:324 | |
265 | 369 | msgid "Single player - puzzle mode" |
266 | 370 | msgstr "En spiller - Gåde" |
267 | 371 | |
268 | #: ../../code/menudef.cpp:282 | |
372 | #: ../../code/menudef.cpp:326 | |
269 | 373 | msgid "Single player - vs" |
270 | 374 | msgstr "En spiller - VS" |
271 | 375 | |
272 | #: ../../code/menudef.cpp:284 | |
376 | #: ../../code/menudef.cpp:338 | |
377 | msgid "Block Attack - Rise of the blocks" | |
378 | msgstr "Block Attack - Blokkenes opstand" | |
379 | ||
380 | #: ../../code/menudef.cpp:342 | |
273 | 381 | msgid "Multi player" |
274 | 382 | msgstr "Flere spillere" |
275 | 383 | |
276 | #: ../../code/menudef.cpp:286 | |
384 | #: ../../code/menudef.cpp:344 | |
277 | 385 | msgid "Configure" |
278 | 386 | msgstr "Instillinger" |
279 | 387 | |
280 | #: ../../code/menudef.cpp:288 | |
388 | #: ../../code/menudef.cpp:346 | |
281 | 389 | msgid "Highscores" |
282 | 390 | msgstr "Bedste resultater" |
283 | 391 | |
284 | #: ../../code/MenuSystem.cpp:145 ../../code/MenuSystem.cpp:153 | |
285 | #: ../../code/MenuSystem.cpp:166 ../../code/ScoresDisplay.cpp:162 | |
392 | #: ../../code/MenuSystem.cpp:162 ../../code/MenuSystem.cpp:170 | |
393 | #: ../../code/MenuSystem.cpp:183 ../../code/ScoresDisplay.cpp:184 | |
286 | 394 | msgid "Back" |
287 | 395 | msgstr "Tilbage" |
288 | 396 | |
289 | #: ../../code/MenuSystem.cpp:156 ../../code/MenuSystem.cpp:169 | |
397 | #: ../../code/MenuSystem.cpp:173 ../../code/MenuSystem.cpp:186 | |
290 | 398 | msgid "Exit" |
291 | 399 | msgstr "Afslut" |
292 | 400 | |
293 | #: ../../code/ScoresDisplay.cpp:51 | |
401 | #: ../../code/ScoresDisplay.cpp:71 | |
294 | 402 | msgid "Endless:" |
295 | 403 | msgstr "Evighed:" |
296 | 404 | |
297 | #: ../../code/ScoresDisplay.cpp:54 | |
405 | #: ../../code/ScoresDisplay.cpp:74 | |
298 | 406 | msgid "Time Trial:" |
299 | 407 | msgstr "På tid:" |
300 | 408 | |
301 | #: ../../code/ScoresDisplay.cpp:77 | |
409 | #: ../../code/ScoresDisplay.cpp:97 | |
302 | 410 | msgid "Stats" |
303 | 411 | msgstr "Statistik" |
304 | 412 | |
305 | #: ../../code/ScoresDisplay.cpp:79 | |
413 | #: ../../code/ScoresDisplay.cpp:99 | |
306 | 414 | msgid "Chains" |
307 | 415 | msgstr "Kæder" |
308 | 416 | |
309 | #: ../../code/ScoresDisplay.cpp:87 | |
417 | #: ../../code/ScoresDisplay.cpp:107 | |
310 | 418 | msgid "Lines Pushed: " |
311 | 419 | msgstr "Linjer skubbet: " |
312 | 420 | |
313 | #: ../../code/ScoresDisplay.cpp:92 | |
421 | #: ../../code/ScoresDisplay.cpp:112 | |
314 | 422 | msgid "Puzzles solved: " |
315 | 423 | msgstr "Gåder løst: " |
316 | 424 | |
317 | #: ../../code/ScoresDisplay.cpp:97 | |
425 | #: ../../code/ScoresDisplay.cpp:117 | |
318 | 426 | msgid "Run time: " |
319 | 427 | msgstr "Kørselstid: " |
320 | 428 | |
321 | #: ../../code/ScoresDisplay.cpp:100 ../../code/ScoresDisplay.cpp:113 | |
429 | #: ../../code/ScoresDisplay.cpp:120 ../../code/ScoresDisplay.cpp:133 | |
322 | 430 | #, c-format |
323 | 431 | msgid "Days: %i" |
324 | 432 | msgstr "Dage: %i" |
325 | 433 | |
326 | #: ../../code/ScoresDisplay.cpp:102 ../../code/ScoresDisplay.cpp:115 | |
434 | #: ../../code/ScoresDisplay.cpp:122 ../../code/ScoresDisplay.cpp:135 | |
327 | 435 | #, c-format |
328 | 436 | msgid "Hours: %i" |
329 | 437 | msgstr "Timer: %i" |
330 | 438 | |
331 | #: ../../code/ScoresDisplay.cpp:104 ../../code/ScoresDisplay.cpp:117 | |
439 | #: ../../code/ScoresDisplay.cpp:124 ../../code/ScoresDisplay.cpp:137 | |
332 | 440 | #, c-format |
333 | 441 | msgid "Minutes: %i" |
334 | 442 | msgstr "Minutter: %i" |
335 | 443 | |
336 | #: ../../code/ScoresDisplay.cpp:106 ../../code/ScoresDisplay.cpp:119 | |
444 | #: ../../code/ScoresDisplay.cpp:126 ../../code/ScoresDisplay.cpp:139 | |
337 | 445 | #, c-format |
338 | 446 | msgid "Seconds: %i" |
339 | 447 | msgstr "Sekunder: %i" |
340 | 448 | |
341 | #: ../../code/ScoresDisplay.cpp:110 | |
449 | #: ../../code/ScoresDisplay.cpp:130 | |
342 | 450 | msgid "Play time: " |
343 | msgstr "Spilletid:" | |
344 | ||
345 | #: ../../code/ScoresDisplay.cpp:123 | |
451 | msgstr "Spilletid: " | |
452 | ||
453 | #: ../../code/ScoresDisplay.cpp:143 | |
346 | 454 | msgid "VS CPU (win/loss)" |
347 | msgstr "mod computeren (vundet/tabt)" | |
348 | ||
349 | #: ../../code/ScoresDisplay.cpp:164 ../../code/BlockGameSdl.inc:315 | |
350 | #: ../../code/BlockGameSdl.inc:333 | |
455 | msgstr "Mod computeren (vundet/tabt)" | |
456 | ||
457 | #: ../../code/ScoresDisplay.cpp:188 ../../code/BlockGameSdl.inc:54 | |
351 | 458 | msgid "Next" |
352 | 459 | msgstr "Næste" |
353 | 460 | |
354 | #: ../../code/ScoresDisplay.cpp:167 | |
461 | #: ../../code/ScoresDisplay.cpp:192 | |
355 | 462 | #, c-format |
356 | 463 | msgid "Page %i of %i" |
357 | 464 | msgstr "Side %i af %i" |
358 | 465 | |
359 | #: ../../code/BlockGameSdl.inc:293 | |
466 | #: ../../code/BlockGameSdl.inc:50 | |
360 | 467 | msgid "Score:" |
361 | 468 | msgstr "Resultat:" |
362 | 469 | |
363 | #: ../../code/BlockGameSdl.inc:294 | |
470 | #: ../../code/BlockGameSdl.inc:51 | |
364 | 471 | msgid "Time:" |
365 | 472 | msgstr "Tid:" |
366 | 473 | |
367 | #: ../../code/BlockGameSdl.inc:295 | |
474 | #: ../../code/BlockGameSdl.inc:52 | |
368 | 475 | msgid "Chain:" |
369 | 476 | msgstr "Kæde:" |
370 | 477 | |
371 | #: ../../code/BlockGameSdl.inc:296 | |
478 | #: ../../code/BlockGameSdl.inc:53 | |
372 | 479 | msgid "Speed:" |
373 | 480 | msgstr "Hastighed:" |
374 | 481 | |
375 | #: ../../code/BlockGameSdl.inc:305 | |
482 | #: ../../code/BlockGameSdl.inc:55 | |
483 | msgid "Retry" | |
484 | msgstr "Prøv igen" | |
485 | ||
486 | #: ../../code/BlockGameSdl.inc:56 | |
487 | msgid "Skip" | |
488 | msgstr "Spring over" | |
489 | ||
490 | #: ../../code/BlockGameSdl.inc:327 | |
376 | 491 | msgid "Moves left: " |
377 | 492 | msgstr "Træk tilbage: " |
378 | 493 | |
379 | #: ../../code/BlockGameSdl.inc:311 ../../code/BlockGameSdl.inc:329 | |
380 | msgid "Retry" | |
381 | msgstr "Prøv igen" | |
382 | ||
383 | #: ../../code/BlockGameSdl.inc:319 ../../code/BlockGameSdl.inc:337 | |
384 | msgid "Skip" | |
385 | msgstr "Spring over" | |
494 | #: ../../code/BlockGameSdl.inc:350 | |
495 | msgid "Last puzzle" | |
496 | msgstr "Sidste gåde" | |
497 | ||
498 | #: ../../code/BlockGameSdl.inc:370 | |
499 | msgid "Last stage" | |
500 | msgstr "Sidste bane" | |
501 | ||
502 | #: ../../code/ShowFileState.hpp:45 | |
503 | #, c-format | |
504 | msgid "Showing content of: %s" | |
505 | msgstr "Viser indhold af: %s" | |
506 | ||
507 | #~ msgid "Author:" | |
508 | #~ msgstr "Forfatter:" | |
509 | ||
510 | #~ msgid "Contributers:" | |
511 | #~ msgstr "Bidragydere:" | |
512 | ||
513 | #~ msgid "Other credits:" | |
514 | #~ msgstr "Andre bidrag:" | |
386 | 515 | |
387 | 516 | #~ msgid "Total score: %1% in %2%:%3%" |
388 | 517 | #~ msgstr "Sammenlagt resultat: %1% på %2%:%3%" |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: PACKAGE VERSION\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2017-03-29 19:48+0200\n" | |
10 | "POT-Creation-Date: 2018-05-19 18:29+0200\n" | |
11 | 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
12 | 12 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
13 | 13 | "Language-Team: LANGUAGE <LL@li.org>\n" |
16 | 16 | "Content-Type: text/plain; charset=CHARSET\n" |
17 | 17 | "Content-Transfer-Encoding: 8bit\n" |
18 | 18 | |
19 | #: ../../code/DialogBox.cpp:103 | |
19 | #: ../../code/DialogBox.cpp:92 | |
20 | 20 | msgid "Enter to accept" |
21 | 21 | msgstr "" |
22 | 22 | |
23 | #: ../../code/DialogBox.cpp:104 | |
23 | #: ../../code/DialogBox.cpp:93 | |
24 | 24 | msgid "Esc to cancel" |
25 | 25 | msgstr "" |
26 | 26 | |
27 | #: ../../code/levelselect.cpp:78 | |
27 | #: ../../code/HelpAboutState.cpp:62 | |
28 | msgid "Name:" | |
29 | msgstr "" | |
30 | ||
31 | #: ../../code/HelpAboutState.cpp:62 | |
32 | msgid "Block Attack - Rise of the Blocks" | |
33 | msgstr "" | |
34 | ||
35 | #: ../../code/HelpAboutState.cpp:63 | |
36 | msgid "Original name:" | |
37 | msgstr "" | |
38 | ||
39 | #: ../../code/HelpAboutState.cpp:64 | |
40 | msgid "Version:" | |
41 | msgstr "" | |
42 | ||
43 | #: ../../code/HelpAboutState.cpp:65 | |
44 | msgid "Homepage:" | |
45 | msgstr "" | |
46 | ||
47 | #: ../../code/HelpAboutState.cpp:66 | |
48 | msgid "Github page:" | |
49 | msgstr "" | |
50 | ||
51 | #: ../../code/HelpAboutState.cpp:67 | |
52 | msgid "SDL render:" | |
53 | msgstr "" | |
54 | ||
55 | #: ../../code/HelpAboutState.cpp:68 | |
56 | msgid "Save folder:" | |
57 | msgstr "" | |
58 | ||
59 | #: ../../code/HelpAboutState.cpp:69 | |
60 | msgid "Locale:" | |
61 | msgstr "" | |
62 | ||
63 | #: ../../code/HelpAboutState.cpp:70 ../../code/menudef.cpp:309 | |
64 | msgid "About" | |
65 | msgstr "" | |
66 | ||
67 | #: ../../code/HelpGamepadState.cpp:54 | |
68 | msgid "Move cursor" | |
69 | msgstr "" | |
70 | ||
71 | #: ../../code/HelpGamepadState.cpp:55 | |
72 | msgid "Push line" | |
73 | msgstr "" | |
74 | ||
75 | #: ../../code/HelpGamepadState.cpp:56 | |
76 | msgid "Back (Menu)" | |
77 | msgstr "" | |
78 | ||
79 | #: ../../code/HelpGamepadState.cpp:57 | |
80 | msgid "Switch" | |
81 | msgstr "" | |
82 | ||
83 | #: ../../code/HelpGamepadState.cpp:58 | |
84 | msgid "Confirm" | |
85 | msgstr "" | |
86 | ||
87 | #: ../../code/HelpGamepadState.cpp:59 | |
88 | msgid "" | |
89 | "Only SDL2 compatible controllers are supported!\n" | |
90 | "Supported controllers: " | |
91 | msgstr "" | |
92 | ||
93 | #: ../../code/HelpHowtoState.cpp:127 | |
94 | msgid "Switch block horizontally" | |
95 | msgstr "" | |
96 | ||
97 | #: ../../code/HelpHowtoState.cpp:128 | |
98 | msgid "Match 3 to clear" | |
99 | msgstr "" | |
100 | ||
101 | #: ../../code/HelpHowtoState.cpp:129 | |
102 | msgid "Create combos!" | |
103 | msgstr "" | |
104 | ||
105 | #: ../../code/HelpHowtoState.cpp:130 | |
106 | msgid "Drop blocks!" | |
107 | msgstr "" | |
108 | ||
109 | #: ../../code/HelpHowtoState.cpp:131 | |
110 | msgid "Create a chain effect" | |
111 | msgstr "" | |
112 | ||
113 | #: ../../code/levelselect.cpp:92 | |
28 | 114 | msgid "Select Puzzle" |
29 | 115 | msgstr "" |
30 | 116 | |
31 | #: ../../code/levelselect.cpp:81 | |
117 | #: ../../code/levelselect.cpp:95 | |
32 | 118 | msgid "Stage Clear Level Select" |
33 | 119 | msgstr "" |
34 | 120 | |
35 | #: ../../code/levelselect.cpp:187 | |
121 | #: ../../code/levelselect.cpp:201 | |
36 | 122 | #, c-format |
37 | 123 | msgid "Best score: %i" |
38 | 124 | msgstr "" |
39 | 125 | |
40 | #: ../../code/levelselect.cpp:188 | |
126 | #: ../../code/levelselect.cpp:202 | |
41 | 127 | #, c-format |
42 | 128 | msgid "Time used: %s" |
43 | 129 | msgstr "" |
44 | 130 | |
45 | #: ../../code/levelselect.cpp:191 | |
131 | #: ../../code/levelselect.cpp:205 | |
46 | 132 | #, c-format |
47 | 133 | msgid "Time used: %d : %02d" |
48 | 134 | msgstr "" |
49 | 135 | |
50 | #: ../../code/levelselect.cpp:196 | |
136 | #: ../../code/levelselect.cpp:210 | |
51 | 137 | #, c-format |
52 | 138 | msgid "Total score: %i in %i:%02i" |
53 | 139 | msgstr "" |
54 | 140 | |
55 | #: ../../code/main.cpp:431 ../../code/main.cpp:544 | |
141 | #: ../../code/main.cpp:452 ../../code/main.cpp:583 | |
56 | 142 | msgid "AI" |
57 | 143 | msgstr "" |
58 | 144 | |
59 | #: ../../code/main.cpp:434 | |
145 | #: ../../code/main.cpp:455 | |
60 | 146 | msgid "Playing field" |
61 | 147 | msgstr "" |
62 | 148 | |
63 | #: ../../code/main.cpp:505 | |
149 | #: ../../code/main.cpp:529 | |
64 | 150 | msgid "Time Trial" |
65 | 151 | msgstr "" |
66 | 152 | |
67 | #: ../../code/main.cpp:506 | |
153 | #: ../../code/main.cpp:530 | |
68 | 154 | msgid "Score as much as possible in 2 minutes" |
69 | 155 | msgstr "" |
70 | 156 | |
71 | #: ../../code/main.cpp:510 | |
157 | #: ../../code/main.cpp:534 | |
72 | 158 | msgid "Stage Clear" |
73 | 159 | msgstr "" |
74 | 160 | |
75 | #: ../../code/main.cpp:511 | |
161 | #: ../../code/main.cpp:535 | |
76 | 162 | msgid "You must clear a number of lines. Speed is rapidly increased." |
77 | 163 | msgstr "" |
78 | 164 | |
79 | #: ../../code/main.cpp:514 | |
165 | #: ../../code/main.cpp:538 | |
80 | 166 | msgid "Puzzle" |
81 | 167 | msgstr "" |
82 | 168 | |
83 | #: ../../code/main.cpp:515 | |
169 | #: ../../code/main.cpp:539 | |
84 | 170 | msgid "Clear the entire board with a limited number of moves." |
85 | 171 | msgstr "" |
86 | 172 | |
87 | #: ../../code/main.cpp:518 | |
173 | #: ../../code/main.cpp:542 | |
88 | 174 | msgid "Endless" |
89 | 175 | msgstr "" |
90 | 176 | |
91 | #: ../../code/main.cpp:519 | |
177 | #: ../../code/main.cpp:543 | |
92 | 178 | msgid "Score as much as possible. No time limit." |
93 | 179 | msgstr "" |
94 | 180 | |
95 | #: ../../code/main.cpp:523 | |
181 | #: ../../code/main.cpp:553 | |
96 | 182 | msgid "Objective:" |
97 | 183 | msgstr "" |
98 | 184 | |
99 | #: ../../code/main.cpp:529 | |
185 | #: ../../code/main.cpp:563 | |
100 | 186 | msgid "Movement keys:" |
101 | 187 | msgstr "" |
102 | 188 | |
103 | #: ../../code/main.cpp:532 | |
189 | #: ../../code/main.cpp:565 | |
104 | 190 | msgid "Switch: " |
105 | 191 | msgstr "" |
106 | 192 | |
107 | #: ../../code/main.cpp:534 | |
193 | #: ../../code/main.cpp:567 | |
108 | 194 | msgid "Restart: " |
109 | 195 | msgstr "" |
110 | 196 | |
111 | #: ../../code/main.cpp:537 | |
197 | #: ../../code/main.cpp:570 | |
112 | 198 | msgid "Push line: " |
113 | 199 | msgstr "" |
114 | 200 | |
115 | #: ../../code/main.cpp:945 | |
201 | #: ../../code/main.cpp:995 | |
116 | 202 | msgid "Player 1" |
117 | 203 | msgstr "" |
118 | 204 | |
119 | #: ../../code/main.cpp:946 | |
205 | #: ../../code/main.cpp:996 | |
120 | 206 | msgid "Player 2" |
121 | 207 | msgstr "" |
122 | 208 | |
123 | #: ../../code/menudef.cpp:135 ../../code/menudef.cpp:203 | |
209 | #: ../../code/menudef.cpp:138 ../../code/menudef.cpp:206 | |
124 | 210 | msgid "Music: On" |
125 | 211 | msgstr "" |
126 | 212 | |
127 | #: ../../code/menudef.cpp:135 ../../code/menudef.cpp:203 | |
213 | #: ../../code/menudef.cpp:138 ../../code/menudef.cpp:206 | |
128 | 214 | msgid "Music: Off" |
129 | 215 | msgstr "" |
130 | 216 | |
131 | #: ../../code/menudef.cpp:143 ../../code/menudef.cpp:204 | |
217 | #: ../../code/menudef.cpp:146 ../../code/menudef.cpp:207 | |
132 | 218 | msgid "Sound: On" |
133 | 219 | msgstr "" |
134 | 220 | |
135 | #: ../../code/menudef.cpp:143 ../../code/menudef.cpp:204 | |
221 | #: ../../code/menudef.cpp:146 ../../code/menudef.cpp:207 | |
136 | 222 | msgid "Sound: Off" |
137 | 223 | msgstr "" |
138 | 224 | |
139 | #: ../../code/menudef.cpp:150 ../../code/menudef.cpp:205 | |
225 | #: ../../code/menudef.cpp:153 ../../code/menudef.cpp:208 | |
140 | 226 | msgid "Fullscreen: On" |
141 | 227 | msgstr "" |
142 | 228 | |
143 | #: ../../code/menudef.cpp:150 ../../code/menudef.cpp:205 | |
229 | #: ../../code/menudef.cpp:153 ../../code/menudef.cpp:208 | |
144 | 230 | msgid "Fullscreen: Off" |
145 | 231 | msgstr "" |
146 | 232 | |
147 | #: ../../code/menudef.cpp:156 | |
233 | #: ../../code/menudef.cpp:159 | |
148 | 234 | msgid "Enter player 1 name:" |
149 | 235 | msgstr "" |
150 | 236 | |
151 | #: ../../code/menudef.cpp:162 | |
237 | #: ../../code/menudef.cpp:165 | |
152 | 238 | msgid "Enter player 2 name:" |
153 | 239 | msgstr "" |
154 | 240 | |
155 | #: ../../code/menudef.cpp:172 | |
241 | #: ../../code/menudef.cpp:175 | |
156 | 242 | msgid "Change key bindings" |
157 | 243 | msgstr "" |
158 | 244 | |
159 | #: ../../code/menudef.cpp:173 | |
245 | #: ../../code/menudef.cpp:176 | |
160 | 246 | msgid "Left" |
161 | 247 | msgstr "" |
162 | 248 | |
163 | #: ../../code/menudef.cpp:174 | |
249 | #: ../../code/menudef.cpp:177 | |
164 | 250 | msgid "Right" |
165 | 251 | msgstr "" |
166 | 252 | |
167 | #: ../../code/menudef.cpp:175 | |
253 | #: ../../code/menudef.cpp:178 | |
168 | 254 | msgid "Up" |
169 | 255 | msgstr "" |
170 | 256 | |
171 | #: ../../code/menudef.cpp:176 | |
257 | #: ../../code/menudef.cpp:179 | |
172 | 258 | msgid "Down" |
173 | 259 | msgstr "" |
174 | 260 | |
175 | #: ../../code/menudef.cpp:177 | |
261 | #: ../../code/menudef.cpp:180 | |
176 | 262 | msgid "Push" |
177 | 263 | msgstr "" |
178 | 264 | |
179 | #: ../../code/menudef.cpp:178 | |
265 | #: ../../code/menudef.cpp:181 | |
180 | 266 | msgid "Change" |
181 | 267 | msgstr "" |
182 | 268 | |
183 | #: ../../code/menudef.cpp:197 | |
269 | #: ../../code/menudef.cpp:200 | |
184 | 270 | msgid "Configuration" |
185 | 271 | msgstr "" |
186 | 272 | |
187 | #: ../../code/menudef.cpp:207 | |
273 | #: ../../code/menudef.cpp:210 | |
188 | 274 | msgid "Change player 1's name" |
189 | 275 | msgstr "" |
190 | 276 | |
191 | #: ../../code/menudef.cpp:209 | |
277 | #: ../../code/menudef.cpp:212 | |
192 | 278 | msgid "Change player 2's name" |
193 | 279 | msgstr "" |
194 | 280 | |
195 | #: ../../code/menudef.cpp:211 | |
281 | #: ../../code/menudef.cpp:214 | |
196 | 282 | msgid "Change player 1's keys" |
197 | 283 | msgstr "" |
198 | 284 | |
199 | #: ../../code/menudef.cpp:213 | |
285 | #: ../../code/menudef.cpp:216 | |
200 | 286 | msgid "Change player 2's keys" |
201 | 287 | msgstr "" |
202 | 288 | |
203 | #: ../../code/menudef.cpp:225 | |
289 | #: ../../code/menudef.cpp:228 | |
204 | 290 | msgid "Single player VS" |
205 | 291 | msgstr "" |
206 | 292 | |
207 | #: ../../code/menudef.cpp:241 | |
293 | #: ../../code/menudef.cpp:244 | |
208 | 294 | msgid "Very easy" |
209 | 295 | msgstr "" |
210 | 296 | |
211 | #: ../../code/menudef.cpp:242 | |
297 | #: ../../code/menudef.cpp:245 | |
212 | 298 | msgid "Easy" |
213 | 299 | msgstr "" |
214 | 300 | |
215 | #: ../../code/menudef.cpp:243 | |
301 | #: ../../code/menudef.cpp:246 | |
216 | 302 | msgid "Below normal" |
217 | 303 | msgstr "" |
218 | 304 | |
219 | #: ../../code/menudef.cpp:244 | |
305 | #: ../../code/menudef.cpp:247 | |
220 | 306 | msgid "Normal" |
221 | 307 | msgstr "" |
222 | 308 | |
223 | #: ../../code/menudef.cpp:245 | |
309 | #: ../../code/menudef.cpp:248 | |
224 | 310 | msgid "Above normal" |
225 | 311 | msgstr "" |
226 | 312 | |
227 | #: ../../code/menudef.cpp:246 | |
313 | #: ../../code/menudef.cpp:249 | |
228 | 314 | msgid "Hard" |
229 | 315 | msgstr "" |
230 | 316 | |
231 | #: ../../code/menudef.cpp:247 | |
317 | #: ../../code/menudef.cpp:250 | |
232 | 318 | msgid "Hardest" |
233 | 319 | msgstr "" |
234 | 320 | |
235 | #: ../../code/menudef.cpp:259 | |
321 | #: ../../code/menudef.cpp:262 | |
236 | 322 | msgid "Multiplayer" |
237 | 323 | msgstr "" |
238 | 324 | |
239 | #: ../../code/menudef.cpp:261 | |
325 | #: ../../code/menudef.cpp:264 | |
240 | 326 | msgid "Two player - time trial" |
241 | 327 | msgstr "" |
242 | 328 | |
243 | #: ../../code/menudef.cpp:263 | |
329 | #: ../../code/menudef.cpp:266 | |
244 | 330 | msgid "Two player - vs" |
245 | 331 | msgstr "" |
246 | 332 | |
247 | #: ../../code/menudef.cpp:272 | |
333 | #: ../../code/menudef.cpp:290 ../../code/menudef.cpp:305 | |
334 | msgid "Credits" | |
335 | msgstr "" | |
336 | ||
337 | #: ../../code/menudef.cpp:295 ../../code/menudef.cpp:348 | |
338 | msgid "Help" | |
339 | msgstr "" | |
340 | ||
341 | #: ../../code/menudef.cpp:297 | |
342 | msgid "How to" | |
343 | msgstr "" | |
344 | ||
345 | #: ../../code/menudef.cpp:301 | |
346 | msgid "Gamepad" | |
347 | msgstr "" | |
348 | ||
349 | #: ../../code/menudef.cpp:316 ../../code/menudef.cpp:340 | |
350 | msgid "Single player" | |
351 | msgstr "" | |
352 | ||
353 | #: ../../code/menudef.cpp:318 | |
354 | msgid "Single player - endless" | |
355 | msgstr "" | |
356 | ||
357 | #: ../../code/menudef.cpp:320 | |
358 | msgid "Single player - time trial" | |
359 | msgstr "" | |
360 | ||
361 | #: ../../code/menudef.cpp:322 | |
362 | msgid "Single player - stage clear" | |
363 | msgstr "" | |
364 | ||
365 | #: ../../code/menudef.cpp:324 | |
366 | msgid "Single player - puzzle mode" | |
367 | msgstr "" | |
368 | ||
369 | #: ../../code/menudef.cpp:326 | |
370 | msgid "Single player - vs" | |
371 | msgstr "" | |
372 | ||
373 | #: ../../code/menudef.cpp:338 | |
248 | 374 | msgid "Block Attack - Rise of the blocks" |
249 | 375 | msgstr "" |
250 | 376 | |
251 | #: ../../code/menudef.cpp:274 | |
252 | msgid "Single player - endless" | |
253 | msgstr "" | |
254 | ||
255 | #: ../../code/menudef.cpp:276 | |
256 | msgid "Single player - time trial" | |
257 | msgstr "" | |
258 | ||
259 | #: ../../code/menudef.cpp:278 | |
260 | msgid "Single player - stage clear" | |
261 | msgstr "" | |
262 | ||
263 | #: ../../code/menudef.cpp:280 | |
264 | msgid "Single player - puzzle mode" | |
265 | msgstr "" | |
266 | ||
267 | #: ../../code/menudef.cpp:282 | |
268 | msgid "Single player - vs" | |
269 | msgstr "" | |
270 | ||
271 | #: ../../code/menudef.cpp:284 | |
377 | #: ../../code/menudef.cpp:342 | |
272 | 378 | msgid "Multi player" |
273 | 379 | msgstr "" |
274 | 380 | |
275 | #: ../../code/menudef.cpp:286 | |
381 | #: ../../code/menudef.cpp:344 | |
276 | 382 | msgid "Configure" |
277 | 383 | msgstr "" |
278 | 384 | |
279 | #: ../../code/menudef.cpp:288 | |
385 | #: ../../code/menudef.cpp:346 | |
280 | 386 | msgid "Highscores" |
281 | 387 | msgstr "" |
282 | 388 | |
283 | #: ../../code/MenuSystem.cpp:145 ../../code/MenuSystem.cpp:153 | |
284 | #: ../../code/MenuSystem.cpp:166 ../../code/ScoresDisplay.cpp:162 | |
389 | #: ../../code/MenuSystem.cpp:162 ../../code/MenuSystem.cpp:170 | |
390 | #: ../../code/MenuSystem.cpp:183 ../../code/ScoresDisplay.cpp:184 | |
285 | 391 | msgid "Back" |
286 | 392 | msgstr "" |
287 | 393 | |
288 | #: ../../code/MenuSystem.cpp:156 ../../code/MenuSystem.cpp:169 | |
394 | #: ../../code/MenuSystem.cpp:173 ../../code/MenuSystem.cpp:186 | |
289 | 395 | msgid "Exit" |
290 | 396 | msgstr "" |
291 | 397 | |
292 | #: ../../code/ScoresDisplay.cpp:51 | |
398 | #: ../../code/ScoresDisplay.cpp:71 | |
293 | 399 | msgid "Endless:" |
294 | 400 | msgstr "" |
295 | 401 | |
296 | #: ../../code/ScoresDisplay.cpp:54 | |
402 | #: ../../code/ScoresDisplay.cpp:74 | |
297 | 403 | msgid "Time Trial:" |
298 | 404 | msgstr "" |
299 | 405 | |
300 | #: ../../code/ScoresDisplay.cpp:77 | |
406 | #: ../../code/ScoresDisplay.cpp:97 | |
301 | 407 | msgid "Stats" |
302 | 408 | msgstr "" |
303 | 409 | |
304 | #: ../../code/ScoresDisplay.cpp:79 | |
410 | #: ../../code/ScoresDisplay.cpp:99 | |
305 | 411 | msgid "Chains" |
306 | 412 | msgstr "" |
307 | 413 | |
308 | #: ../../code/ScoresDisplay.cpp:87 | |
414 | #: ../../code/ScoresDisplay.cpp:107 | |
309 | 415 | msgid "Lines Pushed: " |
310 | 416 | msgstr "" |
311 | 417 | |
312 | #: ../../code/ScoresDisplay.cpp:92 | |
418 | #: ../../code/ScoresDisplay.cpp:112 | |
313 | 419 | msgid "Puzzles solved: " |
314 | 420 | msgstr "" |
315 | 421 | |
316 | #: ../../code/ScoresDisplay.cpp:97 | |
422 | #: ../../code/ScoresDisplay.cpp:117 | |
317 | 423 | msgid "Run time: " |
318 | 424 | msgstr "" |
319 | 425 | |
320 | #: ../../code/ScoresDisplay.cpp:100 ../../code/ScoresDisplay.cpp:113 | |
426 | #: ../../code/ScoresDisplay.cpp:120 ../../code/ScoresDisplay.cpp:133 | |
321 | 427 | #, c-format |
322 | 428 | msgid "Days: %i" |
323 | 429 | msgstr "" |
324 | 430 | |
325 | #: ../../code/ScoresDisplay.cpp:102 ../../code/ScoresDisplay.cpp:115 | |
431 | #: ../../code/ScoresDisplay.cpp:122 ../../code/ScoresDisplay.cpp:135 | |
326 | 432 | #, c-format |
327 | 433 | msgid "Hours: %i" |
328 | 434 | msgstr "" |
329 | 435 | |
330 | #: ../../code/ScoresDisplay.cpp:104 ../../code/ScoresDisplay.cpp:117 | |
436 | #: ../../code/ScoresDisplay.cpp:124 ../../code/ScoresDisplay.cpp:137 | |
331 | 437 | #, c-format |
332 | 438 | msgid "Minutes: %i" |
333 | 439 | msgstr "" |
334 | 440 | |
335 | #: ../../code/ScoresDisplay.cpp:106 ../../code/ScoresDisplay.cpp:119 | |
441 | #: ../../code/ScoresDisplay.cpp:126 ../../code/ScoresDisplay.cpp:139 | |
336 | 442 | #, c-format |
337 | 443 | msgid "Seconds: %i" |
338 | 444 | msgstr "" |
339 | 445 | |
340 | #: ../../code/ScoresDisplay.cpp:110 | |
446 | #: ../../code/ScoresDisplay.cpp:130 | |
341 | 447 | msgid "Play time: " |
342 | 448 | msgstr "" |
343 | 449 | |
344 | #: ../../code/ScoresDisplay.cpp:123 | |
450 | #: ../../code/ScoresDisplay.cpp:143 | |
345 | 451 | msgid "VS CPU (win/loss)" |
346 | 452 | msgstr "" |
347 | 453 | |
348 | #: ../../code/ScoresDisplay.cpp:164 ../../code/BlockGameSdl.inc:315 | |
349 | #: ../../code/BlockGameSdl.inc:333 | |
454 | #: ../../code/ScoresDisplay.cpp:188 ../../code/BlockGameSdl.inc:54 | |
350 | 455 | msgid "Next" |
351 | 456 | msgstr "" |
352 | 457 | |
353 | #: ../../code/ScoresDisplay.cpp:167 | |
458 | #: ../../code/ScoresDisplay.cpp:192 | |
354 | 459 | #, c-format |
355 | 460 | msgid "Page %i of %i" |
356 | 461 | msgstr "" |
357 | 462 | |
358 | #: ../../code/BlockGameSdl.inc:293 | |
463 | #: ../../code/BlockGameSdl.inc:50 | |
359 | 464 | msgid "Score:" |
360 | 465 | msgstr "" |
361 | 466 | |
362 | #: ../../code/BlockGameSdl.inc:294 | |
467 | #: ../../code/BlockGameSdl.inc:51 | |
363 | 468 | msgid "Time:" |
364 | 469 | msgstr "" |
365 | 470 | |
366 | #: ../../code/BlockGameSdl.inc:295 | |
471 | #: ../../code/BlockGameSdl.inc:52 | |
367 | 472 | msgid "Chain:" |
368 | 473 | msgstr "" |
369 | 474 | |
370 | #: ../../code/BlockGameSdl.inc:296 | |
475 | #: ../../code/BlockGameSdl.inc:53 | |
371 | 476 | msgid "Speed:" |
372 | 477 | msgstr "" |
373 | 478 | |
374 | #: ../../code/BlockGameSdl.inc:305 | |
479 | #: ../../code/BlockGameSdl.inc:55 | |
480 | msgid "Retry" | |
481 | msgstr "" | |
482 | ||
483 | #: ../../code/BlockGameSdl.inc:56 | |
484 | msgid "Skip" | |
485 | msgstr "" | |
486 | ||
487 | #: ../../code/BlockGameSdl.inc:327 | |
375 | 488 | msgid "Moves left: " |
376 | 489 | msgstr "" |
377 | 490 | |
378 | #: ../../code/BlockGameSdl.inc:311 ../../code/BlockGameSdl.inc:329 | |
379 | msgid "Retry" | |
380 | msgstr "" | |
381 | ||
382 | #: ../../code/BlockGameSdl.inc:319 ../../code/BlockGameSdl.inc:337 | |
383 | msgid "Skip" | |
384 | msgstr "" | |
491 | #: ../../code/BlockGameSdl.inc:350 | |
492 | msgid "Last puzzle" | |
493 | msgstr "" | |
494 | ||
495 | #: ../../code/BlockGameSdl.inc:370 | |
496 | msgid "Last stage" | |
497 | msgstr "" | |
498 | ||
499 | #: ../../code/ShowFileState.hpp:45 | |
500 | #, c-format | |
501 | msgid "Showing content of: %s" | |
502 | msgstr "" |
3 | 3 | # First check for formatting errors |
4 | 4 | bash source/misc/lint/runLint.sh |
5 | 5 | |
6 | docker build -f source/misc/docker/Dockerfile.Ubuntu14.04build . -t blockattack_test | |
6 | docker build -f source/misc/docker/Dockerfile.Fedora25build . -t blockattack_test | |
7 | #docker build -f source/misc/docker/Dockerfile.Ubuntu14.04build . -t blockattack_test | |
7 | 8 | docker build -f source/misc/docker/Dockerfile.WindoesBuild . -t blockattack_test |
1 | 1 | |
2 | 2 | ; HM NIS Edit Wizard helper defines |
3 | 3 | !define PRODUCT_NAME "Block Attack - Rise Of the Blocks" |
4 | !define PRODUCT_VERSION "2.1.2" | |
4 | !define PRODUCT_VERSION "2.2.0" | |
5 | 5 | !define PRODUCT_PUBLISHER "Poul Sander" |
6 | 6 | !define PRODUCT_WEB_SITE "http://www.blockattack.net" |
7 | 7 | !define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\blockattack.exe" |