/* Copyright (c) 1990 Commodore-Amiga, Inc.
 *
 * This example is provided in electronic form by Commodore-Amiga, Inc. for
 * use with the 1.3 revisions of the Addison-Wesley Amiga reference manuals. 
 * The 1.3 Addison-Wesley Amiga Reference Manual series contains additional
 * information on the correct usage of the techniques and operating system
 * functions presented in this example.  The source and executable code of
 * this example may only be distributed in free electronic form, via bulletin
 * board or as part of a fully non-commercial and freely redistributable
 * diskette.  Both the source and executable code (including comments) must
 * be included, without modification, in any copy.  This example may not be
 * published in printed form or distributed with any commercial product.
 * However, the programming techniques and support routines set forth in
 * this example may be used in the development of original executable
 * software products for Commodore Amiga computers.
 * All other rights reserved.
 * This example is provided "as-is" and is subject to change; no warranties
 * are made.  All use is at your own risk.  No liability or responsibility
 * is assumed.
 */

/* anim.c 19oct89 original code by Dave Lucas.
** rework by (CATS)
**
** lattice c 5.04
** lc -b1 -cfist -v -y anim.c
** blink FROM LIB:c.o anim.o /animtools/animtools.o LIB LIB:lc.lib TO anim
*/
#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/gels.h>
#include <exec/memory.h>

#include "/animtools/animtools.h"
#include "/animtools/animtools_proto.h"

#include <stdlib.h>
#include <stdio.h>
#include <proto/all.h>
int CXBRK(void) { return(0); } /* disable lattice CTRL-C handling */

/*--------------------------------------------------------------*/
/*                                                              */
/*--------------------------------------------------------------*/
struct AnimOb *setupBoing(SHORT dbufing);
struct AnimOb *setupMan(SHORT dbufing);
struct AnimOb *setupWorm(SHORT dbufing);

WORD anEdgeORoutine(struct AnimOb *anOb);
WORD bounceORoutine(struct AnimOb *anOb);
WORD goInFrontOfHead(struct AnimComp *aComp);
WORD goBehindHead(struct AnimComp *aComp);

VOID runAnimation(struct Window *win, SHORT dbufing,
				  struct AnimOb **animKey, struct BitMap **myBitMaps);

LONG setupPlanes(struct BitMap *bitMap, LONG depth, LONG width, LONG height);
struct BitMap **setupBitMaps(LONG depth, LONG width, LONG height);
VOID freePlanes(struct BitMap *bitMap, LONG depth, LONG width, LONG height);
VOID freeBitMaps(struct BitMap **myBitMaps,
					LONG depth, LONG width, LONG height);

struct GelsInfo *setupDisplay(struct Window **win,
							  SHORT dbufing, struct BitMap **myBitMaps);
VOID DrawGels(struct Window *win, struct AnimOb **animKey, SHORT dbufing,
			  WORD *toggleFrame, struct BitMap **myBitMaps);

/*--------------------------------------------------------------*/
/*                                                              */
/*--------------------------------------------------------------*/
#define ANFS	ANFRACSIZE
#define BNFS	(ANFRACSIZE-2)
#define CNFS	(ANFRACSIZE-2)

/* These are used for MeMask (1 << ID_xxx) */
#define ID_BORDER 0
#define ID_WRM    3
#define ID_BNG    4
#define ID_MAN    5

#define SBMWIDTH  320  /* My screen size constants. */
#define SBMHEIGHT 200
#define SBMDEPTH    4
#define SCRNMODE NULL  /* (HIRES | LACE) for NewScreen, ends up in view.*/

#define RBMWIDTH  320  /* My raster size constants. (These CAN differ) */
#define RBMHEIGHT 200
#define RBMDEPTH    4

/*--------------------------------------------------------------*/
/*                                                              */
/*--------------------------------------------------------------*/
struct NewScreen ns =
	{
	0, 0, SBMWIDTH, SBMHEIGHT, SBMDEPTH, 0, 0, SCRNMODE,
	CUSTOMSCREEN | SCREENQUIET, NULL, NULL, NULL, NULL
	};

/* this is a little window that will let us get a CLOSEWINDOW message */
struct NewWindow nw =
	{
	0, 0, 25, 12, 0, 0, CLOSEWINDOW,
	WINDOWCLOSE | BORDERLESS | RMBTRAP, NULL, NULL, NULL, NULL,
	NULL, 0, 0, SBMWIDTH, SBMHEIGHT-1, CUSTOMSCREEN
	};

struct IntuitionBase *IntuitionBase	= NULL;
struct GfxBase		 *GfxBase		= NULL;

int return_code;

/*--------------------------------------------------------------*/
/* constants for the worm sequence.                             */
/*--------------------------------------------------------------*/
#include "image_worm.h"

