/*  :ts=8 bk=0
 *
 * anim.c:	Animation drivers.
 *
 * Leo L. Schwab			8710.2		(415) 456-3960
 */
#include <exec/types.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <graphics/rastport.h>
#include "marketroid.h"

extern struct object	ob_grunt;	/*  Special case for movement  */
extern struct BitMap	*sbm;

struct BitMap		*getxpmap();
struct List		guys;
static int		opened;
int			leftbound, rightbound, topbound, bottombound;

struct obcontrol *
addobject (ob, pri, dir, x, y)
register struct object *ob;
int pri, dir, x, y;
{
	register struct obcontrol	*oc;

	if (!(oc = AllocMem ((long) sizeof (*oc), MEMF_CLEAR)))
		return (0);

	oc -> node.ln_Pri	= pri;
	oc -> ob		= ob;
	oc -> x			= x;
	oc -> y			= y;
	oc -> frame		= 0;
	oc -> delay		= 1;

	if (oc->x + ob->width > rightbound ||
	    oc->x < leftbound ||
	    oc->y + ob->height > bottombound ||
	    oc->y < topbound)
		oc -> flags |= OFFSCREEN;

	setdir (oc, dir);
	Enqueue (&guys, oc);
	return (oc);
}

removeobject (oc)
register struct obcontrol *oc;
{
	register struct object	*ob = oc -> ob;
	register struct BitMap	*bm;

	/*  Pull it out of the list  */
	Remove (oc);

	/*  Erase the image from the screen  */
	if (!(oc -> flags & OFFSCREEN))
		BltBitMap (ob -> bitmap, 0L, 0L,
			   sbm, (long) oc -> x, (long) oc -> y,
			   (long) ob -> width, (long) ob -> height,
			   0L, 0xffL, NULL);

	/*  Free dynamically allocated stuff  */
	if (bm = (struct BitMap *) oc -> node.ln_Name) {
		if (bm -> Planes[0])
			FreeRaster (bm -> Planes[0],
				    (long) ob->width, bm->Rows * 4L);
		FreeMem (bm, (long) sizeof (*bm));
	}
	FreeMem (oc, (long) sizeof (*oc));
}

deleteallof (ob)
register struct object *ob;
{
	register struct obcontrol	*oc;

	oc = (struct obcontrol *) guys.lh_Head;
	while (oc -> node.ln_Succ) {
		oc = (struct obcontrol *) oc -> node.ln_Succ;
		if (((struct obcontrol *) oc -> node.ln_Pred) -> ob == ob)
			removeobject (oc -> node.ln_Pred);
	}
}

setdir (oc, dir)
register struct obcontrol *oc;
int dir;
{
	switch (dir) {
	case UP:
		oc -> dx = 0;
		oc -> dy = -(oc -> ob -> incr);
		oc -> xoff = oc->ob->upx;
		oc -> yoff = oc->ob->upy;
		break;

	case DOWN:
		oc -> dx = 0;
		oc -> dy = oc -> ob -> incr;
		oc -> xoff = oc->ob->downx;
		oc -> yoff = oc->ob->downy;
		break;

	case LEFT:
		oc -> dy = 0;
		oc -> dx = -2 * oc->ob->incr;
		oc -> xoff = oc->ob->leftx;
		oc -> yoff = oc->ob->lefty;
		break;

	case RIGHT:
		oc -> dy = 0;
		oc -> dx = 2 * oc->ob->incr;
		oc -> xoff = oc->ob->rightx;
		oc -> yoff = oc->ob->righty;
		break;
	}
	oc -> dir = dir;
}

setbounds (lb, rb, tb, bb)
{
	leftbound	= lb;
	rightbound	= rb;
	topbound	= tb;
	bottombound	= bb;
}

setexplode (oc)
register struct obcontrol *oc;
{
	oc -> flags |= EXPLODING;
	oc -> dir = 1;
	oc -> frame = 0;
	oc -> delay = 1;
}

cls (rp)
struct RastPort *rp;
{
	register BYTE	pensav = rp -> FgPen;

	SetAPen (rp, 0L);
	RectFill (rp, (long) leftbound, (long) topbound,
		  rightbound - 1L, bottombound - 1L);
	SetAPen (rp, (long) pensav);
}

