1 /* Copyright (C) 1993, 1992 Nathan Sidwell */
2 /* RCS $Id: demo.c,v 4.17 1995/12/22 11:56:52 nathan Exp $ */
3 #include "xmris.h"
4 #define SCORING_LEN 22
5 /*{{{ static*/
6 static char CONST *table_names[2] = {"Roll of honour", "Today's contenders"};
7 /*}}}*/
8 /*{{{ prototypes*/
9 static PROTOANIMATE(animate_board);
10 static PROTOANIMATE(animate_def_keys);
11 static PROTOANIMATE(animate_keys);
12 static PROTOANIMATE(animate_name);
13 static PROTOANIMATE(animate_score);
14 static PROTOANIMATE(animate_sprites);
15 static VOIDFUNC back_mris PROTOARG((unsigned));
16 static VOIDFUNC back_score PROTOARG((HIGH_SCORE CONST *, unsigned, unsigned));
17 static int back_title PROTOARG((TITLE CONST *, unsigned));
18 static int move_demo PROTOARG((unsigned));
19 static VOIDFUNC move_mris PROTOARG((VOIDARG));
20 static VOIDFUNC print_scores PROTOARG((HIGH_SCORE CONST *));
21 /*}}}*/
22 /*{{{ ANIMATE animate_board(next)*/
FUNCANIMATE(animate_board,next)23 static FUNCANIMATE(animate_board, next)
24 /*
25 * animates a demonstration board. Puts on the M R I & S letters
26 * and moves them about a bit, before returning from whence I came.
27 */
28 {
29 static PROTOANIMATE((*rts));
30 static int state;
31 static int cell_x;
32 static int cell_y;
33 PROTOVOID(*then);
34
35 then = (PROTOVOID(*))animate_board;
36 if(next)
37 /*{{{ start*/
38 {
39 rts = (PROTOANIMATE((*)))next;
40 global.screen = chaotic() % board.boards + 1;
41 extra.select = chaotic() % 5;
42 extra.got = chaotic() & 0x1F;
43 create_xtra_monster(extra.select);
44 new_board();
45 state = 0;
46 then = animate_zoom((void (*) PROTOARG((VOIDARG)))animate_board);
47 }
48 /*}}}*/
49 else if(!global.count || global.pause || global.throw == 1 ||
50 global.pressed & (1 << KEY_QUIT | 1 << KEY_KEYBOARD))
51 /*{{{ end*/
52 {
53 if(global.throw == 1 && state > 1)
54 global.pedestal = global.screen - 1;
55 timer_set((unsigned long)0, TIMING_OFF);
56 assert(rts != (PROTOANIMATE((*)))NULL);
57 then = (*rts)((PROTOVOID(*))NULL);
58 }
59 /*}}}*/
60 else if(state == 0 || global.pressed & 0xF)
61 /*{{{ start again*/
62 {
63 if(global.pressed & 0xF)
64 /*{{{ change screen*/
65 {
66 unsigned shift;
67
68 timer_set((unsigned long)0, TIMING_OFF);
69 if(global.pressed & 0x1)
70 shift = 10;
71 else if(global.pressed & 0x2)
72 shift = board.boards * 10 - 10;
73 else if(global.pressed & 0x4)
74 shift = board.boards - 1;
75 else
76 shift = 1;
77 global.screen = (global.screen + shift) % board.boards;
78 if(!global.screen)
79 global.screen = board.boards;
80 global.pressed &= ~0xF;
81 new_board();
82 XCopyArea(display.display, display.copy, display.window,
83 GCN(GC_COPY), BORDER_LEFT, BORDER_TOP,
84 BOARD_WIDTH, BOARD_HEIGHT, BORDER_LEFT, BORDER_TOP);
85 add_background(PIXELX(8, -GAP_WIDTH), PIXELY(-1, 0),
86 (CELL_WIDTH + GAP_WIDTH) * 4 + GAP_WIDTH, CELL_HEIGHT);
87 }
88 /*}}}*/
89 cell_y = 0;
90 cell_x = -1;
91 global.state = MODE_DEMO;
92 spawn_monster(0, 4, 3, 3, global.start.x, global.start.y, 0, 0);
93 extra.count = FRAMES_PER_SECOND - 1;
94 monster.list[0].stop = 1;
95 monster.den = 0xF;
96 monster.drones = 5;
97 monster.normals = 4;
98 monster.delay = 0;
99 player.old_ball.count = 16;
100 global.diamond = 0;
101 bounce_ball();
102 /*{{{ plonk on M R I S*/
103 {
104 unsigned ix;
105
106 calc_extra_home(0);
107 for(ix = 4; ix--;)
108 {
109 unsigned x, y;
110 unsigned j;
111 CELL *cptr;
112
113 do
114 {
115 do
116 j = chaotic();
117 while(j >= CELLS_ACROSS * CELLS_DOWN);
118 x = j % CELLS_ACROSS;
119 y = j / CELLS_ACROSS;
120 cptr = BOARDCELL(x, y);
121 }
122 while(!cptr->xtra || cptr->xtra == 255);
123 spawn_monster(0, SPRITE_MRIS + ix + (chaotic() & 4), 0, 0,
124 (int)x, (int)y, 0, 0);
125 }
126 }
127 /*}}}*/
128 global.count = DISPLAY_HOLD;
129 timer_set(FRAME_RATE, TIMING_ON);
130 state++;
131 }
132 /*}}}*/
133 else
134 /*{{{ animate*/
135 {
136 /*{{{ set den?*/
137 while(cell_y != CELLS_DOWN)
138 {
139 cell_x++;
140 if(cell_x == CELLS_ACROSS)
141 {
142 cell_x = 0;
143 cell_y++;
144 }
145 if(BOARDCELL(cell_x, cell_y)->den)
146 {
147 set_back_sprite(SPRITE_DEN, cell_x, cell_y);
148 break;
149 }
150 }
151 /*}}}*/
152 /*{{{ calc distances?*/
153 if(monster.den && monster.normals != monster.drones)
154 {
155 unsigned ix;
156
157 monster.drones = monster.normals;
158 ix = choose_direction(monster.den);
159 monster.den ^= 1 << ix;
160 calc_extra_home(4 + ix);
161 }
162 /*}}}*/
163 if(!extra.count--)
164 new_xtra();
165 if(player.ball.state)
166 bounce_ball();
167 else if(!monster.delay && chaotic() < 4)
168 {
169 unsigned throw;
170
171 throw = global.throw;
172 global.throw = 1;
173 bounce_ball();
174 global.throw = throw;
175 }
176 /*{{{ show a diamond?*/
177 if(!global.diamond && !monster.delay && !chaotic())
178 {
179 unsigned j;
180 int x, y;
181 CELL *cptr;
182
183 do
184 {
185 do
186 {
187 j = chaotic();
188 x = j % CELLS_ACROSS;
189 y = j / CELLS_ACROSS;
190 cptr = BOARDCELL(x, y);
191 }
192 while(j >= CELLS_DOWN * CELLS_ACROSS ||
193 (x == global.start.x && y == global.start.y) ||
194 cptr->sprite);
195 }
196 while(!cptr->visit || cptr[CELL_STRIDE].visit);
197 global.diamond = 1;
198 spawn_monster(0, 6, 0, 0, x, y, 0, 0)->count = DIAMOND_DELAY;
199 }
200 /*}}}*/
201 if(!monster.delay--)
202 monster.delay = 31;
203 move_mris();
204 if(monster.monsters == 1)
205 global.count--;
206 }
207 /*}}}*/
208 return then;
209 }
210 /*}}}*/
211 /*{{{ ANIMATE animate_def_keys(next)*/
FUNCANIMATE(animate_def_keys,next)212 static FUNCANIMATE(animate_def_keys, next)
213 /*
214 * Alter the control keys. This is a bit weird, compared to the other
215 * animate functions, 'cos there is no timeout on it. It's entirely
216 * driven by the user pressing enough keys.
217 * Don't let the user define one key to two functions.
218 */
219 {
220 static PROTOANIMATE((*rts));
221 PROTOVOID(*then);
222
223 then = (PROTOVOID(*))animate_def_keys;
224 if(next)
225 /*{{{ start*/
226 {
227 unsigned ix;
228
229 rts = (PROTOANIMATE((*)))next;
230 global.state = MODE_KEY_DEF;
231 global.key = NoSymbol;
232 monster.monsters = 0;
233 apple.apples = 0;
234 update.back.backs = 0;
235 update.score.scores = 0;
236 player.ball.state = 0;
237 player.ball.count = 16;
238 player.old_ball.state = 0;
239 player.old_ball.count = 16;
240 XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
241 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
242 back_mris(0);
243 monster.den = 0;
244 monster.delay = 0;
245 while(!strchr(title_text[monster.delay].text, '%'))
246 {
247 back_title(&title_text[monster.delay], monster.delay);
248 monster.delay++;
249 }
250 for(ix = monster.delay; strchr(title_text[ix + 1].text, '%'); ix++)
251 back_title(&title_text[ix], ix);
252 XCopyArea(display.display, display.back, display.copy, GCN(GC_COPY),
253 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
254 global.count = 0;
255 }
256 /*}}}*/
257 else
258 /*{{{ start again*/
259 {
260 TITLE CONST *tptr;
261 unsigned lookup;
262 KeyCode key;
263
264 tptr = &title_text[monster.delay + global.count];
265 key = global.key;
266 if(key == data.keysyms[KEY_THROW])
267 key = data.keysyms[tptr->ix];
268 for(lookup = 0; lookup != KEYS; lookup++)
269 if((monster.den & 1 << lookup) && data.keysyms[lookup] == key)
270 break;
271 if(lookup == KEYS)
272 {
273 data.keysyms[tptr->ix] = key;
274 monster.den |= 1 << tptr->ix;
275 XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
276 0, PIXELY(monster.delay + global.count, 0),
277 WINDOW_WIDTH, CELL_HEIGHT);
278 back_title(tptr, monster.delay + global.count);
279 XCopyArea(display.display, display.back, display.copy,
280 GCN(GC_COPY), 0, PIXELY(monster.delay + global.count, 0),
281 WINDOW_WIDTH, CELL_HEIGHT,
282 0, PIXELY(monster.delay + global.count, 0));
283 while(lookup--)
284 if(!(monster.den & 1 << lookup) && data.keysyms[lookup] == key)
285 {
286 unsigned ix;
287
288 for(ix = monster.delay; title_text[ix].ix != lookup; ix++)
289 /* EMPTY */;
290 XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
291 CENTER_X + (font.width + 1) / 2, PIXELY(ix, 0),
292 WINDOW_WIDTH / 2, CELL_HEIGHT);
293 XCopyArea(display.display, display.back, display.copy,
294 GCN(GC_COPY), CENTER_X + (font.width + 1) / 2,
295 PIXELY(ix, 0), WINDOW_WIDTH / 2,
296 CELL_HEIGHT, CENTER_X + (font.width + 1) / 2,
297 PIXELY(ix, 0));
298 monster.den |= 1 << lookup;
299 }
300 global.count++;
301 }
302 }
303 /*}}}*/
304 global.pause = 0;
305 if(global.count < KEYS)
306 /*{{{ animate*/
307 {
308 /*{{{ help line*/
309 {
310 TITLE CONST *tptr;
311 char buffer[49];
312 size_t length;
313
314 XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
315 0, PIXELY(CELLS_DOWN - 1, 0), WINDOW_WIDTH,
316 CELL_HEIGHT * 2 + GAP_HEIGHT);
317 strcpy(buffer, "Press new ");
318 tptr = &title_text[monster.delay + global.count];
319 strncat(buffer, tptr->text, strchr(tptr->text, ' ') - tptr->text);
320 strcat(buffer, " key");
321 length = strlen(buffer);
322 assert(length < sizeof(buffer));
323 XDrawImageString(display.display, display.back, GCN(GC_TEXT),
324 CENTER_X - (int)(length * font.width / 2),
325 PIXELY(CELLS_DOWN - 1, CELL_HEIGHT / 2) + font.center,
326 buffer, (int)length);
327 if(!(monster.den & 1 << tptr->ix))
328 {
329 sprintf(buffer, "%s for current binding",
330 XKeysymToString(data.keysyms[KEY_THROW]));
331 length = strlen(buffer);
332 assert(length < sizeof(buffer));
333 XDrawImageString(display.display, display.back, GCN(GC_TEXT),
334 CENTER_X - (int)(length * font.width / 2),
335 PIXELY(CELLS_DOWN, CELL_HEIGHT / 2) + font.center,
336 buffer, (int)length);
337 }
338 XCopyArea(display.display, display.back, display.copy, GCN(GC_COPY),
339 0, PIXELY(CELLS_DOWN - 1, 0), WINDOW_WIDTH,
340 CELL_HEIGHT * 2 + GAP_HEIGHT, 0, PIXELY(CELLS_DOWN - 1, 0));
341 }
342 /*}}}*/
343 refresh_window();
344 global.key = NoSymbol;
345 }
346 /*}}}*/
347 else
348 /*{{{ end*/
349 {
350 global.pressed = 0;
351 global.throw = 0;
352 assert(rts != (PROTOANIMATE((*)))NULL);
353 then = (*rts)((PROTOVOID(*))NULL);
354 }
355 /*}}}*/
356 return then;
357 }
358 /*}}}*/
359 /*{{{ ANIMATE animate_demo(next)*/
FUNCANIMATE(animate_demo,next)360 extern FUNCANIMATE(animate_demo, next)
361 /*
362 * The main demonstration control loop. Start or quit the game
363 * as necessary, or just cycle round the key display, demo board, and
364 * high score tables.
365 */
366 {
367 static int state = 1;
368 PROTOVOID(*then);
369
370 then = NULL;
371 assert(!next);
372 global.pause = 0;
373 if(global.pressed & 1 << KEY_QUIT)
374 global.quit = 1;
375 else if(global.throw == 1)
376 {
377 state = 0;
378 global.throw = 2;
379 then = animate_game((PROTOVOID(*))NULL);
380 }
381 else if(global.pressed & 1 << KEY_KEYBOARD)
382 {
383 state = 2;
384 then = animate_def_keys((PROTOVOID(*))animate_demo);
385 }
386 else
387 {
388 static PROTOANIMATE((*modes[])) =
389 {
390 (PROTOANIMATE((*)))animate_score,
391 (PROTOANIMATE((*)))animate_name,
392 (PROTOANIMATE((*)))animate_keys,
393 (PROTOANIMATE((*)))animate_sprites,
394 (PROTOANIMATE((*)))animate_board,
395 };
396
397 then = modes[state++]((PROTOVOID(*))animate_demo);
398 if(state == 3 && data.sprites == False)
399 state = 4;
400 else if(state == 5)
401 state = 0;
402 }
403 return then;
404 }
405 /*}}}*/
406 /*{{{ ANIMATE animate_diamond(next)*/
FUNCANIMATE(animate_diamond,next)407 extern FUNCANIMATE(animate_diamond, next)
408 /*
409 * A gem of a screen.
410 */
411 {
412 static PROTOANIMATE((*rts));
413 PROTOVOID(*then);
414
415 then = (PROTOVOID(*))animate_diamond;
416 if(next)
417 /*{{{ start*/
418 {
419 rts = (PROTOANIMATE((*)))next;
420 monster.monsters = 0;
421 apple.apples = 0;
422 update.back.backs = 0;
423 update.score.scores = 0;
424 player.ball.state = 0;
425 player.ball.count = 8;
426 add_background(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT - 1);
427 XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
428 BORDER_LEFT + 1, BORDER_TOP + 1, BOARD_WIDTH - 2, BOARD_HEIGHT - 2);
429 spawn_monster(0, 4, 3, 3, CELLS_ACROSS + 1, CELLS_DOWN / 2,
430 -(CELL_WIDTH + GAP_WIDTH) * CELLS_ACROSS, 0);
431 spawn_monster(0, 6, 0, 0, CELLS_ACROSS / 2, CELLS_DOWN / 2, 0, 0);
432 if(!global.lives)
433 monster.list[0].ghosting = GHOSTING;
434 draw_extra_letter(extra.select);
435 global.state = MODE_GAME_DEMO;
436 global.count = 3;
437 timer_set(FRAME_RATE, TIMING_PAUSE);
438 }
439 /*}}}*/
440 /*{{{ skip?*/
441 if(global.throw == 1)
442 {
443 global.throw = 2;
444 global.count = 0;
445 }
446 /*}}}*/
447 if(!global.count)
448 /*{{{ end*/
449 {
450 assert(rts != (PROTOANIMATE((*)))NULL);
451 then = (*rts)((PROTOVOID(*))NULL);
452 }
453 /*}}}*/
454 else
455 /*{{{ animate*/
456 {
457 if(monster.list[0].offset.x == VEL_X - (CELL_WIDTH + GAP_WIDTH) *
458 (CELLS_ACROSS + 2 - CELLS_ACROSS / 2))
459 {
460 monster.list[0].pushing = 1;
461 new_face(&monster.list[0]);
462 monster.list[1].cell.x = CELLS_ACROSS + 1;
463 monster.list[1].offset.x = -(CELL_WIDTH + GAP_WIDTH) *
464 (CELLS_ACROSS + 1 - CELLS_ACROSS / 2);
465 }
466 if(!move_demo(MONSTER_CYCLES))
467 global.count--;
468 }
469 /*}}}*/
470 return then;
471 }
472 /*}}}*/
473 /*{{{ ANIMATE animate_extra_life(next)*/
FUNCANIMATE(animate_extra_life,next)474 extern FUNCANIMATE(animate_extra_life, next)
475 /*
476 * animate an extra life
477 */
478 {
479 static PROTOANIMATE((*rts));
480 static char CONST *text[] = {"Congratulations", "You win extra", NULL};
481 PROTOVOID(*then);
482
483 then = (PROTOVOID(*))animate_extra_life;
484 if(next)
485 /*{{{ start*/
486 {
487 rts = (PROTOANIMATE((*)))next;
488 monster.monsters = 0;
489 apple.apples = 0;
490 update.back.backs = 0;
491 update.score.scores = 0;
492 player.ball.state = 0;
493 player.ball.count = 3;
494 player.old_ball.state = 0;
495 player.old_ball.count = 16;
496 add_background(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT - 1);
497 XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
498 BORDER_LEFT + 1, BORDER_TOP + 1, BOARD_WIDTH - 2, BOARD_HEIGHT - 2);
499 /*{{{ display some text*/
500 {
501 unsigned line;
502 char CONST **tptr;
503
504 line = BORDER_TOP + CELL_HEIGHT;
505 for(tptr = text; *tptr; tptr++)
506 {
507 size_t length;
508
509 length = strlen(*tptr);
510 XDrawImageString(display.display, display.back, GCN(GC_TEXT),
511 CENTER_X - (int)(length * font.width / 2),
512 (int)line + font.ascent, *tptr, (int)length);
513 line += font.ascent + font.descent;
514 }
515 }
516 /*}}}*/
517 spawn_monster(0, 4, 3, 3, 3, CELLS_DOWN / 2,
518 -(CELL_WIDTH + GAP_WIDTH) * 3, 0);
519 spawn_monster(0, 0, 2, 2, 0, CELLS_DOWN / 2,
520 (CELLS_ACROSS - 1) * (CELL_WIDTH + GAP_WIDTH), 0);
521 if(!global.lives)
522 monster.list[0].ghosting = GHOSTING;
523 draw_extra_letter(extra.select);
524 /*{{{ add m r i s*/
525 {
526 unsigned i;
527
528 for(i = 4; i--;)
529 {
530 back_sprite(SPRITE_MRIS + 4 + i, 0,
531 PIXELX(CELLS_ACROSS / 2 - 2 + (int)i, 0), PIXELY(2, 0));
532 spawn_monster(0, SPRITE_MRIS + i, 1, 1,
533 CELLS_ACROSS / 2 - 2 + (int)i, 2,
534 0, -(CELL_HEIGHT + GAP_HEIGHT) * 3 -
535 (int)CELL_HEIGHT / 2 * (int)i);
536 }
537 }
538 /*}}}*/
539 /*{{{ create a path*/
540 {
541 unsigned i;
542 CELL *cptr;
543
544 apple.apples = 0;
545 for(cptr = BOARDCELL(0, CELLS_DOWN / 2), i = CELLS_ACROSS; i--; cptr++)
546 {
547 cptr->visit = 1;
548 cptr->sprite = 0;
549 cptr->distance = CELLS_ACROSS - i;
550 cptr->depths[0] = 0;
551 cptr->depths[1] = 0;
552 cptr->depths[2] = -(CELL_WIDTH + GAP_WIDTH);
553 cptr->depths[3] = CELL_WIDTH + GAP_WIDTH;
554 cptr->holes[0] = 0;
555 cptr->holes[1] = 0;
556 cptr->holes[2] = 0;
557 cptr->holes[3] = 0;
558 }
559 BOARDCELL(0, CELLS_DOWN / 2)->depths[2] = 0;
560 BOARDCELL(CELLS_ACROSS - 1, CELLS_DOWN / 2)->depths[3] = 0;
561 }
562 /*}}}*/
563 global.state = MODE_GAME_DEMO;
564 global.count = 3;
565 timer_set(FRAME_RATE, TIMING_PAUSE);
566 }
567 /*}}}*/
568 if(global.throw == 1)
569 {
570 global.count = 0;
571 global.throw = 2;
572 }
573 if(!global.count)
574 /*{{{ end*/
575 {
576 unsigned i;
577 int x;
578
579 extra.got = 0;
580 create_xtra_monster(extra.select);
581 for(i = 5; i--;)
582 draw_extra_letter(i);
583 x = PIXELX((int)global.lives - 1, 0);
584 XCopyArea(display.display, sprites[SPRITE_PLAYER + 6].mask,
585 display.back, GCN(GC_MASK), 0, 0,
586 CELL_WIDTH, CELL_HEIGHT, x, PIXELY(CELLS_DOWN, 0));
587 XCopyArea(display.display, sprites[SPRITE_PLAYER + 6].image,
588 display.back, GCN(GC_OR), 0, 0,
589 CELL_WIDTH, CELL_HEIGHT, x, PIXELY(CELLS_DOWN, 0));
590 add_background(x, PIXELY(CELLS_DOWN, 0), CELL_WIDTH, CELL_HEIGHT);
591 global.lives++;
592 assert(rts != (PROTOANIMATE((*)))NULL);
593 then = (*rts)((PROTOVOID(*))NULL);
594 }
595 /*}}}*/
596 else
597 /*{{{ animate*/
598 {
599 unsigned throw;
600
601 throw = global.throw;
602 if(!monster.list[0].offset.x && global.count == 3)
603 {
604 global.count--;
605 global.throw = 1;
606 }
607 bounce_ball();
608 global.throw = throw;
609 if(player.ball.state == 3)
610 player.ball.state = 4;
611 else if(player.ball.state == 4)
612 {
613 player.ball.pixel.x = PIXELX(global.lives - 1, 0) + CELL_WIDTH / 2;
614 player.ball.pixel.y = BORDER_TOP + BOARD_HEIGHT + 1 +
615 CELL_HEIGHT / 2 - (CELL_HEIGHT + GAP_HEIGHT) * 3;
616 }
617 else if(!player.ball.state && global.count == 2)
618 {
619 monster.list[0].face = 10;
620 spawn_monster(0, SPRITE_PLAYER + 6, 0, 1, (int)global.lives - 1,
621 CELLS_DOWN, 0, -(CELL_HEIGHT + GAP_HEIGHT) * 3);
622 global.count--;
623 }
624 if(global.count == 1)
625 player.ball.count = 8;
626 if(!move_demo(MONSTER_CYCLES) && global.count == 1)
627 global.count--;
628 }
629 /*}}}*/
630 return then;
631 }
632 /*}}}*/
633 /*{{{ ANIMATE animate_history(next)*/
FUNCANIMATE(animate_history,next)634 extern FUNCANIMATE(animate_history, next)
635 /*
636 * shows the history list
637 */
638 {
639 static PROTOANIMATE((*rts));
640 PROTOVOID(*then);
641
642 then = (PROTOVOID(*))animate_history;
643 if(next)
644 /*{{{ start*/
645 {
646 rts = (PROTOANIMATE((*)))next;
647 monster.monsters = 0;
648 apple.apples = 0;
649 update.back.backs = 0;
650 update.score.scores = 0;
651 player.ball.state = 0;
652 player.ball.count = 16;
653 player.old_ball.state = 0;
654 player.old_ball.count = 16;
655 add_background(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT - 1);
656 draw_extra_letter(extra.select);
657 XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
658 BORDER_LEFT + 1, BORDER_TOP + 1, BOARD_WIDTH - 2, BOARD_HEIGHT - 2);
659 /*{{{ add in the parts*/
660 {
661 unsigned base;
662 unsigned screens;
663 unsigned ix;
664 unsigned sprite;
665 MONSTER *mptr;
666 char text[11];
667 size_t length;
668 static unsigned sprites[4] =
669 {SPRITE_CHERRY, SPRITE_NORMAL + 4, 4, 6};
670
671 screens = global.screen < CELLS_DOWN - 2 ?
672 global.screen : CELLS_DOWN - 2;
673 base = global.screen - screens;
674 for(ix = screens; ix--;)
675 {
676 unsigned long time;
677
678 sprintf(text, "Garden %d", base + ix + 1);
679 length = strlen(text);
680 XDrawImageString(display.display, display.back, GCN(GC_TEXT),
681 PIXELX(CELLS_ACROSS / 2 - 1,
682 -GAP_WIDTH) - (int)(font.width * length),
683 PIXELY((int)(screens - ix),
684 (CELL_HEIGHT + GAP_HEIGHT) / 2) + font.center,
685 text, (int)length);
686 time = history.times[screens - 1 - ix] / 1000;
687 sprintf(text, "%3u\'%02u\"", (int)(time / 60), (int)(time % 60));
688 length = strlen(text);
689 XDrawImageString(display.display, display.back, GCN(GC_TEXT),
690 PIXELX(CELLS_ACROSS / 2 + 2, 0),
691 PIXELY((int)(screens - ix),
692 (CELL_HEIGHT + GAP_HEIGHT) / 2) + font.center,
693 text, (int)length);
694 if(history.prize & 1 << (screens - 1 - ix))
695 spawn_monster(0, SPRITE_PRIZE_BASE +
696 (base + ix) % SPRITE_PRIZES, 0, 0,
697 CELLS_ACROSS / 2 + 1, (int)(screens - ix),
698 0, (int)ix * (2 * CELL_HEIGHT + GAP_HEIGHT) +
699 CELLS_DOWN * GAP_HEIGHT + CELL_HEIGHT / 2 + GAP_HEIGHT +
700 (CELL_HEIGHT + GAP_HEIGHT) *
701 (2 + CELLS_DOWN - (int)screens));
702 sprite = sprites[(history.ending >> (screens - 1 - ix) * 2) & 3];
703 mptr = spawn_monster(0, sprite,
704 0, 0, CELLS_ACROSS / 2 - 1, (int)(screens - ix),
705 0, (int)ix * (2 * CELL_HEIGHT + GAP_HEIGHT) +
706 CELLS_DOWN * GAP_HEIGHT + (CELL_HEIGHT + GAP_HEIGHT) *
707 (2 + CELLS_DOWN - (int)screens));
708 if(sprite == 4)
709 mptr->face = 10;
710 }
711 }
712 /*}}}*/
713 global.state = MODE_GAME_DEMO;
714 global.count = DISPLAY_HOLD;
715 timer_set(FRAME_RATE, TIMING_PAUSE);
716 }
717 /*}}}*/
718 /*{{{ skip?*/
719 if(global.throw == 1)
720 {
721 global.throw = 2;
722 global.count = 0;
723 }
724 /*}}}*/
725 if(!global.count)
726 /*{{{ end*/
727 {
728 assert(rts != (PROTOANIMATE((*)))NULL);
729 then = (*rts)((PROTOVOID(*))NULL);
730 }
731 /*}}}*/
732 else
733 /*{{{ animate*/
734 {
735 if(!move_demo(MONSTER_CYCLES))
736 global.count--;
737 }
738 /*}}}*/
739 return then;
740 }
741 /*}}}*/
742 /*{{{ ANIMATE animate_keys(next)*/
FUNCANIMATE(animate_keys,next)743 static FUNCANIMATE(animate_keys, next)
744 /*
745 * show the current key bindings and stuff
746 */
747 {
748 static PROTOANIMATE((*rts));
749 PROTOVOID(*then);
750
751 then = (PROTOVOID(*))animate_keys;
752 if(next)
753 /*{{{ start*/
754 {
755 rts = (PROTOANIMATE((*)))next;
756 global.state = MODE_DEMO;
757 monster.monsters = 0;
758 apple.apples = 0;
759 update.back.backs = 0;
760 update.score.scores = 0;
761 player.ball.state = 0;
762 player.ball.count = 16;
763 player.old_ball.state = 0;
764 player.old_ball.count = 16;
765 XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
766 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
767 /*{{{ put on M R I S*/
768 {
769 unsigned missing;
770 int x, y;
771
772 missing = chaotic() & 3;
773 back_mris((unsigned)(1 << missing));
774 y = chaotic() % (CELLS_DOWN + 2) * (CELL_HEIGHT + GAP_HEIGHT);
775 x = chaotic() & 1 ? CELLS_ACROSS * (CELL_WIDTH + GAP_WIDTH) :
776 -CELLS_ACROSS * (CELL_WIDTH + GAP_WIDTH);
777 spawn_monster(0, SPRITE_MRIS + missing, 0, 0,
778 4 + (int)missing, -1, x, y);
779 }
780 /*}}}*/
781 XCopyArea(display.display, display.back, display.copy, GCN(GC_COPY),
782 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
783 /*{{{ put on the title text*/
784 {
785 TITLE CONST *tptr;
786 unsigned ix;
787 unsigned gnome;
788
789 gnome = 0;
790 for(tptr = title_text, ix = 0; tptr->text; tptr++, ix++)
791 if(back_title(tptr, ix))
792 /*{{{ spawn monster*/
793 {
794 unsigned type;
795 int cellx;
796 int offsetx;
797
798 do
799 {
800 type = chaotic() & 3;
801 if(type & 2)
802 type++;
803 }
804 while(type == 4 && gnome);
805 if(type == 4)
806 gnome = 1;
807 if(chaotic() & 1)
808 {
809 cellx = -2;
810 offsetx = (CELLS_ACROSS + 2) * (CELL_WIDTH + GAP_WIDTH);
811 }
812 else
813 {
814 cellx = CELLS_ACROSS + 1;
815 offsetx = -(CELLS_ACROSS + 5) * (CELL_WIDTH + GAP_WIDTH);
816 }
817 offsetx += CELL_WIDTH * (chaotic() & 3);
818 spawn_monster(0, type, 0, 0, cellx, (int)ix, offsetx, 0);
819 }
820 /*}}}*/
821 }
822 /*}}}*/
823 global.count = DISPLAY_HOLD;
824 global.missed = 0;
825 refresh_window();
826 timer_set(FRAME_RATE, TIMING_ON);
827 }
828 /*}}}*/
829 if(!global.count || global.pause || global.throw == 1 ||
830 global.pressed & (1 << KEY_QUIT | 1 << KEY_KEYBOARD))
831 /*{{{ end*/
832 {
833 timer_set((unsigned long)0, TIMING_OFF);
834 assert(rts != (PROTOANIMATE((*)))NULL);
835 then = (*rts)((PROTOVOID(*))NULL);
836 }
837 /*}}}*/
838 else
839 /*{{{ animate*/
840 {
841 if(!move_demo(MONSTER_CYCLES))
842 global.count--;
843 }
844 /*}}}*/
845 return then;
846 }
847 /*}}}*/
848 /*{{{ ANIMATE animate_name(next)*/
FUNCANIMATE(animate_name,next)849 static FUNCANIMATE(animate_name, next)
850 /*
851 * Animates the game name in an exciting way.
852 */
853 {
854 static PROTOANIMATE((*rts));
855 static int state;
856 PROTOVOID(*then);
857
858 then = (PROTOVOID(*))animate_name;
859 if(next)
860 /*{{{ start*/
861 {
862 rts = (PROTOANIMATE((*)))next;
863 state = -1;
864 extra.got = 0x1F;
865 blank_board(board.list[chaotic() % board.boards], 0);
866 XCopyArea(display.display, display.back, display.copy, GCN(GC_COPY),
867 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
868 player.old_ball.count = 16;
869 player.ball.count = 16;
870 then = animate_zoom((void (*) PROTOARG((VOIDARG)))animate_name);
871 }
872 /*}}}*/
873 else if(!global.count || global.pause || global.throw == 1 ||
874 global.pressed & (1 << KEY_QUIT | 1 << KEY_KEYBOARD))
875 /*{{{ end*/
876 {
877 assert(rts != (PROTOANIMATE((*)))NULL);
878 then = (*rts)((PROTOVOID(*))NULL);
879 }
880 /*}}}*/
881 else if(state == -1)
882 /*{{{ start again*/
883 {
884 global.state = MODE_DEMO;
885 player.old_ball.state = 0;
886 player.old_ball.count = 16;
887 player.ball.state = 3;
888 player.ball.count = BALL_EXPLODE;
889 refresh_window();
890 timer_set(NAME_RATE, TIMING_OFF);
891 state = 0;
892 }
893 /*}}}*/
894 else
895 /*{{{ animate*/
896 {
897 unsigned throw;
898
899 throw = global.throw;
900 if(player.ball.state == 3)
901 /*{{{ exploded?*/
902 {
903 if(lettering[state])
904 {
905 player.ball.state = 4;
906 player.ball.count--;
907 if(lettering[state])
908 {
909 monster.list[0].pixel.x =
910 PIXELX(LETTERX(lettering[state][0]), 0);
911 monster.list[0].pixel.y =
912 PIXELY(LETTERY(lettering[state][0]), 0);
913 }
914 }
915 else
916 global.count--;
917 }
918 /*}}}*/
919 else if(player.ball.state)
920 {
921 bounce_ball();
922 /*{{{ imploded?*/
923 if(!player.ball.state)
924 {
925 unsigned char letter;
926 CELL *cptr;
927
928 letter = lettering[state][0];
929 spawn_monster(0, 4, 0, 0,
930 LETTERX(letter), LETTERY(letter), 0, 0);
931 cptr = BOARDCELL(LETTERX(letter), LETTERY(letter));
932 cptr->visit = 1;
933 update.set = 0;
934 munch_hole(cptr, PIXELX(LETTERX(letter), 0),
935 PIXELY(LETTERY(letter), 0));
936 add_background(update.tl.x, update.tl.y,
937 (unsigned)(update.br.x - update.tl.x),
938 (unsigned)(update.br.y - update.tl.y));
939 global.count = 1;
940 player.ball.count = 16;
941 }
942 /*}}}*/
943 }
944 else
945 {
946 /*{{{ new direction?*/
947 if(!monster.list[0].count)
948 {
949 unsigned char letter;
950
951 letter = lettering[state][global.count++];
952 if(letter == LETTEREND)
953 {
954 monster.list[0].type = 5;
955 player.ball.pixel.x = monster.list[0].pixel.x +
956 CELL_WIDTH / 2;
957 player.ball.pixel.y = monster.list[0].pixel.y +
958 CELL_HEIGHT / 2;
959 player.ball.state = 2;
960 player.ball.count = 0;
961 global.count = DISPLAY_HOLD *
962 (unsigned)(FRAME_RATE / NAME_RATE);
963 state++;
964 }
965 else
966 {
967 monster.list[0].dir = LETTERDIR(letter);
968 monster.list[0].count = LETTERDIST(letter);
969 new_face(&monster.list[0]);
970 }
971 }
972 /*}}}*/
973 if(!monster.list[0].cycle)
974 {
975 monster.list[0].cycle = MONSTER_CYCLES;
976 monster.list[0].image++;
977 if(monster.list[0].image == MONSTER_IMAGES)
978 monster.list[0].image = 0;
979 }
980 monster.list[0].cycle--;
981 if(monster.list[0].type != 5 && move_muncher(&monster.list[0]))
982 monster.list[0].count--;
983 }
984 global.throw = throw;
985 }
986 /*}}}*/
987 return then;
988 }
989 /*}}}*/
990 /*{{{ ANIMATE animate_score(next)*/
FUNCANIMATE(animate_score,next)991 static FUNCANIMATE(animate_score, next)
992 /*
993 * pick a high score table and show it
994 */
995 {
996 static PROTOANIMATE((*rts));
997 PROTOVOID(*then);
998 unsigned cycle;
999 unsigned start;
1000 unsigned term;
1001
1002 then = (PROTOVOID(*))animate_score;
1003 cycle = start = term = 0;
1004 if(next)
1005 /*{{{ new*/
1006 {
1007 rts = (PROTOANIMATE((*)))next;
1008 check_scores();
1009 start = 1;
1010 cycle = !scoring.display || !scoring.display->score;
1011 }
1012 /*}}}*/
1013 else if(!global.count || global.pause || global.throw == 1 ||
1014 global.pressed & (1 << KEY_QUIT | 1 << KEY_KEYBOARD))
1015 /*{{{ end*/
1016 {
1017 cycle = 1;
1018 term = 1;
1019 }
1020 /*}}}*/
1021 else if(global.pressed & 0xF)
1022 /*{{{ cycle*/
1023 {
1024 start = 1;
1025 cycle = global.pressed & 0x5 ? 1 : 2;
1026 global.pressed &= ~0xF;
1027 }
1028 /*}}}*/
1029 /*{{{ cycle scoring.display?*/
1030 if(cycle)
1031 {
1032 static HIGH_SCORE *list[3] =
1033 {scoring.today, scoring.high, scoring.personal};
1034 unsigned ix;
1035 unsigned count;
1036
1037 if(!scoring.display)
1038 ix = 0;
1039 else
1040 for(ix = 3; ix--;)
1041 if(list[ix] == scoring.display)
1042 break;
1043 for(count = 3; (ix = (ix + cycle) % 3), count--;)
1044 if(list[ix]->score)
1045 {
1046 scoring.display = list[ix];
1047 break;
1048 }
1049 if(!scoring.display)
1050 {
1051 term = 1;
1052 start = 0;
1053 }
1054 }
1055 /*}}}*/
1056 if(start)
1057 /*{{{ initialize*/
1058 {
1059 global.state = MODE_DEMO;
1060 monster.monsters = 0;
1061 apple.apples = 0;
1062 update.back.backs = 0;
1063 update.score.scores = 0;
1064 player.ball.state = 0;
1065 player.ball.count = 16;
1066 player.old_ball.state = 0;
1067 player.old_ball.count = 16;
1068 XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
1069 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
1070 XDrawLine(display.display, display.back, GCN(GC_BORDER),
1071 BORDER_LEFT, PIXELY(CELLS_DOWN, 0) - 1,
1072 BORDER_LEFT + BOARD_WIDTH, PIXELY(CELLS_DOWN, 0) - 1);
1073 back_mris(0);
1074 /*{{{ check if splitting lines*/
1075 {
1076 size_t length;
1077 HIGH_SCORE CONST *sptr;
1078 unsigned ix;
1079
1080 length = 0;
1081 for(sptr = scoring.display, ix = 0;
1082 sptr->score && ix != HIGH_SCORES; sptr++, ix++)
1083 {
1084 size_t temp;
1085
1086 temp = strlen(sptr->name);
1087 if(temp > length)
1088 length = temp;
1089 }
1090 monster.den = (length + SCORING_LEN) * font.width >
1091 PIXELX(CELLS_ACROSS, -GAP_WIDTH) - PIXELX(0, 0);
1092 }
1093 /*}}}*/
1094 /*{{{ heading*/
1095 {
1096 char CONST *string;
1097 size_t length;
1098
1099 if(scoring.display == scoring.personal)
1100 string = scoring.mine.name;
1101 else
1102 string = table_names[scoring.display != scoring.high];
1103 length = strlen(string);
1104 XDrawImageString(display.display, display.back, GCN(GC_TEXT),
1105 PIXELX(CELLS_ACROSS / 2, 0) - (int)(length * font.width / 2),
1106 PIXELY(0, CELL_HEIGHT / 2) + font.center,
1107 string, (int)length);
1108 if(scoring.display == scoring.personal)
1109 string = " Score Garden Time Date";
1110 else
1111 string = " Score Garden Time Name";
1112 length = strlen(string);
1113 XDrawImageString(display.display, display.back, GCN(GC_TEXT),
1114 PIXELX(0, 0), PIXELY(1, CELL_HEIGHT / 2) + font.center,
1115 string, (int)length);
1116 if(scoring.mine.score)
1117 back_score(&scoring.mine, CELLS_DOWN, 0);
1118 }
1119 /*}}}*/
1120 XCopyArea(display.display, display.back, display.copy, GCN(GC_COPY),
1121 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
1122 /*{{{ put on the scores*/
1123 {
1124 HIGH_SCORE CONST *sptr;
1125 unsigned ix;
1126
1127 for(sptr = scoring.display, ix = 0;
1128 sptr->score && ix != HIGH_SCORES; sptr++, ix++)
1129 back_score(sptr, 2 + ix, monster.den);
1130 spawn_monster(0, 4, 3, 3, 0, (int)ix + 2,
1131 -2 * (CELL_WIDTH + GAP_WIDTH), 0);
1132 }
1133 /*}}}*/
1134 global.count = SCORE_HOLD;
1135 global.missed = 0;
1136 refresh_window();
1137 timer_set(SCORE_RATE, TIMING_OFF);
1138 }
1139 /*}}}*/
1140 else if(term)
1141 /*{{{ term*/
1142 {
1143 assert(rts != (PROTOANIMATE((*)))NULL);
1144 then = (*rts)((PROTOVOID(*))NULL);
1145 }
1146 /*}}}*/
1147 else
1148 /*{{{ animate*/
1149 {
1150 add_background(PIXELX(monster.list[0].cell.x, monster.list[0].offset.x +
1151 (monster.list[0].offset.x > 0 ? CELL_WIDTH - VEL_X : 0)),
1152 PIXELY(monster.list[0].cell.y, monster.list[0].offset.y -
1153 (monster.list[0].offset.y < 0 ? 0 : GAP_HEIGHT / 2)),
1154 monster.list[0].offset.x ? VEL_X : CELL_WIDTH,
1155 CELL_HEIGHT + GAP_HEIGHT);
1156 if(!move_demo(SCORE_CYCLES))
1157 global.count--;
1158 /*{{{ turn player?*/
1159 if(monster.list[0].offset.x == 0 &&
1160 monster.list[0].offset.y == 0 && monster.list[0].cell.y > 0)
1161 {
1162 if(monster.list[0].dir == 0)
1163 {
1164 if(monster.list[0].cell.x)
1165 /*{{{ go left*/
1166 {
1167 monster.list[0].offset.x =
1168 monster.list[0].cell.x * (CELL_WIDTH + GAP_WIDTH);
1169 monster.list[0].cell.x = 0;
1170 }
1171 /*}}}*/
1172 else
1173 /*{{{ go right*/
1174 {
1175 size_t length;
1176
1177 if(monster.den)
1178 length = CELLS_ACROSS - 1;
1179 else
1180 {
1181 size_t temp;
1182 unsigned ix;
1183
1184 ix = monster.list[0].cell.y - 2;
1185 length = strlen(scoring.display[ix].name);
1186 if(ix != 0)
1187 {
1188 temp = strlen(scoring.display[ix - 1].name);
1189 if(temp > length)
1190 length = temp;
1191 }
1192 length = ((length + SCORING_LEN) * font.width +
1193 GAP_WIDTH) / (CELL_WIDTH + GAP_WIDTH) ;
1194 }
1195 monster.list[0].cell.x = length;
1196 monster.list[0].offset.x =
1197 -(length * (CELL_WIDTH + GAP_WIDTH));
1198 }
1199 /*}}}*/
1200 }
1201 else if(monster.list[0].cell.y != 2)
1202 /*{{{ go up 1*/
1203 {
1204 monster.list[0].cell.y--;
1205 monster.list[0].offset.y = CELL_HEIGHT + GAP_HEIGHT;
1206 }
1207 /*}}}*/
1208 else
1209 /*{{{ go off screen*/
1210 {
1211 monster.list[0].cell.y = -2;
1212 monster.list[0].offset.y = 4 * (CELL_HEIGHT + GAP_HEIGHT);
1213 }
1214 /*}}}*/
1215 }
1216 /*}}}*/
1217 }
1218 /*}}}*/
1219 return then;
1220 }
1221 /*}}}*/
1222 /*{{{ ANIMATE animate_sprites(next)*/
FUNCANIMATE(animate_sprites,next)1223 static FUNCANIMATE(animate_sprites, next)
1224 /*
1225 * show off the sprites
1226 */
1227 {
1228 static PROTOANIMATE((*rts));
1229 static unsigned pressed = 0;
1230 static MONSTER *happy;
1231 PROTOVOID(*then);
1232
1233 then = (PROTOVOID(*))animate_sprites;
1234 if(next)
1235 /*{{{ initialize*/
1236 {
1237 rts = (PROTOANIMATE((*)))next;
1238 global.state = MODE_DEMO;
1239 monster.monsters = 0;
1240 apple.apples = 0;
1241 update.back.backs = 0;
1242 update.score.scores = 0;
1243 player.ball.state = 0;
1244 player.ball.count = 16;
1245 player.old_ball.state = 0;
1246 player.old_ball.count = 16;
1247 extra.select = 0;
1248 extra.got = 0;
1249 XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
1250 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
1251 /*{{{ M R I S*/
1252 {
1253 unsigned ix;
1254
1255 for(ix = 4; ix--;)
1256 {
1257 back_sprite(SPRITE_MRIS + ix, 0, PIXELX(4 + (int)ix, 0),
1258 PIXELY(-1, 0));
1259 back_sprite(SPRITE_MRIS + 4 + ix, 0, PIXELX((int)ix, 0),
1260 PIXELY(-1, 0));
1261 back_sprite(SPRITE_MRIS + 4 + ix, 0, PIXELX(8 + (int)ix, 0),
1262 PIXELY(-1, 0));
1263 }
1264 }
1265 /*}}}*/
1266 /*{{{ normal, munch, drone, player*/
1267 {
1268 unsigned ix;
1269
1270 for(ix = 12; ix--;)
1271 {
1272 back_sprite(SPRITE_NORMAL + ix, 0,
1273 PIXELX(0, 0), PIXELY((int)ix, 0));
1274 back_sprite(SPRITE_MUNCHER + ix, 0,
1275 PIXELX(1, 0), PIXELY((int)ix, 0));
1276 back_sprite(SPRITE_DRONE + ix, 0,
1277 PIXELX(3, 0), PIXELY((int)ix, 0));
1278 back_sprite(SPRITE_PLAYER + ix, 0,
1279 PIXELX(4, 0), PIXELY((int)ix, 0));
1280 }
1281 }
1282 /*}}}*/
1283 /*{{{ xtra*/
1284 {
1285 unsigned ix;
1286
1287 for(ix = 10; ix--;)
1288 {
1289 if(ix & 1)
1290 create_xtra_monster(ix >> 1);
1291 back_sprite(SPRITE_XTRA + ix, 0,
1292 PIXELX(2, 0), PIXELY((int)ix, 0));
1293 }
1294 }
1295 /*}}}*/
1296 back_sprite(SPRITE_CHOMP, 0, PIXELX(2, 0), PIXELY(10, 0));
1297 back_sprite(SPRITE_CHOMP + 1, 0, PIXELX(2, 0), PIXELY(11, 0));
1298 /*{{{ squished*/
1299 {
1300 unsigned ix;
1301
1302 for(ix = 5; ix--;)
1303 {
1304 back_sprite(SPRITE_SQUISHED + (ix << 1), 0,
1305 PIXELX((int)ix, 0), PIXELY(12, 0));
1306 back_sprite(SPRITE_SQUISHED + 1 + (ix << 1), 0,
1307 PIXELX((int)ix, 0), PIXELY(12, CELL_HEIGHT / 2));
1308 }
1309 }
1310 /*}}}*/
1311 /*{{{ push, pause, happy, dead*/
1312 {
1313 unsigned ix;
1314
1315 for(ix = 4; ix--;)
1316 back_sprite(SPRITE_PLAYER_PUSH + ix, 0,
1317 PIXELX(5, 0), PIXELY((int)ix, 0));
1318 for(ix = 4; ix--;)
1319 back_sprite(SPRITE_PLAYER_REST + ix, 0,
1320 PIXELX(5, 0), PIXELY((int)ix + 4, 0));
1321 for(ix = 2; ix--;)
1322 back_sprite(SPRITE_PLAYER_HAPPY + ix, 0,
1323 PIXELX(5, 0), PIXELY((int)ix + 8, 0));
1324 if(data.gender != False)
1325 for(ix = 2; ix--;)
1326 back_sprite(SPRITE_PLAYER_DEAD + ix, 0,
1327 PIXELX(5, 0), PIXELY((int)ix + 10, 0));
1328 }
1329 /*}}}*/
1330 back_sprite(SPRITE_BALL, 0, PIXELX(6, CELL_WIDTH / 2 - BALL_WIDTH / 2),
1331 PIXELY(0, CELL_HEIGHT / 4 - BALL_HEIGHT / 2));
1332 back_sprite(SPRITE_SEAT, 0, PIXELX(6, 0), PIXELY(0, CELL_HEIGHT / 2));
1333 back_sprite(SPRITE_DEN, 0, PIXELX(6, 0), PIXELY(1, 0));
1334 back_sprite(SPRITE_CHERRY, 0, PIXELX(6, 0), PIXELY(2, 0));
1335 /*{{{ prizes*/
1336 {
1337 unsigned ix;
1338
1339 for(ix = SPRITE_PRIZES; ix--;)
1340 back_sprite(SPRITE_PRIZE_BASE + ix, 0,
1341 PIXELX(6, 0), PIXELY((int)ix + 3, 0));
1342 for(ix = 3; ix--;)
1343 back_sprite(SPRITE_DIAMOND + ix, 0,
1344 PIXELX(10, CELL_WIDTH / 2), PIXELY((int)ix + 8, 0));
1345 }
1346 /*}}}*/
1347 /*{{{ lettering*/
1348 {
1349 back_sprite(SPRITE_DIGITS, 0, PIXELX(5, GAP_WIDTH +
1350 (int)sprites[SPRITE_EXTRA].size.x),
1351 PIXELY(CELLS_DOWN - 1, CELL_HEIGHT / 2 - DIGIT_HEIGHT / 2));
1352 back_sprite(SPRITE_EXTRA, 0, PIXELX(5, 0), PIXELY(CELLS_DOWN - 1,
1353 CELL_HEIGHT / 2 - (int)sprites[SPRITE_EXTRA].size.y / 2));
1354 back_sprite(SPRITE_EXTRA + 1, 0,
1355 PIXELX(CELLS_ACROSS - 1, CELL_WIDTH) -
1356 (int)sprites[SPRITE_EXTRA + 1].size.x,
1357 PIXELY(CELLS_DOWN - 1,
1358 CELL_HEIGHT / 2 - (int)sprites[SPRITE_EXTRA + 1].size.y / 2));
1359 }
1360 /*}}}*/
1361 /*{{{ apples*/
1362 {
1363 unsigned ix;
1364
1365 for(ix = 2; ix--;)
1366 back_sprite(SPRITE_GHOST + ix, 0,
1367 PIXELX(10, CELL_WIDTH / 2), PIXELY((int)ix, 0));
1368 for(ix = 6; ix--;)
1369 back_sprite(SPRITE_APPLE + ix, 0,
1370 PIXELX(10, apple_sizes[ix].offset.x + CELL_WIDTH / 2),
1371 PIXELY((int)ix + 2, apple_sizes[ix].offset.y));
1372 }
1373 /*}}}*/
1374 /*{{{ backgrounds*/
1375 {
1376 unsigned ix;
1377
1378 color_set((int)0);
1379 for(ix = BACKGROUNDS * FILLS; ix--;)
1380 {
1381 XGCValues gcv;
1382
1383 gcv.fill_style = FillOpaqueStippled;
1384 gcv.background = data.mono != False ? display.white :
1385 colors[display.dynamic ? COLOR_DYNAMIC :
1386 backgrounds[ix % BACKGROUNDS][0]].pixel;
1387 gcv.stipple = fills[ix / BACKGROUNDS].mask;
1388 gcv.foreground = data.mono != False ?
1389 display.black : colors[display.dynamic ?
1390 COLOR_DYNAMIC + 1 : backgrounds[ix % BACKGROUNDS][1]].pixel;
1391 XChangeGC(display.display, GCN(GC_BOARD),
1392 GCFillStyle | GCForeground | GCBackground | GCStipple, &gcv);
1393 XFillRectangle(display.display, display.back, GCN(GC_BOARD),
1394 PIXELX(7, -(GAP_WIDTH / 2)),
1395 PIXELY((int)ix, -(GAP_HEIGHT / 2)),
1396 (CELL_WIDTH + GAP_WIDTH) * 3, CELL_HEIGHT + GAP_HEIGHT);
1397 XDrawRectangle(display.display, display.back, GCN(GC_SET),
1398 PIXELX(7, -(GAP_WIDTH / 2)),
1399 PIXELY((int)ix, -(GAP_HEIGHT / 2)),
1400 (CELL_WIDTH + GAP_WIDTH) * 3 - 1,
1401 CELL_HEIGHT + GAP_HEIGHT - 1);
1402 }
1403 for(ix = BACKGROUNDS * FILLS; ix--;)
1404 {
1405 back_sprite(SPRITE_APPLE, 1, PIXELX(8,
1406 (CELL_WIDTH + GAP_WIDTH) / 2), PIXELY((int)ix, 0));
1407 back_sprite(SPRITE_CHERRY, 1, PIXELX(8,
1408 -(CELL_WIDTH + GAP_WIDTH) / 2), PIXELY((int)ix, 0));
1409 }
1410 global.count = 1;
1411 }
1412 /*}}}*/
1413 /*{{{ spawn things*/
1414 {
1415 unsigned ix;
1416
1417 for(ix = 5; ix--;)
1418 spawn_monster(0, ix, 3, 3, (int)ix, CELLS_DOWN, 0, 0);
1419 spawn_monster(0, 4, 3, 7, 5, CELLS_DOWN, 0, 0)->pushing = 1;
1420 spawn_monster(0, 4, 3, 9, 6, CELLS_DOWN, 0, 0);
1421 happy = spawn_monster(0, 4, 3, 10, 7, CELLS_DOWN, 0, 0);
1422 spawn_monster(0, 3, 3, 6, 8, CELLS_DOWN, 0, 0)->chew = 1;
1423 spawn_monster(0, 6, 0, 0, 9, CELLS_DOWN, 0, 0);
1424 spawn_apple(10, CELLS_DOWN, CELL_WIDTH / 2, 0)->count = 0;
1425 }
1426 /*}}}*/
1427 XCopyArea(display.display, display.back, display.copy, GCN(GC_COPY),
1428 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
1429 refresh_window();
1430 timer_set(FRAME_RATE * 2, TIMING_OFF);
1431 }
1432 /*}}}*/
1433 if(global.pause || global.pressed & (1 << KEY_QUIT | 1 << KEY_KEYBOARD))
1434 /*{{{ end*/
1435 {
1436 assert(rts != (PROTOANIMATE((*)))NULL);
1437 then = (*rts)((PROTOVOID(*))NULL);
1438 }
1439 /*}}}*/
1440 else
1441 /*{{{ animate*/
1442 {
1443 MONSTER *mptr;
1444 unsigned count;
1445 unsigned dir;
1446
1447 dir = global.pressed & ~pressed & 0xF;
1448 if(dir)
1449 dir = choose_direction(dir) | 4;
1450 /*{{{ throw?*/
1451 if(global.throw == 1)
1452 {
1453 global.throw = 2;
1454 /*{{{ new backgrounds*/
1455 {
1456 if(global.count == BACKGROUNDS)
1457 {
1458 color_set(BACKGROUND_DRONES);
1459 global.count = 0;
1460 }
1461 else
1462 color_set((int)global.count++ % BACKGROUNDS);
1463 }
1464 /*}}}*/
1465 if(happy->type == 4)
1466 {
1467 unsigned ix;
1468
1469 for(ix = 8; ix--;)
1470 if(monster.list[0].face == player_dies[ix])
1471 break;
1472 happy->type = SPRITE_PLAYER_DEAD + ix * 2;
1473 }
1474 else
1475 happy->type = 4;
1476 /*{{{ xtra*/
1477 {
1478 unsigned ix;
1479 unsigned got;
1480
1481 got = extra.got;
1482 extra.got = happy->type != 4 ? 0x1F : 0;
1483 for(ix = 10; ix--;)
1484 {
1485 if(ix & 1)
1486 create_xtra_monster(ix >> 1);
1487 back_sprite(SPRITE_XTRA + ix, 1,
1488 PIXELX(2, 0), PIXELY((int)ix, 0));
1489 }
1490 add_background(PIXELX(2, 0), PIXELY(0, 0),
1491 CELL_WIDTH, CELL_HEIGHT * 10 + GAP_HEIGHT * 9);
1492 extra.got = got;
1493 create_xtra_monster(extra.select);
1494 }
1495 /*}}}*/
1496 }
1497 /*}}}*/
1498 for(mptr = monster.list, count = monster.monsters; count--; mptr++)
1499 {
1500 /*{{{ new face?*/
1501 if(dir)
1502 {
1503 if(mptr->type < 5 && mptr->face < 10)
1504 {
1505 if(mptr->face < 6 || dir >= 6)
1506 {
1507 unsigned shift;
1508
1509 shift = mptr->face > 7;
1510 mptr->dir = dir & 0x3;
1511 new_face(mptr);
1512 if(shift)
1513 mptr->face += 6;
1514 if(mptr->type == 2)
1515 {
1516 extra.select++;
1517 if(extra.select == 5)
1518 {
1519 extra.select = 0;
1520 extra.got ^= 0x1F;
1521 }
1522 create_xtra_monster(extra.select);
1523 }
1524 }
1525 }
1526 else if(happy->type != 4)
1527 {
1528 unsigned ix;
1529
1530 for(ix = 8; ix--;)
1531 if(monster.list[0].face == player_dies[ix])
1532 break;
1533 happy->type = SPRITE_PLAYER_DEAD + ix * 2;
1534 }
1535 }
1536 /*}}}*/
1537 if(!mptr->cycle)
1538 /*{{{ next image*/
1539 {
1540 mptr->cycle = mptr->type == 6 ? DIAMOND_CYCLES : MONSTER_CYCLES;
1541 mptr->image++;
1542 if(mptr->image == (mptr->type != 6 ?
1543 MONSTER_IMAGES : DIAMOND_IMAGES))
1544 mptr->image = 0;
1545 if(mptr == happy && mptr->type != 4)
1546 {
1547 mptr->type++;
1548 if(mptr->type == SPRITE_PLAYER_DEAD + 8)
1549 mptr->type = SPRITE_PLAYER_DEAD;
1550 else if(mptr->type == SPRITE_PLAYER_DEAD + 16)
1551 mptr->type = SPRITE_PLAYER_DEAD + 8;
1552 }
1553 }
1554 /*}}}*/
1555 mptr->cycle--;
1556 }
1557 if(!apple.list[0].count)
1558 {
1559 apple.list[0].count = MONSTER_CYCLES;
1560 apple.list[0].state++;
1561 if(apple.list[0].state == 6)
1562 apple.list[0].state = 0;
1563 }
1564 apple.list[0].count--;
1565 pressed = global.pressed;
1566 }
1567 /*}}}*/
1568 return then;
1569 }
1570 /*}}}*/
1571 /*{{{ void back_mris(shell)*/
1572 static VOIDFUNC back_mris
1573 FUNCARG((shell),
1574 unsigned shell
1575 )
1576 /*
1577 * Stuff a M R I or S sprite onto the background
1578 */
1579 {
1580 unsigned ix;
1581
1582 for(ix = 4; ix--;)
1583 back_sprite(SPRITE_MRIS + ix + 4 * !!((1 << ix) & shell), 0,
1584 PIXELX(4 + (int)ix, 0), PIXELY(-1, 0));
1585 return;
1586 }
1587 /*}}}*/
1588 /*{{{ void back_score(sptr, ix. split)*/
1589 static VOIDFUNC back_score
1590 FUNCARG((sptr, ix, split),
1591 HIGH_SCORE CONST *sptr
1592 ARGSEP unsigned ix
1593 ARGSEP unsigned split
1594 )
1595 /*
1596 * stuff a score onto the background
1597 */
1598 {
1599 size_t length;
1600 char string[SCORING_LEN];
1601 char *name;
1602 int y;
1603 int height;
1604
1605 sprintf(string, "%8lu %3u %3u\'%02u\"",
1606 sptr->score, sptr->screen, sptr->elapsed / 60, sptr->elapsed % 60);
1607 assert(strlen(string) == SCORING_LEN - 1);
1608 height = font.digitup + font.digitdown + font.ascent + font.descent;
1609 y = PIXELY((int)ix, CELL_HEIGHT / 2);
1610 XDrawString(display.display, display.back, GCN(GC_TEXT),
1611 PIXELX(0, 0), y + (split ? height > CELL_HEIGHT + GAP_HEIGHT - 2 ?
1612 font.digitup - (CELL_HEIGHT + GAP_HEIGHT) / 2 + 1 :
1613 font.digitup - height / 2 :
1614 font.center), string, SCORING_LEN - 1);
1615 name = (char *)sptr->name;
1616 length = strlen(name);
1617 /*{{{ compress name for this user?*/
1618 if(ix == CELLS_DOWN && (length + SCORING_LEN) * font.width >
1619 PIXELX(CELLS_ACROSS, -GAP_WIDTH) - PIXELX(0, 0) && scoring.alternate)
1620 {
1621 char *ptr;
1622
1623 ptr = name;
1624 while(*ptr)
1625 {
1626 for(name = ptr; *name == ' '; name++)
1627 /* EMPTY */;
1628 ptr = strchr(name, ' ');
1629 if(!ptr)
1630 break;
1631 if(ptr[-1] != '.' && ptr > name + 1)
1632 break;
1633 }
1634 length = ptr ? ptr - name : strlen(name);
1635 if(!length)
1636 {
1637 name = scoring.alternate;
1638 length = strlen(name);
1639 }
1640 }
1641 /*}}}*/
1642 XDrawString(display.display, display.back, GCN(GC_TEXT),
1643 split ? PIXELX(CELLS_ACROSS, -GAP_WIDTH) - (length + 2) * font.width :
1644 PIXELX(0, 0) + SCORING_LEN * font.width,
1645 y + (split ? height > CELL_HEIGHT + GAP_HEIGHT - 2 ?
1646 (CELL_HEIGHT + GAP_HEIGHT) / 2 - 1 - font.descent :
1647 height / 2 - font.descent : font.center),
1648 name, (int)length);
1649 return;
1650 }
1651 /*}}}*/
1652 /*{{{ int back_title(tptr, ix)*/
1653 static int back_title
1654 FUNCARG((tptr, ix),
1655 TITLE CONST *tptr
1656 ARGSEP unsigned ix
1657 )
1658 /*
1659 * stuff a title line onto the background
1660 */
1661 {
1662 char buffer[65];
1663 size_t length;
1664
1665 /*{{{ insert key string?*/
1666 {
1667 char CONST *ptr;
1668
1669 ptr = strchr(tptr->text, '%');
1670 if(!ptr)
1671 strcpy(buffer, tptr->text);
1672 else
1673 sprintf(buffer, tptr->text, XKeysymToString(data.keysyms[tptr->ix]));
1674 }
1675 /*}}}*/
1676 length = strlen(buffer);
1677 if(length)
1678 {
1679 char CONST *ptr;
1680 unsigned shift;
1681
1682 ptr = strchr(buffer, '-');
1683 if(ptr)
1684 shift = (ptr - buffer + 1) * font.width;
1685 else
1686 shift = 0;
1687 XDrawImageString(display.display, display.back, GCN(GC_TEXT),
1688 CENTER_X - (int)(shift ? shift : length * font.width / 2),
1689 PIXELY((int)ix, CELL_HEIGHT / 2) + font.center,
1690 buffer, (int)length);
1691 }
1692 return length;
1693 }
1694 /*}}}*/
1695 /*{{{ void list_scores()*/
1696 extern VOIDFUNC list_scores FUNCARGVOID
1697 /*
1698 * lists the high score tables to stdout
1699 */
1700 {
1701 unsigned flag;
1702
1703 flag = 1;
1704 if(scoring.high[0].score)
1705 {
1706 printf("%s\n", table_names[0]);
1707 print_scores(scoring.high);
1708 flag = 0;
1709 }
1710 if(scoring.today[0].score)
1711 {
1712 printf("%s\n", table_names[1]);
1713 print_scores(scoring.today);
1714 flag = 0;
1715 }
1716 else if(!flag)
1717 printf("Everyone's busy today.\n");
1718 if(scoring.personal[0].score)
1719 {
1720 printf("%s'%s personal best\n", scoring.mine.name,
1721 scoring.mine.name[strlen(scoring.mine.name) - 1] == 's' ? "" : "s");
1722 print_scores(scoring.personal);
1723 flag = 0;
1724 }
1725 if(flag)
1726 printf("Nobody ever plays with me.\n");
1727 return;
1728 }
1729 /*}}}*/
1730 /*{{{ int move_demo(cycle)*/
1731 static int move_demo
1732 FUNCARG((cycle),
1733 unsigned cycle
1734 )
1735 /*
1736 * moves the monsters used in the demo screens
1737 * we take each monster with a non-zero offset, and move it
1738 * towards a zero offset (changing x first)
1739 * it might get blown up by the ball
1740 * returns the number of objects which moved
1741 */
1742 {
1743 MONSTER *mptr;
1744 unsigned i;
1745 unsigned moved;
1746
1747 moved = 0;
1748 for(mptr = monster.list, i = monster.monsters; i--; mptr++)
1749 {
1750 if(mptr->shot)
1751 mptr->type = 5;
1752 else
1753 {
1754 if(mptr->offset.x)
1755 /*{{{ left or right*/
1756 {
1757 int dir;
1758
1759 moved++;
1760 if(mptr->offset.x > 0)
1761 {
1762 dir = 2;
1763 mptr->offset.x -= VEL_X;
1764 mptr->pixel.x -= VEL_X;
1765 }
1766 else
1767 {
1768 dir = 3;
1769 mptr->offset.x += VEL_X;
1770 mptr->pixel.x += VEL_X;
1771 }
1772 if(dir != mptr->dir)
1773 {
1774 mptr->dir = dir;
1775 new_face(mptr);
1776 }
1777 }
1778 /*}}}*/
1779 else if(mptr->offset.y)
1780 /*{{{ up or down*/
1781 {
1782 int dir;
1783
1784 moved++;
1785 if(mptr->offset.y > 0)
1786 {
1787 dir = 0;
1788 mptr->offset.y -= VEL_Y;
1789 mptr->pixel.y -= VEL_Y;
1790 }
1791 else
1792 {
1793 dir = 1;
1794 mptr->offset.y += VEL_Y;
1795 mptr->pixel.y += VEL_Y;
1796 }
1797 if(dir != mptr->dir)
1798 {
1799 mptr->dir = dir;
1800 new_face(mptr);
1801 }
1802 }
1803 /*}}}*/
1804 else
1805 mptr->stop = 1;
1806 if(!mptr->stop || mptr->type == 6 || mptr->face == 10)
1807 {
1808 if(!mptr->cycle)
1809 {
1810 mptr->cycle = mptr->type == 6 ? DIAMOND_CYCLES : cycle;
1811 mptr->image++;
1812 if(mptr->image == (mptr->type == 6 ?
1813 DIAMOND_IMAGES : MONSTER_IMAGES))
1814 mptr->image = 0;
1815 }
1816 mptr->cycle--;
1817 }
1818 }
1819
1820 }
1821 return moved;
1822 }
1823 /*}}}*/
1824 /*{{{ void move_mris()*/
1825 static VOIDFUNC move_mris FUNCARGVOID
1826 /*
1827 * moves M R I S sprites around the board
1828 * towards the top
1829 */
1830 {
1831 unsigned i;
1832 MONSTER *mptr;
1833
1834 for(mptr = &monster.list[1], i = monster.monsters - 1; i--; mptr++)
1835 if(mptr->type == 6)
1836 /*{{{ diamond*/
1837 {
1838 if(!mptr->count--)
1839 {
1840 global.diamond = 2;
1841 mptr->type = 5;
1842 }
1843 if(mptr->count == DIAMOND_GHOSTING)
1844 mptr->ghosting = GHOSTING;
1845 if(!mptr->cycle)
1846 {
1847 mptr->cycle = mptr->type == 6 ? DIAMOND_CYCLES : MONSTER_CYCLES;
1848 mptr->image++;
1849 if(mptr->image == (mptr->type == 6 ?
1850 DIAMOND_IMAGES : MONSTER_IMAGES))
1851 mptr->image = 0;
1852 }
1853 mptr->cycle--;
1854 }
1855 /*}}}*/
1856 else
1857 /*{{{ letter*/
1858 {
1859 CELL *cptr;
1860 unsigned chosen;
1861
1862 assert(mptr->type >= SPRITE_MRIS && mptr->type < SPRITE_MRIS + 8);
1863 cptr = BOARDCELL(mptr->cell.x, mptr->cell.y);
1864 chosen = !(monster.den & 1 << ((mptr->type - SPRITE_MRIS) & 3));
1865 if(mptr->offset.x || mptr->offset.y)
1866 move_movable(mptr, cptr);
1867 else if(!mptr->cell.y && chosen &&
1868 mptr->cell.x == ((mptr->type - SPRITE_MRIS) & 3) + 4)
1869 {
1870 back_sprite(mptr->type, 0, mptr->old_pixel.x, mptr->old_pixel.y);
1871 mptr->type = 5;
1872 monster.normals--;
1873 }
1874 else
1875 {
1876 unsigned valid;
1877 unsigned temp;
1878
1879 valid = valid_directions(mptr, cptr);
1880 temp = valid & (0xF ^ (1 << (mptr->dir ^ 1)));
1881 if(temp)
1882 valid &= temp;
1883 if(chosen)
1884 {
1885 temp = run_home(mptr, cptr);
1886 if(temp & valid)
1887 valid &= temp;
1888 }
1889 else
1890 valid &= 0xF;
1891 mptr->dir = choose_direction(valid);
1892 move_movable(mptr, cptr);
1893 }
1894 }
1895 /*}}}*/
1896 return;
1897 }
1898 /*}}}*/
1899 /*{{{ void print_scores(sptr)*/
1900 static VOIDFUNC print_scores
1901 FUNCARG((sptr),
1902 HIGH_SCORE CONST *sptr
1903 )
1904 {
1905 unsigned count;
1906
1907 for(count = HIGH_SCORES; sptr->score && count--; sptr++)
1908 printf("%9lu %3u %3u\'%02u\" %s\n", sptr->score, sptr->screen,
1909 sptr->elapsed / 60, sptr->elapsed % 60, sptr->name);
1910 return;
1911 }
1912 /*}}}*/
1913