NEWBOB newWrmBob =
	{
	NULL,				/* nb_Image - image is null for sequences	*/
						/*			  data comes from NEWANIMSEQ	*/
	WRMWWIDTH,			/* nb_WordWidth - width of the bob			*/
	WRMHEIGHT,			/* nb_LineHeight - height of the bob		*/
	WRMDEPTH,			/* nb_ImageDepth - depth of image data		*/
	0x3,				/* nb_PlanePick - planes to put data in		*/
	0x0,				/* nb_PlaneOnOff - empty planes to set		*/
	SAVEBACK | OVERLAY,	/* nb_BFlags - flags for the bob			*/
	0,					/* nb_DBuf - 1=dbuf, 0=not dbuf				*/
	RBMDEPTH,			/* nb_RasDepth - num planes in raster		*/
	0,					/* nb_X - bob x position					*/
	0,					/* nb_Y - bob y position					*/
	(1L << ID_BORDER),	/* nb_HitMask - hit mask					*/
	(1L << ID_WRM),		/* nb_MeMask - me mask						*/
	};
NEWANIMSEQ newWrmSeq =
	{
	NULL,				/* nas_HeadOb - head object for the seq		*/
	(WORD *)WrmImage,	/* nas_Images - array of images				*/
	WrmXTranses,		/* nas_Xt - x offsets from central object	*/
	WrmYTranses,		/* nas_Yt - y offsets from central object	*/
	WrmTimes,			/* nas_Times - timing values for objects	*/
	WrmCRoutines,		/* nas_Routines - array of comp routines	*/
	0,					/* nas_CFlags - comp flags					*/
	WRMCOUNT,			/* nas_Count - number of comps in sequence	*/
	0,					/* nas_SingleImage - 1=single image for seq.*/
						/*					 0=multiple images      */
	};

/*--------------------------------------------------------------*/
/* constants for the man sequence.                              */
/*--------------------------------------------------------------*/
#define MANRINGY		-1
#define MANRINGX		36
#define MANHEIGHT		62
#define MANWIDTH		46

#define MANTCOUNT		4
#define MANTHEIGHT		34
#define MANTWIDTH		9
#define MANTDEPTH		2
#define MANTWWIDTH		((MANTWIDTH + 15) / 16)

#define MANACOUNT		8
#define MANAHEIGHT		22
#define MANAWIDTH		24
#define MANADEPTH		2
#define MANAWWIDTH		((MANAWIDTH + 15) / 16)

#define MANLCOUNT		8
#define MANLHEIGHT		30
#define MANLWIDTH		26
#define MANLDEPTH		2
#define MANLWWIDTH		((MANLWIDTH + 15) / 16)

#include "image_man.h"

NEWBOB newNearLegBob =
	{
	NULL, MANLWWIDTH, MANLHEIGHT, MANLDEPTH, 0x3, 0xC,
	SAVEBACK | OVERLAY, 0, RBMDEPTH, 0,0, (1L<<ID_BORDER), (1L<<ID_MAN),
	};
NEWANIMSEQ newNearLegSeq =
	{
	NULL, (WORD *)manLImage, manLXTranses, manLYTranses, manLTimes,
	manLCRoutines, 0, MANLCOUNT, 0,
	};

NEWBOB newFarLegBob =
	{
	NULL, MANLWWIDTH, MANLHEIGHT, MANLDEPTH, 0x3, 0x8,
	SAVEBACK | OVERLAY, 0, RBMDEPTH, 0,0, (1L<<ID_BORDER), (1L<<ID_MAN),
	};
NEWANIMSEQ newFarLegSeq =
	{
	NULL, (WORD *)manLImage, manLXTranses, manLYTranses, manLTimes,
	manLCRoutines, 0, MANLCOUNT, 0,
	};

NEWBOB newNearArmBob =
	{
	NULL, MANAWWIDTH, MANAHEIGHT, MANADEPTH, 0x3, 0xC,
	SAVEBACK | OVERLAY, 0, RBMDEPTH, 0,0, (1L<<ID_BORDER), (1L<<ID_MAN),
	};
NEWANIMSEQ newNearArmSeq =
	{
	NULL, (WORD *)manAImage, manAXTranses, manAYTranses, manATimes,
	manACRoutines, 0, MANACOUNT, 0,
	};

NEWBOB newFarArmBob =
	{
	NULL, MANAWWIDTH, MANAHEIGHT, MANADEPTH, 0x3, 0x8,
	SAVEBACK | OVERLAY, 0, RBMDEPTH, 0,0, (1L<<ID_BORDER), (1L<<ID_MAN),
	};
NEWANIMSEQ newFarArmSeq =
	{
	NULL, (WORD *)manAImage, manAXTranses, manAYTranses, manATimes,
	manACRoutines, 0, MANACOUNT, 0,
	};

