/* 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;
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, 0x0fff, 0x0fff, 0x0fff,
        0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
        0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff,
        0x0fff, 0x0fff, 0x0fff, 0x0fff,
        0x0fff, 0x0fff, 0x0fff, 0x0fff,
        0x0fff, 0x0fff, 0x0ffe, 0x0fff,
        0x0fff, 0x0fff, 0x0fff, 0x0fff
        };
UWORD colorset0[ ] = { 0x0fff, 0x0fff, 0x0fff }; /* same as colors 17-19 */
UWORD colorset1[ ] = { 0x0fff, 0x0fff, 0x0fff }; /* 21-23 */
UWORD colorset2[ ] = { 0x0fff, 0x0fff, 0x0fff }; /* 25-27 */
UWORD colorset3[ ] = { 0x0fff, 0x0fff, 0x0fff }; /* 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).
 */
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. */
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 */
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);
}
