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 = &sect[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, "&lt")) != NULL) {
283          *p = '<';
284          memmove(p + 1, p + 3, strlen(p + 2));
285       }
286 
287       while ((p = strstr(buf, "&gt")) != 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