NEWBOB newTrunkBob =
	{
	NULL, MANTWWIDTH, MANTHEIGHT, MANTDEPTH, 0x3, 0xC,
	SAVEBACK | OVERLAY, 0, RBMDEPTH, 0,0, (1L<<ID_BORDER), (1L<<ID_MAN),
	};
NEWANIMSEQ newTrunkSeq =
	{
	NULL, (WORD *)manTImage, manTXTranses, manTYTranses, manTTimes,
	manTCRoutines, 0, MANTCOUNT, 0,
	};

/*--------------------------------------------------------------*/
/* constants for the boing ball and sattellites.                */
/*--------------------------------------------------------------*/
#define BNG3RINGY		0
#define BNG3RINGX		0
#define BNG3COUNT		6
#define BNG3HEIGHT		25
#define BNG3WIDTH		29
#define BNG3DEPTH		3
#define BNG3WWIDTH		((BNG3WIDTH + 15) / 16)

#define SATCOUNT		16
#define SATHEIGHT		5
#define SATWIDTH		5
#define SATDEPTH		2
#define SATWWIDTH		((SATWIDTH + 15) / 16)

#include "image_boing.h"

NEWBOB newBoingBob =
	{
	NULL, BNG3WWIDTH, BNG3HEIGHT, BNG3DEPTH, 0xD, 0x2,
	SAVEBACK | OVERLAY, 0, RBMDEPTH, 0,0, (1L<<ID_BORDER), (1L<<ID_BNG),
	};
NEWANIMSEQ newBoingSeq =
	{
	NULL, (WORD *)boing3Image, boing3XTranses, boing3YTranses,
	boing3Times, boing3CRoutines, 0, BNG3COUNT, 0,
	};

NEWBOB newSatABob =
	{
	NULL, SATWWIDTH, SATHEIGHT, SATDEPTH, 0xC, 0x0,
	SAVEBACK | OVERLAY, 0, RBMDEPTH, 0,0, (1L<<ID_BORDER), (1L<<ID_BNG),
	};
NEWANIMSEQ newSatASeq =
	{
	NULL, (WORD *)satImage, satAXTranses, satAYTranses,
	satTimes, satACRoutines, 0, SATCOUNT, 1,
	};
NEWBOB newSatBBob =
	{
	NULL, SATWWIDTH, SATHEIGHT, SATDEPTH, 0xC, 0x3,
	SAVEBACK | OVERLAY, 0, RBMDEPTH, 0,0, (1L<<ID_BORDER), (1L<<ID_BNG),
	};
NEWANIMSEQ newSatBSeq =
	{
	NULL, (WORD *)satImage, satBXTranses, satBYTranses,
	satTimes, satBCRoutines, 0, SATCOUNT, 1,
	};
NEWBOB newSatCBob =
	{
	NULL, SATWWIDTH, SATHEIGHT, SATDEPTH, 0x3, 0x0,
	SAVEBACK | OVERLAY, 0, RBMDEPTH, 0,0, (1L<<ID_BORDER), (1L<<ID_BNG),
	};
NEWANIMSEQ newSatCSeq =
	{
	NULL, (WORD *)satImage, satCXTranses, satCYTranses,
	satTimes, satCCRoutines, 0, SATCOUNT, 1,
	};

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
/*                      PROCEDURES                              */
/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/