animate ()
{
	register struct obcontrol	*oc;

	for (oc = (struct obcontrol *) guys.lh_Head;
	     oc -> node.ln_Succ;
	     oc = (struct obcontrol *) oc -> node.ln_Succ) {
		if (!--(oc->delay)) {
			/*  Countdown ran out, update image  */
			moveobject (oc);
			if (oc -> flags & EXPLODING)
				/*  Special mod. for exploding stuff  */
				oc -> delay = 2;
			else if (oc -> ob == &ob_grunt)
				/*  Grunts move randomly  */
				oc -> delay = rnd (oc->ob->framerate) +
					      oc -> ob -> framerate;
			else
				oc -> delay = oc -> ob -> framerate;
		}
	}
}

moveobject (oc)
register struct obcontrol *oc;
{
	register struct object	*ob = oc -> ob;
	long			ex, ey, sizx, sizy;
	long			yoff;

	yoff = oc -> yoff;
	if (oc -> flags & FREEZE)	/*  Don't move it, just draw it  */
		goto frozen;		/*  Up yours, Djikstra :-)  */

	if (oc -> flags & EXPLODING) {
		explode (oc);
		return;
	}

	switch (oc -> dir) {
	case UP:
		oc -> y += oc -> dy;
		ex = oc -> x;
		ey = oc -> y + ob -> height;
		sizx = ob -> width;
		sizy = -oc -> dy;
		break;

	case DOWN:
		ex = oc -> x;
		ey = oc -> y;
		sizx = ob -> width;
		sizy = oc -> dy;
		oc -> y += oc -> dy;
		break;

	case LEFT:
		oc -> x += oc -> dx;
		ex = oc -> x + ob -> width;
		ey = oc -> y;
		sizx = -oc -> dx;
		sizy = ob -> height;
		break;

	case RIGHT:
		ex = oc -> x;
		ey = oc -> y;
		sizx = oc -> dx;
		sizy = ob -> height;
		oc -> x += oc -> dx;
		break;
	}

	/*
	 * This next 'if' is tricky.  If the object wanders off the screen
	 * in any way, we mark it as OFFSCREEN and don't render it.  If,
	 * during the last animation pass, it was onscreen, we erase it at
	 * it's last known position.
	 */
	if (oc->x + ob->width > rightbound ||
	    oc->x < leftbound ||
	    oc->y + ob->height > bottombound ||
	    oc->y < topbound) {
		if (!(oc -> flags & OFFSCREEN))
			BltBitMap
			 (ob -> bitmap, 0L, 0L,
			  sbm, (long) oc->x - oc->dx, (long) oc->y - oc->dy,
			  (long) ob -> width, (long) ob -> height,
			  0L, 0xffL, NULL);
		oc -> flags |= OFFSCREEN;
		return;
	}

	/*  Do erasure only if it really moved and was onscreen last time  */
	if (sizx && sizy && !(oc->flags & OFFSCREEN))
		BltBitMap (ob -> bitmap, 0L, 0L, sbm, ex, ey, sizx, sizy,
			   0L, 0xffL, NULL);

	/*  Calculate new frame, if multiframed object  */
	if (ob -> animseq)
		yoff += (ob -> animseq)[oc -> frame];

frozen:
	/*  Draw new guy  */
	BltBitMap (ob -> bitmap, (long) oc -> xoff, yoff,
		   sbm, (long) oc -> x, (long) oc -> y,
		   (long) ob -> width, (long) ob -> height,
		   0xc0L, 0xffL, NULL);

	if (++oc -> frame >= ob -> nframes)
		oc -> frame = 0;
	oc -> flags &= ~OFFSCREEN;
}

