/* Hop 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 "BTD.h"

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

struct Library *MathIeeeDoubBasBase,*MathIeeeDoubTransBase;

#define TRONTAG(o) (BTD_Client+(o))

#define TRON_Pixels     TRONTAG(0)
#define TRON_NumPlayers TRONTAG(1)

#define MAX_PIXELS 20L /* Pixels per Frame */
#define DEF_PIXELS 4L

#define DEF_NUM_PLAYERS 2
#define MAX_NUM_PLAYERS 5

struct BTDInteger TronIntParams[] =
 {
  TRON_Pixels,"Pixels per Frame",BTDPT_INTEGER,DEF_PIXELS,1L,MAX_PIXELS,TRUE,
  TRON_NumPlayers,"Number of Players",BTDPT_INTEGER,DEF_NUM_PLAYERS,2L,MAX_NUM_PLAYERS,TRUE
 };

struct BTDNode *TronParams[] =
 {
  &TronIntParams[0].BI_Node,
  &TronIntParams[1].BI_Node,
  NULL
 };

struct BTDInfo TronInfo =
 {
  BTDI_Revision,MAKE_ID('T','R','O','N'),
  "Tron","Based on the movie Tron","Matthias Scheler",
  TronParams
 };

#define DIRECT_UP    0
#define DIRECT_RIGHT 1
#define DIRECT_DOWN  2
#define DIRECT_LEFT  3

#define NUM_DIRECT 4

LONG XInc[NUM_DIRECT] = {0L,1L,0L,-1L};
LONG YInc[NUM_DIRECT] = {-1L,0L,1L,0L};

ULONG Turn090[NUM_DIRECT] = {DIRECT_RIGHT,DIRECT_DOWN,DIRECT_LEFT,DIRECT_UP};
ULONG Turn180[NUM_DIRECT] = {DIRECT_DOWN,DIRECT_LEFT,DIRECT_UP,DIRECT_RIGHT};
ULONG Turn270[NUM_DIRECT] = {DIRECT_LEFT,DIRECT_UP,DIRECT_RIGHT,DIRECT_DOWN};

#define CRASH_DELAY 48L /* must be multiple of 6 */

struct TronStruct
 {
  struct BTDDrawInfo *ts_BTDDrawInfo;
  LONG   ts_Left,ts_Top,ts_Right,ts_Bottom;
  ULONG  ts_CrashDelay,ts_CrashIndex;
  ULONG  ts_Pixels;
  ULONG  ts_NumPlayers,ts_Alive;
  LONG   ts_X[MAX_NUM_PLAYERS],ts_Y[MAX_NUM_PLAYERS];
  ULONG  ts_Direct[MAX_NUM_PLAYERS],ts_Count[MAX_NUM_PLAYERS];
  BYTE   *ts_CrashColor;
  LONG   ts_RandN,ts_RandF,ts_RandI;
 };

UBYTE TronRedTab[MAX_NUM_PLAYERS]   = {0xFF,0x00,0xFF,0x00,0xFF};
UBYTE TronGreenTab[MAX_NUM_PLAYERS] = {0xFF,0x00,0x00,0xFF,0x00};
UBYTE TronBlueTab[MAX_NUM_PLAYERS]  = {0x00,0xFF,0x00,0x00,0xFF};

/* library stuff */

char MyBlankerName[] = "tron.btd";
char MyBlankerID[]   = "Tron 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);
}

/* random generator */

void __regargs InitRandom(struct TronStruct *TronStruct,ULONG Instance)

{
 ULONG Time[2];

 CurrentTime (&Time[0],&Time[1]);
 TronStruct->ts_RandN=(LONG)Time[0];

 if (Time[1]<1024L) Time[1]|=1;
 else Time[1]>>=10;
 Time[1]^=Instance;

 TronStruct->ts_RandF=4*Time[1]+1;
 TronStruct->ts_RandI=2*Time[1]+1;
}

WORD __regargs Random(struct TronStruct *TronStruct,WORD Max)

