/* Life Library */

#include <exec/memory.h>
#include <exec/execbase.h>
#include <graphics/gfxbase.h>
#include <intuition/intuitionbase.h>
#include <libraries/iffparse.h>
#include <utility/tagitem.h>

#include <clib/macros.h>

#define __USE_SYSBASE 42

#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/utility.h>

#include <math.h>
#include <string.h>

#include <BTD.h>

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Library *UtilityBase;

/* #define DEBUG YES */

#ifdef DEBUG 

void KPrintF(char *,...);

#define DEBUG_PRINTF(a,b)  KPrintF(a,b);
#define DEBUG_PRINT(a)     KPrintF(a)
#else
#define DEBUG_PRINTF(a,b)
#define DEBUG_PRINT(a)
#endif

#define QTAG(o) (BTD_Client+(o))

#define LP_ChangePat QTAG(0)
#define LP_Shooter QTAG(1)
#define LP_Cycle   QTAG(2)
#define LP_Colors  QTAG(3)
#define LP_NRow    QTAG(4)

#define MAX_CHPT  5000L /* seconds until a new graphic is plotted */
#define MAX_LINES 100L
#define MAX_SHOOT     59L
#define MAX_COLORS 255L
#define MAX_NROW 64L

#define DEF_CHPT  1000L
#define DEF_LINES 10L
#define DEF_SHOOT 10L
#define DEF_COLORS 31L
#define DEF_NROW  32L

#define	MAXROWS 155
#define MAXCOLS 144
#define TIMEOUT 30

#define WHITEPEN (LP->BTDDrawInfo->BDI_Pens[0])

struct lifestruct {
    struct BTDDrawInfo *BTDDrawInfo;
    LONG         xs;
    LONG         ys;
    LONG         xb;
    LONG         yb;
    LONG         sub;
    LONG         generation;
    LONG         shooterTime;
    LONG         shooterTimeOut;
    LONG         nrows;
    LONG         ncols;
    LONG         changepat;
    UWORD buffer[(MAXROWS + 2) * (MAXCOLS + 2) + 2];
    UWORD tempbuf[MAXCOLS * 2];
    UWORD lastbuf[MAXCOLS];
    UWORD agebuf[(MAXROWS + 2) * (MAXCOLS + 2)];
    LONG   ls_RandN,ls_RandF,ls_RandI;
    UWORD fates[256];
    UWORD colors;
};

#define FindTagData(l,t,d) GetTagData((t),(d),(l))

struct BTDInteger LifeIntParams[] =
 {
  LP_ChangePat,"Change Pattern",BTDPT_INTEGER,DEF_CHPT,1L,MAX_CHPT,TRUE,
  LP_Shooter,"Shooter Time",BTDPT_INTEGER,DEF_SHOOT,1L,MAX_SHOOT,TRUE,
  LP_Colors,"Colors",BTDPT_INTEGER,DEF_COLORS,2L,MAX_COLORS,TRUE,
  LP_NRow,"Rows & Cols",BTDPT_INTEGER,DEF_NROW,16L,MAX_NROW,TRUE
 };

struct BTDNode *LifeParams[] = 
 {
  &LifeIntParams[0].BI_Node,
  &LifeIntParams[1].BI_Node,
  &LifeIntParams[2].BI_Node,
  &LifeIntParams[3].BI_Node,NULL
 };

struct BTDInfo LifeInfo =
 {
  BTDI_Revision,MAKE_ID('L','I','F','E'),
  "Life Blanker","Good old James Conway automaton","Markus Illenseer 1994",
  LifeParams
 };

/* library stuff */

char MyBlankerName[] = "life.btd";
char MyBlankerID[]   = "Life Blanker V" VERSION "." REVISION " for BTD";

LONG MyBlankerLibInit(void)

