/* vsprite.final.c, merged with its support files */

/* execute makesimple vsprite.final ; Amiga C 1.1 (Lattice 3.03) */

#include "exec/types.h"
#include "intuition/intuition.h"
#include "graphics/sprite.h"
#include "exec/memory.h"
#include "graphics/gels.h"

/* ask system to create and manage MAXSP vsprites */
#define MAXSP 28 

/* define possible speeds for vsprites in counts per vblank */

SHORT speed[] = { 1, 2, -1, -2 };

SHORT xmove[MAXSP], ymove[MAXSP];  	
				/* sprite directions of movement */
struct VSprite *vsprite[MAXSP];	/* MAXSP simple sprites */
struct VSprite *vspr;		/* pointer to a sprite */
short maxgot;			/* max # of sprites we created */

struct GelsInfo mygelsinfo;	/* the window's RastPort needs one
				 * of these in order to do VSprites */

struct Window *w;	/* pointer to a Window   */
struct RastPort *rp;	/* pointer to a RastPort */
struct Screen *s;	/* a pointer to a Screen */	
struct ViewPort *vp;	/* pointer to a ViewPort */

struct Window *OpenWindow();
struct Screen *OpenScreen();
LONG GfxBase;
LONG IntuitionBase;

/* 18 words of sprite data = 9 lines of sprite data */

UWORD sprite_data[ ] = {
			0x0fc3, 0x0000, /* image data line 1*/
			0x3ff3, 0x0000, /* image data line 2*/
			0x30c3, 0x0000, /* image data line 3*/
			0x0000, 0x3c03, /* image data line 4*/
			0x0000, 0x3fc3, /* image data line 5*/
			0x0000, 0x03c3, /* image data line 6*/
			0xc033, 0xc033, /* image data line 7*/
			0xffc0, 0xffc0, /* image data line 8*/
			0x3f03, 0x3f03, /* image data line 9*/
			};
UWORD *sprdata;

void movesprites()
{
	short i;

	for (i=0; i<maxgot; i++)
	{
		vspr = vsprite[i];

		vspr->X = xmove[i]+vspr->X;
		vspr->Y = ymove[i]+vspr->Y;

		/* move the sprites ... here. */

		if(vspr->X >= 300 || vspr->X <= 0) xmove[i]=-xmove[i];
		if(vspr->Y >= 190 || vspr->Y <= 0) ymove[i]=-ymove[i];
		
	}
	SortGList(rp);	/* get the list in order */
	DrawGList(rp, vp);	/* create the sprite instructions */

	MakeScreen(s);  /* ask Intuition to pull it all together */
	RethinkDisplay(); /* and to show us what we have now. */
}


#define AZCOLOR 1
#define WHITECOLOR 2

#define WC WINDOWCLOSE
#define WS WINDOWSIZING
#define WDP WINDOWDEPTH
#define WDR WINDOWDRAG

#define NORMALFLAGS (WC|WS|WDP)
/* Did not use windowdrag because dont want screen to be moved. */

/* Allow window sizing so user can decrease size of window, then
 * increase it again, thus erasing the background text and more
 * easily see that some vsprites wink out and into existence when
 * too many sprites are on a single horizontal plane and the
 * vsprite machine runs out of sprites to assign.
 */

/* myfont1 specifies characteristics of the default font;
 * this case selects the 80 column font that displays as 
 * 40 columns in low resolution mode.
 */	
struct TextAttr myfont1 = { "topaz.font", 8, 0, 0 };
	
struct NewScreen myscreen1 = {
	0, 0, 	  	/* LeftEdge, TopEdge ... where to put screen */
	320, 200, 	/* Width, Height ... size of the screen */
	5,	  	/* 5 planes Depth, means 2 to the 5th or 
		   	* 32 different colors to choose from once 
		  	* the screen is opened.
		   	*/
	1, 0,	  	/* DetailPen, BlockPen */
	SPRITES,	/* ViewModes ... value of 0 = low resolution */
	CUSTOMSCREEN,  	/* Type of screen */
	&myfont1, 	/* Font to be used as default for this screen */
	"32 Color Test", /* DefaultTitle for its title bar */
	NULL,     	/* screens user-gadgets, always NULL, ignored */
	NULL };
			/* address of custom bitmap for screen, 
		   	 * not used in this example 
		   	 */


struct NewWindow myWindow = {
	0, 		/* LeftEdge for window measured in pixels, 
			   at the current horizontal resolution,
			   from the leftmost edge of the Screen */
	0,		/* TopEdge for window is measured in lines 
			   from the top of the current Screen.   */
	320, 185,	/* Width, Height of this window */ 
	0,   		/* DetailPen - what pen number is to be 
			   used to draw the borders of the window */ 
	1,		/* BlockPen - what pen number is to be 
			   used to draw system generated window
			   gadgets */
			/* (for DetailPen and BlockPen, the value
			    of -1 says "use the default value")  */
	CLOSEWINDOW, 	/* simplesprite program used INTUITICKS also */
			/* IDCMP Flags */
	NORMALFLAGS | GIMMEZEROZERO | ACTIVATE,
			/* Window Flags:  (see below for more info) */
	NULL,		/* FirstGadget */
	NULL,		/* CheckMark   */
	"Click Close Gadget To Stop",	/* Window title */
	NULL,		/* Pointer to Screen if not workbench */
	NULL,		/* Pointer to BitMap if a SUPERBITMAP window */
	320, 10,	/* minimum width, minimum height */
	320, 200,	/* maximum width, maximum height */
	CUSTOMSCREEN
	};

