/************************************************************************/
/*                                                                      */
/*                                   ######################             */
/*      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) );
}


