1 /* Copyright (C) 1993, 1992 Nathan Sidwell */
2 /* RCS $Id: monster.c,v 4.6 1993/12/10 11:52:23 nathan Stable $ */
3 #include "xmris.h"
4 /*{{{  MONSTER *extra_escape()*/
5 extern MONSTER *extra_escape FUNCARGVOID
6 /*
7  * remove the extra monster from the top, so it
8  * can run around
9  */
10 {
11   int       x;
12 
13   extra.escape = 1;
14   x = PIXELX(4, extra.select * XTRA_SPACING);
15   XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
16       x, PIXELY(-1, 0), CELL_WIDTH, CELL_HEIGHT);
17   add_background(x, PIXELY(-1, 0), CELL_WIDTH, CELL_HEIGHT);
18   x -= BORDER_LEFT + GAP_WIDTH;
19   return spawn_monster(1, 2, 1, 1, x / (CELL_WIDTH + GAP_WIDTH), 0,
20       x % (CELL_WIDTH + GAP_WIDTH), -CELL_HEIGHT);
21 }
22 /*}}}*/
23 /*{{{  void extra_dies()*/
24 extern VOIDFUNC extra_dies FUNCARGVOID
25 /*
26  * the extra monster has died,
27  * put it back at the top
28  * and maybe alter the state
29  */
30 {
31   unsigned  got;
32 
33   if(global.state == 2)
34     {
35       color_set(BACKGROUND_NORMAL);
36       global.state = 3;
37       monster.den = 0;
38       monster.delay = 0;
39     }
40   got = extra.got & (1 << extra.select);
41   extra.got |= 1 << extra.select;
42   extra.escape = 0;
43   extra.count = FRAMES_PER_SECOND - 1;
44   if(!got)
45     create_xtra_monster(extra.select);
46   draw_extra();
47   return;
48 }
49 /*}}}*/
50 /*{{{  void move_monsters()*/
51 extern VOIDFUNC move_monsters FUNCARGVOID
52 /*
53  * move all the monsters
54  */
55 {
56   MONSTER   *mptr;
57   unsigned  i;
58   unsigned  nearest;
59   unsigned  farthest;
60 
61   nearest = 255;
62   farthest = 0;
63   for(mptr = &monster.list[1], i = monster.monsters - 1; i--; mptr++)
64     {
65       if(mptr->type == 5 || mptr->squished)
66 	/* EMPTY */;
67       else if(mptr->shot || (mptr->type == 3 && global.state == 3))
68 	/*{{{  shot*/
69 	{
70 	  if(BOARDCELL(mptr->cell.x, mptr->cell.y)->distance ==
71 	      monster.nearest)
72 	    global.difficulty++;
73 	  if(mptr->type < 2)
74 	    monster.normals--;
75 	  else if(mptr->type < 4)
76 	    /*{{{  convert to apple*/
77 	    {
78 	      APPLE     *aptr;
79 
80 	      if(mptr->type == 2)
81 		extra_dies();
82 	      else if(mptr->type == 3)
83 		monster.drones--;
84 	      if(mptr->offset.x > (CELL_WIDTH + GAP_WIDTH) / 2)
85 		{
86 		  mptr->offset.x -= CELL_WIDTH + GAP_WIDTH;
87 		  mptr->cell.x++;
88 		}
89 	      else if(mptr->offset.x < -(CELL_WIDTH + GAP_WIDTH) / 2)
90 		{
91 		  mptr->offset.x += CELL_WIDTH + GAP_WIDTH;
92 		  mptr->cell.x--;
93 		}
94 	      aptr = spawn_apple(mptr->cell.x, mptr->cell.y,
95 		  mptr->offset.x, mptr->offset.y);
96 	      if(mptr->type == 3 && global.state == 3)
97 		aptr->ghost = APPLE_GHOST_DELAY;
98 	    }
99 	    /*}}}*/
100 	  else
101 	    global.diamond = 2;
102 	  if(mptr->shot)
103 	    add_score(mptr->type == 6 ?
104 		(unsigned)(8000 / SCORE_ROUND) : (unsigned)(500 / SCORE_ROUND),
105 		mptr->pixel.x + CELL_WIDTH / 2,
106 		mptr->pixel.y + CELL_HEIGHT / 2);
107 	  mptr->type = 5;
108 	}
109 	/*}}}*/
110       else if(mptr->type == 6)
111 	/*{{{  diamond*/
112 	{
113 	  /*{{{  new image?*/
114 	  if(!mptr->cycle)
115 	    {
116 	      mptr->cycle = DIAMOND_CYCLES;
117 	      mptr->image++;
118 	      if(mptr->image == DIAMOND_IMAGES)
119 		mptr->image = 0;
120 	    }
121 	  /*}}}*/
122 	  mptr->cycle--;
123 	  if(!mptr->count--)
124 	    {
125 	      global.diamond = 0;
126 	      mptr->type = 5;
127 	    }
128 	  if(mptr->count == DIAMOND_GHOSTING)
129 	    mptr->ghosting = GHOSTING;
130 	  if(!mptr->stop)
131 	    move_diamond(mptr, BOARDCELL(mptr->cell.x, mptr->cell.y));
132 	}
133 	/*}}}*/
134       else if(global.state != 4)
135 	{
136 	  CELL      *cptr;
137 
138 	  cptr = BOARDCELL(mptr->cell.x, mptr->cell.y);
139 	  assert(cptr->visit);
140 	  /*{{{  new image?*/
141 	  if(!mptr->cycle)
142 	    {
143 	      mptr->cycle = MONSTER_CYCLES;
144 	      mptr->image++;
145 	      if(mptr->image == MONSTER_IMAGES)
146 		mptr->image = 0;
147 	    }
148 	  /*}}}*/
149 	  if((!mptr->count || mptr->type & 2) && !mptr->pause)
150 	    mptr->cycle--;
151 	  /*{{{  set nearest and farthest*/
152 	  if(mptr->type & 2 || global.state != 2)
153 	    {
154 	      if(nearest > cptr->distance)
155 		nearest = cptr->distance;
156 	      if(farthest < cptr->distance)
157 		farthest = cptr->distance;
158 	    }
159 	  /*}}}*/
160 	  if(mptr->type & 2 && !mptr->chew)
161 	    /*{{{  xtra or drone*/
162 	    {
163 	      if(monster.delay && mptr->type == 2 && global.state == 2 &&
164 		  (mptr->cell.y || mptr->offset.y >= 0) &&
165 		  !(mptr->offset.x % VEL_X))
166 		/*{{{  give birth*/
167 		{
168 		  monster.delay--;
169 		  if(!monster.delay && monster.den)
170 		    {
171 		      monster.delay = XTRA_BIRTH_DELAY;
172 		      monster.den--;
173 		      spawn_monster(2, 3, mptr->dir, mptr->face,
174 			  mptr->cell.x, mptr->cell.y,
175 			  mptr->offset.x, mptr->offset.y);
176 		      monster.drones++;
177 		      i++;
178 		    }
179 		}
180 		/*}}}*/
181 	      else
182 		{
183 		  unsigned  valid;
184 
185 		  if(extra.escape == 2 && mptr->pixel.y < PIXELY(0, 0))
186 		    valid = mptr->offset.y == -(CELL_HEIGHT + GAP_HEIGHT) ?
187 			0x0 : 0x1;
188 		  else
189 		    {
190 		      valid = valid_directions(mptr, cptr);
191 		      /*{{{  near an apple?*/
192 		      {
193 			unsigned  i;
194 			APPLE     *aptr;
195 			int       x, y;
196 
197 			x = mptr->pixel.x;
198 			y = mptr->pixel.y;
199 			for(aptr = apple.list, i = apple.apples; i--; aptr++)
200 			  {
201 			    if(!aptr->monsters && aptr->state < 3 &&
202 				!aptr->chewed &&
203 				INRANGE(aptr->pixel.x - x, VEL_X - CELL_WIDTH,
204 				    CELL_WIDTH - VEL_X + 1) &&
205 				INRANGE(aptr->pixel.y - y, VEL_Y - CELL_HEIGHT,
206 				    CELL_HEIGHT - VEL_Y + 1))
207 			      {
208 				if((mptr->dir & 2 ?
209 				    INRANGE(aptr->pixel.x - x,
210 				      -VEL_X, VEL_X + 1) :
211 				    INRANGE(aptr->pixel.x - x,
212 				      -VEL_X * 3, VEL_X * 3 + 1)) &&
213 				    INRANGE(aptr->pixel.y - y, -VEL_Y,
214 				      aptr->state == 2 ?
215 					VEL_Y + APPLE_VEL_Y + 1 : VEL_Y + 1) &&
216 				    (aptr->state < 2 || !mptr->dir ||
217 				    aptr->pixel.y >= y))
218 				  {
219 				    mptr->chew = 1;
220 				    aptr->chewed = 1;
221 				  }
222 				else
223 				  {
224 				    if(mptr->dir != 0 && valid & 0x2 &&
225 					aptr->pixel.y > y)
226 				      valid = 0x2;
227 				    else if(mptr->dir != 1 && valid & 0x1 &&
228 					aptr->pixel.y < y)
229 				      valid = 0x1;
230 				    else if(mptr->dir != 2 && valid & 0x8 &&
231 					aptr->pixel.x > x)
232 				      valid = 0x8;
233 				    else if(mptr->dir != 3 && valid & 0x4 &&
234 					aptr->pixel.x < x)
235 				      valid = 0x4;
236 				  }
237 				break;
238 			      }
239 			  }
240 		      }
241 		      /*}}}*/
242 		    }
243 		  if(mptr->offset.x || mptr->offset.y)
244 		    /* EMPTY */;
245 		  else if(extra.escape == 2)
246 		    mptr->fast = 0;
247 		  else if(global.state == 3)
248 		    mptr->fast = 1;
249 		  if(!valid)
250 		    {
251 		      mptr->type = 5;
252 		      extra.escape = 0;
253 		      extra.count = FRAMES_PER_SECOND - 1;
254 		      draw_extra();
255 		    }
256 		  else
257 		    /*{{{  pick a direction*/
258 		    {
259 		      unsigned  temp;
260 
261 		      if(player.ball.state == 1 && cptr->ball)
262 			/*{{{  run away*/
263 			{
264 			  temp = 0;
265 			  if(mptr->offset.y <= 0 &&
266 			      cptr[-CELL_STRIDE].ball < cptr->ball)
267 			    temp |= 0x1;
268 			  if(mptr->offset.y >= 0 &&
269 			      cptr[CELL_STRIDE].ball < cptr->ball)
270 			    temp |= 0x2;
271 			  if(mptr->offset.x <= 0 &&
272 			      cptr[-1].ball < cptr->ball)
273 			    temp |= 0x4;
274 			  if(mptr->offset.x >= 0 &&
275 			      cptr[1].ball < cptr->ball)
276 			    temp |= 0x8;
277 			  if(temp & valid)
278 			    valid &= temp | (temp << 4);
279 			}
280 			/*}}}*/
281 		      temp = valid & (~(0x11 << (mptr->dir ^ 1)));
282 		      if(mptr->type == 2)
283 			/*{{{  xtra*/
284 			{
285 			  if((player.ball.state != 0) != mptr->gomunch)
286 			    temp = valid;
287 			  if(player.ball.state ||
288 			      (global.state == 2 ?
289 				mptr->count == monster.farthest :
290 				mptr->count != monster.nearest))
291 			    /*{{{  go towards*/
292 			    {
293 			      if(extra.escape == 2)
294 				temp &= 0xF;
295 			      if(temp & 0xF0)
296 				valid = temp >> 4;
297 			      else if(temp)
298 				valid = temp;
299 			      else
300 				valid &= 0xF;
301 			    }
302 			    /*}}}*/
303 			  else
304 			    /*{{{  run away*/
305 			    {
306 			      unsigned  suess;
307 
308 			      suess = (temp ^ (temp >> 4)) & 0xF;
309 			      if(suess)
310 				valid = suess;
311 			      else if(temp & 0xF)
312 				valid = temp & 0xF;
313 			      else
314 				valid &= 0xF;
315 			    }
316 			    /*}}}*/
317 			  if(extra.escape == 2)
318 			    {
319 			      if(mptr->pixel.y <= PIXELY(0, 0) &&
320 				  mptr->pixel.x ==
321 				  PIXELX(4, extra.select * XTRA_SPACING))
322 				valid = 0x1;
323 			      else
324 				/*{{{  run home*/
325 				{
326 				  unsigned  preferred;
327 
328 				  preferred = run_home(mptr, cptr);
329 				  if(preferred & valid)
330 				    valid &= preferred;
331 				}
332 				/*}}}*/
333 			    }
334 			}
335 			/*}}}*/
336 		      else
337 			/*{{{  drone*/
338 			{
339 			  if(temp & 0xF0)
340 			    valid = temp >> 4;
341 			  else if(temp)
342 			    valid = temp;
343 			  else if(valid & 0xF0)
344 			    valid >>= 4;
345 			  else
346 			    valid &= 0xF;
347 			}
348 			/*}}}*/
349 		      valid = choose_direction(valid);
350 		      if(mptr->pixel.y == PIXELY(0, 0) && mptr->dir == 1)
351 			{
352 			  int       x;
353 
354 			  x = (mptr->offset.x + (valid & 1 ? 0 : 3)) /
355 			      VEL_X * VEL_X;
356 			  mptr->pixel.x += x - mptr->offset.x;
357 			  mptr->offset.x = x;
358 			}
359 		      if(valid != mptr->dir)
360 			{
361 			  mptr->dir = valid;
362 			  new_face(mptr);
363 			}
364 		      mptr->count = cptr->distance;
365 		      cptr = move_movable(mptr, cptr);
366 		      if(mptr->pixel.y >= PIXELY(0, 0))
367 			/*{{{  walked into apple?*/
368 			{
369 			  unsigned  i;
370 			  APPLE     *aptr;
371 			  int       x, y;
372 			  int       width, height;
373 
374 			  x = mptr->pixel.x;
375 			  y = mptr->pixel.y;
376 			  /*{{{  set offset*/
377 			  switch(mptr->dir)
378 			  {
379 			    /*{{{  case 0:*/
380 			    case 0:
381 			    {
382 			      x -= VEL_X - 1;
383 			      y -= APPLE_VEL_Y - 1;
384 			      width = VEL_X * 2 - 1;
385 			      height = VEL_Y + APPLE_VEL_Y * 2 - 1;
386 			      break;
387 			    }
388 			    /*}}}*/
389 			    /*{{{  case 1:*/
390 			    case 1:
391 			    {
392 			      x -= VEL_X - 1;
393 			      width = VEL_X * 2 - 1;
394 			      height = VEL_Y + APPLE_VEL_Y;
395 			      break;
396 			    }
397 			    /*}}}*/
398 			    /*{{{  case 2:*/
399 			    case 2:
400 			    {
401 			      x -= VEL_X + APPLE_VEL_X - 1;
402 			      y -= VEL_Y - 1;
403 			      width = (VEL_X + APPLE_VEL_X) * 2 - 1;
404 			      height = APPLE_VEL_Y + VEL_Y;
405 			      break;
406 			    }
407 			    /*}}}*/
408 			    /*{{{  case 3:*/
409 			    case 3:
410 			    {
411 			      x -= VEL_X + APPLE_VEL_X - 1;
412 			      y -= VEL_Y - 1;
413 			      width = (VEL_X + APPLE_VEL_X) * 2 - 1;
414 			      height = APPLE_VEL_Y + VEL_Y;
415 			      break;
416 			    }
417 			    /*}}}*/
418 			    /*{{{  default: inhibit optimizer warning*/
419 			    default:
420 			    {
421 			      width = height = 0;
422 			      break;
423 			    }
424 			    /*}}}*/
425 			  }
426 			  /*}}}*/
427 			  for(aptr = apple.list, i = apple.apples; i--; aptr++)
428 			    {
429 			      if(aptr->state < 3 && !aptr->chewed &&
430 				  INRANGE(aptr->pixel.x - x, 0, width) &&
431 				  INRANGE(aptr->pixel.y - y, 0, height))
432 				{
433 				  if(aptr->monsters)
434 				    {
435 				      assert(aptr->list && aptr->state == 2);
436 				      mptr->list = aptr->list;
437 				      aptr->list = mptr;
438 				      squish_monster(mptr);
439 				      mptr->pixel.y +=
440 					CELL_HEIGHT - CELL_HEIGHT / 4;
441 				      aptr->monsters++;
442 				    }
443 				  else
444 				    {
445 				      mptr->chew = 1;
446 				      aptr->chewed = 1;
447 				    }
448 				  break;
449 				}
450 			    }
451 			}
452 			/*}}}*/
453 		      /*{{{  go home adjust*/
454 		      if(extra.escape == 2 && mptr->type == 2 &&
455 			  mptr->pixel.y == PIXELY(0, 0) && mptr->dir & 2)
456 			{
457 			  int       x;
458 
459 			  x = PIXELX(4, extra.select * XTRA_SPACING) -
460 			      mptr->pixel.x;
461 			  if(mptr->dir & 1 ? x < 0 && x > -VEL_X :
462 			      x > 0 && x < VEL_X)
463 			    {
464 			      mptr->pixel.x += x;
465 			      mptr->offset.x += x;
466 			    }
467 			}
468 		      /*}}}*/
469 		      mptr->gomunch = player.ball.state != 0;
470 		    }
471 		    /*}}}*/
472 		}
473 	    }
474 	    /*}}}*/
475 	  else
476 	    /*{{{  normal or muncher or chewing*/
477 	    {
478 	      if(global.state == 2 && !mptr->push && !mptr->chew)
479 		{
480 		  if(mptr->count)
481 		    mptr->count--;
482 		  else
483 		    mptr->count = 1;
484 		}
485 	      else
486 		{
487 		  unsigned  valid;
488 		  unsigned  pause;
489 
490 		  pause = 0;
491 		  if(mptr->chew == 1)
492 		    /*{{{  chewing*/
493 		    {
494 		      mptr->chew = 2;
495 		      mptr->count = CHOMP_DELAY;
496 		      mptr->image = 0;
497 		      mptr->cycle = MONSTER_CYCLES - 1;
498 		    }
499 		    /*}}}*/
500 		  valid = valid_directions(mptr, cptr);
501 		  if(mptr->type || mptr->cont)
502 		    /* EMPTY */;
503 		  else if((mptr->pause || mptr->stop) &&
504 		      chaotic() < (mptr->dir & 2 ? 0 : (0x10 << mptr->dir) &
505 		      valid ? global.screen / STOP_TOGGLE_CONT_NEAR :
506 		      STOP_TOGGLE_CONT_STOP) + STOP_TOGGLE_CONT_PEDESTAL +
507 		      global.screen / STOP_TOGGLE_CONT_SCREEN_SCALE)
508 		    /*{{{  set cont & turn round*/
509 		    {
510 		      mptr->cont = 1;
511 		      mptr->dir ^= 1;
512 		      new_face(mptr);
513 		    }
514 		    /*}}}*/
515 		  else if(!cptr->distance)
516 		    mptr->cont = 1;
517 		  if(mptr->push)
518 		    /*{{{  disable left or right*/
519 		    {
520 		      if(mptr->push < 0)
521 			valid &= 0x77;
522 		      else
523 			valid &= 0xBB;
524 		      if(mptr->offset.y >= 0)
525 			{
526 			  if(valid & 0x2)
527 			    {
528 			      valid &= ~0x11;
529 			      mptr->cont = 1;
530 			    }
531 			}
532 		      else
533 			{
534 			  if(valid & 0x1)
535 			    {
536 			      valid &= ~0x22;
537 			      mptr->cont = 1;
538 			    }
539 			}
540 		    }
541 		    /*}}}*/
542 		  else if(mptr->count)
543 		    pause = 1;
544 		  else if(mptr->pause || mptr->stop)
545 		    /*{{{  test go munch or turn round*/
546 		    {
547 		      if(!mptr->type)
548 			{
549 			  if(chaotic() < GO_MUNCH_PROB * global.difficulty)
550 			    mptr->gomunch = 1;
551 			}
552 		      else if(mptr->stop || chaotic() < PUSH_TURN_PROB)
553 			{
554 			  mptr->dir ^= 1;
555 			  new_face(mptr);
556 			  mptr->count = GO_MUNCH_DELAY;
557 			}
558 		      valid = 0;
559 		      mptr->stop = 0;
560 		      mptr->pause = 0;
561 		    }
562 		    /*}}}*/
563 		  else if(!mptr->offset.x && !mptr->offset.y)
564 		    /*{{{  intersection stuff*/
565 		    {
566 		      mptr->fast = !mptr->type && global.state == 3;
567 		      if(mptr->gomunch || (!cptr->distance &&
568 			  !(valid & 1 << mptr->dir) &&
569 			  chaotic() < TRAPPED_MUNCH_PROB))
570 			/*{{{  start munching?*/
571 			{
572 			  unsigned  temp;
573 
574 			  mptr->panic = 0;
575 			  temp = valid & 0xF;
576 			  if(!mptr->cell.y)
577 			    temp |= 0x1;
578 			  else if(mptr->cell.y == CELLS_DOWN - 1)
579 			    temp |= 0x2;
580 			  if(!mptr->cell.x)
581 			    temp |= 0x4;
582 			  else if(mptr->cell.x == CELLS_ACROSS - 1)
583 			    temp |= 0x8;
584 			  if(temp != 0xF)
585 			    {
586 			      mptr->type = 1;
587 			      mptr->count = GO_MUNCH_DELAY;
588 			      mptr->gomunch = 0;
589 			    }
590 			}
591 			/*}}}*/
592 		      else if(mptr->type)
593 			/*{{{  stop munching?*/
594 			{
595 			  int       temp;
596 
597 			  temp = valid & 0xF;
598 			  if((temp & -temp) != temp && (temp == 0xF ||
599 			      chaotic() >= MUNCH_CONT_PEDESTAL +
600 			      global.screen / MUNCH_CONT_SCREEN_SCALE))
601 			    {
602 			      mptr->type = 0;
603 			      mptr->count = STOP_MUNCH_DELAY;
604 			      valid = 0;
605 			    }
606 			}
607 			/*}}}*/
608 		      else if(!mptr->type)
609 			/*{{{  cont & gomunch stuff*/
610 			{
611 			  if(chaotic() < GO_MUNCH_PROB * global.difficulty)
612 			    mptr->gomunch = 1;
613 			  if(mptr->cont)
614 			    {
615 			      if(chaotic() < CLEAR_CONT_PEDESTAL +
616 				  global.visited / CLEAR_CONT_VISIT_SCALE +
617 				  (monster.den ? 0 : global.screen /
618 				  (monster.normals * monster.normals) *
619 				  CLEAR_CONT_MONSTER_SCALE))
620 				mptr->cont = 0;
621 			    }
622 			  else
623 			    {
624 			      if(chaotic() < SET_CONT_PEDESTAL)
625 				mptr->cont = 1;
626 			    }
627 			}
628 			/*}}}*/
629 		    }
630 		    /*}}}*/
631 		  if(mptr->count)
632 		    mptr->count--;
633 		  if(pause)
634 		    /* EMPTY */;
635 		  else if(mptr->type == 1)
636 		    /*{{{  move the muncher*/
637 		    {
638 		      if(mptr->offset.x || mptr->offset.y)
639 			/*{{{  carry on*/
640 			{
641 			  CELL      *nptr;
642 
643 			  nptr = move_muncher(mptr);
644 			  if(nptr)
645 			    {
646 			      cptr = nptr;
647 			      if(nptr->sprite == SPRITE_CHERRY)
648 				{
649 				  global.cherries--;
650 				  nptr->sprite = 0;
651 				}
652 			    }
653 			}
654 			/*}}}*/
655 		      else
656 			/*{{{  pick new direction*/
657 			{
658 			  int     temp;
659 
660 			  temp = ~valid & 0xF;
661 			  if(!mptr->cell.y)
662 			    temp &= 0xE;
663 			  else if(mptr->cell.y == CELLS_DOWN - 1)
664 			    temp &= 0xD;
665 			  if(!mptr->cell.x)
666 			    temp &= 0xB;
667 			  else if(mptr->cell.x == CELLS_ACROSS - 1)
668 			    temp &= 0x7;
669 			  if(!temp)
670 			    temp = valid & 0xF;
671 			  if(mptr->cell.x < monster.list[0].cell.x)
672 			    valid = 0x8;
673 			  else if(mptr->cell.x > monster.list[0].cell.x)
674 			    valid = 0x4;
675 			  else
676 			    valid = 0;
677 			  if(!(valid & temp))
678 			    {
679 			      if(mptr->pixel.y < monster.list[0].pixel.y)
680 				valid = 0x2;
681 			      else if(mptr->pixel.y > monster.list[0].pixel.y)
682 				valid = 0x1;
683 			      if(!(valid & temp))
684 				valid = temp;
685 			    }
686 			  assert(valid);
687 			  for(temp = 0; !(valid & 1); temp++)
688 			    valid >>= 1;
689 			  if(temp != mptr->dir)
690 			    {
691 			      mptr->dir = temp;
692 			      new_face(mptr);
693 			    }
694 			  move_muncher(mptr);
695 			}
696 			/*}}}*/
697 		    }
698 		    /*}}}*/
699 		  else if(valid)
700 		    /*{{{  pick a direction*/
701 		    {
702 		      unsigned  temp;
703 
704 		      if(mptr->chew)
705 			valid &= 0xF;
706 		      else if(mptr->panic)
707 			/*{{{  panic mode*/
708 			{
709 			  temp = valid & ((1 << (mptr->dir ^ 1)) ^ 0xF);
710 			  if(mptr->offset.x < VEL_X * 4 - CELL_WIDTH &&
711 			      valid & 0x8)
712 			    valid = 0x8;
713 			  else if(mptr->offset.x > CELL_WIDTH - VEL_X * 4 &&
714 			      valid & 0x4)
715 			    valid = 0x4;
716 			  else if(temp & 0xC)
717 			    valid = temp & 0xC;
718 			  else if(valid & 0xC)
719 			    valid &= 0xC;
720 			  else if(valid & 0x2 && (mptr->dir == 1 ||
721 			      mptr->offset.y < VEL_Y - CELL_HEIGHT / 2))
722 			    valid = 0x2;
723 			  else if(valid & 1)
724 			    valid = 0x1;
725 			  else
726 			    valid &= 0xF;
727 			  mptr->panic++;
728 			  if(mptr->panic > PANIC_COUNT)
729 			    mptr->panic = 0;
730 			}
731 			/*}}}*/
732 		      else
733 			{
734 			  if(player.ball.state == 1 && cptr->ball)
735 			    /*{{{  run away*/
736 			    {
737 			      temp = 0;
738 			      if(mptr->offset.y <= 0 &&
739 				  cptr[-CELL_STRIDE].ball < cptr->ball)
740 				temp |= 0x1;
741 			      if(mptr->offset.y >= 0 &&
742 				  cptr[CELL_STRIDE].ball < cptr->ball)
743 				temp |= 0x2;
744 			      if(mptr->offset.x <= 0 &&
745 				  cptr[-1].ball < cptr->ball)
746 				temp |= 0x4;
747 			      if(mptr->offset.x >= 0 &&
748 				  cptr[1].ball < cptr->ball)
749 				temp |= 0x8;
750 			      if(temp & valid)
751 				valid &= temp | (temp << 4);
752 			    }
753 			    /*}}}*/
754 			  if(mptr->cont || mptr->gomunch)
755 			    /*{{{  continue mode*/
756 			    {
757 			      valid &= 0xF;
758 			      temp = valid & ~(1 << (mptr->dir ^ 1));
759 			      if(temp)
760 				valid = temp;
761 			    }
762 			    /*}}}*/
763 			  else if(valid & 0xF0)
764 			    valid = valid & valid >> 4;
765 			}
766 		      valid = choose_direction(valid);
767 		      temp = mptr->dir;
768 		      if(valid != mptr->dir)
769 			{
770 			  mptr->dir = valid;
771 			  if(mptr->push && (valid ^ temp) != 1)
772 			    {
773 			      mptr->push = 0;
774 			      mptr->cont = 1;
775 			    }
776 			  new_face(mptr);
777 			}
778 		      if(!apple_stop(mptr, cptr))
779 			cptr = move_movable(mptr, cptr);
780 		      if(mptr->push)
781 			{
782 			  mptr->dir = temp;
783 			  mptr->push = 0;
784 			}
785 		    }
786 		    /*}}}*/
787 		  if(!mptr->count)
788 		    mptr->chew = 0;
789 		}
790 	    }
791 	    /*}}}*/
792 	}
793     }
794   monster.nearest = nearest;
795   monster.farthest = farthest;
796   return;
797 }
798 /*}}}*/
799 /*{{{  void new_xtra()*/
800 extern VOIDFUNC new_xtra FUNCARGVOID
801 /*
802  * increment the extra monster, and draw it up
803  */
804 {
805   static int direction = 1;
806 
807   draw_extra_letter(extra.select);
808   if(direction < 0 ? extra.select == 0 : extra.select == 4)
809     direction = -direction;
810   extra.select += direction;
811   create_xtra_monster(extra.select);
812   extra.count = extra.got & (1 << extra.select) ?
813       XTRA_GOT_DELAY - 1 : XTRA_NEW_DELAY - 1;
814   draw_extra();
815   return;
816 }
817 /*}}}*/
818 /*{{{  MONSTER *spawn_monster(insert, type, dir, face, cx, cy, ox, oy)*/
819 extern MONSTER *spawn_monster
820 FUNCARG((insert, type, dir, face, cx, cy, ox, oy),
821 	unsigned  insert /* where to insert in list (0 for end) */
822 ARGSEP  unsigned  type  /* type of monster 0-4 */
823 ARGSEP  unsigned  dir   /* direction 0-3 */
824 ARGSEP  unsigned  face  /* face 0-5 */
825 ARGSEP  int       cx    /* cell x */
826 ARGSEP  int       cy    /* cell y */
827 ARGSEP  int       ox    /* offset x */
828 ARGSEP  int       oy    /* offset y */
829 )
830 /*
831  * create a new monster onto the monster list
832  * if insert != 0 the insert the monster at that point on the
833  * list, adjusting the monster chains appropriately
834  * returns a pointer to the new monster
835  */
836 {
837   MONSTER   *mptr;
838 
839   assert(monster.monsters != MONSTERS);
840   assert((!(ox % VEL_X) || (oy < 0 && !cy)) && !(oy % VEL_Y));
841   if(insert)
842     {
843       MONSTER   *sptr;
844       APPLE     *aptr;
845       unsigned  ix;
846 
847       assert(insert <= monster.monsters);
848       mptr = &monster.list[insert];
849       for(sptr = &monster.list[monster.monsters]; sptr-- != mptr;)
850 	{
851 	  if(sptr->list && sptr->list >= mptr)
852 	    sptr->list++;
853 	  memcpy(sptr + 1, sptr, sizeof(MONSTER));
854 	}
855       for(aptr = apple.list, ix = apple.apples; ix--; aptr++)
856 	if(aptr->list && aptr->list >= mptr)
857 	  aptr->list++;
858       monster.monsters++;
859     }
860   else
861     mptr = &monster.list[monster.monsters++];
862   mptr->dir = dir;
863   mptr->type = type;
864   mptr->face = face;
865   mptr->push = 0;
866   mptr->gomunch = mptr->cont = mptr->chew = mptr->pause = mptr->stop = 0;
867   mptr->panic = mptr->shot = mptr->squished = 0;
868   mptr->fast = mptr->pushing = 0;
869   mptr->ghosting = 0;
870   mptr->count = 0;
871   mptr->cell.x = cx;
872   mptr->cell.y = cy;
873   mptr->offset.x = ox;
874   mptr->offset.y = oy;
875   mptr->old_pixel.x = mptr->pixel.x = PIXELX(cx, ox);
876   mptr->old_pixel.y = mptr->pixel.y = PIXELY(cy, oy);
877   mptr->list = NULL;
878   mptr->image = chaotic() % MONSTER_IMAGES;
879   mptr->cycle = chaotic() % MONSTER_CYCLES;
880   mptr->old_sprite = 0;
881   mptr->back = 0;
882   mptr->on = 1;
883   return mptr;
884 }
885 /*}}}*/
886