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

/*
**  Crabs - also for AMIGA under lattace C
*/

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

#include "crabs.h"

 /* intuition variables */
struct Library	      *IntuitionBase = NULL, *GfxBase = NULL;
struct Screen	      *WBScrn = NULL;
struct Window	      *Window = NULL;	     /* Window pointer */
struct RastPort       *RPwindow = NULL;      /* windows raster port */
struct Remember       *Remember = NULL;      /* memory allocation memory */
struct Task	      *MyTask = NULL;	     /* my tasks address */
struct IntuiMessage   *message = NULL;
int		      WBScrX, WBScrY;	     /* size of workbench screen */
ULONG		      WindowSignal;	     /* IDCMP window signal */
struct NewWindow      NW = {
	30, 200, 350, 40, 0, 1, 	      /* position, size and colors */
	CLOSEWINDOW,			     /* IDCMP messages */
	ACTIVATE| SIMPLE_REFRESH| WINDOWDEPTH|
	   WINDOWDRAG| WINDOWCLOSE,	     /* window flags */
	NULL, NULL,			     /* gadget, checkmark */
	(UBYTE *) " << CRABS >> ",           /* name of window */
	NULL,NULL,			     /* screen-> , BitMap-> */
	0,0,0,0,			     /* min and max size (disable) */
	WBENCHSCREEN			     /* screen type */
	};


   /* program varibles */
typedef struct {
  int		  x, y;
}		point;

typedef enum {
    Looking,	      /* crab is looking for food */
    Nibbling,	      /* crab took a nibble and is stepping back */
    Munching	      /* crab is eating the food (after step back) */
} crabstatus;

static struct {
  crabstatus	  stat;       /* current status of crab */
  point 	  pos;	      /* upper left corner screen coords */
  point 	  vel;	      /* velocity (pixels/cycle) */
  point 	  old;	      /* last position for backward step */
  int		  lp;	      /* life points of this crab */
  struct RastPort *RP;	      /* which map is the crab using */
}     crab[MAXCRABS];	      /* crabs' state */

int	NumCrabs = 0;	      /* number of crabs on screen */

/* Crab images to be drawen to the screen using the mask */
unsigned short chip   crabup[] =	  /* facing up */
#  include "crabup.i"
#ifndef ONEIMAGE
unsigned short chip   crabdown[] =	  /* facing down */
#  include "crabdown.i"
unsigned short chip   crabright[] =	  /* facing right */
#  include "crabright.i"
unsigned short chip   crableft[] =	  /* facing left */
#  include "crableft.i"
#endif
unsigned short chip   crabmask[] =	  /* crabs mask */
#  include "crabmask.i"

  /* Bitmaps and raster ports of the screens */
struct BitMap	BMtemp, BMmask, BMup;
struct RastPort RPtemp, RPmask, RPup, RPscreen;
#ifndef ONEIMAGE
struct BitMap	BMdown, BMright, BMleft;
struct RastPort RPdown, RPright, RPleft;
#endif


  /* function declarations */
extern void	Init(), OpenLibraries(), InitWindow(), InitRP(), Print();
extern void	CrabsExit(), Cycle(), DrawCrab(), HideCrab(), ModVel(), NewVel();
extern BOOL	MoveCrab(), TestCrab(), TestBackground();
extern void	RandInit();
extern int	RandInt();

#ifdef CBACK
  /* Cback.o initializations */
LONG _stack = 2000;
char *_procname = "Crabs";
LONG _priority = -1;
LONG _BackGroundIO = 0;
#endif

void
_main()
{
  Init();

  for (;;) {
    Cycle();
  }
  /* NOTREACHED */
}

void
Init()
/* set up initial crab layer */
{
  int		  i;	       /* crab # */

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

  if( ! (Window = OpenWindow( &NW )) )
    CrabsExit(NULL);
  RPwindow = Window->RPort;
  WindowSignal = 1<<Window->UserPort->mp_SigBit;

  InitWindow();

  MyTask = FindTask( NULL );
  WBScrn = Window->WScreen;
  WBScrX = WBScrn->Width -SIZE;
  WBScrY = WBScrn->Height -SIZE/2;

  memcpy((char *) &RPscreen, (char *) &(WBScrn->RastPort),
				      sizeof( struct RastPort ));

  InitRP(&RPup,    &BMup,    &crabup);
  InitRP(&RPmask,  &BMmask,  &crabmask);
  InitRP(&RPtemp,  &BMtemp,  NULL);
#ifndef ONEIMAGE
  InitRP(&RPdown,  &BMdown,  &crabdown);
  InitRP(&RPleft,  &BMdown,  &crableft);
  InitRP(&RPright, &BMright, &crabright);
#endif

  RandInit();

  /* Create initial set of crabs: */
  for (NumCrabs = 0; NumCrabs < STARTCRABS; NumCrabs++) {
    for (i = PLACELIM; i > 0; i--) {
      crab[NumCrabs].pos.x = RandInt(0, WBScrX-1);
      crab[NumCrabs].pos.y = RandInt(0, WBScrY-1);
      crab[NumCrabs].old = crab[NumCrabs].pos;
      if( !TestBackground(NumCrabs) )
	break;				    /* space found */
    }
    if( !i )                           /* did we reach the placement limit */
      CrabsExit("Can't place crabs! No empty space found!");
    crab[NumCrabs].lp = REPROlp / 2;
    crab[NumCrabs].stat = Looking;
    NewVel(NumCrabs);
    DrawCrab(NumCrabs);
  }
}