{
 if (GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",37L))
  {
   if (IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",37L))
    {
     if (UtilityBase=OpenLibrary("utility.library",37L)) return TRUE;

     CloseLibrary (&IntuitionBase->LibNode);
    }
   CloseLibrary (&GfxBase->LibNode);
  }
 return FALSE;
}

void MyBlankerLibFree(void)

{
 CloseLibrary (UtilityBase);
 CloseLibrary (&IntuitionBase->LibNode);
 CloseLibrary (&GfxBase->LibNode);
}


#define RAND(a,m) (Random(a,m)-(m)/2)

void __regargs InitRandom(struct lifestruct *LP,ULONG Instance)
{
 ULONG Time[2];

 CurrentTime (&Time[0],&Time[1]);
 LP->ls_RandN=(LONG)Time[0];
 if (Time[1]<1024L) Time[1]|=1;
 else Time[1]>>=10;
 Time[1]^=Instance;

 LP->ls_RandF=4*Time[1]+1;
 LP->ls_RandI=2*Time[1]+1;
}

WORD __regargs Random(struct lifestruct *LP,WORD Max)
{
 LP->ls_RandN=LP->ls_RandF*LP->ls_RandN+LP->ls_RandI;
 if (LP->ls_RandN<0L) LP->ls_RandN=-LP->ls_RandN;

 return (WORD)(LP->ls_RandN%Max);
}

struct BTDInfo *QueryMyBlanker(void)
{
 return &LifeInfo;
}

/* Buffer stores the data for each cell. Each cell is stored as
 * 8 bits representing the presence of a critter in each of it's
 * surrounding 8 cells. There is an empty row and column around
 * the whole array to allow stores without bounds checking as well
 * as an extra row at the end for the fetches into tempbuf.
 *
 * Tempbuf stores the data for the next two rows so that we know
 * the state of those critter before he was modified by the fate
 * of the critters that have already been processed.
 *
 * Agebuf stores the age of each critter.
 */

#define	UPLT	0x01
#define UP	0x02
#define UPRT	0x04
#define LT	0x08
#define RT	0x10
#define DNLT	0x20
#define DN	0x40
#define DNRT	0x80

/* Fates is a lookup table for the fate of a critter. The 256
 * entries represent the 256 possible combinations of the 8
 * neighbor cells. Each entry is one of BIRTH (create a cell
 * or leave one alive), SAME (leave the cell alive or dead),
 * or DEATH (kill anything in the cell).
 */
#define BIRTH	0
#define SAME	1
#define DEATH	2

static LONG  patterns[][128] = {
    {				/* EIGHT */
	-3, -3, -2, -3, -1, -3,
	-3, -2, -2, -2, -1, -2,
	-3, -1, -2, -1, -1, -1,
	0, 0, 1, 0, 2, 0,
	0, 1, 1, 1, 2, 1,
	0, 2, 1, 2, 2, 2,
	99
    },
    {				/* PULSAR */
	1, 1, 2, 1, 3, 1, 4, 1, 5, 1,
	1, 2, 5, 2,
	99
    },
    {				/* BARBER */
	-7, -7, -6, -7,
	-7, -6, -5, -6,
	-5, -4, -3, -4,
	-3, -2, -1, -2,
	-1, 0, 1, 0,
	1, 2, 3, 2,
	3, 4, 5, 4,
	4, 5, 5, 5,
	99
    },
    {				/* HERTZ */
	-2, -6, -1, -6,
	-2, -5, -1, -5,
	-7, -3, -6, -3, -2, -3, -1, -3, 0, -3, 1, -3, 5, -3, 6, -3,
	-7, -2, -5, -2, -3, -2, 2, -2, 4, -2, 6, -2,
	-5, -1, -3, -1, -2, -1, 2, -1, 4, -1,
	-7, 0, -5, 0, -3, 0, 2, 0, 4, 0, 6, 0,
	-7, 1, -6, 1, -2, 1, -1, 1, 0, 1, 1, 1, 5, 1, 6, 1,
	-2, 3, -1, 3,
	-2, 4, -1, 4,
	99
    },
    {				/* TUMBLER */
	-6, -6, -5, -6, 6, -6, 7, -6,
	-6, -5, -5, -5, 6, -5, 7, -5,
	-5, 5, 6, 5,
	-7, 6, -5, 6, 6, 6, 8, 6,
	-7, 7, -5, 7, 6, 7, 8, 7,
	-7, 8, -6, 8, 7, 8, 8, 8,
	99
    },
    {				/* PERIOD4 */
	-5, -8, -4, -8,
	-7, -7, -5, -7,
	-8, -6, -2, -6,
	-7, -5, -3, -5, -2, -5,
	-5, -3, -3, -3,
	-4, -2,
	99
    },
    {				/* PERIOD5 */
	-5, -8, -4, -8,
	-6, -7, -3, -7,
	-7, -6, -2, -6,
	-8, -5, -1, -5,
	-8, -4, -1, -4,
	-7, -3, -2, -3,
	-6, -2, -3, -2,
	-5, -1, -4, -1,
	99
    },
    {				/* PERIOD6 */
	-4, -8, -3, -8,
	-8, -7, -7, -7, -5, -7,
	-8, -6, -7, -6, -4, -6, -1, -6,
	-3, -5, -1, -5,
	-2, -4,
	-3, -2, -2, -2,
	-3, -1, -2, -1,
	99
    },
    {				/* PINWHEEL */
	-4, -8, -3, -8,
	-4, -7, -3, -7,
	-4, -5, -3, -5, -2, -5, -1, -5,
	-5, -4, -3, -4, 0, -4, 2, -4, 3, -4,
	-5, -3, -1, -3, 0, -3, 2, -3, 3, -3,
	-8, -2, -7, -2, -5, -2, -2, -2, 0, -2,
	-8, -1, -7, -1, -5, -1, 0, -1,
	-4, 0, -3, 0, -2, 0, -1, 0,
	-2, 2, -1, 2,
	-2, 3, -1, 3,
	99
    },
    {				/* ] */
	-1, -1, 0, -1, 1, -1,
	0, 0, 1, 0,
	-1, 1, 0, 1, 1, 1,
	99
    },
    {				/* cc: */
	-3, -1, -2, -1, -1, -1, 1, -1, 2, -1, 3, -1,
	-3, 0, -2, 0, 1, 0, 2, 0,
	-3, 1, -2, 1, -1, 1, 1, 1, 2, 1, 3, 1,
	99
    },
    {				/* DOLBY */
	-3, -1, -2, -1, -1, -1, 1, -1, 2, -1, 3, -1,
	-3, 0, -2, 0, 2, 0, 3, 0,
	-3, 1, -2, 1, -1, 1, 1, 1, 2, 1, 3, 1,
	99
    },
    {				/* HORIZON */
	-15, 0, -14, 0, -13, 0, -12, 0, -11, 0,
	-10, 0, -9, 0, -8, 0, -7, 0, -6, 0,
	-5, 0, -4, 0, -3, 0, -2, 0, -1, 0,
	4, 0, 3, 0, 2, 0, 1, 0, 0, 0,
	9, 0, 8, 0, 7, 0, 6, 0, 5, 0,
	14, 0, 13, 0, 12, 0, 11, 0, 10, 0,
	99
    },
    {				/* SHEAR */
	-7, -2, -6, -2, -5, -2, -4, -2, -3, -2,
	-2, -2, -1, -2, 0, -2, 1, -2, 2, -2,
	-5, -1, -4, -1, -3, -1, -2, -1, -1, -1,
	0, -1, 1, -1, 2, -1, 3, -1, 4, -1,
	-3, 0, -2, 0, -1, 0, 0, 0, 1, 0,
	2, 0, 3, 0, 4, 0, 5, 0, 6, 0,
	-10, 1, -9, 1, -8, 1, -7, 1, -6, 1,
	-5, 1, -4, 1, -3, 1, -2, 1, -1, 1,
	-10, 2, -9, 2, -8, 2, -7, 2, -6, 2,
	-5, 2, -4, 2, -3, 2, -2, 2, -1, 2,
	99
    },
    {				/* VERTIGO */
	0, -7,
	0, -6,
	0, -5,
	0, -4,
	0, -3,
	0, -2,
	0, -1,
	0, 0,
	0, 7,
	0, 6,
	0, 5,
	0, 4,
	0, 3,
	0, 2,
	0, 1,
	99
    },
    {				/* CROSSBAR */
	-5, 0, -4, 0, -3, 0, -2, 0, -1, 0, 4, 0, 3, 0, 2, 0, 1, 0, 0, 0,
	99
    },
    {				/* GOALPOSTS */
	-8, -7, 8, -7,
	-8, -6, 8, -6,
	-8, -5, 8, -5,
	-8, -4, 8, -4,
	-8, -3, 8, -3,
	-8, -2, 8, -2,
	-8, -1, 8, -1,
	-8, 0, 8, 0,
	-8, 1, 8, 1,
	-8, 2, 8, 2,
	-8, 3, 8, 3,
	-8, 4, 8, 4,
	-8, 5, 8, 5,
	-8, 6, 8, 6,
	-8, 7, 8, 7,
	99
    },
    {				/* \ */
	-8, -8, -7, -8,
	-7, -7, -6, -7,
	-6, -6, -5, -6,
	-5, -5, -4, -5,
	-4, -4, -3, -4,
	-3, -3, -2, -3,
	-2, -2, -1, -2,
	-1, -1, 0, -1,
	0, 0, 1, 0,
	1, 1, 2, 1,
	2, 2, 3, 2,
	3, 3, 4, 3,
	4, 4, 5, 4,
	5, 5, 6, 5,
	6, 6, 7, 6,
	7, 7, 8, 7,
	99
    },
    {				/* LABYRINTH */
	-4, -4, -3, -4, -2, -4, -1, -4, 0, -4, 1, -4, 2, -4, 3, -4, 4, -4,
	-4, -3, 0, -3, 4, -3,
	-4, -2, -2, -2, -1, -2, 0, -2, 1, -2, 2, -2, 4, -2,
	-4, -1, -2, -1, 2, -1, 4, -1,
	-4, 0, -2, 0, -1, 0, 0, 0, 1, 0, 2, 0, 4, 0,
	-4, 1, -2, 1, 2, 1, 4, 1,
	-4, 2, -2, 2, -1, 2, 0, 2, 1, 2, 2, 2, 4, 2,
	-4, 3, 0, 3, 4, 3,
	-4, 4, -3, 4, -2, 4, -1, 4, 0, 4, 1, 4, 2, 4, 3, 4, 4, 4,
	99
    },
    {/* ill */
     -5, -5, -3, -5, -1, -5,
     -5, -4, -3, -4, -1, -4,
             -3, -3, -1, -3,
     -5, -2, -3, -2, -1, -2,
     -5, -1, -3, -1, -1, -1,
     -5,  0, -3,  0, -1,  0,
     99
    },
    {/* r-pentomino, extracted from the SF-book 'Ox' from Piers Anthony*/
              0, -1,  1, -1,
     -1,  0,  0,  0,
              0,  1,
     99
    }

};

#define NPATS	(sizeof patterns / sizeof patterns[0])

void drawcell(struct lifestruct *LP,LONG row, LONG col)
{
    WORD x1,x2,y1,y2;

    SetAPen(LP->BTDDrawInfo->BDI_RPort,WHITEPEN);
    if (LP->colors > 2) {
	UWORD *loc = LP->buffer + ((row + 1) * (LP->ncols + 2)) + col + 1;
	UWORD *ageptr = LP->agebuf + (loc - LP->buffer);
	UWORD age = *ageptr;

	/* if we aren't up to blue yet, then keep aging the cell. */
	if (age < (((LP->colors+2)*7)/10))
	    ++age;

        SetAPen(LP->BTDDrawInfo->BDI_RPort,LP->BTDDrawInfo->BDI_Pens[age]);
	*ageptr = age;
    }

    x1=(WORD)(LP->xb + LP->xs * col)-LP->sub;
    y1=(WORD)(LP->yb + LP->ys * row)-LP->sub;
    x2=(WORD)(LP->xs)-LP->sub-1+x1;
    y2=(WORD)(LP->ys)-LP->sub-1+y1;

    if(x2<x1) x2=x1;
    if(y2<y1) y2=y1;

    if(x1<0) x1=0;
    if(y1<0) y1=0;

    RectFill(LP->BTDDrawInfo->BDI_RPort,x1,y1,x2,y2);
}


void erasecell(struct lifestruct *LP,LONG row, LONG col)
{
    WORD x1,x2,y1,y2;

    x1=(WORD)(LP->xb + LP->xs * col)-LP->sub;
    y1=(WORD)(LP->yb + LP->ys * row)-LP->sub;
    x2=(WORD)(LP->xs)-LP->sub-1+x1;
    y2=(WORD)(LP->ys)-LP->sub-1+y1;

    if(x2<x1) x2=x1;
    if(y2<y1) y2=y1;

    if(x1<0) x1=0;
    if(y1<0) y1=0;

    SetAPen(LP->BTDDrawInfo->BDI_RPort,BTD_BgPen);
    RectFill(LP->BTDDrawInfo->BDI_RPort,x1,y1,x2,y2);
}


void spawn(struct lifestruct *LP,UWORD *loc)
{
    UWORD *ulloc, *ucloc, *urloc, *clloc, *crloc, *llloc, *lcloc, *lrloc,
               *arloc;
    LONG         off, row, col, lastrow;

    lastrow = (LP->nrows) * (LP->ncols + 2);
    off = loc - LP->buffer;
    col = off % (LP->ncols + 2);
    row = (off - col) / (LP->ncols + 2);
    ulloc = loc - LP->ncols - 3;
    ucloc = loc - LP->ncols - 2;
    urloc = loc - LP->ncols - 1;
    clloc = loc - 1;
    crloc = loc + 1;
    arloc = loc + 1;
    llloc = loc + LP->ncols + 1;
    lcloc = loc + LP->ncols + 2;
    lrloc = loc + LP->ncols + 3;
    if (row == 1) {
	ulloc += lastrow;
	ucloc += lastrow;
	urloc += lastrow;
    }
    if (row == LP->nrows) {
	llloc -= lastrow;
	lcloc -= lastrow;
	lrloc -= lastrow;
    }
    if (col == 1) {
	ulloc += LP->ncols;
	clloc += LP->ncols;
	llloc += LP->ncols;
    }
    if (col == LP->ncols) {
	urloc -= LP->ncols;
	crloc -= LP->ncols;
	lrloc -= LP->ncols;
    }
    *ulloc |= UPLT;
    *ucloc |= UP;
    *urloc |= UPRT;
    *clloc |= LT;
    *crloc |= RT;
    *arloc |= RT;
    *llloc |= DNLT;
    *lcloc |= DN;
    *lrloc |= DNRT;

    *(LP->agebuf + (loc - LP->buffer)) = 0;
}


void kill(struct lifestruct *LP,UWORD *loc)
{
    UWORD *ulloc, *ucloc, *urloc, *clloc, *crloc, *llloc, *lcloc,
               *lrloc, *arloc;
    LONG         off, row, col, lastrow;

    lastrow = (LP->nrows) * (LP->ncols + 2);
    off = loc - LP->buffer;
    col = off % (LP->ncols + 2);
    row = (off - col) / (LP->ncols + 2);
    ulloc = loc - LP->ncols - 3;
    ucloc = loc - LP->ncols - 2;
    urloc = loc - LP->ncols - 1;
    clloc = loc - 1;
    crloc = loc + 1;
    arloc = loc + 1;
    llloc = loc + LP->ncols + 1;
    lcloc = loc + LP->ncols + 2;
    lrloc = loc + LP->ncols + 3;
    if (row == 1) {
	ulloc += lastrow;
	ucloc += lastrow;
	urloc += lastrow;
    }
    if (row == LP->nrows) {
	llloc -= lastrow;
	lcloc -= lastrow;
	lrloc -= lastrow;
    }
    if (col == 1) {
	ulloc += LP->ncols;
	clloc += LP->ncols;
	llloc += LP->ncols;
    }
    if (col == LP->ncols) {
	urloc -= LP->ncols;
	crloc -= LP->ncols;
	lrloc -= LP->ncols;
    }
    *ulloc &= ~UPLT;
    *ucloc &= ~UP;
    *urloc &= ~UPRT;
    *clloc &= ~LT;
    *crloc &= ~RT;
    *arloc &= ~RT;
    *llloc &= ~DNLT;
    *lcloc &= ~DN;
    *lrloc &= ~DNRT;
}


void setcell(struct lifestruct *LP,LONG row, LONG col)
{
    UWORD *loc;

    loc = LP->buffer + ((row + 1) * (LP->ncols + 2)) + col + 1;
    spawn(LP,loc);
    drawcell(LP,row, col);
}


void init_fates(struct lifestruct *LP)
{
    LONG         i, bits, neighbors;

    for (i = 0; i < 256; i++) {
	neighbors = 0;
	for (bits = i; bits; bits &= (bits - 1))
	    neighbors++;
	if (neighbors == 3)
	    LP->fates[i] = BIRTH;
	else if (neighbors == 2)
	    LP->fates[i] = SAME;
	else
	    LP->fates[i] = DEATH;
    }
}

void InitNewLife(struct lifestruct *LP)
{
    LONG row, col;
    LONG *patptr;
    ULONG Time[2];

    LP->generation = 0;
    CurrentTime (&Time[0],&Time[1]);
    LP->shooterTimeOut = Time[0];

    SetAPen(LP->BTDDrawInfo->BDI_RPort,BTD_BgPen);
    RectFill(LP->BTDDrawInfo->BDI_RPort,LP->BTDDrawInfo->BDI_Left,
                                        LP->BTDDrawInfo->BDI_Top,
                                        LP->BTDDrawInfo->BDI_Width+LP->BTDDrawInfo->BDI_Left-1,
                                        LP->BTDDrawInfo->BDI_Height+LP->BTDDrawInfo->BDI_Top-1);


    bzero(LP->buffer, sizeof(LP->buffer));
    patptr = &patterns[Random(LP,NPATS)][0];
    while ((col = *patptr++) != 99) {
	row = *patptr++;
	col += LP->ncols / 2;
	row += LP->nrows / 2;
	setcell(LP,row, col);
    }
}

struct lifestruct *InitMyBlanker(struct TagItem *TagList)
{

 LONG ChangePat,Shoot,Colors;
 struct lifestruct *LP;
 struct BTDDrawInfo *BTDDrawInfo;
 ULONG *Error,Dummy,Index,Instance;
 LONG Step;

 LONG row, col, NRow;
 LONG *patptr;
 ULONG Time[2];

 if ((BTDDrawInfo=(struct BTDDrawInfo *)
                   FindTagData(TagList,BTD_DrawInfo,NULL))==NULL) return NULL;
 Error=(LONG *)FindTagData(TagList,BTD_Error,(ULONG)&Dummy);
 if ((LP=AllocVec(sizeof(struct lifestruct),MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  {
   *Error=BTDERR_Memory;
   return NULL;
  }

 ChangePat=FindTagData(TagList,LP_ChangePat,DEF_CHPT);
 Instance=FindTagData(TagList,BTD_Instance,0L);
 Shoot=FindTagData(TagList,LP_Shooter,DEF_SHOOT);
 Colors=FindTagData(TagList,LP_Colors,DEF_COLORS);
 NRow=FindTagData(TagList,LP_NRow,DEF_NROW);

 LP->BTDDrawInfo=BTDDrawInfo;

 InitRandom(LP,Instance);
 CurrentTime (&Time[0],&Time[1]);

 init_fates(LP);

 LP->generation = 0;
 LP->shooterTime = Shoot;
 LP->shooterTimeOut = Time[0];
 LP->colors=Colors;

 LP->changepat=ChangePat;

 LP->ncols = NRow;
 LP->nrows = NRow;
 LP->xs = (BTDDrawInfo->BDI_Width-1) / LP->ncols;
 LP->ys = (BTDDrawInfo->BDI_Height-1) / LP->nrows;
 LP->xb = BTDDrawInfo->BDI_Left+1+(BTDDrawInfo->BDI_Width - LP->xs * LP->ncols) / 2;
 LP->yb = BTDDrawInfo->BDI_Top+(BTDDrawInfo->BDI_Height - LP->ys * LP->nrows) / 2;
 LP->sub= (LP->xs<4?1:2);

 bzero(LP->buffer, sizeof(LP->buffer)); 
 patptr = &patterns[Random(LP,NPATS)][0];
 while ((col = *patptr++) != 99) {
	row = *patptr++;
	col += LP->ncols / 2;
	row += LP->nrows / 2;
	setcell(LP,row, col);
    }

 Step=(256/LP->colors);

 for(Index=0; Index<LP->colors; Index++)
  {
   static UWORD r,g,b;

   if((r=(Step*Index))>255) r=255;
   if((g=(128-Step*Index>128?(Step*Index)*(-1):(Step*Index)))>255) g=255;
   if((b=(255-Step*Index))>255) b=255; 

   BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[Index]]=r;
   BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[Index]]=g;
   BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[Index]]=b;
  }

 BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[0]]=0;
 BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[0]]=0;
 BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[0]]=255;

 BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[1]]=255;
 BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[1]]=0;
 BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[1]]=0;

 BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[LP->colors-1]]=16;
 BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[LP->colors-1]]=0;
 BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[LP->colors-1]]=0;

 for(Index=0; Index<LP->colors; Index++)
  BTDDrawInfo->BDI_Changed[BTDDrawInfo->BDI_Pens[Index]]=TRUE;

 LP->BTDDrawInfo=BTDDrawInfo;

 return LP;

}


