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