/*

   This is the C-language source for the s_animate routine.  We don't
expect you'll want to tinker with it, but it might be interesting for
you to see how things were done.

*/

#include <tb/tb.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/collide.h>
#include <intuition/intuition.h>
#include <graphics/sprite.h>

#define BUserStuff WORD
#define BUserExt Priority

#include <graphics/gels.h>

struct AO /* our animation object */
  {WORD X,Y,XVel,YVel,XAccel,YAccel; /* in pixels/32 */
   WORD Width,Height; /* width and height (pixels/32) for collisions */
   struct SimpleSprite *SSprite; /* sprite pointer for simple sprites */
   struct VSprite *VSprite; /* vsprite pointer for bobs */
   struct AO *Link; /* link for all our objects */
   WORD HitMask,MeMask; /* collision masks */
   BYTE FollowX,FollowY;}; /* flags -- follow mouse x, mouse y */

LONG SysBase;
LONG GfxBase;
LONG IntuitionBase;
struct AO *animKey;
struct Bob *FirstBob;

#define COLLQSIZE 100 /* fifty collisions */

static LONG clist[COLLQSIZE]; /* collision queue */
static LONG *clisti; /* insert pointer */
static LONG *clistd; /* delete pointer */
static LONG *cliste; /* eoq pointer */

static struct GelsInfo MG;
static struct VSprite vsa,vsb;
static WORD NullSprite[4];

#define CLUECODE 0xAAAA

struct BK /* a box keep string */
  {LONG Length; /* string length */
   WORD Width; /* image width in pixels */
   UWORD Clue; /* clue that this is really a box keep string */
   struct BitMap BitMap; /* the bitmap, no planeptrs */
   WORD ImageData[1];}; /* start of image data */


void s_animate(arg,uv)
   register LONG **arg;
   struct TBUserVector *uv;

  {struct TBInt *tbrip, *tbxip, *tbyip;
   struct TBString *tbwsp, *tbssp;
   struct Screen *sp;
   register struct RastPort *rp;
   register struct ViewPort *vp;
   WORD code,x,y,result;
   register char *p1;

   extern void init_all(), animate_all();
   extern void addob(), removeob(), freeob();
   extern struct ViewPort *ViewPortAddress();

   tbrip = (struct TBInt *) *arg--; /* first arg: function code */
   code = tbrip->integer;

   tbwsp = (struct TBString *) **arg--; /* second arg */
   p1 = *((char **) tbwsp->text); /* usually a pointer */

   tbxip = (struct TBInt *) *arg--; /* third arg */
   x = tbxip->integer;

   tbyip = (struct TBInt *) *arg--; /* fourth arg */
   y = tbyip->integer;

   tbrip = (struct TBInt *) *arg--; /* fifth arg: result code */
   tbrip->exponent = -1; /* result will be an integer */

   tbssp = (struct TBString *) **arg; /* sixth arg */

   SysBase = uv->SysBase;
   GfxBase = uv->GfxBase;
   IntuitionBase = uv->IntuitionBase;
   sp = uv->window->WScreen;
   rp = &(sp->RastPort);
   vp = &(sp->ViewPort);
   result = 0; /* assume success */

   switch(code)
     {case 0: /* initialize everything */
         init_all(rp,vp,sp,uv->window);
         break;
      case 1: /* free everything */
         while(animKey) freeob(animKey,rp,vp);
         animate_all(tbwsp,tbssp,tbxip,rp,vp,0);
         ClearPointer(uv->window);
         rp->GelsInfo = NULL;
         break;
      case 2: /* create Sprite-type object */
         result = makesprite(tbssp,tbwsp,tbrip->integer,x,y,rp,vp);
         break;
      case 3: /* create Bob-type object */
         result = makebob(tbssp,tbwsp,x,y,rp);
         break;
      case 4: /* destroyob */
         freeob(p1,rp,vp);
         break;
      case 5: /* animate_quick */
         animate_all(tbwsp,tbssp,tbxip,rp,vp,sp,0);
         break;
      case 6: /* animate_all */
         animate_all(tbwsp,tbssp,tbxip,rp,vp,sp,1);
         break;
      case 7: /* setob_priority */
         prioritize(p1,x);
         break;
      case 8: /* getsprite_color */
         getcolor(p1,x,tbyip,vp);
         break;
      case 9: /* setsprite_color */
         setcolor(p1,x,y,vp);
         break;}

   tbrip->integer = result;}