void
OpenLibraries()
{
  if( ! (IntuitionBase = OpenLibrary( "intuition.library", 0 )) )
    CrabsExit("Can't open Intuition Library");
  if( ! (GfxBase = OpenLibrary( "graphics.library", 0 )) )
    CrabsExit("Can't Open Graphics Library");
}

void
InitWindow()
{
  Move(RPwindow, Window->BorderLeft,
	  Window->BorderTop + RPwindow->TxBaseline );
  SetAPen(RPwindow, 3);
  Print("Reproducing Crabs by Anthony Thyssen");
  SetAPen(RPwindow, 1);
  Print("   Original idea from Scientific American");
}

void
InitRP(rp, bm, image)
  struct RastPort *rp;
  struct BitMap   *bm;
  short 	  *image;
{
  InitBitMap(bm,  1, SIZE,SIZE/2);
  if( image )
    bm->Planes[0] = (PLANEPTR) image;
  else
    if( !(bm->Planes[0] = (PLANEPTR)
	 AllocRemember( &Remember, RASSIZE(SIZE,SIZE/2),
	     MEMF_CHIP | MEMF_CLEAR) ) ) {    /* allocate with memory */
      CrabsExit("Can't alloc raster");
    }
  InitRastPort( rp );
  rp->BitMap = bm;
}

void
Print(str)
  char *str;
{
  Text( RPwindow, str, strlen(str) );
  Move( RPwindow, Window->BorderLeft,
      RPwindow->cp_y + RPwindow->TxHeight); /* move to next line */
}

void
CrabsExit(exitmessage)
  char *exitmessage;
/* exit crabs - remove all traces */
{
  int		  i;

  for (i = 0; i < NumCrabs; ++i)
    HideCrab(i);

  RefreshWindowFrame(Window);
  InitWindow();
  if( Window && exitmessage ) {
    Print(exitmessage);
    /*printf("%s\n", exitmessage); /* Debugging */
    Delay(200);
  }

  FreeRemember( &Remember, TRUE );    /* Frre all allocated memory */

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

  _exit(0);
}

void
Cycle()
  /* one motion cycle for all crabs */
{
  int		  i;

  /* test for window closure */
  if( message = (struct IntuiMessage *)GetMsg(Window->UserPort) ) {
    ReplyMsg( (struct Message *)message );
    switch( message->Class ) {
      case CLOSEWINDOW : /* window closed message */
	CrabsExit("Crabs are Exiting");
    }
  } /* Message test */

  if( NumCrabs < 5 )
    Delay( DELAY );         /* slow down the small number of crabs */

  for (i = 0; i < NumCrabs; i++) {

    /* did something bite the crab - image corrupted ? */
    if (TestCrab(i)) {              /* if so damage the crab */
      crab[i].lp -= BITElp;
      NewVel(i);
    }

    switch( crab[i].stat ) {        /* do the nessary move */
      case Looking:
	HideCrab(i);                 /* erase crab from previous position */
	crab[i].old = crab[i].pos;   /* record current position */
	if (MoveCrab(i))             /* find the new positioN */
	  crab[i].lp -= BOUNCElp;    /* crab bounced - that smarts */
	if (TestBackground(i))       /* Test if crab hits an object */
	  crab[i].stat = Nibbling;   /* if so crab will now nibble */
	crab[i].lp -= MOVElp;	     /* drain movement cost from crab */
	if (crab[i].lp < 0) {        /* check on starvation */
	  crab[i] = crab[--NumCrabs];
	  if (NumCrabs == 0)
	    CrabsExit("The last Crab just died!\n");
	  i--;
	  continue;		     /* repeat this crab as it is the next one */
	}
	DrawCrab(i);                 /* redraw crab here */
	ModVel(i, 1);                /* very slight direction change */
	break;

      case Nibbling:
	HideCrab(i);                 /* erase crab from previous position */
	crab[i].pos = crab[i].old;   /* move crab backwards */
	DrawCrab(i);                 /* draw crab here */
	crab[i].stat = Munching;     /* chew over the food */
	break;

      case Munching:
	crab[i].lp += FEEDlp;	     /* feed the food to the crab */
	ModVel(i, FEEDVEL);          /* look in roughly the same dir as before */
	crab[i].stat = Looking;      /* time taken to eat - look for more */
	if (crab[i].lp >= REPROlp) { /* check for reproduction */
	  HideCrab(i);               /* remove parent */
	  crab[i].lp /= 2;	     /* half life force */
	  if (NumCrabs < MAXCRABS) { /* is a new crab is possible */
	    crab[NumCrabs] = crab[i]; /* child is a copy of parent */
	    crab[NumCrabs].vel.x = -crab[NumCrabs].vel.x; /* reverse dir */
	    crab[NumCrabs].vel.y = -crab[NumCrabs].vel.y;
	    crab[NumCrabs].lp -= BITElp; /* child is weak */
	    MoveCrab(i);             /* move crabs away from each other */
	    MoveCrab(NumCrabs++);    /* move child twice - inc number crabs */
	    /* leave the child undrawen and corrupted! */
	  }
	  DrawCrab(i);               /* draw parent regardless of child state */
	}
	break;
    }
  }				    /* move next crab */
}