void EndMyBlanker(struct lifestruct *LP)
{
DEBUG_PRINT("Life: FreeMem\n");
 FreeVec (LP);
}


void AnimMyBlanker(struct lifestruct *LP)
{
    UWORD *loc, *temploc, *lastloc;
    LONG         row, col;
    UWORD fate;
    ULONG Time[2];

    loc = LP->buffer + LP->ncols + 2 + 1;
    temploc = LP->tempbuf;
    /* copy the first 2 rows to the tempbuf */
    bcopy(loc, temploc, LP->ncols);
    bcopy(loc + LP->ncols + 2, temploc + LP->ncols, LP->ncols);

    lastloc = LP->lastbuf;
    /* copy the last row to another buffer for wraparound */
    bcopy(loc + ((LP->nrows - 1) * (LP->ncols + 2)), lastloc, LP->ncols);

    for (row = 0; row < LP->nrows; ++row) {
	for (col = 0; col < LP->ncols; ++col) {
	    fate = LP->fates[*temploc];
	    *temploc = (row == (LP->nrows - 3)) ?
		*(lastloc + col) :
		*(loc + (LP->ncols + 2) * 2);
	    switch (fate) {
	    case BIRTH:
		if (!(*(loc + 1) & RT)) {
		    spawn(LP,loc);
		}
		/* NO BREAK */
	    case SAME:
		if (*(loc + 1) & RT) {
		    drawcell(LP,row, col);
		}
		break;
	    case DEATH:
		if (*(loc + 1) & RT) {
		    kill(LP,loc);
		    erasecell(LP,row, col);
		}
		break;
	    }
	    loc++;
	    temploc++;
	}
	loc += 2;
	if (temploc >= LP->tempbuf + LP->ncols * 2)
	    temploc = LP->tempbuf;
    }

    if (++LP->generation > LP->changepat)
	InitNewLife(LP);

    /*
     * generate a randomized shooter aimed roughly toward the center of the
     * screen after timeout.
     */
    CurrentTime (&Time[0],&Time[1]);
    if (LP->shooterTime>0 && Time[0]-LP->shooterTimeOut > LP->shooterTime) {
	LONG         hsp = Random(LP,(LP->ncols - 5)) + 3;
	LONG         vsp = Random(LP,(LP->nrows - 5)) + 3;
	LONG         hoff = 1;
	LONG         voff = 1;
	if (vsp > LP->nrows / 2)
	    voff = -1;
	if (hsp > LP->ncols / 2)
	    hoff = -1;
	setcell(LP,vsp + 0 * voff, hsp + 2 * hoff);
	setcell(LP,vsp + 1 * voff, hsp + 2 * hoff);
	setcell(LP,vsp + 2 * voff, hsp + 2 * hoff);
	setcell(LP,vsp + 2 * voff, hsp + 1 * hoff);
	setcell(LP,vsp + 1 * voff, hsp + 0 * hoff);
        CurrentTime (&Time[0],&Time[1]);
	LP->shooterTimeOut = Time[0];
    }
}

ULONG PenCountMyBlanker(struct TagItem *TagList)

{
 return FindTagData(TagList,LP_Colors,DEF_COLORS);
}