void init_all(rp,vp,sp,wp)
   struct RastPort *rp;
   struct ViewPort *vp;
   register struct Screen *sp;
   struct Window *wp;

  {while (animKey) freeob(animKey,rp,vp);
   InitGels(&vsa,&vsb,&MG); /* initialize gels list */
   SetPointer(wp,NullSprite,0,0,0,0); /* hide pointer */
   MG.leftmost = sp->LeftEdge << 5; /* copy bounds */
   MG.rightmost = (sp->LeftEdge+sp->Width) << 5;
   MG.topmost = sp->TopEdge << 5;
   MG.bottommost = (sp->TopEdge+sp->Height) << 5;
   rp->GelsInfo = &MG; /* it goes with this RastPort */
   cliste = clist+COLLQSIZE;  /* end pointer */
   for (clisti=cliste; clisti!=clist; *--clisti=0);  /* zero queue */
   clistd = clisti;} /* reset insert and delete pointers */


void animate_all(wp,sp,xp,rp,vp,scp,ctest)
   struct TBString *wp, *sp;
   struct TBInt *xp;
   register struct RastPort *rp;
   struct ViewPort *vp;
   struct Screen *scp;
   register WORD ctest;

  {register WORD i,x,y,xmax,ymax;
   LONG MeHit;
   register struct AO *ob1, *ob2;
   register struct View *View;
   extern struct View *ViewAddress();
   
   View = ViewAddress();
   
   for (i=xp->integer; (i!=0) && !(ctest && *clistd); i--)
     {xmax = scp->MouseX << 5;
      ymax = scp->MouseY << 5;
      for (ob1=animKey;ob1;ob1=ob1->Link)
        {x = ob1->X; y = ob1->Y;
         x += ob1->XVel; y += ob1->YVel;
         if (ob1->FollowX) x = xmax;
         if (ob1->FollowY) y = ymax;
         ob1->X = x; ob1->Y = y;
         ob1->XVel += ob1->XAccel; ob1->YVel += ob1->YAccel;
         x >>= 5; y >>= 5;
         if (ob1->SSprite) MoveSprite(vp,ob1->SSprite,x,y);
         else
           {ob1->VSprite->X = x;
            ob1->VSprite->Y = y;}}

      SortGList(rp);
      DrawGList(rp,vp);

      for (ob1=animKey;ob1;ob1=ob1->Link)
        {x = ob1->X; y = ob1->Y;
         xmax = x + ob1->Width; ymax = y + ob1->Height;
         MeHit = (((LONG) ob1->MeMask) << 16) + ob1->HitMask;

         if (MeHit & 1)
           {if (x<=MG.leftmost) Collision(ob1,1);
            if (xmax>=MG.rightmost) Collision(ob1,2);
            if (y<=MG.topmost) Collision(ob1,3);
            if (ymax>=MG.bottommost) Collision(ob1,4);}

         if (MeHit) for (ob2=ob1->Link;ob2;ob2=ob2->Link)
           if ((xmax >= ob2->X) &&
               (ymax >= ob2->Y) &&
               (MeHit & *((LONG *) &ob2->HitMask)) && 
               (x <= ob2->X + ob2->Width) &&
               (y <= ob2->Y + ob2->Height)) 
              Collision(ob1,ob2);}

      if (i<0) i=-1;}

   if (ctest && *clistd)
     {*((LONG *) wp->text) = *clistd; *clistd++ = 0;
      *((LONG *) sp->text) = *clistd; *clistd++ = 0;
      if (clistd == cliste) clistd = clist;}

  xp->integer = i;}


