/************************************************************************/
/*                                                                      */
/*                                   ######################             */
/*      A n t h o n y                      ##  ##  ##                   */
/*                                         ##########                   */
/*              T h y s s e n            ####  ##  ####                 */
/*                                     ####    ##    ####               */
/*                                   ####      ##      ####             */
/*                                                                      */
/************************************************************************/

/*
**  Spiders - for AMIGA under lattace C
*/

#include <stdio.h>
#include <string.h>
#include <intuition/intuition.h>
#include <graphics/gfx.h>
#include <exec/exec.h>
#include <proto/all.h>
#define   BM_SRC   0xC0      /* minterm source only */
#define   BM_DST   0xA0      /* minterm destination only */

#include "spiders.h"

struct Library       *IntuitionBase = NULL, *GfxBase = NULL;
struct Remember      *Remember = NULL;
struct Screen        *WBScrn = NULL;
struct Window        *Window = NULL;          /* Window pointer */
struct Task          *MyTask = NULL;          /* my tasks address */
struct IntuiMessage  *message;
ULONG                WindowSignal;            /* IDCMP window signal */
int                  WBDepth, WBScrX, WBScrY; /* Screen info */
struct NewWindow     NW = {
        30, 200, 205, 10, 0, 1,               /* position, size and colors */
        CLOSEWINDOW,                          /* IDCMP messages */
        ACTIVATE| SIMPLE_REFRESH| WINDOWDEPTH|
           WINDOWDRAG| WINDOWCLOSE,           /* window flags */
        NULL, NULL,                           /* gadget, checkmark */
        (UBYTE *) " -- Spiders -- ",          /* name of window */
        NULL,NULL,                            /* screen-> , BitMap-> */
        0,0,0,0,                              /* min and max size (disable) */
        WBENCHSCREEN                          /* screen type */
        };


typedef struct {
  int           x, y;
}               point;

typedef enum {
    Sleeping,          /* Waiting to start - off screen */
    Waiting,           /* Comtemplating the decent */
    Decending,         /* Going down screen */
    Landed,            /* Getting a bit of screen */
    Ascending,         /* Going up the screen */
    Munching           /* eating its bit of screen */
} status;

static struct {
  point              ulc;               /* upper left corner of spider */
  status             stat;              /* current status of crab */
  int                time;              /* time count down to actions */
  struct RastPort    bitRP;             /* raster port for screen bit */
  struct BitMap      bitBM;             /* bit map for the screen bit */
}                    Spider[NUMBER];    /* spider' state */

/* images and masks */
unsigned short chip  SpiderImage[] =
#  include "spiderimage.i"
unsigned short chip  MaskImage[] =
#  include "maskimage.i"

struct RastPort      SpiderRP, MaskRP, ScreenRP;      /* RastPorts */
struct BitMap        SpiderBM, MaskBM;                /* BitMaps */

#ifdef CBACK
/* Cback.o varibles */
long _stack = 2000;
char *_procname = "spiders";
long _priority = -1;
long _BackGroundIO = 0;
#endif

  /* function declarations */
extern void  Init(), OpenLibraries(), SpiderExit(), Cycle();
extern void  HideScrBit(), DrawScrBit(), HideSpider(), DrawSpider();
extern int   TestDown(), RandInt();
extern void  RandInit();

void
_main()
  /* I am not using printf's functions so _main() will do */
{
  Init();                              /* set up initial grey crab layer */

  for (;;) {                           /* no way out! */
    Cycle();                           /* move the crabs */
  }
  /* NOTREACHED */
}

