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

#define __USE_SYSBASE 42

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

#include "BTD.h"

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

#define ASTAG(o) (BTD_Client+(o))

#define AS_Wasps ASTAG(0)
#define AS_Bees  ASTAG(1)
#define AS_Tight ASTAG(2)
#define AS_Speed ASTAG(3)
#define AS_Vel   ASTAG(4)
#define AS_Aim   ASTAG(5)
#define AS_Cycle ASTAG(6)

#define MAX_SPEED     4L
#define MAX_WASPS     10L
#define MAX_BEES      500L
#define MAX_TIGHTNESS 10L
#define MAX_VEL       15L

#define DEF_SPEED     3L
#define DEF_WASPS     2L
#define DEF_BEES      25L
#define DEF_TIGHTNESS 6L
#define DEF_VEL       5L

#define BEEACC  3
#define BEEVELOC 2
#define WASPACC 5
#define WASPVELOC 5
#define BORDER  20
#define WASPVEL (WASPVELOC+SP->ss_Velocity)
#define BEEVEL  (BEEVELOC+SP->ss_Velocity)

char *Speeds[] = {"Slow","Normal","Fast","Incredible",NULL};

struct BTDCycle ASwarmCycleParams[] =
 {
  AS_Speed,"Speed",BTDPT_CYCLE,DEF_SPEED,Speeds
 };

struct BTDInteger ASwarmIntParams[] =
 {
  AS_Wasps,"Wasps",BTDPT_INTEGER,DEF_WASPS,1L,MAX_WASPS,TRUE,
  AS_Bees,"Bees",BTDPT_INTEGER,DEF_BEES,1L,MAX_BEES,TRUE,
  AS_Vel,"Velocity",BTDPT_INTEGER,DEF_VEL,1L,MAX_VEL,TRUE,
  AS_Tight,"Tightness",BTDPT_INTEGER,DEF_TIGHTNESS,1L,MAX_TIGHTNESS,TRUE
 };

struct BTDBoolean ASwarmBoolParams[] =
 {
  AS_Cycle,"Cycle",BTDPT_BOOLEAN,TRUE,
  AS_Aim,"Aim Mode",BTDPT_BOOLEAN,FALSE
 };

struct BTDNode *ASwarmParams[] =
 {
  &ASwarmCycleParams[0].BC_Node,
  &ASwarmIntParams[0].BI_Node,&ASwarmIntParams[1].BI_Node,
  &ASwarmIntParams[2].BI_Node,&ASwarmIntParams[3].BI_Node,
  &ASwarmBoolParams[0].BB_Node,&ASwarmBoolParams[1].BB_Node,
  NULL
 };

struct BTDInfo ASwarmInfo =
 {
  BTDI_Revision,MAKE_ID('S','W','R','M'),
  "ASwarm","based on XSwarm","Markus Illenseer and Matthias Scheler",
  ASwarmParams
 };

/* rainbow colors */

#define NUM_RAINBOW_COLORS 6

UBYTE RBRed[NUM_RAINBOW_COLORS+1]   = {0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF};
UBYTE RBGreen[NUM_RAINBOW_COLORS+1] = {0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00};
UBYTE RBBlue[NUM_RAINBOW_COLORS+1]  = {0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00};

#define NUM_STEPS 16

#define NUM_BEE_COLORS (NUM_RAINBOW_COLORS*NUM_STEPS)

/* structure for a swarm, including the wasp */