#include "graphics/gfxmacros.h"

/* #include "event1.c"	*/
/* gets the event handler */
/* event1.c */

HandleEvent(code)
	LONG code;	/* provided by main */
{
	switch(code) 
	{
	case CLOSEWINDOW:
		return(0);
		break;
	case INTUITICKS:	/* could have done much faster
				 * but what the heck, this is
				 * shorter for test purposes
				 */
		movesprites();	/* 10 moves per second; test */
		default:
			break;
	}
return(1);
}

UWORD mycolortable[] = {

	0x0000, 0x0e30, 0x0fff, 0x0b40, 0x0fb0, 0x0bf0,
	0x05d0, 0x0ed0, 0x07df, 0x069f, 0x0c0e,
	0x0f2e, 0x0feb, 0x0c98, 0x0bbb, 0x07df, 

	0x0000, 0x0e30, 0x0fff, 0x0b40, 
	0x0fb0, 0x0bf0, 0x05d0, 0x0ed0, 
	0x07df, 0x069f, 0x0c0e, 0x0f2e, 
	0x0feb, 0x0c98, 0x0bbb, 0x07df 
	};

	/* black, red, white, fire-engine red, orange, yellow,
	lime green, green, aqua, dark blue, purple,
	violet, tan, brown, gray, skyblue, (everything again) */

UWORD colorset0[ ] = { 0x0e30, 0xffff, 0x0b40 }; /* same as colors 17-19 */
UWORD colorset1[ ] = { 0x0bf0, 0x05d0, 0x0ed0 }; /* 21-23 */
UWORD colorset2[ ] = { 0x069f, 0x0c0e, 0x0f2e }; /* 25-27 */
UWORD colorset3[ ] = { 0x0c98, 0x0bbb, 0x07df }; /* 29-31 */
UWORD *colorset[ ] = {
			colorset0, colorset1,
			colorset2, colorset3 };
int choice;
char *numbers[] = { "17","18","19",
		    "20","21","22","23",
		    "24","25","26","27",
		    "28","29","30","31" };

/* #include "ram:purgegels.c" */
/* purgegels.c */

/*
 * 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).
 */

void 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));
}


/* Deallocate memory which has been allocated by the routines Makexxx. */
/* Assumes images and imageshadow deallocated elsewhere. */

void 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));
   }
}

/* end of purgegels.c */

/* #include "ram:readygels.c" */
/* readygels.c */

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

void border_dummy()	/* a dummy collision routine */
{ 
   return; 
}

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) 
   {
      return(-1);
   }

   if ((SpriteTail = (struct VSprite *)AllocMem(sizeof
         (struct VSprite), MEMF_PUBLIC | MEMF_CLEAR)) == 0) 
   {
      FreeMem(SpriteHead, sizeof(struct VSprite));
      return(-2);
   }
   g->sprRsrvd = 0xFC;	/* do not use sprites 0 or 1. */

   if ((g->nextLine = (WORD *)AllocMem(sizeof(WORD) * 8,
         MEMF_PUBLIC | MEMF_CLEAR)) == NULL) 
   {
      FreeMem(SpriteHead, sizeof(struct VSprite));
      FreeMem(SpriteTail, sizeof(struct VSprite));
      return(-3);
   }
 
  if ((g->lastColor = (WORD **)AllocMem(sizeof(LONG) * 8,
         MEMF_PUBLIC | MEMF_CLEAR)) == NULL) 
   {
      FreeMem(g->nextLine,8 * sizeof(WORD));
      FreeMem(SpriteHead, sizeof(struct VSprite));
      FreeMem(SpriteTail, sizeof(struct VSprite));
      return(-4);
   }

  /* Next we prepare 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) 
   {
      FreeMem(g->lastColor, 8 * sizeof(LONG));
      FreeMem(g->nextLine,8 * sizeof(WORD));
      FreeMem(SpriteHead, sizeof(struct VSprite));
      FreeMem(SpriteTail, sizeof(struct VSprite));
      return(-5);
   }

  /* 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);	/* a return value of 0 says all ok, any 
		 * negative value tells you when it failed.
		 * (see the listing as to what the routine
		 * was trying to do... all failures are
		 * due to out of memory conditions).
		 */
}


/* end of readygels.c */

