/* Copyright (C) 1993, 1992 Nathan Sidwell */
/* RCS $Id: move.c,v 4.3 1993/10/25 12:02:18 nathan Stable $ */
#include "xmris.h"
/*{{{  prototypes*/
static VOIDFUNC connect_hole PROTOARG((CELL *));
static int apple_hole PROTOARG((APPLE *, CELL *));
static VOIDFUNC munch_back
    PROTOARG((int, int, unsigned, unsigned, int, int, SPRITE CONST *));
static VOIDFUNC munch_edge_above PROTOARG((CELL *, int, int, unsigned));
static VOIDFUNC munch_edge_below PROTOARG((CELL *, int, int, int, unsigned));
static VOIDFUNC munch_edge_left PROTOARG((CELL *, int, int, unsigned));
static VOIDFUNC munch_edge_right PROTOARG((CELL *, int, int, unsigned));
/*}}}*/
/*{{{  int apple_hole(aptr, cptr)*/
static int apple_hole
FUNCARG((aptr, cptr),
	APPLE   *aptr
ARGSEP  CELL    *cptr
)
/*
 * An apple has fallen, this makes the hole it
 * leaves behind in the hedge, but only in the board table
 * remember to get it drawn too.
 */
{
  int     offset;

  offset = aptr->offset.x / VEL_X * VEL_X;
  if(offset < 0)
    {
      int     max;

      max = cptr[0].depths[2] < cptr[CELL_STRIDE].depths[2] ?
	  cptr[CELL_STRIDE].depths[2] : cptr[0].depths[2];
      if(max >= offset)
	offset = max + VEL_X;
      if(offset < 0)
	{
	  assert(offset > -(CELL_WIDTH + GAP_WIDTH));
	  cptr[CELL_STRIDE].holes[0] = cptr[0].holes[2] = offset;
	  cptr[CELL_STRIDE - 1].holes[1] =
	      cptr[-1].holes[3] = CELL_WIDTH + GAP_WIDTH + offset;
	}
      else
	offset = 0;
    }
  else if(offset > 0)
    {
      int     min;

      min = cptr[0].depths[3] > cptr[CELL_STRIDE].depths[3] ?
	  cptr[CELL_STRIDE].depths[3] : cptr[0].depths[3];
      if(min <= offset)
	offset = min - VEL_X;
      if(offset > 0)
	{
	  assert(offset < (CELL_WIDTH + GAP_WIDTH));
	  cptr[CELL_STRIDE].holes[1] = cptr[0].holes[3] = offset;
	  cptr[CELL_STRIDE + 1].holes[0] =
	      cptr[1].holes[2] = -(CELL_WIDTH + GAP_WIDTH) + offset;
	}
      else
	offset = 0;
    }
  return offset;
}
/*}}}*/
/*{{{  void back_sprite(ix, flag, x, y)*/
extern VOIDFUNC back_sprite
FUNCARG((ix, flag, x, y),
	unsigned  ix
ARGSEP  unsigned  flag
ARGSEP  int       x
ARGSEP  int       y
)
/*
 * copy a sprite onto the background
 */
{
  SPRITE CONST *sptr;

  sptr = &sprites[ix];
  if(display.background != COLOUR_ZERO || flag)
    XCopyArea(display.display, sptr->mask, display.back, GCN(GC_MASK),
	0, 0, sptr->size.x, sptr->size.y, x, y);
  XCopyArea(display.display, sptr->image, display.back, GCN(GC_OR),
      0, 0, sptr->size.x, sptr->size.y, x, y);
  return;
}
/*}}}*/
/*{{{  unsigned choose_direction(valid)*/
extern unsigned choose_direction
FUNCARG((valid),
	unsigned  valid
)
/*
 * pick a direction at chaotic from the valid ones
 */
{
  unsigned  choices;
  unsigned  temp;
  unsigned  choice;

  assert(valid && !(valid & ~0xF));
  for(choices = 0, temp = valid; temp; choices++)
    temp ^= temp & -temp;
  if(choices == 1)
    choice = 0;
  else if(choices == 3)
    choice = chaotic() % 3;
  else
    choice = chaotic() & (choices - 1);
  do
    {
      temp = valid & -valid;
      valid ^= temp;
    }
  while(choice--);
  assert(temp);
  for(valid = 0; !(temp & 1); valid++)
    temp >>= 1;
  return valid;
}
/*}}}*/
/*{{{  void connect_hole(cptr)*/
static VOIDFUNC connect_hole
FUNCARG((cptr),
	CELL      *cptr
)
/*
 * connect the current cell up to its neighbours
 */
{
  cptr[0].visit = 1;
  if(cptr[-CELL_STRIDE].depths[1] >= GAP_HEIGHT)
    {
      cptr[0].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
      cptr[-CELL_STRIDE].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
    }
  if(cptr[CELL_STRIDE].depths[0] <= -GAP_HEIGHT)
    {
      cptr[0].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
      cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
    }
  if(cptr[-1].depths[3] >= GAP_WIDTH)
    {
      cptr[0].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
      cptr[-1].depths[3] = CELL_WIDTH + GAP_WIDTH;
    }
  if(cptr[1].depths[2] <= -GAP_WIDTH)
    {
      cptr[0].depths[3] = CELL_WIDTH + GAP_WIDTH;
      cptr[1].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
    }
  return;
}
/*}}}*/
/*{{{  CELL *drop_apple(aptr, cptr)*/
extern CELL *drop_apple
FUNCARG((aptr, cptr),
	APPLE   *aptr
ARGSEP  CELL    *cptr
)
/*
 * deals with apples which break through to the cell below
 * the apple has already been moved to the new coordinate
 * returns NULL if we stay in the same cell, or a pointer
 * to the new cell
 */
{
  CELL      *new;
  COORD     pixel;
  CELL      *cherry;

  update.set = 0;
  pixel.x = aptr->pixel.x - aptr->offset.x;
  pixel.y = aptr->pixel.y - aptr->offset.y;
  cherry = NULL;
  if(aptr->offset.y <= cptr[0].depths[1])
    {
      if(INRANGE(aptr->offset.y, 1, APPLE_VEL_Y + 1))
	{
	  int     offset;

	  offset = apple_hole(aptr, cptr);
	  if(offset)
	    munch_edge_below(cptr, pixel.x + offset, pixel.y, offset, 0);
	}
      new = NULL;
    }
  else if(!cptr[0].visit)
    {
      aptr->offset.y -= CELL_HEIGHT + GAP_HEIGHT;
      aptr->cell.y += 1;
      pixel.y += CELL_HEIGHT + GAP_HEIGHT;
      new = cptr + CELL_STRIDE;
    }
  else if(cptr[CELL_STRIDE].visit)
    /*{{{  break through below*/
    {
      int   offset;

      offset = apple_hole(aptr, cptr);
      /*{{{  munch*/
      {
	munch_edge_below(cptr, pixel.x + offset, pixel.y,
	    offset, (unsigned)!offset);
	if(!offset && !cptr[0].depths[2])
	  {
	    munch_back(0, 0, EDGE_WIDTH >> 1, GAP_HEIGHT,
		pixel.x + (CELL_WIDTH >> 1) - (EDGE_WIDTH >> 1),
		pixel.y + CELL_HEIGHT - GAP_HEIGHT,
		&sprites[SPRITE_EDGE_BASE + 1]);
	    cherry = cptr;
	  }
	if(!offset && !cptr[0].depths[3])
	  {
	    munch_back(EDGE_WIDTH >> 1, 0,
		EDGE_WIDTH >> 1, GAP_HEIGHT,
		pixel.x + (CELL_WIDTH >> 1),
		pixel.y + CELL_HEIGHT - GAP_HEIGHT,
		&sprites[SPRITE_EDGE_BASE + 1]);
	    cherry = cptr;
	  }
      }
      /*}}}*/
      aptr->offset.y -= CELL_HEIGHT + GAP_HEIGHT;
      aptr->cell.y += 1;
      pixel.y += CELL_HEIGHT + GAP_HEIGHT;
      global.broken = 1;
      new = cptr + CELL_STRIDE;
      if(!offset)
	{
	  cptr[0].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
	  cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
	}
    }
    /*}}}*/
  else if(cptr[0].depths[1] - cptr[CELL_STRIDE * 2].depths[0] >=
      CELL_HEIGHT + GAP_HEIGHT)
    /*{{{  breakthrough 2 below*/
    {
      aptr->offset.y -= CELL_HEIGHT + GAP_HEIGHT;
      aptr->cell.y += 1;
      pixel.y += CELL_HEIGHT + GAP_HEIGHT;
      global.broken = 1;
      cptr[CELL_STRIDE].visit = 1;
      cptr[0].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
      cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
      cptr[CELL_STRIDE].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
      cptr[CELL_STRIDE * 2].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
      if(cptr[CELL_STRIDE - 1].depths[3])
	{
	  cptr[CELL_STRIDE].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
	  cptr[CELL_STRIDE - 1].depths[3] = CELL_WIDTH + GAP_WIDTH;
	}
      if(cptr[CELL_STRIDE + 1].depths[2])
	{
	  cptr[CELL_STRIDE].depths[3] = CELL_WIDTH + GAP_WIDTH;
	  cptr[CELL_STRIDE + 1].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
	}
      munch_hole(cptr + CELL_STRIDE, pixel.x, pixel.y);
      cherry = new = cptr + CELL_STRIDE;
    }
    /*}}}*/
  else
    new = NULL;
  if(cherry && cherry->sprite)
    back_sprite(new->sprite, 0, pixel.x, pixel.y);
  if(update.set)
    {
      bounding_box(aptr->pixel.x, aptr->pixel.y, CELL_WIDTH, CELL_HEIGHT);
      bounding_box(aptr->old_pixel.x, aptr->old_pixel.y,
	CELL_WIDTH, CELL_HEIGHT);
      aptr->back = 1;
      add_background(update.tl.x, update.tl.y,
	  (unsigned)(update.br.x - update.tl.x),
	  (unsigned)(update.br.y - update.tl.y));
    }
  return new;
}
/*}}}*/
/*{{{  CELL *move_diamond(mptr, cptr)*/
extern CELL *move_diamond
FUNCARG((mptr, cptr),
	MONSTER   *mptr
ARGSEP  CELL      *cptr
)
/*
 * move the diamond downwards, munching away as we go
 */
{
  CELL      *nptr;

  assert(!mptr->offset.x && mptr->dir == 1);
  nptr = NULL;
  if(mptr->offset.y)
    /* EMPTY */;
  else if(cptr[CELL_STRIDE].visit || cptr[CELL_STRIDE].sprite ||
      mptr->cell.y == CELLS_DOWN - 1)
    mptr->stop = 1;
  else
    {
      APPLE     *aptr;
      unsigned  count;

      for(aptr = apple.list, count = apple.apples; count--; aptr++)
	if(aptr->state < 3 && aptr->cell.y == mptr->cell.y + 1 &&
	    INRANGE(aptr->pixel.x - mptr->pixel.x,
	      VEL_X - CELL_WIDTH + 1, CELL_WIDTH - VEL_X))
	  {
	    mptr->stop = 1;
	    break;
	  }
    }
  if(!mptr->stop)
    {
      mptr->offset.y += DIAMOND_VEL_Y;
      mptr->pixel.y += DIAMOND_VEL_Y;
      update.set = 0;
      cptr[0].depths[1] = mptr->offset.y;
      if(mptr->offset.y < CELL_HEIGHT + GAP_HEIGHT)
	{
	  munch_back(0, CELL_HEIGHT - DIAMOND_VEL_Y - INTERNAL_HEIGHT,
	      EDGE_WIDTH, DIAMOND_VEL_Y + INTERNAL_HEIGHT,
	      mptr->pixel.x, mptr->pixel.y + CELL_HEIGHT -
	      DIAMOND_VEL_Y - INTERNAL_HEIGHT, &sprites[SPRITE_CENTER_BASE]);
	}
      else
	{
	  nptr = cptr + CELL_STRIDE;
	  nptr[0].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
	  connect_hole(nptr);
	  munch_hole(nptr, mptr->pixel.x, mptr->pixel.y);
	  if(nptr->sprite)
	    back_sprite(nptr->sprite, 0, mptr->pixel.x, mptr->pixel.y);
	  mptr->offset.y = 0;
	  mptr->cell.y += 1;
	  global.broken = 1;
	}
      assert(update.set);
      bounding_box(mptr->pixel.x, mptr->pixel.y, CELL_WIDTH, CELL_HEIGHT);
      bounding_box(mptr->old_pixel.x, mptr->old_pixel.y,
	  CELL_WIDTH, CELL_HEIGHT);
      add_background(update.tl.x, update.tl.y,
	  (unsigned)(update.br.x - update.tl.x),
	  (unsigned)(update.br.y - update.tl.y));
    }
  return nptr;
}
/*}}}*/
/*{{{  CELL *move_movable(mptr, cptr)*/
extern CELL *move_movable
FUNCARG((mptr, cptr),
	MONSTER   *mptr
ARGSEP  CELL      *cptr
)
/*
 * move a non muncher down a path
 * we should never leave known territory
 */
{
  unsigned  delta;

  switch(mptr->dir)
  {
    /*{{{  case 0: (up)*/
    case 0:
      if(!mptr->fast)
	delta = VEL_Y;
      else if(mptr->offset.y >
	  (CELL_HEIGHT + GAP_HEIGHT) - (FAST_STEPS * VEL_Y_FAST))
	delta = VEL_Y_FAST;
      else if(mptr->offset.y > (VEL_Y_FAST * FAST_STEPS))
	delta = VEL_Y;
      else if(mptr->offset.y > -(VEL_Y_FAST * FAST_STEPS))
	delta = VEL_Y_FAST;
      else if(mptr->offset.y >
	  (VEL_Y_FAST * FAST_STEPS) - (CELL_HEIGHT + GAP_HEIGHT))
	delta = VEL_Y;
      else
	delta = VEL_Y_FAST;
      mptr->pixel.y -= delta;
      mptr->offset.y -= delta;
      if(mptr->offset.y == -(CELL_HEIGHT + GAP_HEIGHT) && mptr->cell.y)
	{
	  mptr->offset.y = 0;
	  mptr->cell.y -= 1;
	  cptr -= CELL_STRIDE;
	  if(!cptr->visit)
	    {
	      if(mptr->offset.x < 0)
		{
		  mptr->offset.x += CELL_WIDTH + GAP_WIDTH;
		  mptr->cell.x -= 1;
		  cptr -= 1;
		}
	      else if(mptr->offset.x > 0)
		{
		  mptr->offset.x -= CELL_WIDTH + GAP_WIDTH;
		  mptr->cell.x += 1;
		  cptr += 1;
		}
	      else
		{
		  mptr->offset.y = -(CELL_HEIGHT + GAP_HEIGHT);
		  mptr->cell.y += 1;
		  cptr += CELL_STRIDE;
		}
	    }
	}
      assert(cptr->visit && mptr->cell.y >= 0);
      assert(mptr->offset.y >= -(CELL_HEIGHT + GAP_HEIGHT));
      break;
    /*}}}*/
    /*{{{  case 1: (down)*/
    case 1:
      if(!mptr->fast)
	delta = VEL_Y;
      else if(mptr->offset.y <
	  (FAST_STEPS * VEL_Y_FAST) - (CELL_HEIGHT + GAP_HEIGHT))
	delta = VEL_Y_FAST;
      else if(mptr->offset.y < -(VEL_Y_FAST * FAST_STEPS))
	delta = VEL_Y;
      else if(mptr->offset.y < (VEL_Y_FAST * FAST_STEPS))
	delta = VEL_Y_FAST;
      else if(mptr->offset.y <
	  (CELL_HEIGHT + GAP_HEIGHT) - (VEL_Y_FAST * FAST_STEPS))
	delta = VEL_Y;
      else
	delta = VEL_Y_FAST;
      mptr->pixel.y += delta;
      mptr->offset.y += delta;
      if(mptr->offset.y == CELL_HEIGHT + GAP_HEIGHT)
	{
	  mptr->offset.y = 0;
	  mptr->cell.y += 1;
	  cptr += CELL_STRIDE;
	  if(!cptr->visit)
	    {
	      if(mptr->offset.x < 0)
		{
		  mptr->offset.x += CELL_WIDTH + GAP_WIDTH;
		  mptr->cell.x -= 1;
		  cptr -= 1;
		}
	      else if(mptr->offset.x > 0)
		{
		  mptr->offset.x -= CELL_WIDTH + GAP_WIDTH;
		  mptr->cell.x += 1;
		  cptr += 1;
		}
	      else
		{
		  mptr->offset.y = CELL_HEIGHT + GAP_HEIGHT;
		  mptr->cell.y -= 1;
		  cptr -= CELL_STRIDE;
		}
	    }
	}
      assert(cptr->visit && mptr->cell.y < CELLS_DOWN);
      assert(mptr->offset.y <= CELL_HEIGHT + GAP_HEIGHT);
      break;
    /*}}}*/
    /*{{{  case 2: (left)*/
    case 2:
      if(!mptr->fast)
	delta = VEL_X;
      else if(mptr->offset.x >
	  (CELL_WIDTH + GAP_WIDTH) - (VEL_X_FAST * FAST_STEPS))
	delta = VEL_X_FAST;
      else if(mptr->offset.x > (VEL_X_FAST * FAST_STEPS))
	delta = VEL_X;
      else if(mptr->offset.x > -(VEL_X_FAST * FAST_STEPS))
	delta = VEL_X_FAST;
      else if(mptr->offset.x >
	  (FAST_STEPS * VEL_X_FAST) - (CELL_WIDTH + GAP_WIDTH))
	delta = VEL_X;
      else
	delta = VEL_X_FAST;
      mptr->pixel.x -= delta;
      mptr->offset.x -= delta;
      if(mptr->offset.x == -(CELL_WIDTH + GAP_WIDTH) && cptr[-1].visit)
	{
	  mptr->offset.x = 0;
	  mptr->cell.x -= 1;
	  cptr -= 1;
	}
      assert(cptr->visit && !mptr->offset.y && mptr->cell.x >= 0);
      assert(mptr->offset.x >= -(CELL_WIDTH + GAP_WIDTH));
      break;
    /*}}}*/
    /*{{{  case 3: (right)*/
    case 3:
      if(!mptr->fast)
	delta = VEL_X;
      else if(mptr->offset.x <
	  (FAST_STEPS * VEL_X_FAST) - (CELL_WIDTH + GAP_WIDTH))
	delta = VEL_X_FAST;
      else if(mptr->offset.x < -(VEL_X_FAST * FAST_STEPS))
	delta = VEL_X;
      else if(mptr->offset.x < (VEL_X_FAST * FAST_STEPS))
	delta = VEL_X_FAST;
      else if(mptr->offset.x <
	  (CELL_WIDTH + GAP_WIDTH) - (VEL_X_FAST * FAST_STEPS))
	delta = VEL_X;
      else
	delta = VEL_X_FAST;
      mptr->pixel.x += delta;
      mptr->offset.x += delta;
      if(mptr->offset.x == CELL_WIDTH + GAP_WIDTH && cptr[1].visit)
	{
	  mptr->offset.x = 0;
	  mptr->cell.x += 1;
	  cptr += 1;
	}
      assert(cptr->visit && !mptr->offset.y && mptr->cell.x < CELLS_ACROSS);
      assert(mptr->offset.x <= CELL_WIDTH + GAP_WIDTH);
      break;
    /*}}}*/
  }
  return cptr;
}
/*}}}*/
/*{{{  CELL *move_muncher(mptr)*/
extern CELL *move_muncher
FUNCARG((mptr),
	MONSTER   *mptr /* the object to move */
)
/*
 * moves and munches the board for an object which can munch
 * apple checking is performed here too
 * (ie the man, or a munch monster)
 * the board array is updated as required
 * returns a pointer to the new cell, if we have arrived elsewhere
 * or NULL if we stayed on the same cell
 */
{
  unsigned  broke;
  CELL      *nptr;
  CELL      *cherry;
  CELL      *cptr;
  COORD     pixel;
  COORD     cell;
  SPRITE    *sptr;
  int       knocked;

  assert(!mptr->stop && !mptr->pause);
  broke = 0;
  nptr = NULL;
  cherry = NULL;
  update.set = 0;
  cell.x = mptr->cell.x;
  cell.y = mptr->cell.y;
  cptr = BOARDCELL(cell.x, cell.y);
  pixel.x = PIXELX(cell.x, 0);
  pixel.y = PIXELY(cell.y, 0);
  knocked = 0;
  if(!apple_stop(mptr, cptr))
    {
      switch(mptr->dir)
      {
	/*{{{  case 0: (up)*/
	case 0:
	  /*
	   * if the depth upwards is less than the future depth,
	   * then we have to do some munching
	   */
	  mptr->offset.y -= VEL_Y;
	  mptr->pixel.y = pixel.y + mptr->offset.y;
	  if(cptr[0].depths[0] > mptr->offset.y)
	    /*{{{  munch*/
	    {
	      cptr[0].depths[0] = mptr->offset.y;
	      sptr = &sprites[SPRITE_CENTER_BASE];
	      munch_back(0, 0, CELL_WIDTH, VEL_Y + INTERNAL_HEIGHT,
		  pixel.x, pixel.y + cptr->depths[0], sptr);
	      if(cptr->sprite && mptr->offset.y + VEL_Y + INTERNAL_HEIGHT > 0)
		back_sprite(cptr->sprite, 0, pixel.x, pixel.y);
	      if(mptr->offset.y < -GAP_HEIGHT)
		cherry = cptr - CELL_STRIDE;
	      if(INRANGE(mptr->offset.y,
		  1 - (GAP_HEIGHT + VEL_Y), 1 - GAP_HEIGHT) &&
		  cptr[-CELL_STRIDE].visit)
		{
		  cptr[0].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
		  cptr[-CELL_STRIDE].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
		  munch_edge_above(cptr, pixel.x, pixel.y, 1);
		  broke = 1;
		}
	      if(INRANGE(mptr->offset.y,
		  1 - (INTERNAL_HEIGHT + EXTERNAL_HEIGHT) - VEL_Y,
		  1 - (INTERNAL_HEIGHT + EXTERNAL_HEIGHT)))
		{
		  sptr = &sprites[SPRITE_EDGE_BASE + 1];
		  /*{{{  round top left corner?*/
		  if(cptr[0].depths[2] <= -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
		    munch_back((EDGE_WIDTH >> 1) - (CELL_WIDTH >> 1) -
			EXTERNAL_WIDTH, 3 * GAP_HEIGHT - EXTERNAL_HEIGHT,
			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
			pixel.x - EXTERNAL_WIDTH, pixel.y - EXTERNAL_HEIGHT,
			sptr);
		  /*}}}*/
		  /*{{{  round top right corner?*/
		  if(cptr[0].depths[3] >= (INTERNAL_WIDTH + EXTERNAL_WIDTH))
		    munch_back((EDGE_WIDTH >> 1) + (CELL_WIDTH >> 1),
			3 * GAP_HEIGHT - EXTERNAL_HEIGHT,
			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
			pixel.x + CELL_WIDTH,
			pixel.y - EXTERNAL_HEIGHT, sptr);
		  /*}}}*/
		}
	      /*{{{  knocked through?*/
	      /*
	       * have we bumped into any of the following ?
	       * path from 2 above me
	       * path from above left
	       * path from above right
	       */
	      if(!cptr[-CELL_STRIDE].visit &&
		  ((cptr[-CELL_STRIDE * 2].depths[1] - cptr[0].depths[0] >=
		  CELL_HEIGHT + GAP_HEIGHT * 2) ||
		  (cptr[-CELL_STRIDE - 1].depths[3] >= GAP_WIDTH &&
		    cptr[-CELL_STRIDE - 1].depths[3] - cptr[0].depths[0] >=
		    KNOCK_THROUGH) ||
		  (cptr[-CELL_STRIDE + 1].depths[2] <= -GAP_WIDTH &&
		    cptr[-CELL_STRIDE + 1].depths[2] + cptr[0].depths[0] <=
		    -KNOCK_THROUGH)))
		{
		  knocked = -CELL_STRIDE;
		  cell.y -= 1;
		}
	      /*}}}*/
	      if(cptr->depths[0] == -(CELL_HEIGHT + GAP_HEIGHT))
		{
		  cptr[-CELL_STRIDE].visit = 1;
		  cptr[-CELL_STRIDE].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
		  broke = 1;
		}
	    }
	    /*}}}*/
	  pixel.y -= CELL_HEIGHT + GAP_HEIGHT;
	  if(mptr->offset.y == -(CELL_HEIGHT + GAP_HEIGHT))
	    {
	      mptr->offset.y = 0;
	      mptr->cell.y--;
	      nptr = cptr - CELL_STRIDE;
	    }
	  break;
	/*}}}*/
	/*{{{  case 1: (down)*/
	case 1:
	{
	  mptr->offset.y += VEL_Y;
	  mptr->pixel.y = pixel.y + mptr->offset.y;
	  if(cptr->depths[1] < mptr->offset.y)
	    /*{{{  munch*/
	    {
	      cptr->depths[1] = mptr->offset.y;
	      sptr = &sprites[SPRITE_CENTER_BASE];
	      munch_back(0, CELL_HEIGHT - VEL_Y - INTERNAL_HEIGHT,
		  CELL_WIDTH, VEL_Y + INTERNAL_HEIGHT,
		  pixel.x, pixel.y + cptr[0].depths[1] +
		  CELL_HEIGHT - VEL_Y - INTERNAL_HEIGHT, sptr);
	      if(cptr->sprite && mptr->offset.y < VEL_Y + INTERNAL_HEIGHT)
		back_sprite(cptr->sprite, 0, pixel.x, pixel.y);
	      if(mptr->offset.y > GAP_HEIGHT)
		cherry = cptr + CELL_STRIDE;
	      if(INRANGE(mptr->offset.y, GAP_HEIGHT, GAP_HEIGHT + VEL_Y) &&
		  cptr[CELL_STRIDE].visit)
		{
		  cptr[0].depths[1] = CELL_HEIGHT + GAP_HEIGHT;
		  cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
		  munch_edge_below(cptr, pixel.x, pixel.y, 0, 1);
		  broke = 1;
		}
	      if(INRANGE(mptr->offset.y, INTERNAL_HEIGHT + EXTERNAL_HEIGHT,
		  INTERNAL_HEIGHT + EXTERNAL_HEIGHT + VEL_Y))
		{
		  sptr = &sprites[SPRITE_EDGE_BASE + 1];
		  /*{{{  round bottom left corner?*/
		  if(cptr[0].depths[2] <= -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
		    munch_back((EDGE_WIDTH >> 1) - (CELL_WIDTH >> 1) -
			EXTERNAL_WIDTH, GAP_HEIGHT,
			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
			pixel.x - EXTERNAL_WIDTH, pixel.y + CELL_HEIGHT,
			sptr);
		  /*}}}*/
		  /*{{{  round bottom right corner?*/
		  if(cptr[0].depths[3] >= (INTERNAL_WIDTH + EXTERNAL_WIDTH))
		    munch_back((EDGE_WIDTH >> 1) + (CELL_WIDTH >> 1),
			GAP_HEIGHT,
			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
			pixel.x + CELL_WIDTH, pixel.y + CELL_HEIGHT, sptr);
		  /*}}}*/
		}
	      /*{{{  knocked through?*/
	      /*
	       * have we bumped into any of the following ?
	       * path from 2 below me
	       * path from below left
	       * path from below right
	       */
	      if(!cptr[CELL_STRIDE].visit &&
		  ((cptr[0].depths[1] - cptr[CELL_STRIDE * 2].depths[0] >=
		  CELL_HEIGHT + GAP_HEIGHT * 2) ||
		  (cptr[CELL_STRIDE - 1].depths[3] >= GAP_WIDTH &&
		    cptr[CELL_STRIDE - 1].depths[3] + cptr[0].depths[1] >=
		    KNOCK_THROUGH) ||
		  (cptr[CELL_STRIDE + 1].depths[2] <= -GAP_WIDTH &&
		    cptr[0].depths[1] - cptr[CELL_STRIDE + 1].depths[2] >=
		    KNOCK_THROUGH)))
		{
		  knocked = CELL_STRIDE;
		  cell.y += 1;
		}
	      /*}}}*/
	      if(cptr->depths[1] == (CELL_HEIGHT + GAP_HEIGHT))
		{
		  cptr[CELL_STRIDE].visit = 1;
		  cptr[CELL_STRIDE].depths[0] = -(CELL_HEIGHT + GAP_HEIGHT);
		  broke = 1;
		}
	    }
	    /*}}}*/
	  pixel.y += CELL_HEIGHT + GAP_HEIGHT;
	  if(mptr->offset.y == (CELL_HEIGHT + GAP_HEIGHT))
	    {
	      mptr->offset.y = 0;
	      mptr->cell.y++;
	      nptr = cptr + CELL_STRIDE;
	    }
	  break;
	}
	/*}}}*/
	/*{{{  case 2: (left)*/
	case 2:
	{
	  mptr->offset.x -= VEL_X;
	  mptr->pixel.x = pixel.x + mptr->offset.x;
	  if(cptr[0].depths[2] > mptr->offset.x)
	    /*{{{  munch*/
	    {
	      cptr[0].depths[2] = mptr->offset.x;
	      sptr = &sprites[SPRITE_CENTER_BASE];
	      munch_back(0, 0, VEL_X + INTERNAL_WIDTH, CELL_HEIGHT,
		  pixel.x + cptr[0].depths[2], pixel.y, sptr);
	      if(cptr->sprite && mptr->offset.x + VEL_X + INTERNAL_WIDTH > 0)
		back_sprite(cptr->sprite, 0, pixel.x, pixel.y);
	      if(mptr->offset.x < -GAP_WIDTH)
		cherry = cptr - 1;
	      if(INRANGE(mptr->offset.x, 1 - (GAP_WIDTH + VEL_X),
		  1 - GAP_WIDTH) && cptr[-1].visit)
		{
		  cptr[0].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
		  cptr[-1].depths[3] = CELL_WIDTH + GAP_WIDTH;
		  munch_edge_left(cptr, pixel.x, pixel.y, 1);
		  broke = 1;
		}
	      if(INRANGE(mptr->offset.x,
		  1 - (INTERNAL_WIDTH + EXTERNAL_WIDTH) - VEL_X,
		  1 - (INTERNAL_WIDTH + EXTERNAL_WIDTH)))
		{
		  sptr = &sprites[SPRITE_EDGE_BASE + 0];
		  /*{{{  round left top corner?*/
		  if(cptr[0].depths[0] <=
		      -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
		    munch_back(3 * GAP_WIDTH - EXTERNAL_WIDTH,
			(EDGE_HEIGHT >> 1) - (CELL_HEIGHT >> 1) -
			EXTERNAL_HEIGHT, EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
			pixel.x - EXTERNAL_WIDTH, pixel.y - EXTERNAL_HEIGHT,
			sptr);
		  /*}}}*/
		  /*{{{  round left bottom corner?*/
		  if(cptr[0].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
		    munch_back(3 * GAP_HEIGHT - EXTERNAL_WIDTH,
			(EDGE_HEIGHT >> 1) + (CELL_HEIGHT >> 1),
			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
			pixel.x - EXTERNAL_WIDTH,
			pixel.y + CELL_HEIGHT, sptr);
		  /*}}}*/
		}
	      /*{{{  knocked through?*/
	      /*
	       * have we bumped into any of the following ?
	       * path from 2 left me
	       * path from left above
	       * path from left below
	       */
	      if(!cptr[-1].visit &&
		  ((cptr[-2].depths[3] - cptr[0].depths[2] >=
		    CELL_WIDTH + GAP_WIDTH * 2) ||
		  (cptr[-CELL_STRIDE - 1].depths[1] >= GAP_HEIGHT &&
		    cptr[-CELL_STRIDE - 1].depths[1] - cptr[0].depths[2] >=
		    KNOCK_THROUGH) ||
		  (cptr[CELL_STRIDE - 1].depths[0] <= -GAP_HEIGHT &&
		    cptr[CELL_STRIDE - 1].depths[0] + cptr[0].depths[2] <=
		    -KNOCK_THROUGH)))
		{
		  knocked = -1;
		  cell.x -= 1;
		}
	      /*}}}*/
	      if(cptr->depths[2] == -(CELL_WIDTH + GAP_WIDTH))
		{
		  cptr[-1].visit = 1;
		  cptr[-1].depths[3] = CELL_WIDTH + GAP_WIDTH;
		  broke = 1;
		}
	    }
	    /*}}}*/
	  pixel.x -= CELL_WIDTH + GAP_WIDTH;
	  if(mptr->offset.x == -(CELL_WIDTH + GAP_WIDTH))
	    {
	      mptr->offset.x = 0;
	      mptr->cell.x--;
	      nptr = cptr - 1;
	    }
	  break;
	}
	/*}}}*/
	/*{{{  case 3: (right)*/
	case 3:
	{
	  mptr->offset.x += VEL_X;
	  mptr->pixel.x = pixel.x + mptr->offset.x;
	  if(cptr->depths[3] < mptr->offset.x)
	    /*{{{  munch*/
	    {
	      cptr->depths[3] = mptr->offset.x;
	      sptr = &sprites[SPRITE_CENTER_BASE];
	      munch_back(CELL_WIDTH - VEL_X - INTERNAL_WIDTH, 0,
		  VEL_X + INTERNAL_WIDTH, CELL_HEIGHT,
		  pixel.x + cptr->depths[3] +
		  CELL_WIDTH - VEL_X - INTERNAL_WIDTH, pixel.y, sptr);
	      if(cptr->sprite && mptr->offset.x < VEL_X + INTERNAL_WIDTH)
		back_sprite(cptr->sprite, 0, pixel.x, pixel.y);
	      if(mptr->offset.x > GAP_WIDTH)
		cherry = cptr + 1;
	      if(INRANGE(mptr->offset.x, GAP_WIDTH, GAP_WIDTH + VEL_X) &&
		  cptr[1].visit)
		{
		  cptr[0].depths[3] = CELL_WIDTH + GAP_WIDTH;
		  cptr[1].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
		  munch_edge_right(cptr, pixel.x, pixel.y, 1);
		  broke = 1;
		}
	      if(INRANGE(mptr->offset.x, INTERNAL_WIDTH + EXTERNAL_WIDTH,
		  INTERNAL_WIDTH + EXTERNAL_WIDTH + VEL_X))
		{
		  sptr = &sprites[SPRITE_EDGE_BASE + 0];
		  /*{{{  round right top corner?*/
		  if(cptr[0].depths[0] <=
		      -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
		    munch_back(GAP_HEIGHT, (EDGE_HEIGHT >> 1) -
			(CELL_HEIGHT >> 1) - EXTERNAL_HEIGHT,
			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
			pixel.x + CELL_WIDTH, pixel.y - EXTERNAL_HEIGHT,
			sptr);
		  /*}}}*/
		  /*{{{  round right bottom corner?*/
		  if(cptr[0].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
		    munch_back(GAP_WIDTH,
			(EDGE_HEIGHT >> 1) + (CELL_WIDTH >> 1),
			EXTERNAL_WIDTH, EXTERNAL_HEIGHT,
			pixel.x + CELL_WIDTH, pixel.y + CELL_HEIGHT, sptr);
		  /*}}}*/
		}
	      /*{{{  knocked through?*/
	      /*
	       * have we bumped into any of the following ?
	       * path from 2 right me
	       * path from right above
	       * path from right below
	       */
	      if(!cptr[1].visit && ((cptr[0].depths[3] - cptr[2].depths[2] >=
		  CELL_WIDTH + GAP_WIDTH * 2) ||
		  (cptr[-CELL_STRIDE + 1].depths[1] >= GAP_HEIGHT &&
		    cptr[-CELL_STRIDE + 1].depths[1] + cptr[0].depths[3] >=
		    KNOCK_THROUGH) ||
		  (cptr[CELL_STRIDE + 1].depths[0] <= -GAP_HEIGHT &&
		    cptr[0].depths[3] - cptr[CELL_STRIDE + 1].depths[0] >=
		    KNOCK_THROUGH)))
		{
		  knocked = 1;
		  cell.x += 1;
		}
	      /*}}}*/
	      if(cptr->depths[3] == (CELL_WIDTH + GAP_WIDTH))
		{
		  cptr[1].visit = 1;
		  cptr[1].depths[2] = -(CELL_WIDTH + GAP_WIDTH);
		  broke = 1;
		}
	    }
	    /*}}}*/
	  pixel.x += CELL_WIDTH + GAP_WIDTH;
	  if(mptr->offset.x == (CELL_WIDTH + GAP_WIDTH))
	    {
	      mptr->offset.x = 0;
	      mptr->cell.x++;
	      nptr = cptr + 1;
	    }
	  break;
	}
	/*}}}*/
      }
    }
  /*{{{  knocked through?*/
  /*
   * if we knocked through to an adjoining cell
   * we clear out the specified cell and check all the corners
   * note, cell has already been altered correctly
   * we must also check to se if this launches an apple
   */
  if(knocked)
    {
      broke = 1;
      assert(!cherry || cherry == cptr + knocked);
      cherry = cptr += knocked;
      connect_hole(cptr);
      munch_hole(cptr, pixel.x, pixel.y);
    }
  /*}}}*/
  /*{{{  redraw or blank prize?*/
  if(nptr && nptr->sprite)
    {
      assert(!cherry || cherry == nptr);
      if((mptr->type != 4 && nptr->sprite >= SPRITE_PRIZE_BASE) ||
	  (nptr->sprite != SPRITE_CHERRY && nptr->sprite < SPRITE_PRIZE_BASE))
	back_sprite(nptr->sprite, 0, pixel.x, pixel.y);
      else
	munch_back(0, 0, CELL_WIDTH, CELL_HEIGHT,
	    pixel.x, pixel.y, &sprites[SPRITE_CENTER_BASE +
	    (nptr->sprite != SPRITE_CHERRY)]);
    }
  else if(cherry && cherry->sprite)
    back_sprite(cherry->sprite, 0, pixel.x, pixel.y);
  /*}}}*/
  if(update.set)
    {
      bounding_box(mptr->pixel.x, mptr->pixel.y, CELL_WIDTH, CELL_HEIGHT);
      bounding_box(mptr->old_pixel.x, mptr->old_pixel.y,
	  CELL_WIDTH, CELL_HEIGHT);
      add_background(update.tl.x, update.tl.y,
	  (unsigned)(update.br.x - update.tl.x),
	  (unsigned)(update.br.y - update.tl.y));
      mptr->back = 1;
    }
  if(broke)
    global.broken = 1;
  /* check we haven't wandered off the board */
  assert(!nptr || nptr->visit);
  assert(INRANGE(mptr->cell.y, 0, CELLS_DOWN));
  assert(INRANGE(mptr->cell.x, 0, CELLS_ACROSS));
  assert(mptr->cell.x || mptr->offset.x >= 0);
  assert(mptr->cell.y || mptr->offset.y >= 0);
  assert(mptr->cell.x < CELLS_ACROSS - 1 || mptr->offset.x <= 0);
  assert(mptr->cell.y < CELLS_DOWN - 1 || mptr->offset.y <= 0);
  assert(!mptr->offset.x || !mptr->offset.y);
  return nptr;
}
/*}}}*/
/*{{{  void munch_back(sx, sy, width, height, dx, dy, sprite)*/
static VOIDFUNC munch_back
FUNCARG((sx, sy, width, height, dx, dy, sprite),
	int       sx
ARGSEP  int       sy
ARGSEP  unsigned  width
ARGSEP  unsigned  height
ARGSEP  int       dx
ARGSEP  int       dy
ARGSEP  SPRITE CONST *sprite
)
/*
 * munches the background image with the specified sprite
 */
{
  if(display.background != COLOUR_ONE)
    XCopyArea(display.display, sprite->mask, display.back, GCN(GC_MASK),
	sx, sy, width, height, dx, dy);
  if(display.background != COLOUR_ZERO)
    XCopyArea(display.display, sprite->image, display.back, GCN(GC_OR),
	sx, sy, width, height, dx, dy);
  bounding_box(dx, dy, width, height);
  return;
}
/*}}}*/
/*{{{  void munch_hole(cptr, x, y)*/
extern VOIDFUNC munch_hole
FUNCARG((cptr, x, y),
	CELL      *cptr
ARGSEP  int       x
ARGSEP  int       y
)
/*
 * cut out the background for a whole cell, and
 * deal with connections to the adjoining cells
 * don't forget to replot the cherry
 */
{
  /*{{{  cut out the center*/
  {
    SPRITE    *sptr;

    sptr = &sprites[SPRITE_CENTER_BASE];
    munch_back(0, 0,
	CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
	x, y,
	&sptr[!!(cptr[0].depths[0] || cptr[0].depths[2])]);
    munch_back(CELL_WIDTH >> 1, 0,
	CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
	x + (CELL_WIDTH >> 1), y,
	&sptr[!!(cptr[0].depths[0] || cptr[0].depths[3])]);
    munch_back(CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
	CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
	x + (CELL_WIDTH >> 1), y + (CELL_HEIGHT >> 1),
	&sptr[!!(cptr[0].depths[1] || cptr[0].depths[3])]);
    munch_back(0, CELL_HEIGHT >> 1,
	CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
	x, y + (CELL_HEIGHT >> 1),
	&sptr[!!(cptr[0].depths[1] || cptr[0].depths[2])]);
  }
  /*}}}*/
  if(cptr[0].depths[0] <= -GAP_HEIGHT && cptr[-CELL_STRIDE].visit)
    munch_edge_above(cptr, x, y, 0);
  if(cptr[0].depths[1] >= GAP_HEIGHT && cptr[CELL_STRIDE].visit)
    munch_edge_below(cptr, x, y, 0, 0);
  if(cptr[0].depths[2] <= -GAP_WIDTH && cptr[-1].visit)
    munch_edge_left(cptr, x, y, 0);
  if(cptr[0].depths[3] >= GAP_WIDTH && cptr[1].visit)
    munch_edge_right(cptr, x, y, 0);
  return;
}
/*}}}*/
/*{{{  void munch_edge_above(cptr, x, y, flag)*/
static VOIDFUNC munch_edge_above
FUNCARG((cptr, x, y, flag),
	CELL      *cptr
ARGSEP  int       x
ARGSEP  int       y
ARGSEP  unsigned  flag
)
/*
 * munches an edge above the cell, replots the cherry above if needed
 */
{
  unsigned  check;
    /* remember kiddies, never leave signed cheques lying about */
  unsigned  type;
  SPRITE    *sptr;

  sptr = &sprites[SPRITE_EDGE_BASE + 1];
  check = 0;
  type = 0;
  if(cptr[0].depths[2] <= -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
    type = 2 * GAP_HEIGHT;
  if(cptr[-CELL_STRIDE].depths[2] <= -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
    type += GAP_HEIGHT;
  if(type == 3 * GAP_HEIGHT && (cptr[-CELL_STRIDE - 1].depths[1] -
      cptr[-1].depths[0] >= GAP_HEIGHT))
    type = 4 * GAP_HEIGHT;
  munch_back(0, (int)type, EDGE_WIDTH >> 1, GAP_HEIGHT,
      x + (CELL_WIDTH >> 1) - (EDGE_WIDTH >> 1), y - GAP_HEIGHT, sptr);
  if(flag && !cptr[-CELL_STRIDE].depths[2])
    {
      check = 1;
      munch_back(0, 0, EDGE_WIDTH >> 1, GAP_HEIGHT,
	  x + (CELL_WIDTH >> 1) - (EDGE_WIDTH >> 1),
	  y - GAP_HEIGHT * 2, sptr);
    }
  type = 0;
  if(cptr[0].depths[3] >= (INTERNAL_WIDTH + EXTERNAL_WIDTH))
    type = 2 * GAP_HEIGHT;
  if(cptr[-CELL_STRIDE].depths[3] >= (INTERNAL_WIDTH + EXTERNAL_WIDTH))
    type += GAP_HEIGHT;
  if(type == 3 * GAP_HEIGHT && (cptr[-CELL_STRIDE + 1].depths[1] -
      cptr[1].depths[0] >= GAP_HEIGHT))
    type = 4 * GAP_HEIGHT;
  munch_back(EDGE_WIDTH >> 1, (int)type, EDGE_WIDTH >> 1, GAP_HEIGHT,
      x + (CELL_WIDTH >> 1), y - GAP_HEIGHT, sptr);
  if(flag && !cptr[-CELL_STRIDE].depths[3])
    {
      check = 1;
      munch_back(EDGE_WIDTH >> 1, 0, EDGE_WIDTH >> 1, GAP_HEIGHT,
	  x + (CELL_WIDTH >> 1), y - GAP_HEIGHT * 2, sptr);
    }
  if(check && cptr[-CELL_STRIDE].sprite)
      back_sprite(cptr[-CELL_STRIDE].sprite, 0,
	  x, y - CELL_HEIGHT - GAP_HEIGHT);
  return;
}
/*}}}*/
/*{{{  void munch_edge_below(cptr, x, y, offset, flag)*/
static VOIDFUNC munch_edge_below
FUNCARG((cptr, x, y, offset, flag),
	CELL      *cptr
ARGSEP  int       x
ARGSEP  int       y
ARGSEP  int       offset
ARGSEP  unsigned  flag
)
/*
 * munches an edge below the cell, replots the cherry above if needed
 * the edge can be offset, in the case of non-aligned apples
 */
{
  unsigned  check;
  unsigned  type;
  SPRITE    *sptr;

  sptr = &sprites[SPRITE_EDGE_BASE + 1];
  check = 0;
  type = 0;
  if(cptr[0].depths[2] - offset <= -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
    type = GAP_HEIGHT;
  if(cptr[CELL_STRIDE].depths[2] - offset <=
      -(INTERNAL_WIDTH + EXTERNAL_WIDTH))
    type += 2 * GAP_HEIGHT;
  if(!offset && type == 3 * GAP_HEIGHT && (cptr[-1].depths[1] -
      cptr[CELL_STRIDE - 1].depths[0] >= GAP_HEIGHT))
    type = 4 * GAP_HEIGHT;
  munch_back(0, (int)type, EDGE_WIDTH >> 1, GAP_HEIGHT,
      x + (CELL_WIDTH >> 1) - (EDGE_WIDTH >> 1), y + CELL_HEIGHT, sptr);
  if(flag && !cptr[CELL_STRIDE].depths[2])
    {
      check = 1;
      munch_back(0, 0, EDGE_WIDTH >> 1, GAP_HEIGHT,
	  x + (CELL_WIDTH >> 1) - (EDGE_WIDTH >> 1),
	  y + CELL_HEIGHT + GAP_HEIGHT, sptr);
    }
  type = 0;
  if(cptr[0].depths[3] - offset >= (INTERNAL_WIDTH + EXTERNAL_WIDTH))
    type = GAP_HEIGHT;
  if(cptr[CELL_STRIDE].depths[3] - offset >=
      (INTERNAL_WIDTH + EXTERNAL_WIDTH))
    type += 2 * GAP_HEIGHT;
  if(!offset && type == 3 * GAP_HEIGHT &&
      (cptr[1].depths[1] -
      cptr[CELL_STRIDE + 1].depths[0] >= GAP_HEIGHT))
    type = 4 * GAP_HEIGHT;
  munch_back(EDGE_WIDTH >> 1, (int)type, EDGE_WIDTH >> 1, GAP_HEIGHT,
      x + (CELL_WIDTH >> 1), y + CELL_HEIGHT, sptr);
  if(flag && !cptr[CELL_STRIDE].depths[3])
    {
      check = 1;
      munch_back(EDGE_WIDTH >> 1, 0, EDGE_WIDTH >> 1, GAP_HEIGHT,
	  x + (CELL_WIDTH >> 1), y + CELL_HEIGHT + GAP_HEIGHT, sptr);
    }
  if(check && cptr[CELL_STRIDE].sprite)
      back_sprite(cptr[CELL_STRIDE].sprite, 0,
	  x, y + CELL_HEIGHT + GAP_HEIGHT);
  return;
}
/*}}}*/
/*{{{  void munch_edge_left(cptr, x, y, flag)*/
static VOIDFUNC munch_edge_left
FUNCARG((cptr, x, y, flag),
	CELL      *cptr
ARGSEP  int       x
ARGSEP  int       y
ARGSEP  unsigned  flag
)
/*
 * munches an edge left of the cell, replots the cherry above if needed
 */
{
  unsigned  check;
  unsigned  type;
  SPRITE    *sptr;

  sptr = &sprites[SPRITE_EDGE_BASE + 0];
  check = 0;
  type = 0;
  if(cptr[0].depths[0] <= -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
    type = 2 * GAP_WIDTH;
  if(cptr[-1].depths[0] <= -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
    type += GAP_WIDTH;
  if(type == 3 * GAP_WIDTH && (cptr[-CELL_STRIDE - 1].depths[3] -
      cptr[-CELL_STRIDE].depths[2] >= GAP_WIDTH))
    type = 4 * GAP_WIDTH;
  munch_back((int)type, 0, GAP_HEIGHT, EDGE_HEIGHT >> 1,
      x - GAP_WIDTH, y + (CELL_HEIGHT >> 1) - (EDGE_HEIGHT >> 1), sptr);
  if(flag && !cptr[-1].depths[0])
    {
      check = 1;
      munch_back(0, 0, GAP_WIDTH, EDGE_HEIGHT >> 1, x - GAP_WIDTH * 2,
	  y + (CELL_HEIGHT >> 1) - (EDGE_HEIGHT >> 1), sptr);
    }
  type = 0;
  if(cptr[0].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
    type = 2 * GAP_WIDTH;
  if(cptr[-1].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
    type += GAP_WIDTH;
  if(type == 3 * GAP_WIDTH && (cptr[CELL_STRIDE - 1].depths[3] -
      cptr[CELL_STRIDE].depths[2] >= GAP_WIDTH))
    type = 4 * GAP_WIDTH;
  munch_back((int)type, EDGE_HEIGHT >> 1, GAP_WIDTH, EDGE_HEIGHT >> 1,
      x - GAP_WIDTH, y + (CELL_HEIGHT >> 1), sptr);
  if(flag && !cptr[-1].depths[1])
    {
      check = 1;
      munch_back(0, EDGE_HEIGHT >> 1, GAP_WIDTH, EDGE_HEIGHT >> 1,
	  x - GAP_WIDTH * 2, y + (CELL_HEIGHT >> 1), sptr);
    }
  if(check && cptr[-1].sprite)
      back_sprite(cptr[-1].sprite, 0, x - CELL_WIDTH - GAP_WIDTH, y);
  return;
}
/*}}}*/
/*{{{  void munch_edge_right(cptr, x, y, flag)*/
static VOIDFUNC munch_edge_right
FUNCARG((cptr, x, y, flag),
	CELL      *cptr
ARGSEP  int       x
ARGSEP  int       y
ARGSEP  unsigned  flag
)
/*
 * munches an edge right of the cell, replots the cherry above if needed
 */
{
  unsigned  check;
  unsigned  type;
  SPRITE    *sptr;

  sptr = &sprites[SPRITE_EDGE_BASE + 0];
  check = 0;
  type = 0;
  if(cptr[0].depths[0] <= -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
    type = GAP_WIDTH;
  if(cptr[1].depths[0] <= -(INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
    type += 2 * GAP_WIDTH;
  if(type == 3 * GAP_WIDTH &&
      (cptr[-CELL_STRIDE].depths[3] -
      cptr[-CELL_STRIDE + 1].depths[2] >= GAP_WIDTH))
    type = 4 * GAP_WIDTH;
  munch_back((int)type, 0, GAP_WIDTH, EDGE_HEIGHT >> 1,
      x + CELL_WIDTH, y + (CELL_HEIGHT >> 1) - (EDGE_HEIGHT >> 1), sptr);
  if(flag && !cptr[1].depths[0])
    {
      check = 1;
      munch_back(0, 0, GAP_WIDTH, EDGE_HEIGHT >> 1,
	  x + CELL_WIDTH + GAP_WIDTH,
	  y + (CELL_HEIGHT >> 1) - (EDGE_HEIGHT >> 1), sptr);
    }
  type = 0;
  if(cptr[0].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
    type = GAP_WIDTH;
  if(cptr[1].depths[1] >= (INTERNAL_HEIGHT + EXTERNAL_HEIGHT))
    type += 2 * GAP_WIDTH;
  if(type == 3 * GAP_WIDTH &&
      (cptr[CELL_STRIDE].depths[3] -
      cptr[CELL_STRIDE + 1].depths[2] >= GAP_WIDTH))
    type = 4 * GAP_WIDTH;
  munch_back((int)type, EDGE_HEIGHT >> 1, GAP_WIDTH, EDGE_HEIGHT >> 1,
      x + CELL_WIDTH, y + (CELL_HEIGHT >> 1), sptr);
  if(flag && !cptr[1].depths[1])
    {
      check = 1;
      munch_back(0, EDGE_HEIGHT >> 1, GAP_WIDTH, EDGE_HEIGHT >> 1,
	  x + CELL_WIDTH + GAP_WIDTH, y + (CELL_HEIGHT >> 1), sptr);
    }
  if(check && cptr[1].sprite)
      back_sprite(cptr[1].sprite, 0, x + CELL_WIDTH + GAP_WIDTH, y);
  return;
}
/*}}}*/
/*{{{  void new_face(mptr)*/
extern VOIDFUNC new_face
FUNCARG((mptr),
	MONSTER *mptr
)
/*
 * calculates the required face for the direction,
 * given the old face
 */
{
  int       dir;
  int       face;

  if(mptr->push)
    dir = mptr->dir ^ 1;
  else
    dir = mptr->dir;
  face = mptr->face < 6 ? mptr->face : 2 + (mptr->face & 1);
  if(mptr->squished)
    mptr->face = 16 + (face > 2);
  else if(dir & 2)
    mptr->face = dir;
  else if(face == 2)
    mptr->face = dir;
  else if(face == 3)
    mptr->face = dir + 4;
  else if((dir ^ face) & 1)
    mptr->face ^= 5;
  if(mptr->face & 2 && mptr->pushing)
    mptr->face += 4;
  return;
}
/*}}}*/
/*{{{  unsigned run_home(mptr, cptr)*/
extern unsigned run_home
FUNCARG((mptr, cptr),
	MONSTER   *mptr
ARGSEP  CELL      *cptr
)
/*
 * sets the direction bit to run home
 */
{
  unsigned  preferred;
  int       offset;

  if((offset = mptr->offset.y) != 0)
    /*{{{  up down only*/
    {
      if(offset < 0 ? cptr[0].xtra <
	  cptr[-CELL_STRIDE].xtra :
	  cptr[0].xtra >= cptr[CELL_STRIDE].xtra)
	preferred = 0x2;
      else
	preferred = 0x1;
    }
    /*}}}*/
  else if(cptr->xtra == 1)
    preferred = mptr->pixel.x > PIXELX(4,
	extra.select * XTRA_SPACING) ? 0x4 : 0x8;
  else if((offset = mptr->offset.x) != 0)
    /*{{{  left right only*/
    {
      if(offset < 0 ? cptr[0].xtra < cptr[-1].xtra :
	  cptr[0].xtra >= cptr[1].xtra)
	preferred = 0x8;
      else
	preferred = 0x4;
      if(offset == cptr->holes[0] ||
	  offset == cptr->holes[1])
	{
	  if(cptr[0].xtra > cptr[-CELL_STRIDE].xtra)
	    preferred |= 0x1;
	}
      if(offset == cptr->holes[2] ||
	  offset == cptr->holes[3])
	{
	  if(cptr[0].xtra > cptr[CELL_STRIDE].xtra)
	    preferred |= 0x2;
	}
    }
    /*}}}*/
  else
    /*{{{  at intersection*/
    {
      int       distance;

      distance = cptr->xtra;
      preferred = 0;
      if(distance > cptr[-CELL_STRIDE].xtra)
	preferred |= 0x1;
      if(distance > cptr[CELL_STRIDE].xtra)
	preferred |= 0x2;
      if(distance > cptr[-1].xtra)
	preferred |= 0x4;
      if(distance > cptr[1].xtra)
	preferred |= 0x8;
    }
    /*}}}*/
  return preferred;
}
/*}}}*/
/*{{{  unsigned valid_directions(mptr, cptr)*/
extern unsigned valid_directions
FUNCARG((mptr, cptr),
	MONSTER   *mptr
ARGSEP  CELL      *cptr
)
/*
 * sets the valid and nearer direction bits
 * these are nr, nl, nd, nu, r, l, d, u
 */
{
  unsigned  answer;
  int       offset;

  answer = 0;
  assert(cptr->visit);
  if((offset = mptr->offset.y) != 0)
    /*{{{  up down only*/
    {
      if(!mptr->cell.y && offset < 0)
	answer |= 2;
      else if(mptr->offset.x)
	answer |= 0x3;
      else
	{
	  if(offset > cptr->depths[0])
	    answer |= 0x1;
	  if(offset < cptr->depths[1])
	    answer |= 0x2;
	}
      if(cptr == monster.player)
	answer |= monster.list[0].offset.y > offset ? 0x20 : 0x10;
      else if(offset < 0 ? cptr[0].distance < cptr[-CELL_STRIDE].distance :
	  cptr[0].distance >= cptr[CELL_STRIDE].distance)
	answer |= 0x20;
      else
	answer |= 0x10;
    }
    /*}}}*/
  else if((offset = mptr->offset.x) != 0)
    /*{{{  left right only*/
    {
      if(offset > cptr->depths[2])
	answer |= 0x4;
      if(offset < cptr->depths[3])
	answer |= 0x8;
      if(cptr == monster.player)
	answer |= monster.list[0].offset.x > offset ? 0x80 : 0x40;
      else if(offset < 0 ? cptr[0].distance < cptr[-1].distance :
	  cptr[0].distance >= cptr[1].distance)
	answer |= 0x80;
      else
	answer |= 0x40;
      if(offset == cptr->holes[0] || offset == cptr->holes[1])
	{
	  answer |= 0x1;
	  if(cptr[0].distance > cptr[-CELL_STRIDE].distance)
	    answer |= 0x10;
	}
      if(offset == cptr->holes[2] || offset == cptr->holes[3])
	{
	  answer |= 0x2;
	  if(cptr[0].distance > cptr[CELL_STRIDE].distance)
	    answer |= 0x20;
	}
    }
    /*}}}*/
  else
    /*{{{  at intersection*/
    {
      int       distance;

      if(cptr->depths[0])
	answer |= 0x1;
      if(cptr->depths[1])
	answer |= 0x2;
      if(cptr->depths[2])
	answer |= 0x4;
      if(cptr->depths[3])
	answer |= 0x8;
      distance = cptr->distance;
      if(cptr == monster.player)
	{
	  if(monster.list[0].offset.x < 0)
	    answer |= 0x40;
	  else if(monster.list[0].offset.x > 0)
	    answer |= 0x80;
	  else if(monster.list[0].offset.y < 0)
	    answer |= 0x10;
	  else if(monster.list[0].offset.y > 0)
	    answer |= 0x20;
	}
      else
	{
	  if(distance > cptr[-CELL_STRIDE].distance)
	    answer |= 0x10;
	  if(distance > cptr[CELL_STRIDE].distance)
	    answer |= 0x20;
	  if(distance > cptr[-1].distance)
	    answer |= 0x40;
	  if(distance > cptr[1].distance)
	    answer |= 0x80;
	}
    }
    /*}}}*/
  answer &= answer << 4 | 0xF;
  return answer;
}
/*}}}*/