struct SwarmStruct
 {
  struct BTDDrawInfo *ss_BTDDrawInfo;
  WORD ss_Width;   /* Width and */
  WORD ss_Height;  /* Height of the used area */
  LONG ss_Speed;   /* Speed */
  LONG ss_Count;   /* time until next frame */
  BOOL ss_AimMode; /* Bees may aim another wasp */
  WORD ss_Color;   /* Actual Color */
  WORD ss_DColor;  /* Cycling direction */
  WORD ss_NumWasps;/* total number of the Wasps */
  WORD *ss_WX[4];  /* The Wasps x-Position*/  /* WX[3] is used for Velocity */
  WORD *ss_WY[4];  /* Y-Position */
  WORD *ss_NB;     /* No. of Bees following this Wasp */
  WORD ss_NumBees; /* Total (!) number of Bees */
  LONG ss_Velocity;/* Velocity of the insects */
  WORD ss_BeeAcc;  /* Acceleration of the Bees */
  WORD *ss_X[4];   /* The Bees X-Position */  /* X[3] used for Velocity */
  WORD *ss_Y[4];   /* Y-Position */
  WORD *ss_MW;     /* The aimed Wasp */
  LONG ss_RandN,ss_RandF,ss_RandI;
  UBYTE ss_Red[NUM_BEE_COLORS];
  UBYTE ss_Green[NUM_BEE_COLORS];
  UBYTE ss_Blue[NUM_BEE_COLORS];
 };

/* Ill's strange Macros for easy access to the above structure */

#define BXVel(I)   (SP->ss_X[3][I])
#define BYVel(I)   (SP->ss_Y[3][I])
#define BeeX(P,I)  (SP->ss_X[P][I])
#define BeeY(P,I)  (SP->ss_Y[P][I])
#define MyWasp(I)  (SP->ss_MW[I])

#define WaXVel(I)  (SP->ss_WX[3][I])
#define WaYVel(I)  (SP->ss_WY[3][I])
#define WaspX(P,I) (SP->ss_WX[P][I])
#define WaspY(P,I) (SP->ss_WY[P][I])

/* library stuff */

char MyBlankerName[] = "aswarm.btd";
char MyBlankerID[]   = "ASwarm 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 */

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

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

{
 ULONG Time[2];

 CurrentTime (&Time[0],&Time[1]);
 SwarmStruct->ss_RandN=(LONG)Time[0];

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

 SwarmStruct->ss_RandF=4*Time[1]+1;
 SwarmStruct->ss_RandI=2*Time[1]+1;
}

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

{
 SwarmStruct->ss_RandN=SwarmStruct->ss_RandF*SwarmStruct->ss_RandN+SwarmStruct->ss_RandI;
 if (SwarmStruct->ss_RandN<0L) SwarmStruct->ss_RandN=-SwarmStruct->ss_RandN;

 return (WORD)(SwarmStruct->ss_RandN%Max);
}

/* fast integer square root, result will be not exact for x>255 */

BYTE SquareRootTab[256] = /* The mighty 256 Bytes eater :-) */
 {
  0,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,
  4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,
  5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,
  6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
  9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,
  10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,
  11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
  12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
  12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,
  14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
  14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
  15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15
 };

LONG __regargs FastSQRT(LONG x)

{
 LONG sr;

 sr=1L;
 while (x>255L)
  {
   x/=4L;
   sr*=2L;
  }
 return sr*(LONG)SquareRootTab[x];
}

/* implementation of library functions */

struct BTDInfo *QueryMyBlanker(void)

{
 return &ASwarmInfo;
}

ULONG __regargs SwarmSize(LONG NumWaps,LONG NumBees)

{
 return sizeof(struct SwarmStruct)+sizeof(WORD)*((ULONG)(NumWaps+NumBees)*9L);
}

struct SwarmStruct *InitMyBlanker(struct TagItem *TagList)

{
 struct BTDDrawInfo *BTDDrawInfo;
 ULONG *Error,Dummy,Instance,Index;
 LONG NumWasps,NumBees,Tightness;
 struct SwarmStruct *SP;
 WORD *Ptr;