/* #include "ram:MakeVSprite.c" */
/* MakeVSprite.c */

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) 
   {
      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) 
   {
   	FreeMem(v, sizeof(struct VSprite)); 
   	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) 
   {
   	FreeMem(v, sizeof(struct VSprite)); 
   	FreeMem(v->BorderLine, wordwidth * sizeof(WORD));
        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);
}
/* end of MakeVSprite.c */

int main()
{
	struct IntuiMessage *msg;
	LONG result;
	SHORT k, j, x, y, error;
	UWORD *src, *dest;	/* for copying sprite data to RAM */

	GfxBase = OpenLibrary("graphics.library",0);

	IntuitionBase = OpenLibrary("intuition.library",0);
	/* (error checking left out for brevity here) */

	s = OpenScreen(&myscreen1);  /* try to open it */
	if(s == 0)
	{
    		printf("Can't open myscreen1\n");
    		exit(10);
	}
	myWindow.Screen = s;	/* say where screen is located */

	ShowTitle(s, FALSE);	/* Dont let screen be dragged down...*/

	w = OpenWindow(&myWindow);
	if(w == 0)
	{
		printf("Window didn't open!\n");
		CloseScreen(s);
		exit(20);
	}
	vp = &(s->ViewPort);
	/* set the colors for this viewport */

	LoadRGB4(vp, &mycolortable[0], 32);
	rp = w->RPort;
		/* Now wait for a message to arrive from Intuition 
		 * (task goes to sleep while waiting for the message) 
		 */

	/* Write Text using sprite colors so that demo can show
	 * how the vsprite machine stuffs the colors as it goes
	 * down the screen.  Thus if using vsprites, user should
	 * avoid using the color registers that the vsprites use.
	 */

	/* Notice also that color numbers 17-19 are untouched.  
	 * That is because of the sprResrvd=0xFC in ReadyGels.
	 * (Doesn't allow the virtual sprite machine to access
	 * either sprite 0 or 1... 0 is used by mouse cursor and
	 * shares its colors with 1, so I reserved both of them.
	 */

	for(j=8; j<180; j+=50)
	{
		for(k=0; k<15; k++)
		{
			Move(rp,k*20,j);
			SetAPen(rp,k+17);	/* show 17-31 */
			/* 16, 20, 24, 28 are unaffected by vsprites 
			 * because they are not used by hardware sprites */

			Text(rp,numbers[k],2);
		}
	}
	/* *************************************** */
	/* VSPRITE DEMO SECTION 		   */
	/* *************************************** */

	/* Allocate CHIP memory to hold the actual sprite data */
	/* (necessary if ever to run on an expanded RAM Amiga) */

	sprdata = (UWORD *)AllocMem(36, MEMF_CHIP);

	if(sprdata == NULL)
	{
		/* not enough memory for sprite */
	}
	/* now copy the sprite data into the CHIP RAM. */

	src = sprite_data;  
	dest = sprdata; 	/* source, destination */

	for( j=0; j<18; j++)
	{
		*dest++ = *src++;
	}			

	choice = 0;
	maxgot = 0;

	/* Prepare the GELS system to work with VSPRITE or BOBS */

	error = ReadyGels(&mygelsinfo, rp);

	for(k=0; k<MAXSP; k++)	/* whatever maximum number of vsprites */
	{
		xmove[k]=speed[RangeRand(4)];
		ymove[k]=speed[RangeRand(4)];

		/* establish a position for the sprite */
		x = 10 + RangeRand(280);
		y = 10 + RangeRand(170);

		/* create a vsprite */
		vsprite[k] = MakeVSprite( 9, sprdata, colorset[choice], 
					x, y, 1, 2, VSPRITE);
		/* 9 lines high, using MEMF_CHIP image of a sprite,
		 * with a particular set of colors, at an X,Y location
		 * 1 word wide, 2 planes deep (all vsprites are 2 deep)
		 * and it is a VSPRITE */

		if(vsprite[k] == 0)
		{
			break;	/* ran out of memory! */
		}
		AddVSprite(vsprite[k], rp);

		maxgot++;

		choice++; /* choose a different color set */
		if(choice >= 4) 
		{
			choice = 0;	/* wrap around on colorsets */
		}
	}
	while(1)	/* forever */
	{
		WaitTOF();
		movesprites();

		result = -1;	/* now see if CLOSEWINDOW is waiting */
		msg = (struct IntuiMessage *)GetMsg(w->UserPort);		
		if(msg != 0)
		{
			result = HandleEvent(msg->Class);

			/* Let Intuition reuse the msg */
			ReplyMsg(msg);	
		}
		if(result == 0) 
		{
			break;  /* got a CLOSEWINDOW */
		}
	}

	/* DONE, now cleanup */

	/* Free however many vsprites we actually managed to create */

	for(k=0; k<maxgot; k++)
	{
		DeleteGel(vsprite[k]);				
	}
	/* delete what ReadyGels created */
	PurgeGels(&mygelsinfo);	

	FreeMem(sprdata, 36);	/* free what we allocated. */

	CloseWindow(w);
	CloseScreen(s);
	return (0);
}	



