/* =========================================================================

GELTOOLS.C -

	A FILE CONTAINING USEFUL SETUP TOOLS FOR THE ANIMATION SYSTEM


	author:  Rob Peck, incorporating valuable comments and changes from
		 Barry Whitebook and David Lucas.

=========================================================================== */

#include <exec/types.h>
#include <exec/memory.h>
/* ALWAYS INCLUDE GFX.H before other includes */
#include <graphics/gfx.h>
#include <graphics/gels.h>
#include <graphics/clip.h>
#include <graphics/rastport.h>
#include <graphics/view.h>
#include <graphics/gfxbase.h>


/*******************************************************************************
 * This file is a collection of tools which are used with the vsprite and
 * bob software.  It contains the following:
 *
 * ReadyGels( *gelsinfo, *rastport );
 * PurgeGels( *gelsinfo );
 *
 * struct VSprite *MakeVSprite(lineheight,*image,*colorset,x,y,
 * 	wordwidth,imagedepth,flags);
 * DeleteVSprite( &VSprite );
 *
 * struct Bob *MakeBob(bitwidth,lineheight,imagedepth,*image,
 * 		planePick,planeOnOff,x,y)
 * DeleteBob( &Bob );
 *
 * ReadyGels sets up the defaults of the gel system by initializing the
 * GelsInfo structure you provide.  First it allocates room for and
 * links in lastcolor and nextline. It then uses information in your
 * RastPort structure to establish boundary collision defaults at
 * the outer edges of the raster.  It then links together the GelsInfo
 * and the RastPort which you provide. Next it allocates space for two
 * dummy virtual sprite structures, calls InitGels and SetCollision.
 ! You must already have run LoadView before ReadyGels is called.
 *
 * PurgeGels deallocates all memory which ReadyGels and NewGelList have
 * allocated.  The system will crash if you have not used these
 * routines to allocate the space (you cant deallocate something
 * which you havent allocated in the first place).
 *
 * MakeVSprite allocates enough space for and inits a normal vsprite.
 * DeleteVSprite deallocates the memory it used.
 *
 * MakeBob initializes a standard bob and allocates as much memory as is needed
 * for a normal bob and its vsprite structure, links them together.
 * To find the associated vsprite, look at the back-pointer (see the
 * routine doc itself).
 * DeleteBob deallocates the memory it used.
 *
 * Written by Rob Peck, with thanks to Barry Whitebrook and David Lucas.
 *
 ******************************************************************************/

void border_dummy()
{
   return;
}

/* Caller passes a pointer to his GelsInfo structure which he wants to init,
 * along with a pointer to his IVPArgs.  Default init places the topmost
 * bottommost etc at the outermost boundaries of callers rastport parameters.
 * Caller can change all this stuff after this routine returns.
 */

extern struct RastPort *myRast;

struct VSprite *SpriteHead = NULL;
struct VSprite *SpriteTail = NULL;

/*******************************************************************************
 * This routine cannot be run until the first LoadView(&view) has been
 * executed.  InitGels works with an already active View, so LoadView
 * must have been run first.
 */