/*--------------------------------------------------------------
** setupBoing() - allocate and initialize an object that will
** display as a boing ball with orbiting sattellites.
**
** this is an animation object with four animation sequences.
** (boing and three sattellites.)
** (note that the sattellites all share the same single image data.)
**
** return NULL on failure.
*/
struct AnimOb *setupBoing(SHORT dbufing)
{
struct AnimOb	*bngOb;
struct AnimComp *bngComp;
struct AnimComp *satAComp;
struct AnimComp *satBComp;
struct AnimComp *satCComp;

if (NULL != (bngOb = AllocMem((LONG)sizeof(struct AnimOb), MEMF_CLEAR)))
	{
	bngOb->NextOb		= NULL;
	bngOb->PrevOb		= NULL;
	bngOb->Clock		= 0;
	bngOb->AnY			= 50;
	bngOb->AnX			= 50;
	bngOb->AnOldY		= bngOb->AnY;
	bngOb->AnOldX		= bngOb->AnX;
	bngOb->YVel			= 3 << ANFRACSIZE;
	bngOb->XVel			= 3 << ANFRACSIZE;
	bngOb->YAccel		= 0;
	bngOb->XAccel		= 0;
	bngOb->RingYTrans	= BNG3RINGY << ANFRACSIZE;
	bngOb->RingXTrans	= BNG3RINGX << ANFRACSIZE;
	bngOb->AnimORoutine	= bounceORoutine;
	bngOb->AUserExt		= 0;

	newBoingBob.nb_DBuf    = dbufing;
	newBoingSeq.nas_HeadOb = bngOb;

	satACRoutines[4]	  = goInFrontOfHead;
	satACRoutines[12]	  = goBehindHead;
	satBCRoutines[8]      = goInFrontOfHead;
	satBCRoutines[0]      = goBehindHead;
	satCCRoutines[4]      = goInFrontOfHead;
	satCCRoutines[12]     = goBehindHead;

	newSatABob.nb_DBuf	  = dbufing;
	newSatBBob.nb_DBuf	  = dbufing;
	newSatCBob.nb_DBuf	  = dbufing;
	newSatASeq.nas_HeadOb = bngOb;
	newSatBSeq.nas_HeadOb = bngOb;
	newSatCSeq.nas_HeadOb = bngOb;

	if (NULL != (bngComp = makeSeq(&newBoingBob, &newBoingSeq)))
		{
		bngComp->Flags |= RINGTRIGGER;
		bngOb->HeadComp = bngComp;

		if (NULL != (satAComp = makeSeq(&newSatABob, &newSatASeq)))
			{
			bngComp->AnimBob->Before = satAComp->AnimBob;
			satAComp->AnimBob->After = bngComp->AnimBob;

			if (NULL != (satBComp = makeSeq(&newSatBBob, &newSatBSeq)))
				{
				satAComp->AnimBob->Before = satBComp->AnimBob;
				satBComp->AnimBob->After = satAComp->AnimBob;

				if (NULL != (satCComp = makeSeq(&newSatCBob, &newSatCSeq)))
					{
					satBComp->AnimBob->Before = satCComp->AnimBob;
					satCComp->AnimBob->After = satBComp->AnimBob;

					bngComp->NextComp  = satAComp;
					bngComp->PrevComp  = NULL;

					satAComp->NextComp = satBComp;
					satAComp->PrevComp = bngComp;

					satBComp->NextComp = satCComp;
					satBComp->PrevComp = satAComp;

					satCComp->NextComp = NULL;
					satCComp->PrevComp = satBComp;

					return(bngOb);
					}
				freeSeq(satBComp,RBMDEPTH);
				}
			freeSeq(satAComp,RBMDEPTH);
			}
		freeSeq(bngComp,RBMDEPTH);
		}
	FreeMem(bngOb, (LONG)sizeof(struct AnimOb));
	}
return_code = RETURN_WARN;
return(NULL);
}

/*--------------------------------------------------------------
** setupMan() - allocate and initialize an object that will
** display as a walking man.
**
** this is an animation object with five animation sequences.
** (body, two arms and two legs.)
**
** return NULL on failure.
*/
struct AnimOb *setupMan(SHORT dbufing)
{
struct AnimOb *manOb;

struct AnimComp *trunkComp;
struct AnimComp *farArmComp;
struct AnimComp *farLegComp;
struct AnimComp *nearArmComp;
struct AnimComp *nearLegComp;

if (NULL != (manOb = AllocMem((LONG)sizeof(struct AnimOb), MEMF_CLEAR)))
	{
	manOb->NextOb		= NULL;
	manOb->PrevOb		= NULL;
	manOb->Clock		= 0;
	manOb->AnY			= ((2 * MANHEIGHT) << ANFRACSIZE);
	manOb->AnX			= 0;
	manOb->AnOldY		= manOb->AnY;
	manOb->AnOldX		= manOb->AnX;
	manOb->YVel			= 0;
	manOb->XVel			= 0;
	manOb->YAccel		= 0;
	manOb->XAccel		= 0;
	manOb->RingYTrans	= (MANRINGY << ANFRACSIZE);
	manOb->RingXTrans	= (MANRINGX << ANFRACSIZE);
	manOb->AnimORoutine	= anEdgeORoutine;
	manOb->AUserExt		= 0;

	newTrunkBob.nb_DBuf   = dbufing;
	newFarArmBob.nb_DBuf  = dbufing;
	newFarLegBob.nb_DBuf  = dbufing;
	newNearArmBob.nb_DBuf = dbufing;
	newNearLegBob.nb_DBuf = dbufing;

	newTrunkSeq.nas_HeadOb   = manOb;
	newFarArmSeq.nas_HeadOb  = manOb;
	newFarLegSeq.nas_HeadOb  = manOb;
	newNearArmSeq.nas_HeadOb = manOb;
	newNearLegSeq.nas_HeadOb = manOb;

	if (NULL != (trunkComp = makeSeq(&newTrunkBob, &newTrunkSeq)))
		{
		trunkComp->Flags |= RINGTRIGGER;

		if (NULL != (farArmComp = makeSeq(&newFarArmBob, &newFarArmSeq)))
			{
			trunkComp->AnimBob->Before = farArmComp->AnimBob;
			farArmComp->AnimBob->After = trunkComp->AnimBob;

			if (NULL != (farLegComp = makeSeq(&newFarLegBob, &newFarLegSeq)))
				{
				farArmComp->AnimBob->Before = farLegComp->AnimBob;
				farLegComp->AnimBob->After = farArmComp->AnimBob;

				if (NULL != (nearArmComp =
							makeSeq(&newNearArmBob, &newNearArmSeq)))
					{
					nearArmComp =
						nearArmComp->NextSeq->NextSeq->NextSeq->NextSeq;
					farLegComp->AnimBob->Before = nearArmComp->AnimBob;
					nearArmComp->AnimBob->After = farLegComp->AnimBob;

					if (NULL != (nearLegComp =
									makeSeq(&newNearLegBob, &newNearLegSeq)))
						{
						nearLegComp =
							nearLegComp->NextSeq->NextSeq->NextSeq->NextSeq;
						nearArmComp->AnimBob->Before = nearLegComp->AnimBob;
						nearLegComp->AnimBob->After = nearArmComp->AnimBob;

						manOb->HeadComp = trunkComp;

						trunkComp->PrevComp = NULL;
						trunkComp->NextComp = farArmComp;

						farArmComp->PrevComp = trunkComp;
						farArmComp->NextComp = farLegComp;

						farLegComp->PrevComp = farArmComp;
						farLegComp->NextComp = nearArmComp;

						nearArmComp->PrevComp = farLegComp;
						nearArmComp->NextComp = nearLegComp;

						nearLegComp->PrevComp = nearArmComp;
						nearLegComp->NextComp = NULL;

						return(manOb);
						}
					freeSeq(nearArmComp,RBMDEPTH);
					}
				freeSeq(farLegComp,RBMDEPTH);
				}
			freeSeq(farArmComp,RBMDEPTH);
			}
		freeSeq(trunkComp,RBMDEPTH);
		}
	FreeMem(manOb, (LONG)sizeof(struct AnimOb));
	}
return_code = RETURN_WARN;
return(NULL);
}