makebob(image,s,initx,inity,rp)
   struct BK *image;
   struct TBString *s;
   WORD initx,inity;
   struct RastPort *rp;

  {register struct AO *ob;
   register struct VSprite *vs;
   register struct Bob *bob;
   extern void freeob();
   LONG x;
   
   if (image->Clue != CLUECODE) return(-2);
   ob = (struct AO *) AllocMem(sizeof(struct AO),MEMF_CLEAR);
   if (ob==NULL) return(-1);
   ob->Link = animKey;
   animKey = ob;

   vs = (struct VSprite *) AllocMem(sizeof(struct VSprite),MEMF_CLEAR);
   if (vs==NULL) goto FAIL;
     
   bob = (struct Bob *) AllocMem(sizeof(struct Bob),MEMF_CLEAR);
   if (bob==NULL)
     {FreeMem(vs,sizeof(struct VSprite));
      goto FAIL;}

   vs->VSBob = bob; /* link VSprite and Bob together */
   bob->BobVSprite = vs;
   ob->VSprite = vs; /* link to our object */

   x = image->BitMap.Rows * image->BitMap.BytesPerRow;
   vs->Flags = OVERLAY | SAVEBACK;
   vs->Height = image->BitMap.Rows;
   ob->Height = vs->Height << 5;
   vs->Width = image->BitMap.BytesPerRow>>1;
   ob->Width = image->Width << 5;
   vs->Depth = image->BitMap.Depth;
   ob->X = initx;
   ob->Y = inity;
   ob->HitMask = 1;

   vs->ImageData = (WORD *) AllocMem(x*vs->Depth,MEMF_CHIP);
   if (vs->ImageData==NULL) goto FAIL;
   movmem(image->ImageData,vs->ImageData,x*vs->Depth);
   vs->PlanePick = -1;

   vs->CollMask = (WORD *) AllocMem(x,MEMF_CHIP);
   if (vs->CollMask==NULL) goto FAIL;
   bob->ImageShadow = vs->CollMask;

   bob->SaveBuffer = (WORD *) AllocMem(x*rp->BitMap->Depth,MEMF_CHIP);
   if (bob->SaveBuffer==NULL) goto FAIL;
   InitMasks(vs);
   AddBob(bob,rp);
   prioritize(ob,0);
   *((LONG *) s->text) = (LONG) ob;
   return (0);

FAIL: freeob(ob,NULL,NULL);
   return (-1);}


makesprite(image,s,num,initx,inity,rp,vp)
   struct BK *image;
   struct TBString *s;
   WORD num,initx,inity;
   struct RastPort *rp;
   struct ViewPort *vp;

  {register struct AO *ob;
   register struct SimpleSprite *sp;
   extern void freeob();
   LONG x;
   
   if (image->Clue != CLUECODE) return(-2);
   ob = (struct AO *) AllocMem(sizeof(struct AO),MEMF_CLEAR);
   if (ob==NULL) return(-1);
   ob->Link = animKey;
   animKey = ob;

   sp = (struct SimpleSprite *) 
           AllocMem(sizeof(struct SimpleSprite),MEMF_CLEAR);
   if (sp==NULL) goto FAIL;

   ob->SSprite = sp; /* link simple sprite to our object */
   sp->num = -1; /* no sprite for this yet */

   x = image->BitMap.Rows << 1;
   sp->height = image->BitMap.Rows;
   ob->Height = sp->height << 5;
   if (vp->Modes & LACE) ob->Height <<= 1; /* apparent height interlaced */
   ob->X = initx; ob->Y = inity;
   ob->HitMask = 1;
   sp->x = sp->y = -100; /* image offscreen until animated */
   ob->Width = image->Width << 5;
   if (vp->Modes & HIRES) ob->Width <<= 1; /* apparent width hires */
   sp->posctldata = (UWORD *) AllocMem(x+x+8,MEMF_CHIP|MEMF_CLEAR);
   if (sp->posctldata==NULL) goto FAIL;

  {register WORD *even,*odd,*dest,*end;
      dest = ((WORD *) sp->posctldata) + 2;
      even = image->ImageData; /* source for even words */
      if (image->BitMap.Depth==1) odd = NULL; /* no source for odd words */
      else odd = even + image->BitMap.Rows; /* source for odd words */
      end = dest + x;
      while(dest<end)
        {*dest++ = *even++; /* set low-order image bits */
         if (odd) *dest++ = *odd++; else *dest++ = 0;}} /* and high */

   if (GetSprite(sp,num) == -1)
     {freeob(ob,NULL,NULL);
      return(-3);}
   *((LONG *) s->text) = (LONG) ob;
   return (0);

FAIL: freeob(ob,NULL,NULL);
   return (-1);}


