credits.c (9328B)
1 #include <tuxanci2/display.h>
2 #include <tuxanci2/shapes.h>
3 #include <tuxanci2/text.h>
4 #include <tuxanci2/screen.h>
5 #include <tuxanci2/ft2.h>
6 #include "screens.h"
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include "credits.h"
10 #include "ta_files.h"
11
12 #define TA_CREDITS_STAR_COUNT 512
13 #define TA_CREDITS_TEXT_HOLD_FRAMES 128
14 #define TA_CREDITS_ACCELERATION_FRAMES 512
15 #define TA_CREDITS_FULL_SPEED 512
16 #define TA_CREDITS_Z_ONE 65536
17 #define TA_CREDITS_Z_NEAR 1638
18 #define TA_CREDITS_TEXT_REFERENCE_SIZE 100
19 #define TA_CREDITS_TEXT_IN_FRAMES 32
20 #define TA_CREDITS_TEXT_OUT_FRAMES 32
21
22 #define TA_CREDITS_TEXT_WIDTH_MARGIN 90
23 #define TA_CREDITS_TEXT_HEIGHT_MARGIN 80
24
25 typedef struct {
26 int32_t x;
27 int32_t y;
28 int32_t z;
29 } ta_credits_star;
30
31 typedef struct {
32 ta_text text;
33 int32_t x;
34 int32_t y;
35 } ta_credits_entry;
36
37 static const char *strings[] = {
38 "Tuxánci",
39 "2",
40 "",
41 "Version",
42 "0.30.0",
43 "Credits",
44 "",
45 "Current Developers",
46 "",
47 "Main Developer",
48 "Connor Thomson (GNUfault)\n<connor@gnufault.org>",
49 "",
50 "Original Tuxánci Developers",
51 "",
52 "Former Main Developer",
53 "Dušan Ďurech (Oroborus)\n<dusan.d@centrum.sk>",
54 "",
55 "Former Project manager, coordinator of the development, and website maintainer",
56 "Michal Zima (xHire)\n<~{^_^}~/@semirocket.science>",
57 "",
58 "Former graphic designer and creator of arenas",
59 "Jiří Helebrant (helb)\n<helb@helb.cz>",
60 "",
61 "Former programmer of server, some network stuff and other significant optimizations",
62 "Tomáš Jedrzejek (ZeXx86)\n<zexx86@gmail.com>",
63 "",
64 "Former build system writer and ebuild maintainer",
65 "Tomáš Chvátal (scarabeus)\n<tomas.chvatal@gmail.com>",
66 "",
67 "Former OpenGL rendering programming",
68 "Petr Sýkora (Glubo)\n<petr.sykora@gmail.com>",
69 "",
70 "Former keyboard improver",
71 "Karel Podvolecký (kapo)\n<karel.podvolecky@gmail.com>",
72 "",
73 "Former porter",
74 "Petr L. (Timoo)\n<daac@post.cz>",
75 "",
76 "Former composer",
77 "Roman Kabelka (CJ Neti)\n<neti@cjneti.net>",
78 "",
79 NULL
80 };
81
82 static ta_credits_star stars[TA_CREDITS_STAR_COUNT];
83 static ta_credits_entry *credit_entries;
84 static uint32_t credit_count;
85 static uint32_t frame;
86 static uint32_t random_state;
87 static uint32_t credit_index;
88 static uint32_t credit_frame;
89 static uint32_t credit_text_color;
90
91 static uint32_t random_next(void) {
92 random_state ^= random_state << 13;
93 random_state ^= random_state >> 17;
94 random_state ^= random_state << 5;
95 return random_state;
96 }
97
98 static int32_t random_coordinate(void) {
99 return (int32_t)(random_next() & 0xffffu) - 32768;
100 }
101
102 static int32_t random_depth(void) {
103 return 49152 + (int32_t)((random_next() & 0xffffu) * 16384u / 65535u);
104 }
105
106 static void star_reset(ta_credits_star *star, int32_t depth) {
107 star->x = random_coordinate();
108 star->y = random_coordinate();
109 star->z = depth;
110 }
111
112 static void credit_advance(void) {
113 credit_index++;
114 credit_frame = 0;
115 credit_text_color = 0;
116 }
117
118 static int credit_fits(const char *string, uint32_t size, uint32_t max_width, uint32_t max_height, uint32_t *width, uint32_t *height) {
119 ta_text text = ta_text_create(string, size, 0xffffff, 0, 0);
120
121 if (text == 0) {
122 return -1;
123 }
124
125 *width = ta_text_width(text);
126 *height = ta_text_height(text);
127
128 ta_text_destroy(text);
129
130 return *width <= max_width && *height <= max_height;
131 }
132
133 static uint32_t credit_find_size(const char *string) {
134 uint32_t max_width = ta_display_width() * TA_CREDITS_TEXT_WIDTH_MARGIN / 100;
135 uint32_t max_height = ta_display_height() * TA_CREDITS_TEXT_HEIGHT_MARGIN / 100;
136 uint32_t width;
137 uint32_t height;
138 uint32_t size = TA_CREDITS_TEXT_REFERENCE_SIZE;
139 uint32_t by_width;
140 uint32_t by_height;
141 int fits;
142
143 if (credit_fits(string, size, max_width, max_height, &width, &height) < 0 || width == 0 || height == 0) {
144 return size;
145 }
146
147 by_width = size * max_width / width;
148 by_height = size * max_height / height;
149 size = by_width < by_height ? by_width : by_height;
150
151 if (size > TA_CREDITS_TEXT_REFERENCE_SIZE) {
152 size = TA_CREDITS_TEXT_REFERENCE_SIZE;
153 }
154
155 if (size < 1) {
156 size = 1;
157 }
158
159 while (size > 1) {
160 fits = credit_fits(string, size, max_width, max_height, &width, &height);
161
162 if (fits != 0) {
163 break;
164 }
165
166 size--;
167 }
168
169 return size;
170 }
171
172 static void credit_create_all(void) {
173 uint32_t i;
174
175 credit_count = 0;
176
177 while (strings[credit_count] != NULL) {
178 credit_count++;
179 }
180
181 credit_entries = malloc(credit_count * sizeof(ta_credits_entry));
182
183 if (credit_entries == NULL) {
184 credit_count = 0;
185 return;
186 }
187
188 for (i = 0; i < credit_count; i++) {
189 ta_credits_entry *entry = &credit_entries[i];
190
191 entry->text = 0;
192 entry->x = 0;
193 entry->y = 0;
194
195 if (strings[i][0] == '\0') {
196 continue;
197 }
198
199 entry->text = ta_text_create(strings[i], credit_find_size(strings[i]), 0, 0, 0);
200
201 if (entry->text == 0) {
202 continue;
203 }
204
205 entry->x = ((int32_t)ta_display_width() - (int32_t)ta_text_width(entry->text)) / 2;
206 entry->y = ((int32_t)ta_display_height() - (int32_t)ta_text_height(entry->text)) / 2;
207
208 if (entry->x < 0) {
209 entry->x = 0;
210 }
211
212 if (entry->y < 0) {
213 entry->y = 0;
214 }
215 }
216 }
217
218 static void credit_destroy_all(void) {
219 uint32_t i;
220
221 for (i = 0; i < credit_count; i++) {
222 if (credit_entries[i].text != 0) {
223 ta_text_destroy(credit_entries[i].text);
224 }
225 }
226
227 free(credit_entries);
228 credit_entries = NULL;
229 credit_count = 0;
230 }
231
232 static void credit_draw(void) {
233 ta_credits_entry *entry;
234 uint32_t brightness;
235 uint32_t color;
236
237 if (credit_index >= credit_count) {
238 ta_screen_switch(TA_SCREEN_MAIN_MENU);
239 return;
240 }
241
242 entry = &credit_entries[credit_index];
243
244 if (strings[credit_index][0] == '\0') {
245 if (credit_frame >= TA_CREDITS_TEXT_HOLD_FRAMES) {
246 credit_advance();
247 return;
248 }
249
250 credit_frame++;
251 return;
252 }
253
254 if (credit_frame < TA_CREDITS_TEXT_IN_FRAMES) {
255 brightness = credit_frame * 255u / (TA_CREDITS_TEXT_IN_FRAMES - 1);
256 } else if (credit_frame < TA_CREDITS_TEXT_IN_FRAMES + TA_CREDITS_TEXT_HOLD_FRAMES) {
257 brightness = 255;
258 } else {
259 uint32_t phase = credit_frame - TA_CREDITS_TEXT_IN_FRAMES - TA_CREDITS_TEXT_HOLD_FRAMES;
260
261 if (phase >= TA_CREDITS_TEXT_OUT_FRAMES) {
262 credit_advance();
263 return;
264 }
265
266 brightness = (TA_CREDITS_TEXT_OUT_FRAMES - 1 - phase) * 255u / (TA_CREDITS_TEXT_OUT_FRAMES - 1);
267 }
268
269 color = (brightness << 16) | (brightness << 8) | brightness;
270
271 if (entry->text == 0) {
272 credit_frame++;
273 return;
274 }
275
276 if (credit_text_color != color) {
277 if (ta_text_change_color(entry->text, color)) {
278 credit_text_color = color;
279 }
280 }
281
282 ta_text_draw(entry->text, (uint32_t)entry->x, (uint32_t)entry->y);
283
284 credit_frame++;
285 }
286
287 void ta_screen_credits_init(void) {
288 uint32_t i;
289
290 if (ta_ft2_load(TA_FILE_CREDITS)) {
291 ta_ft2_play();
292 }
293
294 random_state = 0x6d2b79f5u;
295 frame = 0;
296 credit_index = 0;
297 credit_frame = 0;
298 credit_text_color = 0;
299
300 credit_create_all();
301
302 for (i = 0; i < TA_CREDITS_STAR_COUNT; i++) {
303 star_reset(&stars[i], random_depth());
304 }
305 }
306
307 void ta_screen_credits_update(void) {
308 int32_t width = (int32_t)ta_display_width();
309 int32_t height = (int32_t)ta_display_height();
310 int32_t center_x = width / 2;
311 int32_t center_y = height / 2;
312 int32_t shortest = width < height ? width : height;
313 int32_t scale = shortest * 48 / 100;
314 int32_t speed;
315 uint32_t i;
316
317 if (frame < TA_CREDITS_ACCELERATION_FRAMES) {
318 speed = TA_CREDITS_FULL_SPEED * (int32_t)frame / TA_CREDITS_ACCELERATION_FRAMES;
319 } else {
320 speed = TA_CREDITS_FULL_SPEED;
321 }
322
323 ta_shapes_draw_pixel((uint32_t)width, (uint32_t)height, 0);
324
325 for (i = 0; i < TA_CREDITS_STAR_COUNT; i++) {
326 ta_credits_star *star = &stars[i];
327 int32_t x;
328 int32_t y;
329 int32_t brightness;
330 uint32_t color;
331
332 star->z -= speed;
333
334 if (star->z <= TA_CREDITS_Z_NEAR) {
335 star_reset(star, TA_CREDITS_Z_ONE);
336 }
337
338 x = center_x + (star->x * scale * 2) / star->z;
339 y = center_y + (star->y * scale * 2) / star->z;
340
341 if (x < 0 || y < 0 || x >= width || y >= height) {
342 star_reset(star, TA_CREDITS_Z_ONE);
343 continue;
344 }
345
346 brightness = ((TA_CREDITS_Z_ONE - star->z) * 255) / (TA_CREDITS_Z_ONE - TA_CREDITS_Z_NEAR);
347
348 if (brightness < 0) {
349 brightness = 0;
350 }
351
352 if (brightness > 255) {
353 brightness = 255;
354 }
355
356 color = ((uint32_t)brightness << 16) | ((uint32_t)brightness << 8) | (uint32_t)brightness;
357
358 ta_shapes_draw_pixel((uint32_t)x, (uint32_t)y, color);
359 }
360
361 if (frame >= TA_CREDITS_ACCELERATION_FRAMES) {
362 credit_draw();
363 }
364
365 frame++;
366 }
367
368 void ta_screen_credits_destroy(void) {
369 credit_destroy_all();
370 }