 if ((BTDDrawInfo=(struct BTDDrawInfo *)
                   GetTagData(BTD_DrawInfo,NULL,TagList))==NULL) return NULL;
 Error=(ULONG *)GetTagData(BTD_Error,(ULONG)&Dummy,TagList);
 if ((BTDDrawInfo->BDI_Width<(BORDER*3))||
     (BTDDrawInfo->BDI_Height<(BORDER*3)))
  {
   *Error=BTDERR_Size;
   return NULL;
  }
 Instance=GetTagData(BTD_Instance,0L,TagList);

 NumWasps=GetTagData(AS_Wasps,DEF_WASPS,TagList);
 NumBees=GetTagData(AS_Bees,DEF_BEES,TagList);
 if ((SP=AllocVec(SwarmSize(NumWasps,NumBees),MEMF_PUBLIC|MEMF_CLEAR))==NULL)
  {
   *Error=BTDERR_Memory;
   return NULL;
  }

 SP->ss_BTDDrawInfo=BTDDrawInfo;
 SP->ss_Width=BTDDrawInfo->BDI_Width;
 SP->ss_Height=BTDDrawInfo->BDI_Height;
 SP->ss_NumWasps=NumWasps;
 SP->ss_NumBees=NumBees;
 SP->ss_Velocity=GetTagData(AS_Vel,DEF_VEL,TagList);
 Tightness=GetTagData(AS_Tight,DEF_TIGHTNESS,TagList);
 SP->ss_BeeAcc=(Tightness*BEEACC)/MAX_SPEED+1;

 Ptr=(WORD *)&SP[1];
 for (Index=0L; Index<4L; Index++)
  {
   SP->ss_WX[Index]=Ptr;
   Ptr+=NumWasps;
   SP->ss_WY[Index]=Ptr;
   Ptr+=NumWasps;
   SP->ss_X[Index]=Ptr;
   Ptr+=NumBees;
   SP->ss_Y[Index]=Ptr;
   Ptr+=NumBees;
  }
 SP->ss_NB=Ptr;
 SP->ss_MW=Ptr+NumWasps;

 InitRandom (SP,Instance);

 SP->ss_Speed=GetTagData(AS_Speed,DEF_SPEED,TagList)+1L;
 SP->ss_Count=MAX_SPEED;
 SP->ss_AimMode=GetTagData(AS_Aim,FALSE,TagList);

 /* Colors */

 if (GetTagData(AS_Cycle,TRUE,TagList))
  {
   ULONG Col,Step;

   for (Index=0L, Col=0L; Index<NUM_RAINBOW_COLORS; Index++)
    for (Step=0L; Step<NUM_STEPS; Step++, Col++)
     {
      SP->ss_Red[Col]=RBRed[Index]+((RBRed[Index+1]-RBRed[Index])*Step)/NUM_STEPS;
      SP->ss_Green[Col]=RBGreen[Index]+((RBGreen[Index+1]-RBGreen[Index])*Step)/NUM_STEPS;
      SP->ss_Blue[Col]=RBBlue[Index]+((RBBlue[Index+1]-RBBlue[Index])*Step)/NUM_STEPS;
     }

   SP->ss_Color=Random(SP,NUM_BEE_COLORS);
   SP->ss_DColor=Random(SP,2)?(NUM_BEE_COLORS-1):1;
  }
 else
  {
   SP->ss_DColor=0;
   BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[1]]=0xFF;
   BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[1]]=0x00;
   BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[1]]=0x00;
   BTDDrawInfo->BDI_Changed[BTDDrawInfo->BDI_Pens[1]]=TRUE;
  }

 /* Wasps */
 for (Index=0L; Index<NumWasps; Index++)
  {
   WaspX(1,Index)=WaspX(0,Index)=BORDER+Random(SP,SP->ss_Width-2*BORDER);
   WaspY(1,Index)=WaspY(0,Index)=BORDER+Random(SP,SP->ss_Height-2*BORDER);
   WaXVel(Index)=RAND(WASPACC);
   WaYVel(Index)=RAND(WASPACC);
   SP->ss_NB[Index]=0;
  }

 /* Bees */
 for (Index=0L; Index<NumBees; Index++)
  {
   BeeX(1,Index)=BeeX(0,Index)=BORDER+Random(SP,SP->ss_Width-2*BORDER);
   BeeY(1,Index)=BeeY(0,Index)=BORDER+Random(SP,SP->ss_Height-2*BORDER);
   BXVel(Index)=RAND(SP->ss_BeeAcc);
   BYVel(Index)=RAND(SP->ss_BeeAcc);
   SP->ss_NB[MyWasp(Index)=Index%SP->ss_NumWasps]++;
  }

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

 return SP;
}