BOOL
MoveCrab(i)
  int		  i;
/* move crab to on screen position - don't draw it */
/* if the crab hits edge of screen return TRUE */
{
  BOOL	      hit = FALSE;

  crab[i].pos.x += crab[i].vel.x;	 /* motion */
  crab[i].pos.y += crab[i].vel.y;
  if( crab[i].pos.x < 0 ) {
    crab[i].pos.x = 0;
    crab[i].vel.x = abs(crab[i].vel.x);
    hit = TRUE;
  }
  if( crab[i].pos.x > WBScrX ) {
    crab[i].pos.x = WBScrX;
    crab[i].vel.x = -abs(crab[i].vel.x);
    hit = TRUE;
  }
  if( crab[i].pos.y < 0 ) {
    crab[i].pos.y = 0;
    crab[i].vel.y = abs(crab[i].vel.y);
    hit = TRUE;
  }
  if( crab[i].pos.y > WBScrY ) {
    crab[i].pos.y = WBScrY;
    crab[i].vel.y = -abs(crab[i].vel.y);
    hit = TRUE;
  }
  return (hit);
}

BOOL
TestCrab(i)
  int		  i;
/* test the crabs image on the screen. return TRUE if corrupt */
/* invisible crabs are the background texture */
{
  int j;
  UBYTE *byte;

  /* get crab stored on screen */
  ClipBlit( &RPscreen, crab[i].pos.x, crab[i].pos.y,
	    &RPtemp, 0, 0, SIZE, SIZE/2, BM_SRC );
  /* mask out background */
  ClipBlit( &RPmask, 0, 0, &RPtemp, 0, 0,
	    SIZE, SIZE/2, BM_SRC & BM_DST );
  /* xor with crab image */
  ClipBlit( crab[i].RP, 0, 0, &RPtemp, 0, 0,
	    SIZE, SIZE/2, BM_SRC ^ BM_DST );

  /* check the data */
  byte = BMtemp.Planes[0];
  for (j = RASSIZE(SIZE, SIZE/2)-1; j >= 0; j--)
    if( *(byte++) )
      return TRUE;

  return FALSE;
}

BOOL
TestBackground(i)
  int		  i;
/* return TRUE if crab position is not the background */
{
  int  j;
  UBYTE *byte;

  /* get screen at crab location */
  ClipBlit( &RPscreen, crab[i].pos.x, crab[i].pos.y,
	    &RPtemp, 0, 0, SIZE, SIZE/2, BM_SRC );
  /* mask out background */
  ClipBlit( &RPmask, 0, 0, &RPtemp, 0, 0,
	    SIZE, SIZE/2, BM_SRC & BM_DST );

  /* check the data */
  byte = BMtemp.Planes[0];
  for (j = RASSIZE(SIZE, SIZE/2)-1; j >= 0; j--)
    if( *(byte++) )
      return TRUE;

  return FALSE;
}

void
HideCrab(i)
/* over write current crab with screen texture */
{
  ClipBlit( &RPmask, 0, 0, &RPscreen, crab[i].pos.x, crab[i].pos.y,
	    SIZE, SIZE/2, ~BM_SRC & BM_DST );
}

void
DrawCrab(i)
  int		  i;
/* Draw the crabs on the screen if visible */
{
  HideCrab(i);
  crab[i].RP = PICKMAP( crab[i].vel );
  ClipBlit( crab[i].RP, 0, 0, &RPscreen, crab[i].pos.x, crab[i].pos.y,
	    SIZE, SIZE/2, BM_SRC | BM_DST );
}

void
NewVel(i)                              /* assign new velocity to crab */
  int		  i;		       /* crab # */
{
  crab[i].vel.x = RandInt(-MAXVEL, MAXVEL);
  crab[i].vel.y = RandInt(-MAXVEL/2, MAXVEL/2);

  /* Velocity (0,0) is okay since we repeatedly modify all velocities. */
}

void
ModVel(i, dv)                              /* randomly modify crab velocity */
  int		  i,dv; 		  /* crab, delta velocity */
{
  int		  min,max;		   /* range of new velocity */

  min=crab[i].vel.x-dv;
  max=crab[i].vel.x+dv;
  if ( max > MAXVEL)
    max = MAXVEL;
  if ( min < -MAXVEL)
    min = -MAXVEL;
  crab[i].vel.x = RandInt(min, max);

  min=crab[i].vel.y-dv;
  max=crab[i].vel.y+dv;
  if ( max > MAXVEL/2)
    max = MAXVEL/2;
  if ( min < -MAXVEL/2)
    min = -MAXVEL/2;
  crab[i].vel.y = RandInt(min, max);
}

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) );
}