explode (oc)
register struct obcontrol *oc;
{
	/*  Pray....  */
	register struct object	*ob = oc -> ob;
	register struct BitMap	*bm;
	register long		y, by, high;
	UWORD			bpr;

	if (oc -> frame < 0 || oc -> flags & OFFSCREEN)
		return;

	if (!oc -> node.ln_Name)
		/*  No explosion bitmap has been allocated, go get one.  */
		if (!(oc -> node.ln_Name = (char *) getxpmap (oc)))
			die ("Explosion allocation failed.\n");
	bm = (struct BitMap *) oc -> node.ln_Name;

	/*  Clear previous instance of explosion from screen  */
	high = oc -> frame * (ob -> height - 1) + 1;
	y = oc->y - high/2 + ob->height/2;
	if (y < topbound) {
		by = -(y - topbound);
		high -= by;
		y = topbound;
	} else
		by = 0;

	if (y + high >= bottombound)
		high -= y + high - bottombound;

	BltBitMap (bm, 0L, by,
		   sbm, (long) oc -> x, y,
		   (long) ob -> width, high,
		   0x20L, 0xffL, NULL);

	/*  Clear intermediate bitmap  */
	BltBitMap (ob -> bitmap, 0L, 0L,
		   bm, 0L, 0L,
		   (long) ob -> width, (long) ob -> height * 10L,
		   0L, 0xffL, NULL);

	/*  Check to see if explosion is done  */
	if ((oc->frame += oc->dir) > 10 || oc->frame < 1) {
		oc -> frame = -1;
		return;
	}

	/*  Expand image vertically by raping intermediate BitMap  */
	bpr = bm -> BytesPerRow;
	bm -> BytesPerRow *= oc -> frame;	/*  Make it look wider  */
	BltBitMap (ob -> bitmap, (long) oc -> xoff, (long) oc -> yoff,
		   bm, 0L, 0L,
		   (long) ob -> width, (long) ob -> height,
		   0xc0L, 0xffL, NULL);

	/*
	 * We have vertically expanded the bitmap.  We now copy it into
	 * the main screen bitmap.
	 */
	bm -> BytesPerRow = bpr;	/*  Fix it  */
	high = oc -> frame * (ob -> height - 1) + 1;
	y = oc->y - high/2 + ob->height/2;
	if (y < topbound) {
		by = -(y - topbound);
		high -= by;
		y = topbound;
	} else
		by = 0;

	if (y + high >= bottombound)
		high -= y + high - bottombound;

	BltBitMap (bm, 0L, by,
		   sbm, (long) oc -> x, y,
		   (long) ob -> width, high,
		   0xe0L, 0xffL, NULL);
}

struct BitMap *
getxpmap (oc)
register struct obcontrol *oc;
{
	register struct object	*ob = oc -> ob;
	register struct BitMap	*bm;
	register UWORD		*planes;
	int			high;

	high = ob -> height * 10;

	if (!(planes = AllocRaster ((long) ob -> width, high * 4L)))
		return (0);

	if (!(bm = AllocMem ((long) sizeof (*bm), NULL)))
		return (0);

	InitBitMap (bm, 4L, (long) ob -> width, (long) high);
	high *= bm -> BytesPerRow >> 1;
	bm -> Planes[0] = (PLANEPTR) planes;
	bm -> Planes[1] = (PLANEPTR) (planes + high);
	bm -> Planes[2] = (PLANEPTR) (planes + high + high);
	bm -> Planes[3] = (PLANEPTR) (planes + high + high + high);

	/*  Clear this intermediate bitmap  */
	BltBitMap (ob -> bitmap, 0L, 0L,
		   bm, 0L, 0L,
		   (long) ob -> width, ob -> height * 10L,
		   0L, 0xffL, NULL);

	/*  Return on-screen object to known state  */
	if (oc -> dir > 0)	/*  Only if exploding positively  */
		BltBitMap (ob->bitmap, (long) oc -> xoff, (long) oc -> yoff,
			   sbm, (long) oc -> x, (long) oc -> y,
			   (long) ob -> width, (long) ob -> height,
			   0xc0L, 0xffL, NULL);

	return (bm);
}


/*  Housekeeping  */
openanim ()
{
	NewList (&guys);
	opened = 1;
}

closeanim ()
{
	register struct obcontrol	*oc;
	register struct BitMap		*bm;
	int				high;

	if (!opened)
		return;

	while (oc = RemHead (&guys)) {
		if (bm = (struct BitMap *) oc -> node.ln_Name) {
			if (bm -> Planes[0])
/*- - - - - - - - - - - - - - -*/
FreeRaster (bm -> Planes[0], (long) oc->ob->width, oc->ob->height * 10 * 4L);
/*- - - - - - - - - - - - - - -*/
			FreeMem (bm, (long) sizeof (*bm));
		}
		FreeMem (oc, (long) sizeof (*oc));
	}
}