void EndMyBlanker(struct SwarmStruct *SP)

{
 FreeVec (SP);
}

void AnimMyBlanker(struct SwarmStruct *SP)

{
 struct BTDDrawInfo *BTDDrawInfo;
 LONG Index,Left,Top;
 struct RastPort *RP;
 WORD WaspPen,BeePen;

 BTDDrawInfo=SP->ss_BTDDrawInfo;
 if (SP->ss_DColor)
  {
   BTDDrawInfo->BDI_Red[BTDDrawInfo->BDI_Pens[1]]=SP->ss_Red[SP->ss_Color];
   BTDDrawInfo->BDI_Green[BTDDrawInfo->BDI_Pens[1]]=SP->ss_Green[SP->ss_Color];
   BTDDrawInfo->BDI_Blue[BTDDrawInfo->BDI_Pens[1]]=SP->ss_Blue[SP->ss_Color];
   BTDDrawInfo->BDI_Changed[BTDDrawInfo->BDI_Pens[1]]=TRUE;
   SP->ss_Color=(SP->ss_Color+SP->ss_DColor)%NUM_BEE_COLORS;
  }

 WaitTOF();
 if (SP->ss_Count<MAX_SPEED)
  {
   SP->ss_Count++;
   return;
  }
 SP->ss_Count=SP->ss_Speed;

 /* Wasps */

 for (Index=0L; Index<SP->ss_NumWasps; Index++)
  {
   WaspX(2,Index)=WaspX(1,Index);
   WaspX(1,Index)=WaspX(0,Index);
   WaspY(2,Index)=WaspY(1,Index);
   WaspY(1,Index)=WaspY(0,Index);

   WaXVel(Index)+=RAND(WASPACC);
   WaYVel(Index)+=RAND(WASPACC);

   if (WaXVel(Index)>WASPVEL) WaXVel(Index)=WASPVEL;
   if (WaXVel(Index)<-WASPVEL) WaXVel(Index)=-WASPVEL;
   if (WaYVel(Index)>WASPVEL) WaYVel(Index)=WASPVEL;
   if (WaYVel(Index)<-WASPVEL) WaYVel(Index)=-WASPVEL;

   WaspX(0,Index)=WaspX(1,Index)+WaXVel(Index);
   WaspY(0,Index)=WaspY(1,Index)+WaYVel(Index);

   /* Bounce check for Wasps */

   if ((WaspX(0,Index)<BORDER)||(WaspX(0,Index)>SP->ss_Width-BORDER-1))
    {
     WaXVel(Index)=-WaXVel(Index);
     WaspX(0,Index)+=WaXVel(Index);
    }
   if ((WaspY(0,Index)<BORDER)||(WaspY(0,Index)>SP->ss_Height-BORDER-1))
    {
     WaYVel(Index)=-WaYVel(Index);
     WaspY(0,Index)+=WaYVel(Index);
    }
  }

 /* Bees */

 for (Index=0L; Index<SP->ss_NumBees; Index++)
  {
   WORD DX,DY,ChkIndex;
   LONG Distance,NewDistance;

   BeeX(2,Index)=BeeX(1,Index);
   BeeX(1,Index)=BeeX(0,Index);
   BeeY(2,Index)=BeeY(1,Index);
   BeeY(1,Index)=BeeY(0,Index);

   DX=WaspX(1,MyWasp(Index))-BeeX(1,Index);
   DY=WaspY(1,MyWasp(Index))-BeeY(1,Index);
   Distance=FastSQRT(DX*DX+DY*DY);
   if (Distance==0L) Distance=1L;

   if (SP->ss_AimMode) /* Look out for the nearest wasp if Aim-Mode is on */
    for (ChkIndex=0; ChkIndex<SP->ss_NumWasps; ChkIndex++)
     if (ChkIndex!=MyWasp(Index))
      {
       LONG NewDX,NewDY;

       NewDX=WaspX(1,ChkIndex)-BeeX(1,Index);
       NewDY=WaspY(1,ChkIndex)-BeeY(1,Index);
       NewDistance=FastSQRT(NewDX*NewDX+NewDY*NewDY);
       if (Distance>NewDistance)
        {
         DX=NewDX;
         DY=NewDY;
         if (NewDistance==0L) Distance=1L;
         else Distance=NewDistance;
         SP->ss_NB[MyWasp(Index)]--;
         SP->ss_NB[MyWasp(Index)=ChkIndex]++; /* Mark a nearer Wasp */
        }
      }

   BXVel(Index)+=(DX*SP->ss_BeeAcc)/Distance+RAND(3);
   BYVel(Index)+=(DY*SP->ss_BeeAcc)/Distance+RAND(3);

   if (BXVel(Index)>BEEVEL)  BXVel(Index)=BEEVEL;
   if (BXVel(Index)<-BEEVEL) BXVel(Index)=-BEEVEL;
   if (BYVel(Index)>BEEVEL)  BYVel(Index)=BEEVEL;
   if (BYVel(Index)<-BEEVEL) BYVel(Index)=-BEEVEL;

   BeeX(0,Index)=BeeX(1,Index)+BXVel(Index);
   BeeY(0,Index)=BeeY(1,Index)+BYVel(Index);

  /* Bounce check for Bees */

   if ((BeeX(0,Index)<BORDER)||(BeeX(0,Index)>(SP->ss_Width-BORDER-1)))
    {
     BXVel(Index)=-BXVel(Index);
     BeeX(0,Index)+=BXVel(Index);
    }
   if ((BeeY(0,Index)<BORDER)||(BeeY(0,Index)>(SP->ss_Height-BORDER-1)))
    {
     BYVel(Index)=-BYVel(Index);
     BeeY(0,Index)+=BYVel(Index);
    }
  }


 RP=BTDDrawInfo->BDI_RPort;
 Left=BTDDrawInfo->BDI_Left;
 Top=BTDDrawInfo->BDI_Top;
 WaspPen=BTDDrawInfo->BDI_Pens[0];
 BeePen=BTDDrawInfo->BDI_Pens[1];

 /* Move our insects */

 for (Index=0L; Index<SP->ss_NumWasps; Index++)   /* Wasps */
  {
   SetAPen (RP,BTD_BgPen);
   Move (RP,Left+WaspX(2,Index),Top+WaspY(2,Index));
   Draw (RP,Left+WaspX(1,Index),Top+WaspY(1,Index));

   SetAPen (RP,WaspPen);
   Draw (RP,Left+WaspX(0,Index),Top+WaspY(0,Index));
  }

 for (Index=0L; Index<SP->ss_NumBees; Index++)   /* Bees  */
  {
   SetAPen (RP,BTD_BgPen);
   Move (RP,Left+BeeX(2,Index),Top+BeeY(2,Index));
   Draw (RP,Left+BeeX(1,Index),Top+BeeY(1,Index));

   SetAPen (RP,BeePen);
   Draw (RP,Left+BeeX(0,Index),Top+BeeY(0,Index));
  }
}

ULONG PenCountMyBlanker(struct TagItem *TagList)

{
 return 2L;
}