ReadyGels(g, r)
struct RastPort *r;
struct GelsInfo *g;
{
   /* Allocate head and tail of list. */
   if ((SpriteHead = (struct VSprite *)AllocMem(sizeof
         (struct VSprite), MEMF_PUBLIC | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("ReadyGels: No memory for sprite head.\n");
#endif
      return(-1);
   }

   if ((SpriteTail = (struct VSprite *)AllocMem(sizeof
         (struct VSprite), MEMF_PUBLIC | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("ReadyGels: No memory for sprite tail.\n");
#endif
      return(-1);
   }

  /* By setting all bits here, it means that there are NO
   * reserved sprites.  The system can freely use all of the
   * hardware sprites for its own purposes.  The caller will not be
   * trying to independently use any hardware sprites!
   */
   g->sprRsrvd = -1;

  /* The nextline array is used to hold system information about
   * "at which line number on the screen is this hardware sprite
   * again going to become available to be given a new vsprite to
   * display".
   */
   if ((g->nextLine = (WORD *)AllocMem(sizeof(WORD) * 8,
         MEMF_PUBLIC | MEMF_CLEAR)) == NULL) {
#ifdef DEBUG
      kprintf("ReadyGels: No memory for nextline.\n");
#endif
      return(-1);
   }

  /* In the lastcolor pointer array, the system will store
   * a pointer to the color definitions most recently used
   * by the system. .... as a reminder, virtual sprites can
   * be assigned to any of the real hardware sprites which
   * may be available at the time.  The vsprite colors will
   * be written into the hardware sprite register set for
   * the hardware sprite to which that vsprite is assigned.
   * This pointer array contains one pointer to the last
   * set of three colors (from the vsprite structure *sprColors)
   * for each hardware sprite.
   *
   * As the system is scanning to determine which hardware
   * sprite should next be used to represent a vsprite, it
   * checks the contents of this array.  If a hardware sprite
   * is available and already has been assigned this set of
   * colors, no color assignment is needed, and therefore
   * no color change instructions will be generated for the
   * copper list.
   *
   * If all vsprites use a different set of sprColors, (pointers
   * to sprColors are different for all vsprites), then there
   * is a limit of 4 vsprites on a horizontal line.  If, on
   * the other hand, you define, lets say 8 vsprites, with
   * 1 and 2 having the same sprColors, 3 and 4 the same as
   * each other, 5 and 6 the same as each other, and 7 and 8
   * also having the same vsprite colors, then you will be
   * able to have all 8 vsprites on the same horizontal line.
   *
   * In this case, you will be able to put all 8 vsprites on
   * the same horizontal line.  The reason this helps is that
   * the system hardware shares the color registers between pairs
   * of hardware sprites.  The system thus has enough resources
   * to assign all vsprites to hardware sprites in that there
   * are 4 color-sets for 8 vsprites, exactly matching the
   * hardware maximum capabilities.
   *
   * Note that lastcolor will not be used for bobs. Just sprites.
   */
   if ((g->lastColor = (WORD **)AllocMem(sizeof(LONG) * 8,
         MEMF_PUBLIC | MEMF_CLEAR)) == NULL) {
#ifdef DEBUG
      kprintf("ReadyGels: No memory for lastcolor.\n");
#endif
      return(-1);
   }

  /* This is a table of pointers to the routines which should
   * be performed when DoCollision senses a collision.  This
   * declaration may not be necessary for a basic vsprite with
   * no collision detection implemented, but then it makes for
   * a complete example.
   */
   if ((g->collHandler = (struct collTable *)AllocMem(sizeof(struct
         collTable), MEMF_PUBLIC | MEMF_CLEAR)) == NULL) {
#ifdef DEBUG
      kprintf("ReadyGels: No memory for collHandler.\n");
#endif
      return(-1);
   }

  /* When any part of the object touches or passes across
   * this boundary, it will cause the boundary collision
   * routine to be called.  This is at smash[0] in the
   * collision handler table and is called only if
   * DoCollision is called.
   */
   g->leftmost = 0;
   g->rightmost = r->BitMap->BytesPerRow * 8 - 1;
   g->topmost = 0;
   g->bottommost = r->BitMap->Rows - 1;

   r->GelsInfo = g;  /* Link together the two structures */

   InitGels(SpriteHead, SpriteTail, g );

  /* Pointers initialized to the dummy sprites which will be
   * used by the system to keep track of the animation system.
   */
   SetCollision(0, border_dummy, g);
   WaitTOF();
   return(0);
}

/*******************************************************************************
 * Use this to get rid of the gels stuff when it is not needed any more.
 * You must have allocated the gels info stuff (use the ReadyGels routine).
 */

PurgeGels(g)
struct GelsInfo *g;
{
   if (g->collHandler != NULL)
      FreeMem(g->collHandler, sizeof(struct collTable));
   if (g->lastColor != NULL)
      FreeMem(g->lastColor, sizeof(LONG) * 8);
   if (g->nextLine != NULL)
      FreeMem(g->nextLine, sizeof(WORD) * 8);
   if (g->gelHead != NULL)
      FreeMem(g->gelHead, sizeof(struct VSprite));
   if (g->gelTail != NULL)
      FreeMem(g->gelTail, sizeof(struct VSprite));
}

/******************************************************************************
 * Because MakeVSprite is called by MakeBob, MakeVSprite only creates the
 * VSprite,it doesn't add it to the system list. The calling routine must
 * do an AddVSprite after it is created.
 */

struct VSprite *MakeVSprite(lineheight, image, colorset, x, y,
				wordwidth, imagedepth, flags)
SHORT lineheight;	/* How tall is this vsprite? */
WORD *image;		/* Where is the vsprite image data, should be
			   twice as many words as the value of lineheight */
WORD *colorset;		/* Where is the set of three words which describes
			   the colors that this vsprite can take on? */
SHORT x, y;		/* What is its initial onscreen position? */
SHORT wordwidth, imagedepth, flags;
{
   struct VSprite *v;	/* Make a pointer to the vsprite structure which
			   this routine dynamically allocates */

   if ((v = (struct VSprite *)AllocMem(sizeof(struct VSprite),
         MEMF_PUBLIC | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      printf("MakeVSprite: Couldn't allocate VSprite.\n");
#endif
      return(0);
   }

   v->Flags = flags;	/* Is this a vsprite, not a bob? */

   v->Y = y;		/* Establish initial position relative to */
   v->X = x;		/* the Display coordinates. */

   v->Height = lineheight;	/* The Caller says how high it is. */
   v->Width = wordwidth;	/* A vsprite is always 1 word (16 bits) wide. */

  /* There are two kinds of depth... the depth of the image itself, and the
   * depth of the playfield into which it will be drawn. The image depth
   * says how much data space will be needed to store an image if it's
   * dynamically allocated. The playfield depth establishes how much space
   * will be needed to save and restore the background when a bob is drawn.
   * A vsprite is always 2 planes deep, but if it's being used to make a
   * bob, it may be deeper...
   */

   v->Depth = imagedepth;

  /* Assume that the caller at least has a default boundary collision
   * routine.... bit 1 of this mask is reserved for boundary collision
   * detect during DoCollision(). The only collisions reported will be
   * with the borders. The caller can change all this later.
   */

   v->MeMask = 1;
   v->HitMask = 1;

   v->ImageData = image;	/* Caller says where to find the image. */

  /* Show system where to find a mask which is a squished down version
   * of the vsprite (allows for fast horizontal border collision detect).
   */

   if ((v->BorderLine = (WORD *)AllocMem((sizeof(WORD)*wordwidth),
         MEMF_PUBLIC | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("MakeVSprite: Couldn't allocate BorderLine.\n");
#endif
      return(0);
   }

  /* Show system where to find the mask which contains a 1 bit for any
   * position in the object in any plane where there is a 1 bit (all planes
   * OR'ed together).
   */

   if ((v->CollMask = (WORD *)AllocMem(sizeof(WORD)*lineheight*wordwidth,
         MEMF_CHIP | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("MakeVSprite: Couldn't allocate CollMask.\n");
#endif
      return(0);
   }

  /* This isn't used for a Bob, just a VSprite. It's where the
   * Caller says where to find the VSprites colors.
   */
   v->SprColors = colorset;

   /* These aren't used for a VSprite, and MakeBob'll do set up for Bob. */
   v->PlanePick = 0x00;
   v->PlaneOnOff = 0x00;

   InitMasks(v);	/* Create the collMask and borderLine */	
   return(v);
}

struct Bob *MakeBob(bitwidth,lineheight,imagedepth,image,
      planePick,planeOnOff, x,y, flags)
SHORT bitwidth,lineheight,imagedepth,planePick,planeOnOff,x,y,flags;
WORD *image;
{
   struct Bob *b;
   struct VSprite *v;
   SHORT wordwidth;

   wordwidth = (bitwidth+15)/16;

  /* Create a vsprite for this bob, it will need to be deallocated
   * later (freed) when this bob gets deleted.
   * Note: No color set for bobs.
   */
   if ((v = MakeVSprite(lineheight, image, NULL, x, y, wordwidth,
         imagedepth, flags)) == 0) {
#ifdef DEBUG
      kprintf("MakeBob: MakeVSprite failed.\n");
#endif
      return(0);
   }

   /* Caller selects which bit planes into which the image is drawn. */
   v->PlanePick = planePick;

   /* What happens to the bit planes into which the image is not drawn. */
   v->PlaneOnOff = planeOnOff;

   if ((b = (struct Bob *)AllocMem(sizeof(struct Bob),
         MEMF_PUBLIC | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("MakeBob: Couldn't allocate bob.\n");
#endif
      return(0);
   }

   v->VSBob = b; /* Link together the bob and its vsprite structures */

   b->Flags = 0; /* Not part of an animation (BOBISCOMP) and don't keep the
			image present after bob is removed (SAVEBOB) */

  /* Tell where to save background. Must have enough space for as many
   * bitplanes deep as the display into which everything is being drawn.
   */

   if ((b->SaveBuffer = (WORD *)AllocMem(sizeof(SHORT) * wordwidth
         * lineheight * imagedepth, MEMF_CHIP | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("MakeBob: Couldn't allocate save buffer.\n");
#endif
      return(0);
   }

   b->ImageShadow = v->CollMask;

  /* Interbob priorities are set such that the earliest defined bobs have
   * the lowest priority, last bob defined is on top.
   */

   b->Before = NULL;	/* Let the caller worry about priority later. */
   b->After = NULL;

   b->BobVSprite = v;

  /* InitMasks does not preset the imageShadow ... caller may elect to use
   * the collMask or to create his own version of a shadow, although it
   * is usually the same.
   */

   b->BobComp = NULL;	/* this is not part of an animation */
   b->DBuffer = NULL;	/* this is not double buffered */

  /* Return a pointer to this newly created bob for additional caller
   * interaction or for AddBob(b);
   */
   return(b);
}

/* Deallocate memory which has been allocated by the routines Makexxx. */
/* Assumes images and imageshadow deallocated elsewhere. */
DeleteGel(v)
struct VSprite *v;
{
   if (v != NULL) {
      if (v->VSBob != NULL) {
         if (v->VSBob->SaveBuffer != NULL) {
            FreeMem(v->VSBob->SaveBuffer, sizeof(SHORT) * v->Width
                  * v->Height * v->Depth);
         }
         if (v->VSBob->DBuffer != NULL) {
            if (v->VSBob->DBuffer->BufBuffer != 0) {
               FreeMem(v->VSBob->DBuffer->BufBuffer,
                     sizeof(SHORT) * v->Width * v->Height * v->Depth);
            }
            FreeMem(v->VSBob->DBuffer, sizeof(struct DBufPacket));
         }
         FreeMem( v->VSBob, sizeof(struct Bob));
      }
      if (v->CollMask != NULL) {
         FreeMem(v->CollMask, sizeof(WORD) * v->Height * v->Width);
      }
      if (v->BorderLine != NULL) {
         FreeMem(v->BorderLine, sizeof(WORD) * v->Width);
      }
      FreeMem(v, sizeof(struct VSprite));
   }
}
