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