/*--------------------------------------------------------------
** setupBoing() - allocate and initialize an object that will
** display as an inch worm.
**
** this is an animation object with only one animation sequence.
**
** return NULL on failure.
*/
struct AnimOb *setupWorm(SHORT dbufing)
{
struct AnimOb   *wormOb;
struct AnimComp *wormComp;

if (NULL != (wormOb = AllocMem((LONG)sizeof(struct AnimOb), MEMF_CLEAR)))
	{
	wormOb->NextOb		 = NULL;
	wormOb->PrevOb		 = NULL;
	wormOb->Clock		 = 0;
	wormOb->AnY			 = ((RBMHEIGHT - WRMHEIGHT - 5) << ANFRACSIZE);
	wormOb->AnX			 = (10L << ANFRACSIZE);
	wormOb->AnOldY		 = wormOb->AnY;
	wormOb->AnOldX		 = wormOb->AnX;
	wormOb->YVel		 = 0;
	wormOb->XVel		 = 0;
	wormOb->YAccel		 = 0;
	wormOb->XAccel		 = 0;
	wormOb->RingYTrans	 = (WRMRINGY << ANFRACSIZE);
	wormOb->RingXTrans	 = (WRMRINGX << ANFRACSIZE);
	wormOb->AnimORoutine = anEdgeORoutine;
	wormOb->AUserExt	 = 0;

	newWrmBob.nb_DBuf	 = dbufing;
	newWrmSeq.nas_HeadOb = wormOb;

	if (NULL != (wormComp = makeSeq(&newWrmBob, &newWrmSeq)))
		{
		wormComp->Flags |= RINGTRIGGER;
		wormOb->HeadComp = wormComp;

		return(wormOb);
		}
	}
return_code = RETURN_WARN;
return(NULL);
}

/*--------------------------------------------------------------
** This ORoutine makes the Object restart on left edge if it has
** gone off the right of the screen. If it has gone off the top,
** it is restarted at the bottom. It is used for the worm and the
** man. It works for animations that move to the right, and
** optionally up, ONLY.
*/
WORD anEdgeORoutine(struct AnimOb *anOb)
{
struct AnimComp *acptr;
struct AnimComp *seqptr;

acptr = anOb->HeadComp;

while (acptr)
	{
	seqptr = acptr;
	do	{
		if (seqptr->AnimBob->BobVSprite->Flags & GELGONE)
			{
			seqptr->AnimBob->BobVSprite->Flags &= ~GELGONE;
			anOb->AnX = -10L << ANFRACSIZE;
			if (anOb->AnY <= 0)
				anOb->AnY = (RBMHEIGHT-60L) << ANFRACSIZE;
			}
		seqptr = seqptr->NextSeq;
		} while (seqptr != acptr && (seqptr));
	acptr = acptr->NextComp;
	}
return(0) ;
}