{
 TronStruct->ts_RandN=TronStruct->ts_RandF*TronStruct->ts_RandN+TronStruct->ts_RandI;
 if (TronStruct->ts_RandN<0L) TronStruct->ts_RandN=-TronStruct->ts_RandN;

 return (WORD)(TronStruct->ts_RandN%Max);
}

/* implementation of library functions */

struct BTDInfo *QueryMyBlanker(void)

{
 return &TronInfo;
}

LONG __regargs BorderDist(struct TronStruct *TronStruct,ULONG Index)

{
 switch (TronStruct->ts_Direct[Index])
  {
   case DIRECT_UP:
    return TronStruct->ts_Y[Index]-TronStruct->ts_Top;
   case DIRECT_RIGHT:
    return TronStruct->ts_Right-TronStruct->ts_X[Index];
   case DIRECT_DOWN:
    return TronStruct->ts_Bottom-TronStruct->ts_Y[Index];
   case DIRECT_LEFT:
    return TronStruct->ts_X[Index]-TronStruct->ts_Left;
  }
 return 0L;
}

void __regargs PlaceCycle(struct TronStruct *TronStruct,ULONG Index)

{
 struct BTDDrawInfo *BTDDrawInfo;

 BTDDrawInfo=TronStruct->ts_BTDDrawInfo;
 do
  {
   TronStruct->ts_X[Index]=Random(TronStruct,BTDDrawInfo->BDI_Width-2L)+
                           1L+TronStruct->ts_Left;
   TronStruct->ts_Y[Index]=Random(TronStruct,BTDDrawInfo->BDI_Height-2L)+
                           1L+TronStruct->ts_Top;
  }
 while (ReadPixel(BTDDrawInfo->BDI_RPort,
                  TronStruct->ts_X[Index],TronStruct->ts_Y[Index])!=BTD_BgPen);

 TronStruct->ts_Direct[Index]=Random(TronStruct,NUM_DIRECT);
 TronStruct->ts_Count[Index]=Random(TronStruct,BorderDist(TronStruct,Index))+1L;

 BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[Index]]=TronRedTab[Index];
 BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[Index]]=TronGreenTab[Index];
 BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[Index]]=TronBlueTab[Index];
 BTDDrawInfo->BDI_Changed[BTDDrawInfo->BDI_Pens[Index]]=TRUE;

 TronStruct->ts_CrashColor[BTDDrawInfo->BDI_Pens[Index]]=TRUE;
}

struct TronStruct *InitMyBlanker(struct TagItem *TagList)

{
 struct BTDDrawInfo *BTDDrawInfo;
 struct TronStruct *TronStruct;
 ULONG *Error,Dummy,Index;
 UWORD MaxPen;