void
Init()
{
  int i, j;

  RandInit();

  OpenLibraries();        /* open the various system structures */

  if( !(Window = OpenWindow( &NW )) )
    SpiderExit();
  WindowSignal = 1<<Window->UserPort->mp_SigBit;

  MyTask = FindTask( NULL );
  WBScrn = Window->WScreen;
  WBDepth = 2;                            /* Assume it (for WB pic progs) */
  WBScrX = WBScrn->Width -XSPIDER;
  WBScrY = WBScrn->Height -YSPIDER;

  /* grab a copy of the screens raster port */
  memcpy( (char *)&ScreenRP, (char *)&WBScrn->RastPort,
                                       sizeof( struct RastPort ) );

  /* define spiders */
  for (i = 0; i < NUMBER; i++) {
    Spider[i].stat = Sleeping;               /* spider is just sleeping */
    Spider[i].time = RandInt(1, SLEEPTIME);
      /* initialise bitmaps */
    InitRastPort( &Spider[i].bitRP );
    Spider[i].bitRP.BitMap = &Spider[i].bitBM;
    InitBitMap( &Spider[i].bitBM, WBDepth, XMASK, YMASK);
    for( j = 0; j < WBDepth; j++ )
      if( !( Spider[i].bitBM.Planes[j] = (PLANEPTR)
           AllocRemember( &Remember, RASSIZE(XMASK, YMASK),
               MEMF_CHIP | MEMF_CLEAR ) ) )
        SpiderExit();
  }
    InitRastPort( &MaskRP );
    MaskRP.BitMap = &MaskBM;
    InitBitMap( &MaskBM, WBDepth, XMASK, YMASK);
    for( j = 0; j < WBDepth; j++ )
      MaskBM.Planes[j] = (PLANEPTR) &MaskImage;

    InitRastPort( &SpiderRP );
    SpiderRP.BitMap = &SpiderBM;
    InitBitMap( &SpiderBM, 1, XSPIDER, YSPIDER);
    SpiderBM.Planes[0] = (PLANEPTR) &SpiderImage;
}

void
OpenLibraries()
{
  if( ! (IntuitionBase = OpenLibrary( "intuition.library", 0 )) )
    SpiderExit();
  if( ! (GfxBase = OpenLibrary( "graphics.library", 0 )) )
    SpiderExit();
}

void
SpiderExit()
/* exit - remove all traces */
{
  FreeRemember( &Remember, TRUE );          /* free all allocated memory */

  if( Window )          CloseWindow( Window );
  if( GfxBase )         CloseLibrary( GfxBase );
  if( IntuitionBase )   CloseLibrary(  IntuitionBase );

  exit(0);
}

void
Cycle()
 /* one motion cycle for all spiders */
{
  int             s;                 /* spider to do */

  /* test for window closure */
  if( message = (void *)GetMsg(Window->UserPort) ) {
    ReplyMsg( (void *) message );
    switch( message->Class ) {
      case CLOSEWINDOW : /* window closed message */
        SpiderExit();
    }
  } /* Message test */

#if DELAY
  Delay(DELAY);
#endif

  for (s = 0; s < NUMBER; s++) {
    switch( Spider[s].stat ) {
      case Sleeping:
        {
          int i, test;

          if( --Spider[s].time ) break;     /* spider not yet ready to start */
          do {
            Spider[s].ulc.x = RandInt(0, WBScrX);
            test = TRUE;                    /* assume this position is OK */
            for(i = 0; i < NUMBER; i++)
              if( Spider[i].stat != Sleeping &&
                  Spider[s].ulc.x > Spider[i].ulc.x - XSPIDER &&
                  Spider[i].ulc.x > Spider[s].ulc.x - XSPIDER ) {
                test = FALSE;   /* spider on screen already in this position */
                break;
              }
          } while ( !test );    /* repeat search until good position found */
          Spider[s].ulc.y = SCREENTOP;
          Spider[s].stat = Waiting;
          Spider[s].time = WAITTIME;
          DrawSpider(s);
        }
        break;

      case Waiting:
          if( Spider[s].time-- )   break;
          Spider[s].stat = Decending;
          break;

      case Decending:
        {
          int   y, speed;

          y = Spider[s].ulc.y;
          speed = TestDown(s);      /* check on landing or screen bottom */
          y += speed;                /* Decend quickly or stop? */
          if( !speed )
            Spider[s].stat = Landed;
          Spider[s].ulc.y = y;
          DrawSpider(s);            /* don't hide spide to leave ... trail */
        }
        break;

      case Landed:
        /* bit of screen already read in testdown() just wait */
        Spider[s].stat = Ascending;
        break;

      case Ascending:
        {
          int   y;

          y = Spider[s].ulc.y;
          y--;                         /* Ascend at a slow rate */
          if( y < SCREENTOP ) {        /* Spider hit rock bottom */
            y = SCREENTOP;
            Spider[s].stat = Munching;
            Spider[s].time = (YMASK+2) * EATRATE-1;
          }
          HideSpider(s); HideScrBit(s);
          Spider[s].ulc.y = y;
          DrawSpider(s); DrawScrBit(s);
        }
        break;

      case Munching:
        if( !Spider[s].time ) {
          /* reinitialize start */
          Spider[s].stat = Sleeping;
          Spider[s].time = RandInt(1, SLEEPTIME);   /* random delay in starting */
          HideSpider(s);
          break;
        }
        if( !(Spider[s].time-- % EATRATE ) ) {       /* eat another bit? */
          HideScrBit(s);
          ScrollRaster( &Spider[s].bitRP, 0, 1, 0, 0, XMASK-1, YMASK-1);
          DrawScrBit(s);
        }
        break;
    }
  }    /* move next spider */
}