/*--------------------------------------------------------------
** This ORoutine makes the Object Bounce off Borders.
*/
WORD bounceORoutine(struct AnimOb *anOb)
{
SHORT Y;
SHORT X;

Y = anOb->AnY >> ANFRACSIZE;
X = anOb->AnX >> ANFRACSIZE;

if ((Y<0 && anOb->YVel < 0) ||
	((Y+anOb->HeadComp->AnimBob->BobVSprite->Height > RBMHEIGHT) &&
	 (anOb->YVel > 0)))
	{
	anOb->YVel = -(anOb->YVel);
	}

if (((X < 0) && (anOb->XVel < 0)) ||
	((X + (anOb->HeadComp->AnimBob->BobVSprite->Width << 4) > RBMWIDTH) &&
	 (anOb->XVel > 0)))
	{
	anOb->XVel = -(anOb->XVel);
	}
return(0) ;
}

/*--------------------------------------------------------------
** This CRoutine rearranges Bob Before and After pointers in a
** way that makes the Component passed look like it is in front
** of its' head component.
** 
** Used for Boing satelittes.
** 
** So that they go in front of AND behind the boing ball.
*/
WORD goInFrontOfHead(struct AnimComp *aComp)
{
/* close up hole */
if (aComp->AnimBob->Before != NULL)
	aComp->AnimBob->Before->After = aComp->AnimBob->After;
if (aComp->AnimBob->After != NULL)
	aComp->AnimBob->After->Before = aComp->AnimBob->Before;

/* reinsert */
aComp->AnimBob->Before = aComp->HeadOb->HeadComp->AnimBob->Before;
aComp->AnimBob->After = aComp->HeadOb->HeadComp->AnimBob;
if (aComp->AnimBob->Before != NULL)
	aComp->AnimBob->Before->After = aComp->AnimBob;
aComp->HeadOb->HeadComp->AnimBob->Before = aComp->AnimBob;

return(0) ;
}

/*--------------------------------------------------------------
** This CRoutine rearranges Bob Before and After pointers in a
** way that makes the Component passed look like it is behind
** its' head component.
*/
WORD goBehindHead(struct AnimComp *aComp)
{
if (aComp->AnimBob->Before != NULL)
	aComp->AnimBob->Before->After = aComp->AnimBob->After;
if (aComp->AnimBob->After != NULL)
	aComp->AnimBob->After->Before = aComp->AnimBob->Before;

aComp->AnimBob->After = aComp->HeadOb->HeadComp->AnimBob->After;
aComp->AnimBob->Before = aComp->HeadOb->HeadComp->AnimBob;
if (aComp->AnimBob->After != NULL)
	aComp->AnimBob->After->Before = aComp->AnimBob;
aComp->HeadOb->HeadComp->AnimBob->After = aComp->AnimBob;

return(0) ;
}

/*--------------------------------------------------------------
**
*/
VOID runAnimation(struct Window *win,
				  SHORT dbufing,
				  struct AnimOb **animKey,
				  struct BitMap **myBitMaps)
{
struct IntuiMessage  *intuiMsg;
WORD toggleFrame;

toggleFrame = 0;

/* everything opened, and allocated, and initialized.
** Now I just hang out, move the gels, tell the system to redraw them,
** and let the collision and anim routines bounce them about.
*/
for (;;)
	{
	/* All the work done here */
	DrawGels(win, animKey, dbufing, &toggleFrame, myBitMaps);

	while (intuiMsg = (struct IntuiMessage *)GetMsg(win->UserPort))
		{
		if (intuiMsg->Class == CLOSEWINDOW)
			{
			ReplyMsg((struct Message *)intuiMsg);
			return;
			}
		ReplyMsg((struct Message *)intuiMsg);
		}
	}
}

/*---------------------------------------------------------
** allocate the bit planes for a screen bit map.
*/
LONG setupPlanes(struct BitMap *bitMap,
				 LONG depth, LONG width, LONG height)
{
SHORT plane_num ;

for (plane_num = 0; plane_num < depth; plane_num++)
	{
	if (NULL != (bitMap->Planes[plane_num] =
					(PLANEPTR)AllocRaster(width, height)))
		BltClear(bitMap->Planes[plane_num], (width / 8) * height, 1);
	else
		{
		freePlanes(bitMap, depth, width, height);
		return_code = RETURN_WARN;
		return(NULL);
		}
	}
return(TRUE);
}

