1 /* Copyright (C) 1993, 1992 Nathan Sidwell */
2 /* RCS $Id: move.c,v 4.3 1993/10/25 12:02:18 nathan Stable $ */
3 #include "xmris.h"
4 /*{{{  prototypes*/
5 static VOIDFUNC connect_hole PROTOARG((CELL *));
6 static int apple_hole PROTOARG((APPLE *, CELL *));
7 static VOIDFUNC munch_back
8     PROTOARG((int, int, unsigned, unsigned, int, int, SPRITE CONST *));
9 static VOIDFUNC munch_edge_above PROTOARG((CELL *, int, int, unsigned));
10 static VOIDFUNC munch_edge_below PROTOARG((CELL *, int, int, int, unsigned));
11 static VOIDFUNC munch_edge_left PROTOARG((CELL *, int, int, unsigned));
12 static VOIDFUNC munch_edge_right PROTOARG((CELL *, int, int, unsigned));
13 /*}}}*/
14 /*{{{  int apple_hole(aptr, cptr)*/
15 static int apple_hole
16 FUNCARG((aptr, cptr),
17 	APPLE   *aptr
18 ARGSEP  CELL    *cptr
19 )
20 /*
21  * An apple has fallen, this makes the hole it
22  * leaves behind in the hedge, but only in the board table
23  * remember to get it drawn too.
24  */
25 {
26   int     offset;
27 
28   offset = aptr->offset.x / VEL_X * VEL_X;
29   if(offset < 0)
30     {
31       int     max;
32 
33       max = cptr[0].depths[2] < cptr[CELL_STRIDE].depths[2] ?
34 	  cptr[CELL_STRIDE].depths[2] : cptr[0].depths[2];
35       if(max >= offset)
36 	offset = max + VEL_X;
37       if(offset < 0)
38 	{
39 	  assert(offset > -(CELL_WIDTH + GAP_WIDTH));
40 	  cptr[CELL_STRIDE].holes[0] = cptr[0].holes[2] = offset;
41 	  cptr[CELL_STRIDE - 1].holes[1] =
42 	      cptr[-1].holes[3] = CELL_WIDTH + GAP_WIDTH + offset;
43 	}
44       else
45 	offset = 0;
46     }
47   else if(offset > 0)
48     {
49       int     min;
50 
51       min = cptr[0].depths[3] > cptr[CELL_STRIDE].depths[3] ?
52 	  cptr[CELL_STRIDE].depths[3] : cptr[0].depths[3];
53       if(min <= offset)
54 	offset = min - VEL_X;
55       if(offset > 0)
56 	{
57 	  assert(offset < (CELL_WIDTH + GAP_WIDTH));
58 	  cptr[CELL_STRIDE].holes[1] = cptr[0].holes[3] = offset;
59 	  cptr[CELL_STRIDE + 1].holes[0] =
60 	      cptr[1].holes[2] = -(CELL_WIDTH + GAP_WIDTH) + offset;
61 	}
62       else
63 	offset = 0;
64     }
65   return offset;
66 }
67 /*}}}*/
68 /*{{{  void back_sprite(ix, flag, x, y)*/
69 extern VOIDFUNC back_sprite
70 FUNCARG((ix, flag, x, y),
71 	unsigned  ix
72 ARGSEP  unsigned  flag
73 ARGSEP  int       x
74 ARGSEP  int       y
75 )
76 /*
77  * copy a sprite onto the background
78  */
79 {
80   SPRITE CONST *sptr;
81 
82   sptr = &sprites[ix];
83   if(display.background != COLOUR_ZERO || flag)
84     XCopyArea(display.display, sptr->mask, display.back, GCN(GC_MASK),
85 	0, 0, sptr->size.x, sptr->size.y, x, y);
86   XCopyArea(display.display, sptr->image, display.back, GCN(GC_OR),
87       0, 0, sptr->size.x, sptr->size.y, x, y);
88   return;
89 }
90 /*}}}*/
91 /*{{{  unsigned choose_direction(valid)*/
92 extern unsigned choose_direction
93 FUNCARG((valid),
94 	unsigned  valid
95 )
96 /*
97  * pick a direction at chaotic from the valid ones
98  */
99 {
100   unsigned  choices;
101   unsigned  temp;
102   unsigned  choice;
103 
104   assert(valid && !(valid & ~0xF));
105   for(choices = 0, temp = valid; temp; choices++)
106     temp ^= temp & -temp;
107   if(choices == 1)
108     choice = 0;
109   else if(choices == 3)
110     choice = chaotic() % 3;
111   else
112     choice = chaotic() & (choices - 1);
113   do
114     {
115       temp = valid & -valid;
116       valid ^= temp;
117     }
118   while(choice--);
119   assert(temp);
120   for(valid = 0; !(temp & 1); valid++)
121     temp >>= 1;
122   return valid;
123 }
124 /*}}}*/
125 /*{{{  void connect_hole(cptr)*/
126 static VOIDFUNC connect_hole
127 FUNCARG((cptr),
128 	CELL      *cptr
129 )
130 /*
131  * connect the current cell up to its neighbours
132  */
133 {
134   cptr[0].visit = 1;
135   if(cptr[-CELL_STRIDE].depths[1] >= GAP_HEIGHT)
136     {
137       cptr[0].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
138       cptr[-CELL_STRIDE].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
139     }
140   if(cptr[CELL_STRIDE].depths[0] <= -GAP_HEIGHT)
141     {
142       cptr[0].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
143       cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
144     }
145   if(cptr[-1].depths[3] >= GAP_WIDTH)
146     {
147       cptr[0].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
148       cptr[-1].depths[3] = CELL_WIDTH + GAP_WIDTH;
149     }
150   if(cptr[1].depths[2] <= -GAP_WIDTH)
151     {
152       cptr[0].depths[3] = CELL_WIDTH + GAP_WIDTH;
153       cptr[1].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
154     }
155   return;
156 }
157 /*}}}*/
158 /*{{{  CELL *drop_apple(aptr, cptr)*/
159 extern CELL *drop_apple
160 FUNCARG((aptr, cptr),
161 	APPLE   *aptr
162 ARGSEP  CELL    *cptr
163 )
164 /*
165  * deals with apples which break through to the cell below
166  * the apple has already been moved to the new coordinate
167  * returns NULL if we stay in the same cell, or a pointer
168  * to the new cell
169  */
170 {
171   CELL      *new;
172   COORD     pixel;
173   CELL      *cherry;
174 
175   update.set = 0;
176   pixel.x = aptr->pixel.x - aptr->offset.x;
177   pixel.y = aptr->pixel.y - aptr->offset.y;
178   cherry = NULL;
179   if(aptr->offset.y <= cptr[0].depths[1])
180     {
181       if(INRANGE(aptr->offset.y, 1, APPLE_VEL_Y + 1))
182 	{
183 	  int     offset;
184 
185 	  offset = apple_hole(aptr, cptr);
186 	  if(offset)
187 	    munch_edge_below(cptr, pixel.x + offset, pixel.y, offset, 0);
188 	}
189       new = NULL;
190     }
191   else if(!cptr[0].visit)
192     {
193       aptr->offset.y -= CELL_HEIGHT + GAP_HEIGHT;
194       aptr->cell.y += 1;
195       pixel.y += CELL_HEIGHT + GAP_HEIGHT;
196       new = cptr + CELL_STRIDE;
197     }
198   else if(cptr[CELL_STRIDE].visit)
199     /*{{{  break through below*/
200     {
201       int   offset;
202 
203       offset = apple_hole(aptr, cptr);
204       /*{{{  munch*/
205       {
206 	munch_edge_below(cptr, pixel.x + offset, pixel.y,
207 	    offset, (unsigned)!offset);
208 	if(!offset && !cptr[0].depths[2])
209 	  {
210 	    munch_back(0, 0, EDGE_WIDTH >> 1, GAP_HEIGHT,
211 		pixel.x + (CELL_WIDTH >> 1) - (EDGE_WIDTH >> 1),
212 		pixel.y + CELL_HEIGHT - GAP_HEIGHT,
213 		&sprites[SPRITE_EDGE_BASE + 1]);
214 	    cherry = cptr;
215 	  }
216 	if(!offset && !cptr[0].depths[3])
217 	  {
218 	    munch_back(EDGE_WIDTH >> 1, 0,
219 		EDGE_WIDTH >> 1, GAP_HEIGHT,
220 		pixel.x + (CELL_WIDTH >> 1),
221 		pixel.y + CELL_HEIGHT - GAP_HEIGHT,
222 		&sprites[SPRITE_EDGE_BASE + 1]);
223 	    cherry = cptr;
224 	  }
225       }
226       /*}}}*/
227       aptr->offset.y -= CELL_HEIGHT + GAP_HEIGHT;
228       aptr->cell.y += 1;
229       pixel.y += CELL_HEIGHT + GAP_HEIGHT;
230       global.broken = 1;
231       new = cptr + CELL_STRIDE;
232       if(!offset)
233 	{
234 	  cptr[0].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
235 	  cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
236 	}
237     }
238     /*}}}*/
239   else if(cptr[0].depths[1] - cptr[CELL_STRIDE * 2].depths[0] >=
240       CELL_HEIGHT + GAP_HEIGHT)
241     /*{{{  breakthrough 2 below*/
242     {
243       aptr->offset.y -= CELL_HEIGHT + GAP_HEIGHT;
244       aptr->cell.y += 1;
245       pixel.y += CELL_HEIGHT + GAP_HEIGHT;
246       global.broken = 1;
247       cptr[CELL_STRIDE].visit = 1;
248       cptr[0].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
249       cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
250       cptr[CELL_STRIDE].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
251       cptr[CELL_STRIDE * 2].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
252       if(cptr[CELL_STRIDE - 1].depths[3])
253 	{
254 	  cptr[CELL_STRIDE].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
255 	  cptr[CELL_STRIDE - 1].depths[3] = CELL_WIDTH + GAP_WIDTH;
256 	}
257       if(cptr[CELL_STRIDE + 1].depths[2])
258 	{
259 	  cptr[CELL_STRIDE].depths[3] = CELL_WIDTH + GAP_WIDTH;
260 	  cptr[CELL_STRIDE + 1].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
261 	}
262       munch_hole(cptr + CELL_STRIDE, pixel.x, pixel.y);
263       cherry = new = cptr + CELL_STRIDE;
264     }
265     /*}}}*/
266   else
267     new = NULL;
268   if(cherry && cherry->sprite)
269     back_sprite(new->sprite, 0, pixel.x, pixel.y);
270   if(update.set)
271     {
272       bounding_box(aptr->pixel.x, aptr->pixel.y, CELL_WIDTH, CELL_HEIGHT);
273       bounding_box(aptr->old_pixel.x, aptr->old_pixel.y,
274 	CELL_WIDTH, CELL_HEIGHT);
275       aptr->back = 1;
276       add_background(update.tl.x, update.tl.y,
277 	  (unsigned)(update.br.x - update.tl.x),
278 	  (unsigned)(update.br.y - update.tl.y));
279     }
280   return new;
281 }
282 /*}}}*/
283 /*{{{  CELL *move_diamond(mptr, cptr)*/
284 extern CELL *move_diamond
285 FUNCARG((mptr, cptr),
286 	MONSTER   *mptr
287 ARGSEP  CELL      *cptr
288 )
289 /*
290  * move the diamond downwards, munching away as we go
291  */
292 {
293   CELL      *nptr;
294 
295   assert(!mptr->offset.x && mptr->dir == 1);
296   nptr = NULL;
297   if(mptr->offset.y)
298     /* EMPTY */;
299   else if(cptr[CELL_STRIDE].visit || cptr[CELL_STRIDE].sprite ||
300       mptr->cell.y == CELLS_DOWN - 1)
301     mptr->stop = 1;
302   else
303     {
304       APPLE     *aptr;
305       unsigned  count;
306 
307       for(aptr = apple.list, count = apple.apples; count--; aptr++)
308 	if(aptr->state < 3 && aptr->cell.y == mptr->cell.y + 1 &&
309 	    INRANGE(aptr->pixel.x - mptr->pixel.x,
310 	      VEL_X - CELL_WIDTH + 1, CELL_WIDTH - VEL_X))
311 	  {
312 	    mptr->stop = 1;
313 	    break;
314 	  }
315     }
316   if(!mptr->stop)
317     {
318       mptr->offset.y += DIAMOND_VEL_Y;
319       mptr->pixel.y += DIAMOND_VEL_Y;
320       update.set = 0;
321       cptr[0].depths[1] = mptr->offset.y;
322       if(mptr->offset.y < CELL_HEIGHT + GAP_HEIGHT)
323 	{
324 	  munch_back(0, CELL_HEIGHT - DIAMOND_VEL_Y - INTERNAL_HEIGHT,
325 	      EDGE_WIDTH, DIAMOND_VEL_Y + INTERNAL_HEIGHT,
326 	      mptr->pixel.x, mptr->pixel.y + CELL_HEIGHT -
327 	      DIAMOND_VEL_Y - INTERNAL_HEIGHT, &sprites[SPRITE_CENTER_BASE]);
328 	}
329       else
330 	{
331 	  nptr = cptr + CELL_STRIDE;
332 	  nptr[0].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
333 	  connect_hole(nptr);
334 	  munch_hole(nptr, mptr->pixel.x, mptr->pixel.y);
335 	  if(nptr->sprite)
336 	    back_sprite(nptr->sprite, 0, mptr->pixel.x, mptr->pixel.y);
337 	  mptr->offset.y = 0;
338 	  mptr->cell.y += 1;
339 	  global.broken = 1;
340 	}
341       assert(update.set);
342       bounding_box(mptr->pixel.x, mptr->pixel.y, CELL_WIDTH, CELL_HEIGHT);
343       bounding_box(mptr->old_pixel.x, mptr->old_pixel.y,
344 	  CELL_WIDTH, CELL_HEIGHT);
345       add_background(update.tl.x, update.tl.y,
346 	  (unsigned)(update.br.x - update.tl.x),
347 	  (unsigned)(update.br.y - update.tl.y));
348     }
349   return nptr;
350 }
351 /*}}}*/
352 /*{{{  CELL *move_movable(mptr, cptr)*/
353 extern CELL *move_movable
354 FUNCARG((mptr, cptr),
355 	MONSTER   *mptr
356 ARGSEP  CELL      *cptr
357 )
358 /*
359  * move a non muncher down a path
360  * we should never leave known territory
361  */
362 {
363   unsigned  delta;
364 
365   switch(mptr->dir)
366   {
367     /*{{{  case 0: (up)*/
368     case 0:
369       if(!mptr->fast)
370 	delta = VEL_Y;
371       else if(mptr->offset.y >
372 	  (CELL_HEIGHT + GAP_HEIGHT) - (FAST_STEPS * VEL_Y_FAST))
373 	delta = VEL_Y_FAST;
374       else if(mptr->offset.y > (VEL_Y_FAST * FAST_STEPS))
375 	delta = VEL_Y;
376       else if(mptr->offset.y > -(VEL_Y_FAST * FAST_STEPS))
377 	delta = VEL_Y_FAST;
378       else if(mptr->offset.y >
379 	  (VEL_Y_FAST * FAST_STEPS) - (CELL_HEIGHT + GAP_HEIGHT))
380 	delta = VEL_Y;
381       else
382 	delta = VEL_Y_FAST;
383       mptr->pixel.y -= delta;
384       mptr->offset.y -= delta;
385       if(mptr->offset.y == -(CELL_HEIGHT + GAP_HEIGHT) && mptr->cell.y)
386 	{
387 	  mptr->offset.y = 0;
388 	  mptr->cell.y -= 1;
389 	  cptr -= CELL_STRIDE;
390 	  if(!cptr->visit)
391 	    {
392 	      if(mptr->offset.x < 0)
393 		{
394 		  mptr->offset.x += CELL_WIDTH + GAP_WIDTH;
395 		  mptr->cell.x -= 1;
396 		  cptr -= 1;
397 		}
398 	      else if(mptr->offset.x > 0)
399 		{
400 		  mptr->offset.x -= CELL_WIDTH + GAP_WIDTH;
401 		  mptr->cell.x += 1;
402 		  cptr += 1;
403 		}
404 	      else
405 		{
406 		  mptr->offset.y = -(CELL_HEIGHT + GAP_HEIGHT);
407 		  mptr->cell.y += 1;
408 		  cptr += CELL_STRIDE;
409 		}
410 	    }
411 	}
412       assert(cptr->visit && mptr->cell.y >= 0);
413       assert(mptr->offset.y >= -(CELL_HEIGHT + GAP_HEIGHT));
414       break;
415     /*}}}*/
416     /*{{{  case 1: (down)*/
417     case 1:
418       if(!mptr->fast)
419 	delta = VEL_Y;
420       else if(mptr->offset.y <
421 	  (FAST_STEPS * VEL_Y_FAST) - (CELL_HEIGHT + GAP_HEIGHT))
422 	delta = VEL_Y_FAST;
423       else if(mptr->offset.y < -(VEL_Y_FAST * FAST_STEPS))
424 	delta = VEL_Y;
425       else if(mptr->offset.y < (VEL_Y_FAST * FAST_STEPS))
426 	delta = VEL_Y_FAST;
427       else if(mptr->offset.y <
428 	  (CELL_HEIGHT + GAP_HEIGHT) - (VEL_Y_FAST * FAST_STEPS))
429 	delta = VEL_Y;
430       else
431 	delta = VEL_Y_FAST;
432       mptr->pixel.y += delta;
433       mptr->offset.y += delta;
434       if(mptr->offset.y == CELL_HEIGHT + GAP_HEIGHT)
435 	{
436 	  mptr->offset.y = 0;
437 	  mptr->cell.y += 1;
438 	  cptr += CELL_STRIDE;
439 	  if(!cptr->visit)
440 	    {
441 	      if(mptr->offset.x < 0)
442 		{
443 		  mptr->offset.x += CELL_WIDTH + GAP_WIDTH;
444 		  mptr->cell.x -= 1;
445 		  cptr -= 1;
446 		}
447 	      else if(mptr->offset.x > 0)
448 		{
449 		  mptr->offset.x -= CELL_WIDTH + GAP_WIDTH;
450 		  mptr->cell.x += 1;
451 		  cptr += 1;
452 		}
453 	      else
454 		{
455 		  mptr->offset.y = CELL_HEIGHT + GAP_HEIGHT;
456 		  mptr->cell.y -= 1;
457 		  cptr -= CELL_STRIDE;
458 		}
459 	    }
460 	}
461       assert(cptr->visit && mptr->cell.y < CELLS_DOWN);
462       assert(mptr->offset.y <= CELL_HEIGHT + GAP_HEIGHT);
463       break;
464     /*}}}*/
465     /*{{{  case 2: (left)*/
466     case 2:
467       if(!mptr->fast)
468 	delta = VEL_X;
469       else if(mptr->offset.x >
470 	  (CELL_WIDTH + GAP_WIDTH) - (VEL_X_FAST * FAST_STEPS))
471 	delta = VEL_X_FAST;
472       else if(mptr->offset.x > (VEL_X_FAST * FAST_STEPS))
473 	delta = VEL_X;
474       else if(mptr->offset.x > -(VEL_X_FAST * FAST_STEPS))
475 	delta = VEL_X_FAST;
476       else if(mptr->offset.x >
477 	  (FAST_STEPS * VEL_X_FAST) - (CELL_WIDTH + GAP_WIDTH))
478 	delta = VEL_X;
479       else
480 	delta = VEL_X_FAST;
481       mptr->pixel.x -= delta;
482       mptr->offset.x -= delta;
483       if(mptr->offset.x == -(CELL_WIDTH + GAP_WIDTH) && cptr[-1].visit)
484 	{
485 	  mptr->offset.x = 0;
486 	  mptr->cell.x -= 1;
487 	  cptr -= 1;
488 	}
489       assert(cptr->visit && !mptr->offset.y && mptr->cell.x >= 0);
490       assert(mptr->offset.x >= -(CELL_WIDTH + GAP_WIDTH));
491       break;
492     /*}}}*/
493     /*{{{  case 3: (right)*/
494     case 3:
495       if(!mptr->fast)
496 	delta = VEL_X;
497       else if(mptr->offset.x <
498 	  (FAST_STEPS * VEL_X_FAST) - (CELL_WIDTH + GAP_WIDTH))
499 	delta = VEL_X_FAST;
500       else if(mptr->offset.x < -(VEL_X_FAST * FAST_STEPS))
501 	delta = VEL_X;
502       else if(mptr->offset.x < (VEL_X_FAST * FAST_STEPS))
503 	delta = VEL_X_FAST;
504       else if(mptr->offset.x <
505 	  (CELL_WIDTH + GAP_WIDTH) - (VEL_X_FAST * FAST_STEPS))
506 	delta = VEL_X;
507       else
508 	delta = VEL_X_FAST;
509       mptr->pixel.x += delta;
510       mptr->offset.x += delta;
511       if(mptr->offset.x == CELL_WIDTH + GAP_WIDTH && cptr[1].visit)
512 	{
513 	  mptr->offset.x = 0;
514 	  mptr->cell.x += 1;
515 	  cptr += 1;
516 	}
517       assert(cptr->visit && !mptr->offset.y && mptr->cell.x < CELLS_ACROSS);
518       assert(mptr->offset.x <= CELL_WIDTH + GAP_WIDTH);
519       break;
520     /*}}}*/
521   }
522   return cptr;
523 }
524 /*}}}*/
525 /*{{{  CELL *move_muncher(mptr)*/
526 extern CELL *move_muncher
527 FUNCARG((mptr),
528 	MONSTER   *mptr /* the object to move */
529 )
530 /*
531  * moves and munches the board for an object which can munch
532  * apple checking is performed here too
533  * (ie the man, or a munch monster)
534  * the board array is updated as required
535  * returns a pointer to the new cell, if we have arrived elsewhere
536  * or NULL if we stayed on the same cell
537  */
538 {
539   unsigned  broke;
540   CELL      *nptr;
541   CELL      *cherry;
542   CELL      *cptr;
543   COORD     pixel;
544   COORD     cell;
545   SPRITE    *sptr;
546   int       knocked;
547 
548   assert(!mptr->stop && !mptr->pause);
549   broke = 0;
550   nptr = NULL;
551   cherry = NULL;
552   update.set = 0;
553   cell.x = mptr->cell.x;
554   cell.y = mptr->cell.y;
555   cptr = BOARDCELL(cell.x, cell.y);
556   pixel.x = PIXELX(cell.x, 0);
557   pixel.y = PIXELY(cell.y, 0);
558   knocked = 0;
559   if(!apple_stop(mptr, cptr))
560     {
561       switch(mptr->dir)
562       {
563 	/*{{{  case 0: (up)*/
564 	case 0:
565 	  /*
566 	   * if the depth upwards is less than the future depth,
567 	   * then we have to do some munching
568 	   */
569 	  mptr->offset.y -= VEL_Y;
570 	  mptr->pixel.y = pixel.y + mptr->offset.y;
571 	  if(cptr[0].depths[0] > mptr->offset.y)
572 	    /*{{{  munch*/
573 	    {
574 	      cptr[0].depths[0] = mptr->offset.y;
575 	      sptr = &sprites[SPRITE_CENTER_BASE];
576 	      munch_back(0, 0, CELL_WIDTH, VEL_Y + INTERNAL_HEIGHT,
577 		  pixel.x, pixel.y + cptr->depths[0], sptr);
578 	      if(cptr->sprite && mptr->offset.y + VEL_Y + INTERNAL_HEIGHT > 0)
579 		back_sprite(cptr->sprite, 0, pixel.x, pixel.y);
580 	      if(mptr->offset.y < -GAP_HEIGHT)
581 		cherry = cptr - CELL_STRIDE;
582 	      if(INRANGE(mptr->offset.y,
583 		  1 - (GAP_HEIGHT + VEL_Y), 1 - GAP_HEIGHT) &&
584 		  cptr[-CELL_STRIDE].visit)
585 		{
586 		  cptr[0].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
587 		  cptr[-CELL_STRIDE].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
588 		  munch_edge_above(cptr, pixel.x, pixel.y, 1);
589 		  broke = 1;
590 		}
591 	      if(INRANGE(mptr->offset.y,
592 		  1 - (INTERNAL_HEIGHT + EXTERNAL_HEIGHT) - VEL_Y,
593 		  1 - (INTERNAL_HEIGHT + EXTERNAL_HEIGHT)))
594 		{
595 		  sptr = &sprites[SPRITE_EDGE_BASE + 1];
596 		  /*{{{  round top left corner?*/
597 		  if(cptr[0].depths[2] <= -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
598 		    munch_back((EDGE_WIDTH >> 1) - (CELL_WIDTH >> 1) -
599 			EXTERNAL_WIDTH, 3 * GAP_HEIGHT - EXTERNAL_HEIGHT,
600 			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
601 			pixel.x - EXTERNAL_WIDTH, pixel.y - EXTERNAL_HEIGHT,
602 			sptr);
603 		  /*}}}*/
604 		  /*{{{  round top right corner?*/
605 		  if(cptr[0].depths[3] >= (INTERNAL_WIDTH + EXTERNAL_WIDTH))
606 		    munch_back((EDGE_WIDTH >> 1) + (CELL_WIDTH >> 1),
607 			3 * GAP_HEIGHT - EXTERNAL_HEIGHT,
608 			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
609 			pixel.x + CELL_WIDTH,
610 			pixel.y - EXTERNAL_HEIGHT, sptr);
611 		  /*}}}*/
612 		}
613 	      /*{{{  knocked through?*/
614 	      /*
615 	       * have we bumped into any of the following ?
616 	       * path from 2 above me
617 	       * path from above left
618 	       * path from above right
619 	       */
620 	      if(!cptr[-CELL_STRIDE].visit &&
621 		  ((cptr[-CELL_STRIDE * 2].depths[1] - cptr[0].depths[0] >=
622 		  CELL_HEIGHT + GAP_HEIGHT * 2) ||
623 		  (cptr[-CELL_STRIDE - 1].depths[3] >= GAP_WIDTH &&
624 		    cptr[-CELL_STRIDE - 1].depths[3] - cptr[0].depths[0] >=
625 		    KNOCK_THROUGH) ||
626 		  (cptr[-CELL_STRIDE + 1].depths[2] <= -GAP_WIDTH &&
627 		    cptr[-CELL_STRIDE + 1].depths[2] + cptr[0].depths[0] <=
628 		    -KNOCK_THROUGH)))
629 		{
630 		  knocked = -CELL_STRIDE;
631 		  cell.y -= 1;
632 		}
633 	      /*}}}*/
634 	      if(cptr->depths[0] == -(CELL_HEIGHT + GAP_HEIGHT))
635 		{
636 		  cptr[-CELL_STRIDE].visit = 1;
637 		  cptr[-CELL_STRIDE].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
638 		  broke = 1;
639 		}
640 	    }
641 	    /*}}}*/
642 	  pixel.y -= CELL_HEIGHT + GAP_HEIGHT;
643 	  if(mptr->offset.y == -(CELL_HEIGHT + GAP_HEIGHT))
644 	    {
645 	      mptr->offset.y = 0;
646 	      mptr->cell.y--;
647 	      nptr = cptr - CELL_STRIDE;
648 	    }
649 	  break;
650 	/*}}}*/
651 	/*{{{  case 1: (down)*/
652 	case 1:
653 	{
654 	  mptr->offset.y += VEL_Y;
655 	  mptr->pixel.y = pixel.y + mptr->offset.y;
656 	  if(cptr->depths[1] < mptr->offset.y)
657 	    /*{{{  munch*/
658 	    {
659 	      cptr->depths[1] = mptr->offset.y;
660 	      sptr = &sprites[SPRITE_CENTER_BASE];
661 	      munch_back(0, CELL_HEIGHT - VEL_Y - INTERNAL_HEIGHT,
662 		  CELL_WIDTH, VEL_Y + INTERNAL_HEIGHT,
663 		  pixel.x, pixel.y + cptr[0].depths[1] +
664 		  CELL_HEIGHT - VEL_Y - INTERNAL_HEIGHT, sptr);
665 	      if(cptr->sprite && mptr->offset.y < VEL_Y + INTERNAL_HEIGHT)
666 		back_sprite(cptr->sprite, 0, pixel.x, pixel.y);
667 	      if(mptr->offset.y > GAP_HEIGHT)
668 		cherry = cptr + CELL_STRIDE;
669 	      if(INRANGE(mptr->offset.y, GAP_HEIGHT, GAP_HEIGHT + VEL_Y) &&
670 		  cptr[CELL_STRIDE].visit)
671 		{
672 		  cptr[0].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
673 		  cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
674 		  munch_edge_below(cptr, pixel.x, pixel.y, 0, 1);
675 		  broke = 1;
676 		}
677 	      if(INRANGE(mptr->offset.y, INTERNAL_HEIGHT + EXTERNAL_HEIGHT,
678 		  INTERNAL_HEIGHT + EXTERNAL_HEIGHT + VEL_Y))
679 		{
680 		  sptr = &sprites[SPRITE_EDGE_BASE + 1];
681 		  /*{{{  round bottom left corner?*/
682 		  if(cptr[0].depths[2] <= -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
683 		    munch_back((EDGE_WIDTH >> 1) - (CELL_WIDTH >> 1) -
684 			EXTERNAL_WIDTH, GAP_HEIGHT,
685 			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
686 			pixel.x - EXTERNAL_WIDTH, pixel.y + CELL_HEIGHT,
687 			sptr);
688 		  /*}}}*/
689 		  /*{{{  round bottom right corner?*/
690 		  if(cptr[0].depths[3] >= (INTERNAL_WIDTH + EXTERNAL_WIDTH))
691 		    munch_back((EDGE_WIDTH >> 1) + (CELL_WIDTH >> 1),
692 			GAP_HEIGHT,
693 			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
694 			pixel.x + CELL_WIDTH, pixel.y + CELL_HEIGHT, sptr);
695 		  /*}}}*/
696 		}
697 	      /*{{{  knocked through?*/
698 	      /*
699 	       * have we bumped into any of the following ?
700 	       * path from 2 below me
701 	       * path from below left
702 	       * path from below right
703 	       */
704 	      if(!cptr[CELL_STRIDE].visit &&
705 		  ((cptr[0].depths[1] - cptr[CELL_STRIDE * 2].depths[0] >=
706 		  CELL_HEIGHT + GAP_HEIGHT * 2) ||
707 		  (cptr[CELL_STRIDE - 1].depths[3] >= GAP_WIDTH &&
708 		    cptr[CELL_STRIDE - 1].depths[3] + cptr[0].depths[1] >=
709 		    KNOCK_THROUGH) ||
710 		  (cptr[CELL_STRIDE + 1].depths[2] <= -GAP_WIDTH &&
711 		    cptr[0].depths[1] - cptr[CELL_STRIDE + 1].depths[2] >=
712 		    KNOCK_THROUGH)))
713 		{
714 		  knocked = CELL_STRIDE;
715 		  cell.y += 1;
716 		}
717 	      /*}}}*/
718 	      if(cptr->depths[1] == (CELL_HEIGHT + GAP_HEIGHT))
719 		{
720 		  cptr[CELL_STRIDE].visit = 1;
721 		  cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
722 		  broke = 1;
723 		}
724 	    }
725 	    /*}}}*/
726 	  pixel.y += CELL_HEIGHT + GAP_HEIGHT;
727 	  if(mptr->offset.y == (CELL_HEIGHT + GAP_HEIGHT))
728 	    {
729 	      mptr->offset.y = 0;
730 	      mptr->cell.y++;
731 	      nptr = cptr + CELL_STRIDE;
732 	    }
733 	  break;
734 	}
735 	/*}}}*/
736 	/*{{{  case 2: (left)*/
737 	case 2:
738 	{
739 	  mptr->offset.x -= VEL_X;
740 	  mptr->pixel.x = pixel.x + mptr->offset.x;
741 	  if(cptr[0].depths[2] > mptr->offset.x)
742 	    /*{{{  munch*/
743 	    {
744 	      cptr[0].depths[2] = mptr->offset.x;
745 	      sptr = &sprites[SPRITE_CENTER_BASE];
746 	      munch_back(0, 0, VEL_X + INTERNAL_WIDTH, CELL_HEIGHT,
747 		  pixel.x + cptr[0].depths[2], pixel.y, sptr);
748 	      if(cptr->sprite && mptr->offset.x + VEL_X + INTERNAL_WIDTH > 0)
749 		back_sprite(cptr->sprite, 0, pixel.x, pixel.y);
750 	      if(mptr->offset.x < -GAP_WIDTH)
751 		cherry = cptr - 1;
752 	      if(INRANGE(mptr->offset.x, 1 - (GAP_WIDTH + VEL_X),
753 		  1 - GAP_WIDTH) && cptr[-1].visit)
754 		{
755 		  cptr[0].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
756 		  cptr[-1].depths[3] = CELL_WIDTH + GAP_WIDTH;
757 		  munch_edge_left(cptr, pixel.x, pixel.y, 1);
758 		  broke = 1;
759 		}
760 	      if(INRANGE(mptr->offset.x,
761 		  1 - (INTERNAL_WIDTH + EXTERNAL_WIDTH) - VEL_X,
762 		  1 - (INTERNAL_WIDTH + EXTERNAL_WIDTH)))
763 		{
764 		  sptr = &sprites[SPRITE_EDGE_BASE + 0];
765 		  /*{{{  round left top corner?*/
766 		  if(cptr[0].depths[0] <=
767 		      -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
768 		    munch_back(3 * GAP_WIDTH - EXTERNAL_WIDTH,
769 			(EDGE_HEIGHT >> 1) - (CELL_HEIGHT >> 1) -
770 			EXTERNAL_HEIGHT, EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
771 			pixel.x - EXTERNAL_WIDTH, pixel.y - EXTERNAL_HEIGHT,
772 			sptr);
773 		  /*}}}*/
774 		  /*{{{  round left bottom corner?*/
775 		  if(cptr[0].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
776 		    munch_back(3 * GAP_HEIGHT - EXTERNAL_WIDTH,
777 			(EDGE_HEIGHT >> 1) + (CELL_HEIGHT >> 1),
778 			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
779 			pixel.x - EXTERNAL_WIDTH,
780 			pixel.y + CELL_HEIGHT, sptr);
781 		  /*}}}*/
782 		}
783 	      /*{{{  knocked through?*/
784 	      /*
785 	       * have we bumped into any of the following ?
786 	       * path from 2 left me
787 	       * path from left above
788 	       * path from left below
789 	       */
790 	      if(!cptr[-1].visit &&
791 		  ((cptr[-2].depths[3] - cptr[0].depths[2] >=
792 		    CELL_WIDTH + GAP_WIDTH * 2) ||
793 		  (cptr[-CELL_STRIDE - 1].depths[1] >= GAP_HEIGHT &&
794 		    cptr[-CELL_STRIDE - 1].depths[1] - cptr[0].depths[2] >=
795 		    KNOCK_THROUGH) ||
796 		  (cptr[CELL_STRIDE - 1].depths[0] <= -GAP_HEIGHT &&
797 		    cptr[CELL_STRIDE - 1].depths[0] + cptr[0].depths[2] <=
798 		    -KNOCK_THROUGH)))
799 		{
800 		  knocked = -1;
801 		  cell.x -= 1;
802 		}
803 	      /*}}}*/
804 	      if(cptr->depths[2] == -(CELL_WIDTH + GAP_WIDTH))
805 		{
806 		  cptr[-1].visit = 1;
807 		  cptr[-1].depths[3] = CELL_WIDTH + GAP_WIDTH;
808 		  broke = 1;
809 		}
810 	    }
811 	    /*}}}*/
812 	  pixel.x -= CELL_WIDTH + GAP_WIDTH;
813 	  if(mptr->offset.x == -(CELL_WIDTH + GAP_WIDTH))
814 	    {
815 	      mptr->offset.x = 0;
816 	      mptr->cell.x--;
817 	      nptr = cptr - 1;
818 	    }
819 	  break;
820 	}
821 	/*}}}*/
822 	/*{{{  case 3: (right)*/
823 	case 3:
824 	{
825 	  mptr->offset.x += VEL_X;
826 	  mptr->pixel.x = pixel.x + mptr->offset.x;
827 	  if(cptr->depths[3] < mptr->offset.x)
828 	    /*{{{  munch*/
829 	    {
830 	      cptr->depths[3] = mptr->offset.x;
831 	      sptr = &sprites[SPRITE_CENTER_BASE];
832 	      munch_back(CELL_WIDTH - VEL_X - INTERNAL_WIDTH, 0,
833 		  VEL_X + INTERNAL_WIDTH, CELL_HEIGHT,
834 		  pixel.x + cptr->depths[3] +
835 		  CELL_WIDTH - VEL_X - INTERNAL_WIDTH, pixel.y, sptr);
836 	      if(cptr->sprite && mptr->offset.x < VEL_X + INTERNAL_WIDTH)
837 		back_sprite(cptr->sprite, 0, pixel.x, pixel.y);
838 	      if(mptr->offset.x > GAP_WIDTH)
839 		cherry = cptr + 1;
840 	      if(INRANGE(mptr->offset.x, GAP_WIDTH, GAP_WIDTH + VEL_X) &&
841 		  cptr[1].visit)
842 		{
843 		  cptr[0].depths[3] = CELL_WIDTH + GAP_WIDTH;
844 		  cptr[1].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
845 		  munch_edge_right(cptr, pixel.x, pixel.y, 1);
846 		  broke = 1;
847 		}
848 	      if(INRANGE(mptr->offset.x, INTERNAL_WIDTH + EXTERNAL_WIDTH,
849 		  INTERNAL_WIDTH + EXTERNAL_WIDTH + VEL_X))
850 		{
851 		  sptr = &sprites[SPRITE_EDGE_BASE + 0];
852 		  /*{{{  round right top corner?*/
853 		  if(cptr[0].depths[0] <=
854 		      -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
855 		    munch_back(GAP_HEIGHT, (EDGE_HEIGHT >> 1) -
856 			(CELL_HEIGHT >> 1) - EXTERNAL_HEIGHT,
857 			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
858 			pixel.x + CELL_WIDTH, pixel.y - EXTERNAL_HEIGHT,
859 			sptr);
860 		  /*}}}*/
861 		  /*{{{  round right bottom corner?*/
862 		  if(cptr[0].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
863 		    munch_back(GAP_WIDTH,
864 			(EDGE_HEIGHT >> 1) + (CELL_WIDTH >> 1),
865 			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
866 			pixel.x + CELL_WIDTH, pixel.y + CELL_HEIGHT, sptr);
867 		  /*}}}*/
868 		}
869 	      /*{{{  knocked through?*/
870 	      /*
871 	       * have we bumped into any of the following ?
872 	       * path from 2 right me
873 	       * path from right above
874 	       * path from right below
875 	       */
876 	      if(!cptr[1].visit && ((cptr[0].depths[3] - cptr[2].depths[2] >=
877 		  CELL_WIDTH + GAP_WIDTH * 2) ||
878 		  (cptr[-CELL_STRIDE + 1].depths[1] >= GAP_HEIGHT &&
879 		    cptr[-CELL_STRIDE + 1].depths[1] + cptr[0].depths[3] >=
880 		    KNOCK_THROUGH) ||
881 		  (cptr[CELL_STRIDE + 1].depths[0] <= -GAP_HEIGHT &&
882 		    cptr[0].depths[3] - cptr[CELL_STRIDE + 1].depths[0] >=
883 		    KNOCK_THROUGH)))
884 		{
885 		  knocked = 1;
886 		  cell.x += 1;
887 		}
888 	      /*}}}*/
889 	      if(cptr->depths[3] == (CELL_WIDTH + GAP_WIDTH))
890 		{
891 		  cptr[1].visit = 1;
892 		  cptr[1].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
893 		  broke = 1;
894 		}
895 	    }
896 	    /*}}}*/
897 	  pixel.x += CELL_WIDTH + GAP_WIDTH;
898 	  if(mptr->offset.x == (CELL_WIDTH + GAP_WIDTH))
899 	    {
900 	      mptr->offset.x = 0;
901 	      mptr->cell.x++;
902 	      nptr = cptr + 1;
903 	    }
904 	  break;
905 	}
906 	/*}}}*/
907       }
908     }
909   /*{{{  knocked through?*/
910   /*
911    * if we knocked through to an adjoining cell
912    * we clear out the specified cell and check all the corners
913    * note, cell has already been altered correctly
914    * we must also check to se if this launches an apple
915    */
916   if(knocked)
917     {
918       broke = 1;
919       assert(!cherry || cherry == cptr + knocked);
920       cherry = cptr += knocked;
921       connect_hole(cptr);
922       munch_hole(cptr, pixel.x, pixel.y);
923     }
924   /*}}}*/
925   /*{{{  redraw or blank prize?*/
926   if(nptr && nptr->sprite)
927     {
928       assert(!cherry || cherry == nptr);
929       if((mptr->type != 4 && nptr->sprite >= SPRITE_PRIZE_BASE) ||
930 	  (nptr->sprite != SPRITE_CHERRY && nptr->sprite < SPRITE_PRIZE_BASE))
931 	back_sprite(nptr->sprite, 0, pixel.x, pixel.y);
932       else
933 	munch_back(0, 0, CELL_WIDTH, CELL_HEIGHT,
934 	    pixel.x, pixel.y, &sprites[SPRITE_CENTER_BASE +
935 	    (nptr->sprite != SPRITE_CHERRY)]);
936     }
937   else if(cherry && cherry->sprite)
938     back_sprite(cherry->sprite, 0, pixel.x, pixel.y);
939   /*}}}*/
940   if(update.set)
941     {
942       bounding_box(mptr->pixel.x, mptr->pixel.y, CELL_WIDTH, CELL_HEIGHT);
943       bounding_box(mptr->old_pixel.x, mptr->old_pixel.y,
944 	  CELL_WIDTH, CELL_HEIGHT);
945       add_background(update.tl.x, update.tl.y,
946 	  (unsigned)(update.br.x - update.tl.x),
947 	  (unsigned)(update.br.y - update.tl.y));
948       mptr->back = 1;
949     }
950   if(broke)
951     global.broken = 1;
952   /* check we haven't wandered off the board */
953   assert(!nptr || nptr->visit);
954   assert(INRANGE(mptr->cell.y, 0, CELLS_DOWN));
955   assert(INRANGE(mptr->cell.x, 0, CELLS_ACROSS));
956   assert(mptr->cell.x || mptr->offset.x >= 0);
957   assert(mptr->cell.y || mptr->offset.y >= 0);
958   assert(mptr->cell.x < CELLS_ACROSS - 1 || mptr->offset.x <= 0);
959   assert(mptr->cell.y < CELLS_DOWN - 1 || mptr->offset.y <= 0);
960   assert(!mptr->offset.x || !mptr->offset.y);
961   return nptr;
962 }
963 /*}}}*/
964 /*{{{  void munch_back(sx, sy, width, height, dx, dy, sprite)*/
965 static VOIDFUNC munch_back
966 FUNCARG((sx, sy, width, height, dx, dy, sprite),
967 	int       sx
968 ARGSEP  int       sy
969 ARGSEP  unsigned  width
970 ARGSEP  unsigned  height
971 ARGSEP  int       dx
972 ARGSEP  int       dy
973 ARGSEP  SPRITE CONST *sprite
974 )
975 /*
976  * munches the background image with the specified sprite
977  */
978 {
979   if(display.background != COLOUR_ONE)
980     XCopyArea(display.display, sprite->mask, display.back, GCN(GC_MASK),
981 	sx, sy, width, height, dx, dy);
982   if(display.background != COLOUR_ZERO)
983     XCopyArea(display.display, sprite->image, display.back, GCN(GC_OR),
984 	sx, sy, width, height, dx, dy);
985   bounding_box(dx, dy, width, height);
986   return;
987 }
988 /*}}}*/
989 /*{{{  void munch_hole(cptr, x, y)*/
990 extern VOIDFUNC munch_hole
991 FUNCARG((cptr, x, y),
992 	CELL      *cptr
993 ARGSEP  int       x
994 ARGSEP  int       y
995 )
996 /*
997  * cut out the background for a whole cell, and
998  * deal with connections to the adjoining cells
999  * don't forget to replot the cherry
1000  */
1001 {
1002   /*{{{  cut out the center*/
1003   {
1004     SPRITE    *sptr;
1005 
1006     sptr = &sprites[SPRITE_CENTER_BASE];
1007     munch_back(0, 0,
1008 	CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
1009 	x, y,
1010 	&sptr[!!(cptr[0].depths[0] || cptr[0].depths[2])]);
1011     munch_back(CELL_WIDTH >> 1, 0,
1012 	CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
1013 	x + (CELL_WIDTH >> 1), y,
1014 	&sptr[!!(cptr[0].depths[0] || cptr[0].depths[3])]);
1015     munch_back(CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
1016 	CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
1017 	x + (CELL_WIDTH >> 1), y + (CELL_HEIGHT >> 1),
1018 	&sptr[!!(cptr[0].depths[1] || cptr[0].depths[3])]);
1019     munch_back(0, CELL_HEIGHT >> 1,
1020 	CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
1021 	x, y + (CELL_HEIGHT >> 1),
1022 	&sptr[!!(cptr[0].depths[1] || cptr[0].depths[2])]);
1023   }
1024   /*}}}*/
1025   if(cptr[0].depths[0] <= -GAP_HEIGHT && cptr[-CELL_STRIDE].visit)
1026     munch_edge_above(cptr, x, y, 0);
1027   if(cptr[0].depths[1] >= GAP_HEIGHT && cptr[CELL_STRIDE].visit)
1028     munch_edge_below(cptr, x, y, 0, 0);
1029   if(cptr[0].depths[2] <= -GAP_WIDTH && cptr[-1].visit)
1030     munch_edge_left(cptr, x, y, 0);
1031   if(cptr[0].depths[3] >= GAP_WIDTH && cptr[1].visit)
1032     munch_edge_right(cptr, x, y, 0);
1033   return;
1034 }
1035 /*}}}*/
1036 /*{{{  void munch_edge_above(cptr, x, y, flag)*/
1037 static VOIDFUNC munch_edge_above
1038 FUNCARG((cptr, x, y, flag),
1039 	CELL      *cptr
1040 ARGSEP  int       x
1041 ARGSEP  int       y
1042 ARGSEP  unsigned  flag
1043 )
1044 /*
1045  * munches an edge above the cell, replots the cherry above if needed
1046  */
1047 {
1048   unsigned  check;
1049     /* remember kiddies, never leave signed cheques lying about */
1050   unsigned  type;
1051   SPRITE    *sptr;
1052 
1053   sptr = &sprites[SPRITE_EDGE_BASE + 1];
1054   check = 0;
1055   type = 0;
1056   if(cptr[0].depths[2] <= -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
1057     type = 2 * GAP_HEIGHT;
1058   if(cptr[-CELL_STRIDE].depths[2] <= -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
1059     type += GAP_HEIGHT;
1060   if(type == 3 * GAP_HEIGHT && (cptr[-CELL_STRIDE - 1].depths[1] -
1061       cptr[-1].depths[0] >= GAP_HEIGHT))
1062     type = 4 * GAP_HEIGHT;
1063   munch_back(0, (int)type, EDGE_WIDTH >> 1, GAP_HEIGHT,
1064       x + (CELL_WIDTH >> 1) - (EDGE_WIDTH >> 1), y - GAP_HEIGHT, sptr);
1065   if(flag && !cptr[-CELL_STRIDE].depths[2])
1066     {
1067       check = 1;
1068       munch_back(0, 0, EDGE_WIDTH >> 1, GAP_HEIGHT,
1069 	  x + (CELL_WIDTH >> 1) - (EDGE_WIDTH >> 1),
1070 	  y - GAP_HEIGHT * 2, sptr);
1071     }
1072   type = 0;
1073   if(cptr[0].depths[3] >= (INTERNAL_WIDTH + EXTERNAL_WIDTH))
1074     type = 2 * GAP_HEIGHT;
1075   if(cptr[-CELL_STRIDE].depths[3] >= (INTERNAL_WIDTH + EXTERNAL_WIDTH))
1076     type += GAP_HEIGHT;
1077   if(type == 3 * GAP_HEIGHT && (cptr[-CELL_STRIDE + 1].depths[1] -
1078       cptr[1].depths[0] >= GAP_HEIGHT))
1079     type = 4 * GAP_HEIGHT;
1080   munch_back(EDGE_WIDTH >> 1, (int)type, EDGE_WIDTH >> 1, GAP_HEIGHT,
1081       x + (CELL_WIDTH >> 1), y - GAP_HEIGHT, sptr);
1082   if(flag && !cptr[-CELL_STRIDE].depths[3])
1083     {
1084       check = 1;
1085       munch_back(EDGE_WIDTH >> 1, 0, EDGE_WIDTH >> 1, GAP_HEIGHT,
1086 	  x + (CELL_WIDTH >> 1), y - GAP_HEIGHT * 2, sptr);
1087     }
1088   if(check && cptr[-CELL_STRIDE].sprite)
1089       back_sprite(cptr[-CELL_STRIDE].sprite, 0,
1090 	  x, y - CELL_HEIGHT - GAP_HEIGHT);
1091   return;
1092 }
1093 /*}}}*/
1094 /*{{{  void munch_edge_below(cptr, x, y, offset, flag)*/
1095 static VOIDFUNC munch_edge_below
1096 FUNCARG((cptr, x, y, offset, flag),
1097 	CELL      *cptr
1098 ARGSEP  int       x
1099 ARGSEP  int       y
1100 ARGSEP  int       offset
1101 ARGSEP  unsigned  flag
1102 )
1103 /*
1104  * munches an edge below the cell, replots the cherry above if needed
1105  * the edge can be offset, in the case of non-aligned apples
1106  */
1107 {
1108   unsigned  check;
1109   unsigned  type;
1110   SPRITE    *sptr;
1111 
1112   sptr = &sprites[SPRITE_EDGE_BASE + 1];
1113   check = 0;
1114   type = 0;
1115   if(cptr[0].depths[2] - offset <= -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
1116     type = GAP_HEIGHT;
1117   if(cptr[CELL_STRIDE].depths[2] - offset <=
1118       -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
1119     type += 2 * GAP_HEIGHT;
1120   if(!offset && type == 3 * GAP_HEIGHT && (cptr[-1].depths[1] -
1121       cptr[CELL_STRIDE - 1].depths[0] >= GAP_HEIGHT))
1122     type = 4 * GAP_HEIGHT;
1123   munch_back(0, (int)type, EDGE_WIDTH >> 1, GAP_HEIGHT,
1124       x + (CELL_WIDTH >> 1) - (EDGE_WIDTH >> 1), y + CELL_HEIGHT, sptr);
1125   if(flag && !cptr[CELL_STRIDE].depths[2])
1126     {
1127       check = 1;
1128       munch_back(0, 0, EDGE_WIDTH >> 1, GAP_HEIGHT,
1129 	  x + (CELL_WIDTH >> 1) - (EDGE_WIDTH >> 1),
1130 	  y + CELL_HEIGHT + GAP_HEIGHT, sptr);
1131     }
1132   type = 0;
1133   if(cptr[0].depths[3] - offset >= (INTERNAL_WIDTH + EXTERNAL_WIDTH))
1134     type = GAP_HEIGHT;
1135   if(cptr[CELL_STRIDE].depths[3] - offset >=
1136       (INTERNAL_WIDTH + EXTERNAL_WIDTH))
1137     type += 2 * GAP_HEIGHT;
1138   if(!offset && type == 3 * GAP_HEIGHT &&
1139       (cptr[1].depths[1] -
1140       cptr[CELL_STRIDE + 1].depths[0] >= GAP_HEIGHT))
1141     type = 4 * GAP_HEIGHT;
1142   munch_back(EDGE_WIDTH >> 1, (int)type, EDGE_WIDTH >> 1, GAP_HEIGHT,
1143       x + (CELL_WIDTH >> 1), y + CELL_HEIGHT, sptr);
1144   if(flag && !cptr[CELL_STRIDE].depths[3])
1145     {
1146       check = 1;
1147       munch_back(EDGE_WIDTH >> 1, 0, EDGE_WIDTH >> 1, GAP_HEIGHT,
1148 	  x + (CELL_WIDTH >> 1), y + CELL_HEIGHT + GAP_HEIGHT, sptr);
1149     }
1150   if(check && cptr[CELL_STRIDE].sprite)
1151       back_sprite(cptr[CELL_STRIDE].sprite, 0,
1152 	  x, y + CELL_HEIGHT + GAP_HEIGHT);
1153   return;
1154 }
1155 /*}}}*/
1156 /*{{{  void munch_edge_left(cptr, x, y, flag)*/
1157 static VOIDFUNC munch_edge_left
1158 FUNCARG((cptr, x, y, flag),
1159 	CELL      *cptr
1160 ARGSEP  int       x
1161 ARGSEP  int       y
1162 ARGSEP  unsigned  flag
1163 )
1164 /*
1165  * munches an edge left of the cell, replots the cherry above if needed
1166  */
1167 {
1168   unsigned  check;
1169   unsigned  type;
1170   SPRITE    *sptr;
1171 
1172   sptr = &sprites[SPRITE_EDGE_BASE + 0];
1173   check = 0;
1174   type = 0;
1175   if(cptr[0].depths[0] <= -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
1176     type = 2 * GAP_WIDTH;
1177   if(cptr[-1].depths[0] <= -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
1178     type += GAP_WIDTH;
1179   if(type == 3 * GAP_WIDTH && (cptr[-CELL_STRIDE - 1].depths[3] -
1180       cptr[-CELL_STRIDE].depths[2] >= GAP_WIDTH))
1181     type = 4 * GAP_WIDTH;
1182   munch_back((int)type, 0, GAP_HEIGHT, EDGE_HEIGHT >> 1,
1183       x - GAP_WIDTH, y + (CELL_HEIGHT >> 1) - (EDGE_HEIGHT >> 1), sptr);
1184   if(flag && !cptr[-1].depths[0])
1185     {
1186       check = 1;
1187       munch_back(0, 0, GAP_WIDTH, EDGE_HEIGHT >> 1, x - GAP_WIDTH * 2,
1188 	  y + (CELL_HEIGHT >> 1) - (EDGE_HEIGHT >> 1), sptr);
1189     }
1190   type = 0;
1191   if(cptr[0].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
1192     type = 2 * GAP_WIDTH;
1193   if(cptr[-1].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
1194     type += GAP_WIDTH;
1195   if(type == 3 * GAP_WIDTH && (cptr[CELL_STRIDE - 1].depths[3] -
1196       cptr[CELL_STRIDE].depths[2] >= GAP_WIDTH))
1197     type = 4 * GAP_WIDTH;
1198   munch_back((int)type, EDGE_HEIGHT >> 1, GAP_WIDTH, EDGE_HEIGHT >> 1,
1199       x - GAP_WIDTH, y + (CELL_HEIGHT >> 1), sptr);
1200   if(flag && !cptr[-1].depths[1])
1201     {
1202       check = 1;
1203       munch_back(0, EDGE_HEIGHT >> 1, GAP_WIDTH, EDGE_HEIGHT >> 1,
1204 	  x - GAP_WIDTH * 2, y + (CELL_HEIGHT >> 1), sptr);
1205     }
1206   if(check && cptr[-1].sprite)
1207       back_sprite(cptr[-1].sprite, 0, x - CELL_WIDTH - GAP_WIDTH, y);
1208   return;
1209 }
1210 /*}}}*/
1211 /*{{{  void munch_edge_right(cptr, x, y, flag)*/
1212 static VOIDFUNC munch_edge_right
1213 FUNCARG((cptr, x, y, flag),
1214 	CELL      *cptr
1215 ARGSEP  int       x
1216 ARGSEP  int       y
1217 ARGSEP  unsigned  flag
1218 )
1219 /*
1220  * munches an edge right of the cell, replots the cherry above if needed
1221  */
1222 {
1223   unsigned  check;
1224   unsigned  type;
1225   SPRITE    *sptr;
1226 
1227   sptr = &sprites[SPRITE_EDGE_BASE + 0];
1228   check = 0;
1229   type = 0;
1230   if(cptr[0].depths[0] <= -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
1231     type = GAP_WIDTH;
1232   if(cptr[1].depths[0] <= -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
1233     type += 2 * GAP_WIDTH;
1234   if(type == 3 * GAP_WIDTH &&
1235       (cptr[-CELL_STRIDE].depths[3] -
1236       cptr[-CELL_STRIDE + 1].depths[2] >= GAP_WIDTH))
1237     type = 4 * GAP_WIDTH;
1238   munch_back((int)type, 0, GAP_WIDTH, EDGE_HEIGHT >> 1,
1239       x + CELL_WIDTH, y + (CELL_HEIGHT >> 1) - (EDGE_HEIGHT >> 1), sptr);
1240   if(flag && !cptr[1].depths[0])
1241     {
1242       check = 1;
1243       munch_back(0, 0, GAP_WIDTH, EDGE_HEIGHT >> 1,
1244 	  x + CELL_WIDTH + GAP_WIDTH,
1245 	  y + (CELL_HEIGHT >> 1) - (EDGE_HEIGHT >> 1), sptr);
1246     }
1247   type = 0;
1248   if(cptr[0].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
1249     type = GAP_WIDTH;
1250   if(cptr[1].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
1251     type += 2 * GAP_WIDTH;
1252   if(type == 3 * GAP_WIDTH &&
1253       (cptr[CELL_STRIDE].depths[3] -
1254       cptr[CELL_STRIDE + 1].depths[2] >= GAP_WIDTH))
1255     type = 4 * GAP_WIDTH;
1256   munch_back((int)type, EDGE_HEIGHT >> 1, GAP_WIDTH, EDGE_HEIGHT >> 1,
1257       x + CELL_WIDTH, y + (CELL_HEIGHT >> 1), sptr);
1258   if(flag && !cptr[1].depths[1])
1259     {
1260       check = 1;
1261       munch_back(0, EDGE_HEIGHT >> 1, GAP_WIDTH, EDGE_HEIGHT >> 1,
1262 	  x + CELL_WIDTH + GAP_WIDTH, y + (CELL_HEIGHT >> 1), sptr);
1263     }
1264   if(check && cptr[1].sprite)
1265       back_sprite(cptr[1].sprite, 0, x + CELL_WIDTH + GAP_WIDTH, y);
1266   return;
1267 }
1268 /*}}}*/
1269 /*{{{  void new_face(mptr)*/
1270 extern VOIDFUNC new_face
1271 FUNCARG((mptr),
1272 	MONSTER *mptr
1273 )
1274 /*
1275  * calculates the required face for the direction,
1276  * given the old face
1277  */
1278 {
1279   int       dir;
1280   int       face;
1281 
1282   if(mptr->push)
1283     dir = mptr->dir ^ 1;
1284   else
1285     dir = mptr->dir;
1286   face = mptr->face < 6 ? mptr->face : 2 + (mptr->face & 1);
1287   if(mptr->squished)
1288     mptr->face = 16 + (face > 2);
1289   else if(dir & 2)
1290     mptr->face = dir;
1291   else if(face == 2)
1292     mptr->face = dir;
1293   else if(face == 3)
1294     mptr->face = dir + 4;
1295   else if((dir ^ face) & 1)
1296     mptr->face ^= 5;
1297   if(mptr->face & 2 && mptr->pushing)
1298     mptr->face += 4;
1299   return;
1300 }
1301 /*}}}*/
1302 /*{{{  unsigned run_home(mptr, cptr)*/
1303 extern unsigned run_home
1304 FUNCARG((mptr, cptr),
1305 	MONSTER   *mptr
1306 ARGSEP  CELL      *cptr
1307 )
1308 /*
1309  * sets the direction bit to run home
1310  */
1311 {
1312   unsigned  preferred;
1313   int       offset;
1314 
1315   if((offset = mptr->offset.y) != 0)
1316     /*{{{  up down only*/
1317     {
1318       if(offset < 0 ? cptr[0].xtra <
1319 	  cptr[-CELL_STRIDE].xtra :
1320 	  cptr[0].xtra >= cptr[CELL_STRIDE].xtra)
1321 	preferred = 0x2;
1322       else
1323 	preferred = 0x1;
1324     }
1325     /*}}}*/
1326   else if(cptr->xtra == 1)
1327     preferred = mptr->pixel.x > PIXELX(4,
1328 	extra.select * XTRA_SPACING) ? 0x4 : 0x8;
1329   else if((offset = mptr->offset.x) != 0)
1330     /*{{{  left right only*/
1331     {
1332       if(offset < 0 ? cptr[0].xtra < cptr[-1].xtra :
1333 	  cptr[0].xtra >= cptr[1].xtra)
1334 	preferred = 0x8;
1335       else
1336 	preferred = 0x4;
1337       if(offset == cptr->holes[0] ||
1338 	  offset == cptr->holes[1])
1339 	{
1340 	  if(cptr[0].xtra > cptr[-CELL_STRIDE].xtra)
1341 	    preferred |= 0x1;
1342 	}
1343       if(offset == cptr->holes[2] ||
1344 	  offset == cptr->holes[3])
1345 	{
1346 	  if(cptr[0].xtra > cptr[CELL_STRIDE].xtra)
1347 	    preferred |= 0x2;
1348 	}
1349     }
1350     /*}}}*/
1351   else
1352     /*{{{  at intersection*/
1353     {
1354       int       distance;
1355 
1356       distance = cptr->xtra;
1357       preferred = 0;
1358       if(distance > cptr[-CELL_STRIDE].xtra)
1359 	preferred |= 0x1;
1360       if(distance > cptr[CELL_STRIDE].xtra)
1361 	preferred |= 0x2;
1362       if(distance > cptr[-1].xtra)
1363 	preferred |= 0x4;
1364       if(distance > cptr[1].xtra)
1365 	preferred |= 0x8;
1366     }
1367     /*}}}*/
1368   return preferred;
1369 }
1370 /*}}}*/
1371 /*{{{  unsigned valid_directions(mptr, cptr)*/
1372 extern unsigned valid_directions
1373 FUNCARG((mptr, cptr),
1374 	MONSTER   *mptr
1375 ARGSEP  CELL      *cptr
1376 )
1377 /*
1378  * sets the valid and nearer direction bits
1379  * these are nr, nl, nd, nu, r, l, d, u
1380  */
1381 {
1382   unsigned  answer;
1383   int       offset;
1384 
1385   answer = 0;
1386   assert(cptr->visit);
1387   if((offset = mptr->offset.y) != 0)
1388     /*{{{  up down only*/
1389     {
1390       if(!mptr->cell.y && offset < 0)
1391 	answer |= 2;
1392       else if(mptr->offset.x)
1393 	answer |= 0x3;
1394       else
1395 	{
1396 	  if(offset > cptr->depths[0])
1397 	    answer |= 0x1;
1398 	  if(offset < cptr->depths[1])
1399 	    answer |= 0x2;
1400 	}
1401       if(cptr == monster.player)
1402 	answer |= monster.list[0].offset.y > offset ? 0x20 : 0x10;
1403       else if(offset < 0 ? cptr[0].distance < cptr[-CELL_STRIDE].distance :
1404 	  cptr[0].distance >= cptr[CELL_STRIDE].distance)
1405 	answer |= 0x20;
1406       else
1407 	answer |= 0x10;
1408     }
1409     /*}}}*/
1410   else if((offset = mptr->offset.x) != 0)
1411     /*{{{  left right only*/
1412     {
1413       if(offset > cptr->depths[2])
1414 	answer |= 0x4;
1415       if(offset < cptr->depths[3])
1416 	answer |= 0x8;
1417       if(cptr == monster.player)
1418 	answer |= monster.list[0].offset.x > offset ? 0x80 : 0x40;
1419       else if(offset < 0 ? cptr[0].distance < cptr[-1].distance :
1420 	  cptr[0].distance >= cptr[1].distance)
1421 	answer |= 0x80;
1422       else
1423 	answer |= 0x40;
1424       if(offset == cptr->holes[0] || offset == cptr->holes[1])
1425 	{
1426 	  answer |= 0x1;
1427 	  if(cptr[0].distance > cptr[-CELL_STRIDE].distance)
1428 	    answer |= 0x10;
1429 	}
1430       if(offset == cptr->holes[2] || offset == cptr->holes[3])
1431 	{
1432 	  answer |= 0x2;
1433 	  if(cptr[0].distance > cptr[CELL_STRIDE].distance)
1434 	    answer |= 0x20;
1435 	}
1436     }
1437     /*}}}*/
1438   else
1439     /*{{{  at intersection*/
1440     {
1441       int       distance;
1442 
1443       if(cptr->depths[0])
1444 	answer |= 0x1;
1445       if(cptr->depths[1])
1446 	answer |= 0x2;
1447       if(cptr->depths[2])
1448 	answer |= 0x4;
1449       if(cptr->depths[3])
1450 	answer |= 0x8;
1451       distance = cptr->distance;
1452       if(cptr == monster.player)
1453 	{
1454 	  if(monster.list[0].offset.x < 0)
1455 	    answer |= 0x40;
1456 	  else if(monster.list[0].offset.x > 0)
1457 	    answer |= 0x80;
1458 	  else if(monster.list[0].offset.y < 0)
1459 	    answer |= 0x10;
1460 	  else if(monster.list[0].offset.y > 0)
1461 	    answer |= 0x20;
1462 	}
1463       else
1464 	{
1465 	  if(distance > cptr[-CELL_STRIDE].distance)
1466 	    answer |= 0x10;
1467 	  if(distance > cptr[CELL_STRIDE].distance)
1468 	    answer |= 0x20;
1469 	  if(distance > cptr[-1].distance)
1470 	    answer |= 0x40;
1471 	  if(distance > cptr[1].distance)
1472 	    answer |= 0x80;
1473 	}
1474     }
1475     /*}}}*/
1476   answer &= answer << 4 | 0xF;
1477   return answer;
1478 }
1479 /*}}}*/
1480