 if ((BTDDrawInfo=(struct BTDDrawInfo *)
                   GetTagData(BTD_DrawInfo,NULL,TagList))==NULL) return NULL;
 Error=(ULONG *)GetTagData(BTD_Error,(ULONG)&Dummy,TagList);
 if ((TronStruct=AllocVec(sizeof(struct TronStruct),MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  {
   *Error=BTDERR_Memory;
   return NULL;
  }
 InitRandom (TronStruct,GetTagData(BTD_Instance,0L,TagList));

 TronStruct->ts_BTDDrawInfo=BTDDrawInfo;
 TronStruct->ts_Left=BTDDrawInfo->BDI_Left;
 TronStruct->ts_Top=BTDDrawInfo->BDI_Top;
 TronStruct->ts_Right=BTDDrawInfo->BDI_Left+BTDDrawInfo->BDI_Width-1;
 TronStruct->ts_Bottom=BTDDrawInfo->BDI_Top+BTDDrawInfo->BDI_Height-1;
 TronStruct->ts_Pixels=GetTagData(TRON_Pixels,DEF_PIXELS,TagList);
 TronStruct->ts_NumPlayers=GetTagData(TRON_NumPlayers,DEF_NUM_PLAYERS,TagList);

 MaxPen=BTDDrawInfo->BDI_Pens[0];
 for (Index=1L; Index<TronStruct->ts_NumPlayers; Index++)
  if (MaxPen<BTDDrawInfo->BDI_Pens[Index]) MaxPen=BTDDrawInfo->BDI_Pens[Index];

 if ((TronStruct->ts_CrashColor=AllocVec(MaxPen+1L,MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  {
   FreeVec (TronStruct);
   *Error=BTDERR_Memory;
   return NULL;
  }

 for (Index=0L; Index<TronStruct->ts_NumPlayers; Index++)
  PlaceCycle (TronStruct,Index);
 TronStruct->ts_Alive=Index;

 return TronStruct;
}

void EndMyBlanker(struct TronStruct *TronStruct)

{
 FreeVec (TronStruct->ts_CrashColor);
 FreeVec (TronStruct);
}

LONG __regargs BorderDistDirect(struct TronStruct *TronStruct,ULONG Index,ULONG Direct)

{
 switch (Direct)
  {
   case DIRECT_UP:
    return TronStruct->ts_Y[Index]-TronStruct->ts_Top;
   case DIRECT_RIGHT:
    return TronStruct->ts_Right-TronStruct->ts_X[Index];
   case DIRECT_DOWN:
    return TronStruct->ts_Bottom-TronStruct->ts_Y[Index];
   case DIRECT_LEFT:
    return TronStruct->ts_X[Index]-TronStruct->ts_Left;
  }
 return 0L;
}

int __regargs CheckCrash(struct TronStruct *TronStruct,ULONG Index,ULONG Direct)

{
 return TronStruct->ts_CrashColor[ReadPixel(TronStruct->ts_BTDDrawInfo->BDI_RPort,
                                            TronStruct->ts_X[Index]+XInc[Direct],
                                            TronStruct->ts_Y[Index]+YInc[Direct])];
}

void __regargs TurnCycle(struct TronStruct *TronStruct,ULONG Index)

{
 ULONG NewDirect[2],Dist[2];
 ULONG Num;

 if (BorderDist(TronStruct,Index)==0L)
  {
   if (BorderDistDirect(TronStruct,Index,Turn090[TronStruct->ts_Direct[Index]])==0L)
    {
     TronStruct->ts_Direct[Index]=Turn270[TronStruct->ts_Direct[Index]];
     return;
    }
   if (BorderDistDirect(TronStruct,Index,Turn270[TronStruct->ts_Direct[Index]])==0L)
    {
     TronStruct->ts_Direct[Index]=Turn090[TronStruct->ts_Direct[Index]];
     return;
    }

   if (CheckCrash(TronStruct,Index,Turn090[TronStruct->ts_Direct[Index]]))
    {
     TronStruct->ts_Direct[Index]=Turn270[TronStruct->ts_Direct[Index]];
     return;
    }
   if (CheckCrash(TronStruct,Index,Turn270[TronStruct->ts_Direct[Index]]))
    {
     TronStruct->ts_Direct[Index]=Turn090[TronStruct->ts_Direct[Index]];
     return;
    }

   Num=2L;
   NewDirect[0]=Turn090[TronStruct->ts_Direct[Index]];
   NewDirect[1]=Turn270[TronStruct->ts_Direct[Index]];
  }
 else
  {
   Num=0L;

   if (Dist[Num]=BorderDistDirect(TronStruct,Index,Turn090[TronStruct->ts_Direct[Index]]))
    if (!CheckCrash(TronStruct,Index,Turn090[TronStruct->ts_Direct[Index]]))
     NewDirect[Num++]=Turn090[TronStruct->ts_Direct[Index]];

   if (Dist[Num]=BorderDistDirect(TronStruct,Index,Turn270[TronStruct->ts_Direct[Index]]))
    if (!CheckCrash(TronStruct,Index,Turn270[TronStruct->ts_Direct[Index]]))
     NewDirect[Num++]=Turn270[TronStruct->ts_Direct[Index]];
  }

 if (Num<2L)
  {
   if (Num) TronStruct->ts_Direct[Index]=NewDirect[0];
   return;
  }

 TronStruct->ts_Direct[Index]=NewDirect[(Random(TronStruct,Dist[0]+Dist[1])<Dist[0])?0:1];
}

void AnimMyBlanker(struct TronStruct *TronStruct)

{
 if (TronStruct->ts_CrashDelay)
  {
   struct BTDDrawInfo *BTDDrawInfo;
   ULONG Index;

   BTDDrawInfo=TronStruct->ts_BTDDrawInfo;
   Index=TronStruct->ts_CrashIndex;
   switch (TronStruct->ts_CrashDelay%6)
    {
     case 0:
      BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[Index]]=255;
      BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[Index]]=255;
      BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[Index]]=255;
      BTDDrawInfo->BDI_Changed[BTDDrawInfo->BDI_Pens[Index]]=TRUE;
      break;
     case 3:
      BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[Index]]=TronRedTab[Index];
      BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[Index]]=TronGreenTab[Index];
      BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[Index]]=TronBlueTab[Index];
      BTDDrawInfo->BDI_Changed[BTDDrawInfo->BDI_Pens[Index]]=TRUE;
    }

   if (--TronStruct->ts_CrashDelay==0L)
    if (--TronStruct->ts_Alive==1L)
     {
      EraseRect (TronStruct->ts_BTDDrawInfo->BDI_RPort,
                 TronStruct->ts_Left,TronStruct->ts_Top,
                 TronStruct->ts_Right,TronStruct->ts_Bottom);

      for (Index=0L; Index<TronStruct->ts_NumPlayers; Index++)
       PlaceCycle (TronStruct,Index);
      TronStruct->ts_Alive=Index;
     }
    else
     {
      BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[Index]]=0;
      BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[Index]]=0;
      BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[Index]]=0;
      BTDDrawInfo->BDI_Changed[BTDDrawInfo->BDI_Pens[Index]]=TRUE;

      TronStruct->ts_CrashColor[BTDDrawInfo->BDI_Pens[Index]]=FALSE;
     }
  }
 else
  {
   ULONG Pixels,Index;

   Pixels=TronStruct->ts_Pixels;
   while (Pixels--)
    for (Index=0L; Index<TronStruct->ts_NumPlayers; Index++)
     if (TronStruct->ts_CrashColor[TronStruct->ts_BTDDrawInfo->BDI_Pens[Index]])
      {
       if (--TronStruct->ts_Count[Index]==0L)
        {
         TurnCycle (TronStruct,Index);
         TronStruct->ts_Count[Index]=Random(TronStruct,BorderDist(TronStruct,Index))+1L;
        }

       if (TronStruct->ts_CrashColor[ReadPixel(TronStruct->ts_BTDDrawInfo->BDI_RPort,
                                               TronStruct->ts_X[Index],TronStruct->ts_Y[Index])])
        {
         TronStruct->ts_CrashDelay=CRASH_DELAY;
         TronStruct->ts_CrashIndex=Index;
         return;
        }
       else
        {
         SetAPen (TronStruct->ts_BTDDrawInfo->BDI_RPort,TronStruct->ts_BTDDrawInfo->BDI_Pens[Index]);
         WritePixel (TronStruct->ts_BTDDrawInfo->BDI_RPort,
                     TronStruct->ts_X[Index],TronStruct->ts_Y[Index]);
        }

       TronStruct->ts_X[Index]+=XInc[TronStruct->ts_Direct[Index]];
       TronStruct->ts_Y[Index]+=YInc[TronStruct->ts_Direct[Index]];

       if (TronStruct->ts_Count[Index]>1L)
        if (CheckCrash(TronStruct,Index,TronStruct->ts_Direct[Index]))
         TronStruct->ts_Count[Index]=1L;
      }
  }

 WaitTOF();
}

ULONG PenCountMyBlanker(struct TagItem *TagList)

{
 return GetTagData(TRON_NumPlayers,DEF_NUM_PLAYERS,TagList);
}