/*---------------------------------------------------------
** allocate the bit maps for a double buffered screen.
*/
struct BitMap **setupBitMaps(LONG depth, LONG width, LONG height)
{
/* this must be static -- it cannot go away when the routine exits. */
static struct BitMap *myBitMaps[2];

if (NULL != (myBitMaps[0] =
		(struct BitMap *)AllocMem((LONG)sizeof(struct BitMap), MEMF_CLEAR)))
	{
	if (NULL != (myBitMaps[1] =
		(struct BitMap *)AllocMem((LONG)sizeof(struct BitMap), MEMF_CLEAR)))
		{
		InitBitMap(myBitMaps[0], depth, width, height);
		InitBitMap(myBitMaps[1], depth, width, height);

		if (NULL != setupPlanes(myBitMaps[0], depth, width, height))
			{
			if (NULL != setupPlanes(myBitMaps[1], depth, width, height))
				return(myBitMaps);

			freePlanes(myBitMaps[0], depth, width, height);
			}
		FreeMem(myBitMaps[1], (LONG)sizeof(struct BitMap));
		}
	FreeMem(myBitMaps[0], (LONG)sizeof(struct BitMap));
	}
return_code = RETURN_WARN;
return(NULL);
}

/*--------------------------------------------------------------
** free up the memory allocated by setupPlanes().
*/
VOID	freePlanes(struct BitMap *bitMap,
				   LONG depth, LONG width, LONG height)
{
SHORT plane_num ;

for (plane_num = 0; plane_num < depth; plane_num++)
	{
	if (NULL != bitMap->Planes[plane_num])
		FreeRaster(bitMap->Planes[plane_num], width, height);
	}
}

/*--------------------------------------------------------------
** free up the memory allocated by setupBitMaps().
*/
VOID	freeBitMaps(struct BitMap **myBitMaps,
					LONG depth, LONG width, LONG height)
{
freePlanes(myBitMaps[0], depth, width, height);
freePlanes(myBitMaps[1], depth, width, height);

FreeMem(myBitMaps[0], (LONG)sizeof(struct BitMap));
FreeMem(myBitMaps[1], (LONG)sizeof(struct BitMap));
}

/*--------------------------------------------------------------
**
*/
struct GelsInfo *setupDisplay(struct Window **win,
							  SHORT dbufing,
							  struct BitMap **myBitMaps)
{
struct GelsInfo	   *gInfo;
struct Screen	   *screen;
struct ViewPort	   *vport;
struct RastPort    *rport;

if (dbufing)
	{
	/* Screen type. We alloc two BitMaps. See DBLBUF comments. */
	ns.Type |= CUSTOMBITMAP;
	ns.CustomBitMap = myBitMaps[0];
	}

if ((screen = (struct Screen *)OpenScreen(&ns)) != NULL)
	{
	vport = &screen->ViewPort;
	rport = &screen->RastPort;

	SetRGB4(vport, 0x0, 0x0, 0x0, 0x0); /* Black */
	SetRGB4(vport, 0x1, 0x0, 0x6, 0x0); /* dk green */
	SetRGB4(vport, 0x2, 0x0, 0x9, 0x0); /* med green */
	SetRGB4(vport, 0x3, 0x0, 0xC, 0x0); /* lt green */
	SetRGB4(vport, 0x4, 0x1, 0x1, 0x7); /* dk blue */
	SetRGB4(vport, 0x5, 0x7, 0x0, 0x8); /* dk violet */
	SetRGB4(vport, 0x6, 0x6, 0x6, 0x6); /* dk grey */
	SetRGB4(vport, 0x7, 0x7, 0x1, 0x0); /* dk red */
	SetRGB4(vport, 0x8, 0x3, 0x3, 0xB); /* med blue */
	SetRGB4(vport, 0x9, 0xB, 0x0, 0xC); /* med violet */
	SetRGB4(vport, 0xA, 0x9, 0x9, 0x9); /* med grey */
	SetRGB4(vport, 0xB, 0xB, 0x0, 0x0); /* med red */
	SetRGB4(vport, 0xC, 0x5, 0x5, 0xF); /* lt blue */
	SetRGB4(vport, 0xD, 0xE, 0x0, 0xF); /* lt violet */
	SetRGB4(vport, 0xE, 0xF, 0xF, 0xF); /* lt grey (white) */
	SetRGB4(vport, 0xF, 0xF, 0x0, 0x0); /* lt red */

	/* put some stuff in the background, so we can
	** see that it does not get destroyed.
	*/
	SetAPen(rport, 0xA);
	SetDrMd(rport, JAM1);
	Move(rport, 70, 105);
	Text(rport, "Animation Example...", 20);

	nw.Screen = screen;
	if ((*win = (struct Window *)OpenWindow(&nw)) != NULL)
		{
		if (dbufing)
			{
			(*win)->WScreen->RastPort.Flags = DBUFFER;

			/* copy the rast port data into the alternate rast port */
			(*win)->WScreen->RastPort.BitMap = myBitMaps[1];
			BltBitMapRastPort(myBitMaps[0], 0,0, &(*win)->WScreen->RastPort,
						0,0, RBMWIDTH, RBMHEIGHT, 0xC0);
			(*win)->WScreen->RastPort.BitMap = myBitMaps[0];
			}

		/* set up the gels system.
		** 0xFC says: when you allocate sprites for me, don't ever use
		** sprites zero or one. This guarantees that sprite zero, the
		** intuition pointer, stays intact. Remember sprite one shares
		** colors with sprite zero.
		*/
		if (NULL != (gInfo = setupGelSys(&(*win)->WScreen->RastPort, 0xFC)))
			return(gInfo);

		CloseWindow(*win);
		}
	CloseScreen(screen);
	}
return_code = RETURN_WARN;
return(NULL);
}

