1 #include <allegro5/allegro.h>
2 #include <ctype.h>
3 #include <math.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include "credits.h"
7 #include "global.h"
8
9 /* for parsing readme.txt */
10 typedef struct TEXT_LIST {
11 char *text;
12 struct TEXT_LIST *next;
13 } TEXT_LIST;
14
15
16 typedef struct README_SECTION {
17 TEXT_LIST *head;
18 TEXT_LIST *tail;
19 char *flat;
20 char *desc;
21 } README_SECTION;
22
23
24 /* for parsing thanks._tx and the various source files */
25 typedef struct CREDIT_NAME {
26 char *name;
27 char *text;
28 struct CREDIT_NAME *next;
29 } CREDIT_NAME;
30
31
32 static CREDIT_NAME *credits = NULL;
33
34 /* text messages (loaded from readme.txt) */
35 static char *title_text;
36 static int title_size;
37 static int title_alloced;
38
39 /* for the text scroller */
40 static int text_char;
41 static int text_width;
42 static int text_pix;
43 static int text_scroll;
44
45 /* for the credits display */
46 static CREDIT_NAME *cred;
47 static int credit_width;
48 static int credit_scroll;
49 static int credit_age;
50 static int credit_speed;
51 static int credit_skip;
52
53 /* formats a list of TEXT_LIST structure into a single string */
format_text(TEXT_LIST * head,char * eol,char * gap)54 static char *format_text(TEXT_LIST * head, char *eol, char *gap)
55 {
56 TEXT_LIST *l;
57 int size = 0;
58 char *s;
59
60 l = head;
61 while (l) {
62 if (l->text[0])
63 size += strlen(l->text) + strlen(eol);
64 else
65 size += strlen(gap) + strlen(eol);
66 l = l->next;
67 }
68
69 s = malloc(size + 1);
70 s[0] = 0;
71
72 l = head;
73 while (l) {
74 if (l->text[0])
75 strcat(s, l->text);
76 else
77 strcat(s, gap);
78 strcat(s, eol);
79 l = l->next;
80 }
81
82 return s;
83 }
84
85
86
87 /* ensures argument of isspace is within valid range */
safe_isspace(unsigned char c)88 static bool safe_isspace(unsigned char c)
89 {
90 return isspace(c);
91 }
92
93
94
95 /* loads the scroller message from readme.txt */
load_text(void)96 static void load_text(void)
97 {
98 README_SECTION sect[] = {
99 {NULL, NULL, NULL, "Introduction"},
100 };
101
102 #define SPLITTER " "
103
104 static char intro_msg[] =
105 "Welcome to the Allegro demonstration game, by Miran Amon, Nick Davies, Elias Pschernig, Thomas Harte & Jakub Wasilewski."
106 SPLITTER
107 "Help skateboarding Ted collect various shop items - see \"About\" for more information!"
108 SPLITTER;
109
110 static char splitter[] = SPLITTER;
111 static char marker[] = "--------";
112 char buf[256];
113 README_SECTION *sec = NULL;
114 TEXT_LIST *l, *p;
115 ALLEGRO_FILE *f;
116 int inblank = true;
117 char *s;
118 int i;
119 ALLEGRO_USTR *u = al_ustr_newf("%s/readme.txt", data_path);
120 f = al_fopen(al_cstr(u), "r");
121 al_ustr_free(u);
122 if (!f) {
123 title_text =
124 "Can't find readme.txt, so this scroller is empty. ";
125 title_size = strlen(title_text);
126 title_alloced = false;
127 return;
128 }
129
130 while (al_fgets(f, buf, sizeof(buf)) != 0) {
131 if (buf[0] == '=') {
132 s = strchr(buf, ' ');
133 if (s) {
134 for (i = strlen(s) - 1; (safe_isspace(s[i])) || (s[i] == '='); i--)
135 s[i] = 0;
136
137 s++;
138
139 sec = NULL;
140 inblank = true;
141
142 for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
143 if (my_stricmp(s, sect[i].desc) == 0) {
144 sec = §[i];
145 break;
146 }
147 }
148 }
149 } else if (sec) {
150 s = buf;
151
152 while ((*s) && (safe_isspace(*s)))
153 s++;
154
155 for (i = strlen(s) - 1; (i >= 0) && (isspace(s[i])); i--)
156 s[i] = 0;
157
158 if ((s[0]) || (!inblank)) {
159 l = malloc(sizeof(TEXT_LIST));
160 l->next = NULL;
161 l->text = malloc(strlen(s) + 1);
162 strcpy(l->text, s);
163
164 if (sec->tail)
165 sec->tail->next = l;
166 else
167 sec->head = l;
168
169 sec->tail = l;
170 }
171
172 inblank = (s[0] == 0);
173 }
174 }
175
176 al_fclose(f);
177
178 title_size = strlen(intro_msg);
179
180 for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
181 if (sect[i].head) {
182 sect[i].flat = format_text(sect[i].head, " ", splitter);
183 title_size +=
184 strlen(sect[i].flat) + strlen(sect[i].desc) +
185 strlen(splitter) + strlen(marker) * 2 + 2;
186 }
187 }
188
189 title_text = malloc(title_size + 1);
190 title_alloced = true;
191
192 strcpy(title_text, intro_msg);
193
194 for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
195 if (sect[i].flat) {
196 strcat(title_text, marker);
197 strcat(title_text, " ");
198 strcat(title_text, sect[i].desc);
199 strcat(title_text, " ");
200 strcat(title_text, marker);
201 strcat(title_text, splitter);
202 strcat(title_text, sect[i].flat);
203 }
204 }
205
206 for (i = 0; i < (int)(sizeof(sect) / sizeof(sect[0])); i++) {
207 l = sect[i].head;
208 while (l) {
209 free(l->text);
210 p = l;
211 l = l->next;
212 free(p);
213 }
214 if (sect[i].flat)
215 free(sect[i].flat);
216 }
217 }
218
219
220 /* sorts a list of credit strings */
sort_credit_list(void)221 static void sort_credit_list(void)
222 {
223 CREDIT_NAME **prev, *p;
224 int n, done;
225
226 do {
227 done = true;
228
229 prev = &credits;
230 p = credits;
231
232 while ((p) && (p->next)) {
233 n = 0;
234
235 if (n == 0) {
236 n = my_stricmp(p->name, p->next->name);
237 }
238
239 if (n > 0) {
240 *prev = p->next;
241 p->next = p->next->next;
242 (*prev)->next = p;
243 p = *prev;
244
245 done = false;
246 }
247
248 prev = &p->next;
249 p = p->next;
250 }
251 } while (!done);
252 }
253
254
255 /* helper to open thanks._tx */
open_thanks(const char * s)256 static ALLEGRO_FILE *open_thanks(const char *s)
257 {
258 ALLEGRO_FILE *f;
259 ALLEGRO_USTR *u = al_ustr_newf("%s/%s", data_path, s);
260 f = al_fopen(al_cstr(u), "r");
261 al_ustr_free(u);
262 return f;
263 }
264
265
266 /* reads credit info from various places */
load_credits(void)267 static void load_credits(void)
268 {
269 char buf[256], *p, *p2;
270 CREDIT_NAME *c = NULL;
271 ALLEGRO_FILE *f;
272
273 /* parse thanks._tx, guessing at the relative location */
274 if ((f = open_thanks("thanks.txt")) == NULL) {
275 return;
276 }
277
278 while (al_fgets(f, buf, sizeof(buf))) {
279 if (my_stricmp(buf, "Thanks!") == 0)
280 break;
281
282 while ((p = strstr(buf, "<")) != NULL) {
283 *p = '<';
284 memmove(p + 1, p + 3, strlen(p + 2));
285 }
286
287 while ((p = strstr(buf, ">")) != NULL) {
288 *p = '>';
289 memmove(p + 1, p + 3, strlen(p + 2));
290 }
291
292 p = buf;
293
294 while ((*p) && (safe_isspace(*p)))
295 p++;
296
297 p2 = p;
298
299 while ((*p2) && ((!safe_isspace(*p2)) || (*(p2 + 1) != '(')))
300 p2++;
301
302 if ((strncmp(p2, " (<email>", 9) == 0) ||
303 (strncmp(p2, " (email", 7) == 0)) {
304 *p2 = 0;
305
306 c = malloc(sizeof(CREDIT_NAME));
307
308 c->name = malloc(strlen(p) + 1);
309 strcpy(c->name, p);
310
311 c->text = NULL;
312
313 c->next = credits;
314 credits = c;
315 } else if (*p) {
316 if (c) {
317 p2 = p + strlen(p) - 1;
318 while ((p2 > p) && (safe_isspace(*p2)))
319 *(p2--) = 0;
320
321 if (c->text) {
322 c->text = realloc(c->text, strlen(c->text) + strlen(p) + 2);
323 strcat(c->text, " ");
324 strcat(c->text, p);
325 } else {
326 c->text = malloc(strlen(p) + 1);
327 strcpy(c->text, p);
328 }
329 }
330 } else
331 c = NULL;
332 }
333
334 al_fclose(f);
335
336 /* sort the lists */
337 sort_credit_list();
338 }
339
340
next_credit(CREDIT_NAME * cred)341 static CREDIT_NAME *next_credit(CREDIT_NAME * cred)
342 {
343 int max, i;
344
345 if (!cred) {
346 cred = credits;
347 if (!cred)
348 return NULL;
349 }
350
351 max = rand() % 1000;
352 for (i = 0; i < max; i++) {
353 if (cred->next) {
354 cred = cred->next;
355 } else {
356 cred = credits;
357 }
358 }
359
360 return cred;
361 }
362
363
init_credits(void)364 void init_credits(void)
365 {
366 /* for the text scroller */
367 text_char = 0xFFFF;
368 text_width = 0;
369 text_pix = 0;
370 text_scroll = 0;
371
372 /* for the credits display */
373 cred = NULL;
374 credit_width = 0;
375 credit_scroll = 0;
376 credit_age = 0;
377 credit_speed = 32;
378 credit_skip = 1;
379
380 load_text();
381 load_credits();
382 }
383
384
update_credits(void)385 void update_credits(void)
386 {
387 /* move the scroller */
388 text_scroll++;
389
390 /* update the credits position */
391 if (credit_scroll <= 0) {
392 cred = next_credit(cred);
393
394 if (cred) {
395 credit_width = al_get_text_width(demo_font, cred->name) + 24;
396
397 if (cred->text) {
398 credit_scroll =
399 al_get_text_width(plain_font, cred->text) + screen_width - credit_width;
400 } else {
401 credit_scroll = 256;
402 }
403
404 credit_age = 0;
405 }
406 } else {
407 credit_scroll--;
408 credit_age++;
409 }
410 }
411
412
draw_credits(void)413 void draw_credits(void)
414 {
415 int c, c2;
416 int y2;
417 ALLEGRO_COLOR col_back, col_font;
418
419 /* for the text scroller */
420 char buf[2] = " ";
421
422 /* for the credits display */
423 char cbuf[2] = " ";
424 char *p;
425
426 col_back = al_map_rgb(222, 222, 222);
427 col_font = al_map_rgb(96, 96, 96);
428
429 /* draw the text scroller */
430 y2 = screen_height - al_get_font_line_height(demo_font);
431 text_pix = -(text_scroll / 1);
432 al_draw_line(0, y2 - 1.5, screen_width, y2 - 1.5, col_font, 1);
433 al_draw_line(0, y2 - 0.5, screen_width, y2 - 0.5, col_font, 1);
434 al_draw_filled_rectangle(0, y2, screen_width, screen_height, col_back);
435 for (text_char = 0; text_char < title_size; text_char++) {
436 buf[0] = title_text[text_char];
437 c = al_get_text_width(demo_font, buf);
438
439 if (text_pix + c > 0) {
440 demo_textout(demo_font, buf, text_pix, y2, col_font);
441 }
442
443 text_pix += c;
444
445 if (text_pix >= screen_width) {
446 break;
447 }
448 }
449
450 /* draw author name/desc credits */
451 y2 = al_get_font_line_height(demo_font);
452 al_draw_filled_rectangle(0, 0, screen_width, y2, col_back);
453 al_draw_line(0, y2 + 0.5, screen_width, y2 + 0.5, col_font, 1);
454 al_draw_line(0, y2 + 1.5, screen_width, y2 + 1.5, col_font, 1);
455 y2 = (al_get_font_line_height(demo_font) - 8) / 2;
456
457 if (cred && cred->text) {
458 c = credit_scroll;
459 p = cred->text;
460 c2 = strlen(p);
461
462 if (c > 0) {
463 if (c2 > c / 8) {
464 p += c2 - c / 8;
465 c &= 7;
466 } else {
467 c -= c2 * 8;
468 }
469
470 c += credit_width;
471
472 while ((*p) && (c < screen_width - 32)) {
473 if (c < credit_width + 96) {
474 c2 = 128 + (c - credit_width - 32) * 127 / 64;
475 } else if (c > screen_width - 96) {
476 c2 = 128 + (screen_width - 32 - c) * 127 / 64;
477 } else {
478 c2 = 255;
479 }
480
481 if ((c2 > 128) && (c2 <= 255)) {
482 c2 = 255 - c2 + 64;
483 cbuf[0] = *p;
484 demo_textout(plain_font, cbuf, c, 0, al_map_rgb(c2, c2, c2));
485 }
486
487 p++;
488 c += 8;
489 }
490 }
491 }
492
493 c = 4;
494
495 if (credit_age < 100) {
496 c -= (100 - credit_age) * (100 - credit_age) * credit_width / 10000;
497 }
498
499 if (credit_scroll < 150) {
500 c += (150 - credit_scroll) * (150 - credit_scroll) * screen_width / 22500;
501 }
502
503 if (cred)
504 demo_textprintf(demo_font, c, 0, col_font, "%s:", cred->name);
505 else
506 demo_textprintf(demo_font, 0, 0, col_font,
507 "thanks._tx not found!");
508 }
509
510
destroy_credits(void)511 void destroy_credits(void)
512 {
513 if ((title_text) && (title_alloced)) {
514 free(title_text);
515 }
516
517 while (credits) {
518 cred = credits;
519 credits = cred->next;
520
521 if (cred->name) {
522 free(cred->name);
523 }
524
525 if (cred->text) {
526 free(cred->text);
527 }
528
529 free(cred);
530 }
531 }
532