int
TestDown(s)
  int             s;
/* returns distance to first screen bit below spider */
/* if the Spider is withing SPEED of that piece  */
/* Also gets the actual piece into the bitRP of spider */
{
  int    i, j, size, speed = SPEED;
  UBYTE *bits0, *bits1;
  /* clear out any old image */
  SetRast( &Spider[s].bitRP, 0 );

  size = WBScrY - Spider[s].ulc.y;
  if( size > YMASK )    size = YMASK;     /* reduce image size */
  if( size < speed )    speed = size;     /* reduce speed - don't over shoot */
  if( !size )           return 0;         /* oops rock bottom! */

  /* get a image from screen */
  ClipBlit( &ScreenRP, Spider[s].ulc.x, Spider[s].ulc.y + YSPIDER,
            &Spider[s].bitRP, 0, 0,
            XMASK, size, BM_SRC);
  /* mask out background */
  ClipBlit( &MaskRP, 0, 0,
            &Spider[s].bitRP, 0, 0,
            XMASK, YMASK, BM_SRC & BM_DST);

  /* check the data to find distance to landing on screen bit */
  bits0 = Spider[s].bitBM.Planes[0];
  bits1 = Spider[s].bitBM.Planes[1];
  for (i = 0; i < speed; i++)
    for( j = 0; j < Spider[s].bitBM.BytesPerRow; j++ )
      if( *(bits0++) || *(bits1++) )   /* if a bit in either plane */
        return i;                      /* return distance to screen bit */

  return speed;                        /* return speed */
}

void
HideScrBit(s)
  int s;
  /* remove screen bit carried */
{
  int  size;

  size = WBScrY - Spider[s].ulc.y;
  if( size > YMASK )    size = YMASK;      /* reduce image size */
  if( !size )           return;            /* noting to erase */

  ClipBlit( &MaskRP, 0, 0,
            &ScreenRP, Spider[s].ulc.x, Spider[s].ulc.y + YSPIDER,
            XMASK, size, ~BM_SRC & BM_DST);
}

void
DrawScrBit(s)
  int s;
/* draw screen bit carried */
{
  int  size;

  size = WBScrY - Spider[s].ulc.y;
  if( size > YMASK )    size = YMASK;     /* reduce image size */
  if( !size )           return;           /* no room to draw */
  ClipBlit( &Spider[s].bitRP, 0, 0,
            &ScreenRP, Spider[s].ulc.x, Spider[s].ulc.y + YSPIDER,
            XMASK, size, BM_SRC | BM_DST);
}

void
HideSpider(s)
  int    s;
/* over write current spider */
{
  ClipBlit( &SpiderRP, 0, 0,
            &ScreenRP, Spider[s].ulc.x, Spider[s].ulc.y,
            XSPIDER, YSPIDER, 0);
}

void
DrawSpider(s)
  int    s;
/* Draw the spider on the screen */
{
  ClipBlit( &SpiderRP, 0, 0,
            &ScreenRP, Spider[s].ulc.x, Spider[s].ulc.y,
            XSPIDER, YSPIDER, BM_SRC);
}

void
RandInit()
{
  srand((int)time(NULL));
}

int
RandInt(lo, hi)                        /* generate random integer in range */
  int             lo, hi;              /* range lo..hi inclusive */
{
  return lo + (int)( (unsigned)rand() % (hi - lo +1) );
}