/*--------------------------------------------------------------
** DrawGels part of loop.
*/
VOID DrawGels(struct Window *win,
			  struct AnimOb **animKey,
			  SHORT dbufing,
			  WORD *toggleFrame,
			  struct BitMap **myBitMaps)
{
Animate(animKey, &win->WScreen->RastPort);

SortGList(&win->WScreen->RastPort);   /* Put the list in order. */
DoCollision(&win->WScreen->RastPort); /* Collision routines may called now */
SortGList(&win->WScreen->RastPort);	  /* Put the list in order. */

if (dbufing)
	win->WScreen->ViewPort.RasInfo->BitMap = myBitMaps[*toggleFrame];

/* Draw 'em. */
DrawGList(&win->WScreen->RastPort, &win->WScreen->ViewPort);

if (dbufing)
	{
	MakeScreen(win->WScreen);	/* Tell intuition to do it's stuff. */
	RethinkDisplay();			/* Does a MrgCop & LoadView. */

	*toggleFrame ^= 1;
	/* Flip to the next BitMap. */
	win->WScreen->RastPort.BitMap = myBitMaps[*toggleFrame];
	}
else
	WaitTOF();
}

/*--------------------------------------------------------------
**
*/
VOID main(int argc, char **argv)
{
struct BitMap	**myBitMaps;
struct AnimOb	 *boingOb;
struct AnimOb	 *wormOb;
struct AnimOb	 *manOb;
struct Window	 *win;
struct Screen	 *screen;
struct GelsInfo	 *gInfo;
struct AnimOb	 *animKey;
SHORT dbufing;

return_code = RETURN_OK; /* a global variable, yech! */

printf("Program will run double buffered if there are\n");
printf("any command line arguements.\n");

if (argc > 1)
	dbufing = 1;  /* run double buffered when arguements */
else
	dbufing = 0;  /* not double buff */

if (NULL == (IntuitionBase = (struct IntuitionBase *)
		OpenLibrary("intuition.library", 33L)))
	return_code = RETURN_FAIL;
else
	{
	if (NULL == (GfxBase = (struct GfxBase *)
			OpenLibrary("graphics.library", 33L)))
		return_code = RETURN_FAIL;
	else
		{
		if ((!dbufing) ||
			(NULL != (myBitMaps=setupBitMaps(RBMDEPTH,RBMWIDTH,RBMHEIGHT))))
			{
			if (NULL != (gInfo = setupDisplay(&win,dbufing,myBitMaps)))
				{
				InitAnimate(&animKey);
				/* Simple sequenced animation. (Boing ball)
				** Simple animation using XTrans and YTrans.
				** (tiny orbiting satellite)
				*/
				if (NULL != (boingOb = setupBoing(dbufing)))
					{
					AddAnimOb(boingOb, &animKey, &win->WScreen->RastPort);
					if (NULL != (wormOb = setupWorm(dbufing)))
						{
						AddAnimOb(wormOb, &animKey, &win->WScreen->RastPort);
						if (NULL != (manOb = setupMan(dbufing)))
							{
							AddAnimOb(manOb, &animKey,
										&win->WScreen->RastPort);
							runAnimation(win, dbufing, &animKey, myBitMaps);
							freeOb(manOb, RBMDEPTH);
							}
						freeOb(wormOb, RBMDEPTH);
						}
					freeOb(boingOb, RBMDEPTH);
					}

				cleanupGelSys(gInfo, &win->WScreen->RastPort);
				screen = win->WScreen;
				CloseWindow(win);
				CloseScreen(screen);
				}

			if (dbufing)
				freeBitMaps(myBitMaps, RBMDEPTH, RBMWIDTH, RBMHEIGHT);
			}
		CloseLibrary((struct Library *)GfxBase);
		}
	CloseLibrary((struct Library *)IntuitionBase);
	}
exit(return_code);
}