void freeob(ob,rp,vp)
   register struct AO *ob;
   struct RastPort *rp;
   struct ViewPort *vp;

  {struct VSprite *vs;
   register struct SimpleSprite *ss;
   register struct AO *ob2;
   register struct Bob *bob;
   register LONG x;

   if (ss = ob->SSprite) 
     {if (ss->num >= 0) FreeSprite(ss->num);
      if (ss->posctldata) FreeMem(ss->posctldata,(ss->height<<1)+8);
      FreeMem(ss,sizeof(struct SimpleSprite));}

   if (vs = ob->VSprite)
     {bob = vs->VSBob;
      if (bob->Before) bob->Before->After = bob->After;
      else if (bob==FirstBob) FirstBob = bob->After;
      if (bob->After) bob->After->Before = bob->Before;
      if (vs->NextVSprite) RemIBob(bob,rp,vp);
      x = (vs->Width * vs->Height) << 1;
      if (vs->ImageData) FreeMem(vs->ImageData,x*vs->Depth);
      if (bob->SaveBuffer) FreeMem(bob->SaveBuffer,x*rp->BitMap->Depth);
      if (vs->CollMask) FreeMem(vs->CollMask,x);
      FreeMem(vs,sizeof(struct VSprite));
      FreeMem(bob,sizeof(struct Bob));}

   if (ob==animKey) animKey = ob->Link;
   else
     {for (ob2=animKey;ob2->Link!=ob;ob2=ob2->Link);
      ob2->Link = ob->Link;}
   FreeMem(ob,sizeof(struct AO));}


prioritize(ob,pri)
   struct AO *ob;
   register WORD pri;

  {register struct Bob *bob1, *bob2;
  
   bob1 = ob->VSprite->VSBob;
   bob1->Priority = pri;
   if (bob1->Before) bob1->Before->After = bob1->After;
   else if (bob1==FirstBob) FirstBob = bob1->After;
   if (bob1->After) bob1->After->Before = bob1->Before;

   if ((!(bob2=FirstBob)) || bob2->Priority<=pri)
     {bob1->After = bob2;
      bob1->Before = NULL;
      if (bob2) bob2->Before = bob1;
      FirstBob = bob1;}
   else
     {while (bob2->After && (bob2->After->Priority>pri))
        bob2 = bob2->After;
      bob1->After = bob2->After;
      if (bob2->After) bob2->After->Before = bob1;
      bob1->Before = bob2;
      bob2->After = bob1;}}


getcolor(ob,color,cmixp,vp)
   struct AO *ob;
   WORD color;
   struct TBInt *cmixp;
   struct ViewPort *vp;
   
  {color += 16 + ((ob->SSprite->num & 6)<<1);
   cmixp->exponent = -1;
   cmixp->integer = *(((WORD *) vp->ColorMap->ColorTable) + color);}


setcolor(ob,color,cmix,vp)
   struct AO *ob;
   WORD color,cmix;
   struct ViewPort *vp;

  {color += 16 + ((ob->SSprite->num & 6) << 1);
   SetRGB4(vp,color,(cmix>>8)&0xF,(cmix>>4)&0xF,cmix&0xF);}
   

Collision(g1,g2)
   LONG g1,g2;

  {if (*clisti) /* queue full? */
     {clistd += 2; /* discard oldest one */
      if (clistd == cliste) clistd = clist;} /* circularize */
   *clisti++ = g1; /* add new stuff to the queue */
   *clisti++ = g2;
   if (clisti == cliste) clisti = clist;} /* circularize and done */
