/************************************************************************************
	ISL PSX LIBRARY	(c) 1999 Interactive Studios Ltd.

	actor.c			Skinned model control routines

************************************************************************************/

#define STATIC_CAM

#include "global.h"
//#include "mapcam.h"	// now in mapdraw
#include "map_draw.h"
#include "collide.h"

#include "actor.h"
#include "maths.h"	// FMEMCPY
#include "custom.h"	// psiCalcChildMatrix

//#include "frog_map.h"

ACTORLIST	actorList;

COLLDATA	actorColl;	// temp collision test stuff


//#define min(a,b) (((a) < (b)) ? (a) : (b))
//#define max(a,b) (((a) > (b)) ? (a) : (b))
#define Bound(a,x,b) min(b,max(a,x))


void actorInitialise()
{
	actorList.numEntries = 0;
	actorList.head.next = actorList.head.prev = &actorList.head;
}

/**************************************************************************
// Routines to allow you to hurl sprouts
**************************************************************************/

#define THROW_BLEND_IN_FRAMES	3
#define THROW_BLEND_OUT_FRAMES	6

void actorSetupForThrowing(ACTOR *actor, PSIOBJECT *throw_root, int anim_segment)
{
	if(anim_segment >= actor->animation.numSegments)
		return;
	actor->animation.throw_root = throw_root;
	actor->animation.throwCounter = 0;
	actor->animation.throwSegStart = actor->animation.pSegments[anim_segment].actualStartFrame;
	actor->animation.throwSegLen = actor->animation.pSegments[anim_segment].actualAnimLength;
}
void actorStartThrow(ACTOR *actor)
{
	if(actor->animation.throwCounter)
		return;

	DB("Throw start\n");
	actor->animation.throwCounter = 1;
}
// Call after setting the keyframes (or keyframes2) for normal animation
void actorDealWithThrow(ACTOR *actor)
{
	int main_frame;
	int throw_frame;
	int blendval;

	if(!actor->animation.throwCounter || !actor->animation.throw_root)
		return;
	main_frame = actor->animation.animData[0].actualFrame;

	if(actor->animation.throwCounter < THROW_BLEND_IN_FRAMES)
	{
		blendval = 4096 * actor->animation.throwCounter / THROW_BLEND_IN_FRAMES;
		throw_frame = actor->animation.throwSegStart;
	}
	else if(actor->animation.throwCounter < THROW_BLEND_IN_FRAMES + actor->animation.throwSegLen)
	{
		blendval = 4096;
		throw_frame = actor->animation.throwSegStart + (actor->animation.throwCounter-THROW_BLEND_IN_FRAMES);
	}
	else
	{
		blendval = 4096 * (THROW_BLEND_IN_FRAMES + actor->animation.throwSegLen + THROW_BLEND_OUT_FRAMES - actor->animation.throwCounter) / THROW_BLEND_OUT_FRAMES;
		throw_frame = actor->animation.throwSegStart + actor->animation.throwSegLen;
	}

//	DB("Throw = %3d. blend=%4d. Frm=%4d\n",actor->animation.throwCounter,blendval,throw_frame);

	psiSetKeyFrames3(actor->animation.throw_root, 
			         main_frame, 
					 throw_frame, 
					 blendval);

	actor->animation.throwCounter++;
	if(actor->animation.throwCounter>=(THROW_BLEND_IN_FRAMES + actor->animation.throwSegLen + THROW_BLEND_OUT_FRAMES))
	{
		actor->animation.throwCounter = 0;
	}
}

/**************************************************************************
	Function 	: actorAdd()
	Purpose 	: adds an actor to the linked list
	Parameters 	: pointer to actor
	Returns 	: none
	Info 		:
**************************************************************************/

void actorAdd(ACTOR *actor)
{
	ACTOR *ptr;

	ptr = actorList.head.next;

	if(actor->next == NULL)
	{
		actorList.numEntries++;
		actor->next = ptr;
		actor->prev = ptr->prev;
		ptr->prev->next = actor;
		ptr->prev = actor;		
	}
}


/**************************************************************************
	Function 	: actorSub()
	Purpose 	: removes an actor from the linked list
	Parameters 	: pointer to actor
	Returns 	: none
	Info 		:
**************************************************************************/

void actorSub(ACTOR *actor)
{
	if(actor->next != NULL)
	{
		actor->prev->next = actor->next;
		actor->next->prev = actor->prev;
		actorList.numEntries--;
		actor->next = NULL;
	}

}


/**************************************************************************
	FUNCTION:	actorFree
	PURPOSE:	free all memory used by actor structure
	PARAMETERS:	ACTOR *
	RETURNS:	
**************************************************************************/

void actorFree(ACTOR *actor)
{
	FREE(actor->psiData.objectTable);
	if(actor->animation.pSegments)	// (can be null)
	{
		FREE(actor->animation.pSegments);
	}
	actorSub(actor);
	FREE(actor);
}


static ULONG 	*segTable;
static PSIOBJECT *psiobNext;

static void actorSetTree(PSIOBJECT *obj,PSIMESH *mesh)
{

		*segTable = (ULONG)obj;
		segTable++;

		obj->meshdata = mesh;

		if (mesh->child)
		{
			obj->child	  = psiobNext;
	   		actorSetTree(psiobNext++,mesh->child);
		}
		if (mesh->next)
		{
			obj->next  = psiobNext;
			actorSetTree(psiobNext++,mesh->next);
		}
}


ANIM_SEGMENT *actorCreateSegmentList(ANIMATION *pSegments, int numSegments)
{
	ANIM_SEGMENT *p = (ANIM_SEGMENT *)MALLOC(sizeof(ANIM_SEGMENT) * numSegments);
	int i;

	for(i=0; i<numSegments; i++)
	{
		p[i].actualStartFrame = pSegments[i].animStart;
		p[i].actualAnimLength = pSegments[i].animEnd - pSegments[i].animStart;

		p[i].scaledStartFrame = p[i].actualStartFrame * 256;
		p[i].scaledAnimLength = p[i].actualAnimLength * 256;
	}

	return p;
}

/**************************************************************************
	FUNCTION:	CreateActor
	PURPOSE:	create an actor structure from PSIModel data
	PARAMETERS:	PSIModel*
	RETURNS:	ACTOR*
**************************************************************************/

ACTOR *actorCreate(PSIMODEL *psiModel)
{

	ULONG len,parts,i;
	ULONG *amem;
	ACTOR *actor;
	PSIOBJECT *psiobRoot;
	PSIMESH *meshRoot;
	char	actorName[32];

	if (psiModel==NULL)
		return 0;

	parts = psiModel->noofmeshes;			// number of objects in this model

	len = parts * sizeof(PSIOBJECT);
	len += sizeof (ACTOR);

	utilPrintf("New actor: %d bytes\n",len);
	
	sprintf(actorName,"ACTOR:%s",(char *)psiModel+4);

	actor = (ACTOR *)MALLOC(len);
	
	// flush memory
	amem = (ULONG *)actor;
	for (i=0; i<(len/4); i++)
		*amem++ = 0;

	actor->psiData.flags = 0;

	if (psiModel->flags & 1)
		actor->psiData.flags = ACTOR_BONED;

// RFW
// It's more useful to have the model pointer
// (Quite apart from which, the model name's not filled in by Jobe ANYWAY)
// (See cus_anim.c, where I'm assuming modelName points to the model in some debug asserts)
	actor->psiData.modelName = (void *)(psiModel);	//&psiModel->name[0];

	actor->psiData.numObjects = parts;

	// list of pointers to model objects/meshes
	actor->psiData.objectTable = (ULONG *)MALLOC(parts * sizeof(int));

	// set primitive pointer
	actor->psiData.primitiveList = psiModel->primOffset;

	// set prim counter
	actor->psiData.noofPrims = psiModel->noofPrims;

	actor->psiData.noofVerts = psiModel->noofVerts;


	meshRoot = (PSIMESH *)psiModel->firstMesh;

	//	set anim frame pointers
	// CPW: actor->animation.numAnimations = psiModel->animSegments;
	actor->animation.numSegments = psiModel->animSegments;

	// CPW: actor->animSegments = (USHORT*)psiModel->animSegmentList;	// offest to frame list

	if(psiModel->animSegments)
		actor->animation.pSegments = actorCreateSegmentList((ANIMATION *)(psiModel->animSegmentList), psiModel->animSegments);
	else
		actor->animation.pSegments = NULL;

	// set up actor structure

	psiobRoot = (PSIOBJECT *)((char*)actor + sizeof(ACTOR));		// object structs follow actor struct
	psiobNext = psiobRoot + 1;

	actor->psiData.object = psiobRoot;									// pointer to 1st object

	// fill in next & child pointers
	segTable = actor->psiData.objectTable;
	actorSetTree(psiobRoot,meshRoot);

	actorAdd(actor);											// add actor to actorList

	actorInitAnim(actor);										// initialise animation structure

	actor->animation.numFrames = psiModel->animEnd;						// total noof frames of animation

	actor->radius = psiModel->radius;
	
	psiInitSortList( (actor->radius * 2) + 8 );	 					// make enough room to z sort polys
	actor->size.vx = actor->size.vy = actor->size.vz = 4096;

	if (psiObjectScan(actor->psiData.object,"MOTION"))
	{
		utilPrintf("motion bone found\n");
		actor->psiData.flags |= ACTOR_MOTIONBONE;
	}
	else
		utilPrintf("motion bone NOT found\n");

	actor->psiData.flags |= ACTOR_DYNAMICSORT;							// by default

	actorSetBoundingRotated(actor,0,0,0,0);						// fill in bounding box info

	return actor;
}

/**************************************************************************
	FUNCTION:	
	PURPOSE:	
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void actorInitAnim(ACTOR *actor)
{
	actor->animation.blendSpeed = 0;
	actor->animation.blendCounter = 4096;
	actor->animation.queueSize = 0;

/*	actor->animation.currentAnimation = -1;

	// CPW: 
	actor->animation.oldAnimation = -1;

	actor->animation.queueAnimation[0] = -1;
	actor->animation.queueAnimation[1] = -1;
	actor->animation.queueAnimation[2] = -1;
	actor->animation.queueAnimation[3] = -1;
	actor->animation.queueAnimation[4] = -1;
	actor->animation.loopAnimation = 1;
	actor->animation.numberQueued = 0;
	actor->animation.animTime = 0;
	// CPW:
	actor->animation.baseTime = 0;
	actor->animation.oldTime = -1;
	actor->animation.blendCounter = -1;

	actor->animation.reachedEndOfAnimation = 0;
*/
}

/**************************************************************************
	FUNCTION:	
	PURPOSE:	
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void actorFlushQueue(ACTOR *pActor)
{
	pActor->animation.queueSize = 0;
}


/**************************************************************************
	FUNCTION:	
	PURPOSE:	
	PARAMETERS:	
	RETURNS:	
**************************************************************************/
/*
void actorSetAnimationSpeed(ACTOR *actor, int speed)
{
	actor->animation.animationSpeed = speed;
}
*/

/**************************************************************************
	FUNCTION:	
	PURPOSE:	
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void actorAdjustPosition(ACTOR *actor)
{

	actor->oldPosition.vx = 0;//actor->position.vx;// actor->accumulator.vx;
	actor->oldPosition.vy = 0;//actor->position.vy;//actor->accumulator.vy;
	actor->oldPosition.vz = 0;//actor->position.vz;//actor->accumulator.vz;
	

	actor->accumulator.vx = 0;
	actor->accumulator.vy = 0;
	actor->accumulator.vz = 0;

//	actor->position = actor->oldPosition;

//	actor->position.vx = actor->position.vy = actor->position.vz = 0;

}


/**************************************************************************
	FUNCTION:	
	PURPOSE:	draw all actors in actorList
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void actorDrawAll()
{

	int loop;
	ACTOR *tempActor;

	loop = actorList.numEntries;
	tempActor = actorList.head.next;
	
	while (loop)
	{
		actorMove(tempActor);

		actorDraw(tempActor);

		tempActor = tempActor->next;
		loop--;
	}
}


/**************************************************************************
	FUNCTION:	
	PURPOSE:	move actor at actor->position
	PARAMETERS:	ACTOR *
	RETURNS:	
**************************************************************************/

/*

void actorMove(ACTOR *actor)
{
	int i,v,rotcalc,rotate;
	PSIOBJECT *world;
	register PSIMODELCTRL	*modctrl = &PSImodelctrl;
	ANIMATION *anim;
	int frame;

   	world = actor->psiData.object;

	if ( !(actor->psiData.flags & ACTOR_DYNAMICSORT) )
	{
		v = 0;
		if (-PSImodelctrl.whichcamera->vpy < actor->position.vy)
		{
			v=4;
		}

		rotcalc=utilCalcAngle(	PSImodelctrl.whichcamera->vpx - actor->position.vx,
							PSImodelctrl.whichcamera->vpz - actor->position.vz)&0xffff;
		rotate=world->rotate.vy+2048; 

		i = utilCalcAngle(	PSImodelctrl.whichcamera->vpx - actor->position.vx,
						PSImodelctrl.whichcamera->vpz - actor->position.vz)&0xffff;
		i=((((rotcalc+rotate)&4095)/1024))&3;
		i += v;
		modctrl->sorttable=i;
	}

	// CPW:

	anim=(ANIMATION *)(actor->animSegments + (actor->animation.currentAnimation*2));
	frame=(actor->animation.baseTime + (anim->animStart << ANIMSHIFT)) >> ANIMSHIFT;

	if((actor->animation.blendCounter==-1) || (actor->animation.blendEnable==0))
	{
		actorSetAnimation(actor, frame);
	}
	else
	{
		ANIMATION *oldAnim=(ANIMATION *)(actor->animSegments + (actor->animation.oldAnimation*2));
		int oldFrame=(actor->animation.baseTime + (oldAnim->animStart << ANIMSHIFT)) >> ANIMSHIFT;

		if(actor->animation.oldTime!=-1)
		{
			actor->animation.oldTime += actor->animation.oldAnimationSpeed;
			if((actor->animation.oldTime >> ANIMSHIFT) < oldAnim->animStart)
			{
				actor->animation.oldTime = oldAnim->animEnd << ANIMSHIFT;
			}
			else
			{
				if((actor->animation.oldTime >> ANIMSHIFT) >= oldAnim->animEnd)
					actor->animation.oldTime = oldAnim->animStart << ANIMSHIFT;
			}

			actorSetAnimation2(actor, (actor->animation.oldTime >> ANIMSHIFT), frame, actor->animation.blendCounter);
		}
		else
		{
			if(oldFrame>oldAnim->animEnd)
				oldFrame=oldAnim->animEnd;

			actorSetAnimation2(actor, oldFrame, frame, actor->animation.blendCounter);
		}
		
		actor->animation.blendCounter+=512;

		if(actor->animation.blendCounter>=4096)
		{
			actor->animation.blendCounter=-1;
			actor->animation.oldTime=-1;
		}
	}

	//actorSetAnimation(actor, actor->animation.frame);

}
*/

/**************************************************************************
	FUNCTION:	
	PURPOSE:	draw actor at actor->position
	PARAMETERS:	ACTOR *
	RETURNS:	
**************************************************************************/

void actorDraw(ACTOR *actor)
{
	PSIOBJECT *world;

   	world = actor->psiData.object;

	// modified on 20/1/2000 by CPW
	if(actor->psiData.flags & ACTOR_MOTIONBONE)
	{
		world->matrix.t[0] = actor->position.vx-mapCamData.gameCam.vpx;	// is this right?
		world->matrix.t[1] = actor->position.vy-mapCamData.gameCam.vpy;
		world->matrix.t[2] = actor->position.vz-mapCamData.gameCam.vpz;
	}
	else
	{

		if((world->rotate.vx) || (world->rotate.vy) || (world->rotate.vz))	// Root-level-rotate. RFW, 26-Apr-2000
		{
			MATRIX		rotmat1;
			RotMatrixYXZ_gte(&world->rotate,&rotmat1);
			gte_SetRotMatrix(&rotmat1);
//			gte_SetTransMatrix			// not doing an rtirtr, so we don't need so set the tx to zero
			gte_ldlvl(&world->matrix.t);
			gte_rtir();
			gte_stlvl(&world->matrix.t);
		}

		world->matrix.t[0] += actor->position.vx-mapCamData.gameCam.vpx;
		world->matrix.t[1] += actor->position.vy-mapCamData.gameCam.vpy;
		world->matrix.t[2] += actor->position.vz-mapCamData.gameCam.vpz;
	}

	PSIrootScale = &actor->psiData.object->scale;
	
	psiCalcWorldMatrix(actor->psiData.object);

	direct2psiDrawSegments(&actor->psiData);
   //	psiDrawSegments(&actor->psiData);
}

void actorDrawWithOverride(ACTOR *actor, int nOver, OVERRIDE *pOver)
{
	int i;
	PSIOBJECT *world;

   	world = actor->psiData.object;

	// modified on 20/1/2000 by CPW
	if(actor->psiData.flags & ACTOR_MOTIONBONE)
	{
		world->matrix.t[0] = actor->position.vx-mapCamData.gameCam.vpx;	// is this right?
		world->matrix.t[1] = actor->position.vy-mapCamData.gameCam.vpy;
		world->matrix.t[2] = actor->position.vz-mapCamData.gameCam.vpz;
	}
	else
	{
		if((world->rotate.vx) || (world->rotate.vy) || (world->rotate.vz))
		{
			MATRIX		rotmat1;

			RotMatrixYXZ_gte(&world->rotate,&rotmat1);

			gte_SetRotMatrix(&rotmat1);
//			gte_SetTransMatrix	// not doing an rtirtr, so we don't need so set the tx to zero
			gte_ldlvl(&world->matrix.t);
			gte_rtir();
			gte_stlvl(&world->matrix.t);
		}


		world->matrix.t[0] += actor->position.vx-mapCamData.gameCam.vpx;
		world->matrix.t[1] += actor->position.vy-mapCamData.gameCam.vpy;
		world->matrix.t[2] += actor->position.vz-mapCamData.gameCam.vpz;
	}

	PSIrootScale = &actor->psiData.object->scale;
	psiCalcWorldMatrix(actor->psiData.object);

	// CPW: do override stuff here, copy in replacement matrices
	for(i=0; i<nOver; i++)
	{
		memcpy(&pOver[i].pPSI->matrixscale, &pOver[i].matrix, sizeof(MATRIX));

		if(pOver[i].pPSI->child)
 			psiCalcChildMatrix(pOver[i].pPSI->child, pOver[i].pPSI);
	}

	direct2psiDrawSegments(&actor->psiData);
//	psiDrawSegments(&actor->psiData);
}


void actorDraw2(ACTOR *actor)
{
	MATRIX		scaleMatrix;
	PSIOBJECT	*world;

	world = actor->psiData.object;

	// create out rotation matrix
	RotMatrixYXZ_gte(&world->rotate,&world->matrix);

	// create our scale matrix
	scaleMatrix = GsIDMATRIX;
	ScaleMatrix(&scaleMatrix, &actor->size);

	// multiply camera matrix by our scale matrix
	// CPW: gte_MulMatrix0(&GsWSMATRIX, &scaleMatrix, &world->matrixscale);

	// then by our rotation matrix
	// CPW: gte_MulMatrix0(&world->matrixscale, &world->matrix, &world->matrixscale);


	// CPW: apparently, the above two lines need to be this
	gte_MulMatrix0(&GsWSMATRIX, &world->matrix, &world->matrixscale);
	gte_MulMatrix0(&world->matrixscale, &scaleMatrix, &world->matrixscale);



	// get our position in camera space
	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);
	gte_ldlvl(&actor->position);
	gte_rtirtr();
	gte_stlvl(&world->matrixscale.t);

}
/*
void drawBones(PSIOBJECT *world, PSIOBJECT *parent)
{
	LINE_F2	*line;
	VERT	worldPos, parentPos;
	long	worldXY, parentXY;

	while(world)
	{
		worldPos.vx = world->matrixscale.t[0];
		worldPos.vy = world->matrixscale.t[1];
		worldPos.vz = world->matrixscale.t[2];
		
		parentPos.vx = parent->matrixscale.t[0];
		parentPos.vy = parent->matrixscale.t[1];
		parentPos.vz = parent->matrixscale.t[2];

		gte_ldv0(&worldPos);
		gte_rtps();
		gte_stsxy(&worldXY);

		gte_ldv0(&parentPos);
		gte_rtps();
		gte_stsxy(&parentXY);

		BEGINPRIM(line, LINE_F2);
		*(u_long *)&line->x0 = *(u_long *)&worldXY;
		*(u_long *)&line->x1 = *(u_long *)&parentXY;
		setRGB0(line, 255, 255, 255);
		setLineF2(line);
		ENDPRIM(line, 0, LINE_F2);

		if(world->child)
			drawBones(world->child, world);

		world = world->next;
	}
}



void actorDrawBones(ACTOR *actor)
{
	PSIOBJECT *world;

   	world = actor->psiData.object;

	if(actor->psiData.flags & ACTOR_MOTIONBONE)
	{
		world->matrix.t[0] = actor->position.vx;
		world->matrix.t[1] = actor->position.vy;
		world->matrix.t[2] = actor->position.vz;
	}
	else
	{
		if((world->rotate.vx) || (world->rotate.vy) || (world->rotate.vz))
		{
			MATRIX		rotmat1;
			RotMatrixYXZ_gte(&world->rotate,&rotmat1);

			gte_SetRotMatrix(&rotmat1);
//			gte_SetTransMatrix	// not doing an rtirtr, so we don't need so set the tx to zero
			gte_ldlvl(&world->matrix.t);
			gte_rtir();
			gte_stlvl(&world->matrix.t);
		}

		world->matrix.t[0] += actor->position.vx;
		world->matrix.t[1] += actor->position.vy;
		world->matrix.t[2] += actor->position.vz;
	}

	PSIrootScale = &world->scale;
	
	psiCalcLocalMatrix(world);

	if(world->child)
	{
		gte_SetRotMatrix(&GsWSMATRIX);
		gte_SetTransMatrix(&GsWSMATRIX);
		drawBones(world->child, world);
	}
}
*/

/**************************************************************************
	FUNCTION:	actorUpdateAnimation()
	PURPOSE:	Set keyframe information for current object frame
	PARAMETERS:	Model structure, frame number
	RETURNS:	
**************************************************************************/

/*

void actorUpdateAnimation()
{
	int loop;
	ACTOR *actor;
	
	loop = actorList.numEntries;
	actor = actorList.head.next;
	while (loop)
	{

		actorUpdateAnimations(actor);
		actor = actor->next;
		loop--;

	}	
}

*/


void actorMaintainAnimation(ACTOR *pActor)
{
	// CPW: increment frames etc.

	ANIM_ANIMATION *pAnimation = &pActor->animation;
	int i;

	if(pAnimation->numSegments == 0)
	{
		// CPW: stinky bodge to stop it crashing on the PC when there are no segments
		psiSetKeyFrames(pActor->psiData.object, 0);
		return;
	}

	for(i=0; i<(pAnimation->blendSpeed?2:1); i++)
	{
		// CPW: process current (new) animation, then the old one (assuming blending's enabled)

		ANIM_DATA *pAD;
		ANIM_SEGMENT *pAS;
		int scaledAnimLength;

		pAD = pAnimation->animData + i;
		pAS = pAnimation->pSegments + pAD->segment;
		scaledAnimLength = pAS->scaledAnimLength;

		// CPW: inc/dec frame
		pAD->scaledFrameOffset += pAD->speed;

// CPW: 		if(pActor->animation.numFrames == 58)
// CPW: 			DB("scaledFrame = %d\n", pAD->scaledFrameOffset);

		pAD->reachedEnd = 0;

		// CPW: make sure anim counter stays within its limits
		// The checks for scaledAnimLength are so single-frame anims can be played looping
		if(pAD->scaledFrameOffset < 0)
		{
			if(pAD->loop)
			{
				if(scaledAnimLength)	// FW
					pAD->scaledFrameOffset += scaledAnimLength;
				else	// FW
					pAD->scaledFrameOffset = 0;	// FW
			}
			else
			{
				pAD->scaledFrameOffset = 0;
				pAD->reachedEnd = 1;
			}
		}
		else
		{
			if(pAD->scaledFrameOffset > scaledAnimLength)// CPW: WAS >=, because ranges are inclusive
			{
				if(pAD->loop)
				{
					if(scaledAnimLength)	// FW
					{
						pAD->scaledFrameOffset -= scaledAnimLength + 256;
						if(pAD->scaledFrameOffset<0)
						{
							// CPW: this is HORRIBLE
							pAD->scaledFrameOffset+=256;
						}
					}
					else	// FW
						pAD->scaledFrameOffset = 0;	// FW
				}
				else
				{
					if(scaledAnimLength)
						pAD->scaledFrameOffset = scaledAnimLength;// CPW: WAS scaledAnimLength-1
					else
						pAD->scaledFrameOffset = 0;
					pAD->reachedEnd = 1;
				}
			}
		}


		// CPW: LASTLY, work out the actual frame number
		pAD->actualFrame = (pAS->scaledStartFrame + pAD->scaledFrameOffset) >> 8;

// CPW: 		if(pActor->animation.numFrames == 67)
// CPW: 			DB("frame = %d\n", pAD->actualFrame);

		if(pAD->actualFrame >= pActor->animation.numFrames)
			pAD->actualFrame = pActor->animation.numFrames-1;
	}

	// CPW: update flag from [0] (current anim)
	pAnimation->endOfCurrentAnim = pAnimation->animData[0].reachedEnd;


	PSIactorScale = &pActor->size;

	if(pAnimation->blendSpeed)
	{
		pAnimation->blendCounter += pAnimation->blendSpeed;
		if(pAnimation->blendCounter > 4096)
		{
			pAnimation->blendCounter = 4096;
		}
		psiSetKeyFrames2(pActor->psiData.object, pAnimation->animData[1].actualFrame,
							pAnimation->animData[0].actualFrame, pAnimation->blendCounter);

		
		if(pAnimation->blendCounter==4096)	// RFW, 20 Jly 00. The characters enemies were permanantly doing blend = 4096 (!)
		{
			pAnimation->blendSpeed = 0;
		}
		
	}
	else
	{
 		psiSetKeyFrames(pActor->psiData.object, pAnimation->animData[0].actualFrame);
		pAnimation->blendCounter = 4096;
		// CPW: psiSetKeyFrames(pActor->psiData.object, 0);
	}

	if(pActor->animation.throwCounter)
	{
		actorDealWithThrow(pActor);
	}


	// CPW: maintain all the queue stuff ...

	if(pAnimation->queueSize>0)
	{	// CPW: anything in the queue?

		// CPW: DB("Queue Size = %d\n", pAnimation->queueSize);

		if(pAnimation->endOfCurrentAnim)
		{	// CPW: it's safe to play a new anim
			int result;
			ANIM_DATA *q;

			q = pAnimation->animQueue;

			// send a "2" to the queuing in actorAnimate: Don't queue, but leave the queue intact
			result = actorAnimate(pActor, q->segment, q->loop, 2, q->speed, 0, pAnimation->blendSpeed);

			if(result == ANIMATE_OK)
			{
				int a;

//				DB("QUEUED OK!\n");

				// Shuffle the list down one
				pAnimation->queueSize--;
				FMEMCPY(&pAnimation->animQueue[0],&pAnimation->animQueue[1],pAnimation->queueSize * sizeof(ANIM_DATA));

			}
		}
	}


	// CPW: sort table stuff ripped from actorMove
	if ( !(pActor->psiData.flags & ACTOR_DYNAMICSORT) )
	{
		int rotate, rotcalc;
		
		PSIOBJECT *world = pActor->psiData.object;
		PSIMODELCTRL	*modctrl = &PSImodelctrl;

		int v = 0;

		if (-PSImodelctrl.whichcamera->vpy < pActor->position.vy)
		{
			v=4;
		}

		rotcalc=utilCalcAngle(	PSImodelctrl.whichcamera->vpx - pActor->position.vx,
							PSImodelctrl.whichcamera->vpz - pActor->position.vz)&0xffff;
		rotate=world->rotate.vy+2048; 

		i = utilCalcAngle(	PSImodelctrl.whichcamera->vpx - pActor->position.vx,
						PSImodelctrl.whichcamera->vpz - pActor->position.vz)&0xffff;
		i=((((rotcalc+rotate)&4095)/1024))&3;
		i += v;
		modctrl->sorttable=i;
	}
	
}

void actorSetAnimSpeed(ACTOR *pActor, int speed)
{
	pActor->animation.animData[0].speed = speed;
}

void actorUpdateAnimations(ACTOR *pActor)
{
	actorMaintainAnimation(pActor);
}


void actorMove(ACTOR *pActor)
{
}


// queue = 0 for play right now, and delete all queued stuff
// queue = 1 for queue the anim
// queue = 2 for play right now, but leave the queue intact (used when the queue itself wants to start the int actorAnimate(ACTOR *pActor, int animNum, char loop, char queue, int actualSpeed, char skipendframe, int blend)
int actorAnimate(ACTOR *pActor, int animNum, char loop, char queue, int actualSpeed, char skipendframe, int blend)
{
	ANIM_DATA	tempAnimData;
	ANIM_ANIMATION	*pAnimation;

	pAnimation = &pActor->animation;

	// CPW: queue = 0;

	// CPW: blend = 0;

	if(queue==1)
	{
		int i;
		if(pAnimation->queueSize == ANIM_MAX_QUEUE_SIZE)
			return ANIMATE_QUEUEFULL;

		// Add our anim to the end of the queue, and stop all previous anims in the queue from looping
		for(i = 0; i < pAnimation->queueSize; i++)
		{
			pAnimation->animQueue[i].loop = 0;
		}
		pAnimation->animData[0].loop = 0;

		// CPW: don't do anything NOW, simply add to queue
		pAnimation->animQueue[pAnimation->queueSize].loop = loop;
		pAnimation->animQueue[pAnimation->queueSize].segment = animNum;
		pAnimation->animQueue[pAnimation->queueSize].speed = actualSpeed;
		pAnimation->animQueue[pAnimation->queueSize].scaledFrameOffset = 0;
		pAnimation->animQueue[pAnimation->queueSize].actualFrame = 0;	//-1;	// RFW. "-1" is not a valid frame number, and causes "range exceeded" assert fails

		pAnimation->queueSize++;
	}
	else
	{
		// Immediate anims kill the queue off, unless we're being called by the queuing system itself
		// (See actorMaintainAnimation, which calls here with "queue = 2")
		if(queue == 0)
			pAnimation->queueSize = 0;

		if(blend == 0)
			pAnimation->blendCounter = 4096;

		if(blend && (pAnimation->blendCounter!=4096))
		{	// CPW: blending hasn't finished
//			return ANIMATE_STILLBLENDING;
// In general, if we've asked for an animation, we want it to happen,
// rather than hanging around waiting for an existing blend to finish.
			pAnimation->blendCounter = 4096;

		}

		if((animNum < 0) || (animNum >= pAnimation->numSegments))
		{
			return ANIMATE_INVALIDANIMATION;
		}

		// CPW: make a backup of current anim state
		memcpy(&tempAnimData, pAnimation->animData + 0, sizeof(ANIM_DATA));

		// CPW: pAnimation->blendEnable = blend;	// CPW: start it off blending (if that's been specified)
		pAnimation->blendSpeed = blend;		
		pAnimation->blendCounter = 0;		// CPW: begin at the beginning, because that's the very best place to start
	
		pAnimation->animData[0].loop = loop;
		pAnimation->animData[0].segment = animNum;
		pAnimation->animData[0].speed = actualSpeed;	// CPW: remember, speed is scaled by 256
		pAnimation->animData[0].scaledFrameOffset = 0;
		pAnimation->animData[0].actualFrame = 0;	//	-1;	// RFW. "-1" is not a valid frame number, and causes "range exceeded" assert fails

		// For backwards-playing animations, start at the end. RFW, 3-May-00
		if(actualSpeed < 0)
		{
			ANIM_SEGMENT *pAS;
			int scaledAnimLength;

			pAS = pAnimation->pSegments + animNum;
			scaledAnimLength = pAS->scaledAnimLength;

			pAnimation->animData[0].scaledFrameOffset = scaledAnimLength - 1;
		}


		memcpy(pAnimation->animData + 1, &tempAnimData, sizeof(ANIM_DATA));
	}

	return ANIMATE_OK;
}



void actorSetAnimation(ACTOR *pActor, ULONG frame)
{
	PSIactorScale = &pActor->size;

#ifdef DEBUG
		{
			PSIMODEL *model = (PSIMODEL *)pActor->psiData.modelName;

			if(frame < 0)
				utilPrintf("frame 0 of setanim < 0 (%d)\n",frame);
			if(frame > model->animEnd)
				utilPrintf("frame 0 of setanim >= %d (%d)\n",model->animEnd,frame);


			ASSERT((frame >= 0) && (frame <= model->animEnd));
		}
#endif

	psiSetKeyFrames(pActor->psiData.object, frame);
}



/**************************************************************************
	FUNCTION:	actorUpdateAnimations
	PURPOSE:	
	PARAMETERS:	
	RETURNS:	
**************************************************************************/
/*
void actorUpdateAnimations(ACTOR *actor)
{
	ACTOR_ANIMATION *actorAnim = &actor->animation;
	ANIMATION *anim;
	int	animLength;
	int i;

	//reject all objects which have no animation
	if(actorAnim->numAnimations == 0)
		return;

	if(actorAnim->currentAnimation < 0)
		return;

	anim =		(ANIMATION*) (actor->animSegments + (actorAnim->currentAnimation*2));
	animLength = anim->animEnd - anim->animStart;
	actorAnim->reachedEndOfAnimation = 0;

	// CPW: 
	actorAnim->baseTime += actorAnim->animationSpeed;
	actorAnim->animTime = actorAnim->baseTime + (anim->animStart << ANIMSHIFT);

	actorAnim->frame = actorAnim->animTime >> ANIMSHIFT;

	//DB("baseTime = %d, frame = %d,start = %d,end = %d\n",actorAnim->baseTime, actorAnim->frame,anim->animStart,anim->animEnd);
	//DB("currentAnim = %d, oldAnim = %d\n", actor->animation.currentAnimation, actor->animation.oldAnimation);

	if(((actorAnim->frame > anim->animEnd) || (actorAnim->frame < anim->animStart)) && (actorAnim->loopAnimation == 0))
	{
		actorAnim->frame =    Bound(actorAnim->frame,anim->animStart,anim->animEnd);

		// CPW:
		actorAnim->baseTime = (actorAnim->frame - anim->animStart) << ANIMSHIFT;
		actorAnim->animTime = actorAnim->frame << ANIMSHIFT;
		actorAnim->reachedEndOfAnimation = actorAnim->currentAnimation+1;

		//queue stuff - do this later
		if(actorAnim->queueAnimation[0] != -1)
		{
			ANIMATION *newAnim;

			// CPW;
			actorAnim->blendCounter=0;
			actorAnim->oldAnimation=actorAnim->currentAnimation;
			actorAnim->oldAnimationSpeed = actorAnim->animationSpeed;

			actorAnim->currentAnimation = actorAnim->queueAnimation[0];//actorAnim;
			actorAnim->loopAnimation = actorAnim->queueLoopAnimation[0];//loop;
			actorAnim->animationSpeed = actorAnim->queueAnimationSpeed[0];//speed;

			// CPW:
			newAnim= (ANIMATION*) (actor->animSegments + (actorAnim->currentAnimation*2));

			if(actorAnim->animationSpeed < 0)
			{	// CPW:

				actorAnim->baseTime = (newAnim->animEnd - newAnim->animStart) << ANIMSHIFT;
				actorAnim->animTime = newAnim->animEnd << ANIMSHIFT;

				// Ugh! Andy, you ought to be ashamed of yourself!
				//actorAnim->animTime = actor->animSegments[actorAnim->currentAnimation*2 +1] <<ANIMSHIFT;
			}
			else
			{
				// CPW:
				actorAnim->baseTime = 0;
				actorAnim->animTime = newAnim->animStart << ANIMSHIFT;

				//actorAnim->animTime = actor->animSegments[actorAnim->currentAnimation*2] <<ANIMSHIFT;
			}

			for(i = 0;i < ANIM_QUEUE_LENGTH - 1;i++)
			{
				actorAnim->queueAnimation[i] = actorAnim->queueAnimation[i + 1];
				actorAnim->queueLoopAnimation[i] = actorAnim->queueLoopAnimation[i + 1];
				actorAnim->queueAnimationSpeed[i] = actorAnim->queueAnimationSpeed[i + 1];
			}
			actorAnim->queueAnimation[ANIM_QUEUE_LENGTH - 1] = -1;
			actorAnim->queueLoopAnimation[ANIM_QUEUE_LENGTH - 1] = -1;
			actorAnim->queueAnimationSpeed[ANIM_QUEUE_LENGTH - 1] = -1;
			if(actorAnim->numberQueued >0) 
			{
				actorAnim->numberQueued--;
			}
		}
	}
	else
	{
		if(actorAnim->frame > anim->animEnd)
		{
			actorAnim->frame = anim->animStart;
			// CPW:
			actorAnim->baseTime = 0;
			actorAnim->animTime = anim->animStart<<ANIMSHIFT;
			actorAdjustPosition(actor);
		}
		else 
		{
			if(actorAnim->frame < anim->animStart)
			{
				actorAnim->frame = anim->animEnd;
				// CPW:
				actorAnim->baseTime = (anim->animEnd - anim->animStart) << ANIMSHIFT;
				actorAnim->animTime = anim->animEnd <<ANIMSHIFT;
				actorAdjustPosition(actor);
			}
		}
	}
}
*/

/**************************************************************************
	FUNCTION:	
	PURPOSE:	
	PARAMETERS:	
	RETURNS:	NOW returns whether actorAnimate was successful or not
				0 = success
**************************************************************************/

/*
int actorAnimate(ACTOR *actor, int animNum, char loop, char queue, int actualSpeed, char skipendframe, char blend)
{
	ACTOR_ANIMATION *actorAnim = &actor->animation;
	ANIMATION *anim;
//	int	actualSpeed;

	if(actorAnim->numAnimations == 0)
	{
		return ANIMATE_NOANIMATIONS;
	}

	if( animNum >= actorAnim->numAnimations )
	{
		utilPrintf("WARNING: Anim index number (%d/%d) out of range!\n", animNum, actorAnim->numAnimations);
		return ANIMATE_INVALIDANIMATION;
	}

	
	if( (actorAnim->currentAnimation == animNum) &&
		(queue == 0) && 
		(actorAnim->reachedEndOfAnimation == 0) &&
		( actualSpeed == actorAnim->animationSpeed) )
//		( speed == actorAnim->animationSpeed) )
	{
		actorAnim->loopAnimation = loop;
		actorFlushQueue(actor);
		return ANIMATE_OK;
	}


	actorAnim->exclusive = skipendframe;
  	actorAnim->blendEnable = blend;
	actorAnim->oldTime=-1;

	if(queue == 0)
	{
		actorAdjustPosition(actor);
		// CPW:
		if(blend)
		{
			if((actorAnim->blendCounter!=-1)  && actorAnim->blendEnable)
			{	// we haven't finished merging from the last anim yet, so we have to disallow this anim change. Sorry!
				return ANIMATE_STILLBLENDING;
			}
		}

		actorAnim->blendCounter=0;
		actorAnim->oldAnimation = actorAnim->currentAnimation;
		actorAnim->oldAnimationSpeed = actorAnim->animationSpeed;

		actorAnim->currentAnimation = animNum;
		actorAnim->loopAnimation = loop;
		actorAnim->animationSpeed = actualSpeed;
		actorAnim->reachedEndOfAnimation = 0;
		anim = (ANIMATION*)(actor->animSegments + (actorAnim->currentAnimation*2));

		// CPW: changing animation counter to be relative to current range
		actorAnim->baseTime = 0;
		actorAnim->oldTime = actorAnim->animTime; // CPW: make a backup of animTime. We'll use this for blending from.
		actorAnim->animTime = anim->animStart << ANIMSHIFT;
		actorAnim->frame = anim->animStart;
		actorFlushQueue(actor);
		if(actualSpeed < 0)
		{
			// CPW: changing animation counter to be relative to current range
			actorAnim->baseTime = (anim->animEnd - anim->animStart)<<ANIMSHIFT;
			actorAnim->animTime = anim->animEnd << ANIMSHIFT;
		}
	}
	else
	{
		if(actorAnim->currentAnimation == -1)
		{
			// CPW:
			actorAnim->blendCounter=0;
			actorAnim->oldAnimation = actorAnim->currentAnimation;
			actorAnim->oldAnimationSpeed = actorAnim->animationSpeed;

			actorAnim->currentAnimation = animNum;
			actorAnim->loopAnimation = loop;
			actorAnim->animationSpeed = actualSpeed;
		}
		else
		{
// Disable loop on all currently queued animations, so they run to the end & then follow through to the one just added
			actorAnim->loopAnimation = 0;
			for(queue = 0; queue < actorAnim->numberQueued; queue++)
			{
				actorAnim->queueLoopAnimation[(int)queue] = 0;
			}

			if(actorAnim->numberQueued == 5)
			{
				return ANIMATE_QUEUEFULL;
			}

			actorAnim->queueAnimation[actorAnim->numberQueued] = animNum;
			actorAnim->queueLoopAnimation[actorAnim->numberQueued] = loop;
			actorAnim->queueAnimationSpeed[actorAnim->numberQueued] = actualSpeed;
			actorAnim->numberQueued++;
		}
	}
	return ANIMATE_OK;	// Correct value, I assume
}
*/


/**************************************************************************
	FUNCTION:	actorSetAnimation()
	PURPOSE:	Set keyframe information for current object frame
	PARAMETERS:	Model structure, frame number
	RETURNS:	
**************************************************************************/

/*
void actorSetAnimation(ACTOR *actor, ULONG frame)
{
	PSIOBJECT *world;
	ACTOR_ANIMATION *actorAnim = &actor->animation;
	ANIMATION *anim;
	long temp0,temp1,temp2;
	VECTOR result;

	if(frame == 0)
	{
		DB("ERK!\n");
	}

	world = actor->psiData.object;
	
	PSIactorScale = &actor->size;

	psiSetKeyFrames(world, frame);

	
	if (actorAnim->exclusive)  
	{
		anim = (ANIMATION*) (actor->animSegments + (actorAnim->currentAnimation*2));
		if(actorAnim->frame >= anim->animEnd)
		{
			actorAnim->frame = anim->animStart;
			// CPW:
			actorAnim->baseTime = 0;
			actorAnim->animTime = anim->animStart << ANIMSHIFT;
			actorAdjustPosition(actor);
		}
		else
		{
			if(actorAnim->frame < anim->animStart)
			{
				actorAnim->frame = anim->animEnd;
				// CPW:
				actorAnim->baseTime = (anim->animEnd - anim->animStart) << ANIMSHIFT;
				actorAnim->animTime = anim->animEnd << ANIMSHIFT;
				actorAdjustPosition(actor);
			}
		}
	}		
		// Store Root Bone movement
		// Use Basic Position - IE Set root bone to zero
}
*/

/**************************************************************************
	FUNCTION:	actorSetAnimation2()
	PURPOSE:	Set keyframe information for current object ((4096 - blend) * frame0) + (blend * frame1)
	PARAMETERS:	actor, frame0, frame1, blend
	RETURNS:	
**************************************************************************/

/*
void actorSetAnimation2(ACTOR *actor, ULONG frame0, ULONG frame1, ULONG blend)
{
	PSIOBJECT *world;
	ACTOR_ANIMATION *actorAnim = &actor->animation;
	ANIMATION *anim;
	long temp0,temp1,temp2;
	VECTOR result;

	world = actor->psiData.object;
	
	PSIactorScale = &actor->size;

	psiSetKeyFrames2(world, frame0, frame1, blend);
	


	if (actorAnim->exclusive)  
	{
		anim = (ANIMATION*) (actor->animSegments + (actorAnim->currentAnimation*2));
		if(actorAnim->frame >= anim->animEnd)
		{
			actorAnim->frame = anim->animStart;
			// CPW:
			actorAnim->baseTime = 0;
			actorAnim->animTime = anim->animStart<<ANIMSHIFT;
			actorAdjustPosition(actor);
		}
		else 
		{
			if(actorAnim->frame < anim->animStart)
			{
				actorAnim->frame = anim->animEnd;
				// CPW:
				actorAnim->baseTime = (anim->animEnd - anim->animStart) << ANIMSHIFT;
				actorAnim->animTime = anim->animEnd <<ANIMSHIFT;
				actorAdjustPosition(actor);
			}
		}
	}		
		// Store Root Bone movement
		// Use Basic Position - IE Set root bone to zero
}
*/

void actorRotate(short angx, short angy, short angz, long movex, long movey, long movez, VECTOR *result)
{																
	SVECTOR  MATHS_ang;											
	VECTOR   MATHS_move;										
	MATRIX   MATHS_rotMat;										
																
	MATHS_ang.vx = (angx);										
	MATHS_ang.vy = (angy);										
	MATHS_ang.vz = (angz);										
																
	MATHS_move.vx = (movex);								
	MATHS_move.vy = (movey);									
	MATHS_move.vz = (movez);									
																
	MATHS_rotMat = GsIDMATRIX;									
	RotMatrixYXZ_gte(&MATHS_ang, &MATHS_rotMat);				
	ApplyMatrixLV(&MATHS_rotMat, &MATHS_move, (result));		
}

/**************************************************************************
	FUNCTION:	actorIsVisible
	PURPOSE:	using actor's preset radius, see if it's screen vivible
	PARAMETERS:	
	RETURNS:	true / false
**************************************************************************/

// Now corrected for zero-origin camera
UBYTE actorIsVisible(ACTOR *actor)
{
	VERT	v[3];
	DVECTOR	scrXY[3];
	long	scrZ[3];
	LINE_F2	*line;

	gte_SetRotMatrix(&GsWSMATRIX);		// load viewpoint matrices
	gte_SetTransMatrix(&GsWSMATRIX);

	v[0].vx = actor->position.vx                 -mapCamData.gameCam.vpx;		// use actor's world position
	v[0].vy = actor->position.vy - actor->radius -mapCamData.gameCam.vpy;
	v[0].vz = actor->position.vz                 -mapCamData.gameCam.vpz;

	v[1].vx = v[0].vx + actor->radius;
	v[1].vy = v[0].vy + actor->radius;
	v[1].vz = v[0].vz;

	v[2].vx = v[0].vx - actor->radius;
	v[2].vy = v[0].vy - actor->radius;
	v[2].vz = v[0].vz;

	gte_ldv3c(&v[0]);
	gte_rtpt();
	gte_stsxy3c(&scrXY[0]);
	gte_stsz3c(&scrZ[0]);

	if (scrZ[0] <= 0)
	{
		return 0;					// return if 'behind' camera
	}

	if ((scrZ[0]-actor->radius)>PSImodelctrl.farclip)
	{
		return 0;					// Too far away
	}

//	TD this bit did'nt seam to be working
//	if( (scrXY[1].vx < -256) || (scrXY[1].vy < -128) || (scrXY[2].vx > 256) || (scrXY[2].vy > 128))
//	{
//		return 0;					// Off screen
//	}

	return 1;
}



/**************************************************************************
	FUNCTION:	
	PURPOSE:	
	PARAMETERS:	
	RETURNS:	
**************************************************************************/
/*
void actorDrawRadiusBox(ACTOR *actor)
{

	BOUNDINGBOX *corners;
	SVECTOR v;
	//long lx,rx,ty,by,ison;
	DVECTOR scrxy1,scrxy2;

//	PSISetBoundingRotated(actor,actor->animation.frame,actor->object->rotate.vx,actor->object->rotate.vy,actor->object->rotate.vz);
//	PSISetBounding(actor,0);

	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);

	corners = &actor->bounding;
//////////////
	corners->maxX = actor->radius;
	corners->maxY = actor->radius;
	corners->maxZ = actor->radius;

	corners->minX = -actor->radius;
	corners->minY = -actor->radius;
	corners->minZ = -actor->radius;

////////////

	polyLine.r0 = 240;
	polyLine.g0 = 0;
	polyLine.b0 = 240;

	v.vx = corners->minX+actor->position.vx;
	v.vy = corners->minY+actor->position.vy;
	v.vz = corners->minZ+actor->position.vz;
	gte_ldv0(&v);
	gte_rtps();
	gte_stsxy(&scrxy1);
	polyLine.x0 = scrxy1.vx;
	polyLine.y0 = scrxy1.vy;

		v.vx = corners->minX+actor->position.vx;
		v.vy = corners->minY+actor->position.vy;
		v.vz = corners->maxZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);

		v.vx = corners->maxX+actor->position.vx;
		v.vy = corners->minY+actor->position.vy;
		v.vz = corners->minZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);
	
		v.vx = corners->minX+actor->position.vx;
		v.vy = corners->maxY+actor->position.vy;
		v.vz = corners->minZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);

	v.vx = corners->maxX+actor->position.vx;
	v.vy = corners->maxY+actor->position.vy;
	v.vz = corners->minZ+actor->position.vz;
	gte_ldv0(&v);
	gte_rtps();
	gte_stsxy(&scrxy1);
	polyLine.x0 = scrxy1.vx;
	polyLine.y0 = scrxy1.vy;

		v.vx = corners->minX+actor->position.vx;
		v.vy = corners->maxY+actor->position.vy;
		v.vz = corners->minZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);

		v.vx = corners->maxX+actor->position.vx;
		v.vy = corners->minY+actor->position.vy;
		v.vz = corners->minZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);
	
		v.vx = corners->maxX+actor->position.vx;
		v.vy = corners->maxY+actor->position.vy;
		v.vz = corners->maxZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);


	v.vx = corners->maxX+actor->position.vx;
	v.vy = corners->minY+actor->position.vy;
	v.vz = corners->maxZ+actor->position.vz;
	gte_ldv0(&v);
	gte_rtps();
	gte_stsxy(&scrxy1);
	polyLine.x0 = scrxy1.vx;
	polyLine.y0 = scrxy1.vy;

		v.vx = corners->maxX+actor->position.vx;
		v.vy = corners->minY+actor->position.vy;
		v.vz = corners->minZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);

		v.vx = corners->minX+actor->position.vx;
		v.vy = corners->minY+actor->position.vy;
		v.vz = corners->maxZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);
	
		v.vx = corners->maxX+actor->position.vx;
		v.vy = corners->maxY+actor->position.vy;
		v.vz = corners->maxZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);

	v.vx = corners->minX+actor->position.vx;
	v.vy = corners->maxY+actor->position.vy;
	v.vz = corners->maxZ+actor->position.vz;
	gte_ldv0(&v);
	gte_rtps();
	gte_stsxy(&scrxy1);
	polyLine.x0 = scrxy1.vx;
	polyLine.y0 = scrxy1.vy;

		v.vx = corners->maxX+actor->position.vx;
		v.vy = corners->maxY+actor->position.vy;
		v.vz = corners->maxZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);

		v.vx = corners->minX+actor->position.vx;
		v.vy = corners->maxY+actor->position.vy;
		v.vz = corners->minZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);
	
		v.vx = corners->minX+actor->position.vx;
		v.vy = corners->minY+actor->position.vy;
		v.vz = corners->maxZ+actor->position.vz;
		gte_ldv0(&v);
		gte_rtps();
		gte_stsxy(&scrxy2);
		polyLine.x1 = scrxy2.vx;
		polyLine.y1 = scrxy2.vy;
		PSIDrawLine(0);


}
*/

/**************************************************************************
	FUNCTION:	PSISetBoundingRotated
	PURPOSE:	set bounding box values for animation frame & rotations
	PARAMETERS:	
	RETURNS:	sets values in actor structure's bounding box
**************************************************************************/

void actorSetBoundingRotated(ACTOR *actor,int frame,int rotX,int rotY, int rotZ)
{
	int x,y,z;

	// save rotations
	
	x = actor->psiData.object->rotate.vx;	
	y = actor->psiData.object->rotate.vy;	
	z = actor->psiData.object->rotate.vz;	

	actor->psiData.object->rotate.vx = rotX;		
	actor->psiData.object->rotate.vy = rotY;
	actor->psiData.object->rotate.vz = rotZ;

	// set the model 'pose'
	actorSetAnimation(actor, frame);

	PSIrootScale = &actor->psiData.object->scale;
	psiCalcLocalMatrix(actor->psiData.object);

	actorCalcSegments(actor);

	// restore rotations
		
	actor->psiData.object->rotate.vx = x;		
	actor->psiData.object->rotate.vy = y;
	actor->psiData.object->rotate.vz = z;
}

/**************************************************************************
	FUNCTION:
	PURPOSE:	find min and max xyz from model 'pose'
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

int actorCalcSegments(ACTOR *actor)
{
	register PSIOBJECT	*world;
	int loop,obs,i,j,q,x,y,z,s;
	long flg;
	VECTOR	tfv;
	short *verts;
	SVECTOR v;
	BOUNDINGBOX *corners;

	q = 0;
	obs = actor->psiData.numObjects;
	corners = &actor->bounding;

	corners->minX = corners->maxX = 0;
	corners->minY = corners->maxY = 0;
	corners->minZ = corners->maxZ = 0;

	for (loop = 0; loop < obs; loop++)
	{
		world = (PSIOBJECT*)actor->psiData.objectTable[loop];

	 	gte_SetRotMatrix(&world->matrixscale);
	   	gte_SetTransMatrix(&world->matrixscale);

		verts=(short*)world->meshdata->vertop;
		j = world->meshdata->vern;

		for (i=0; i < j; i++)
		{

			v.vx = *(verts++);
			v.vy = *(verts++);
			v.vz = *(verts++);

			RotTrans(&v,&tfv,&flg);
			
			verts++;

			x = tfv.vx;
			y = tfv.vy;
			z = tfv.vz;


			if ( x < corners->minX )
				corners->minX = x;
			if ( x > corners->maxX )
				corners->maxX = x;
			if ( y < corners->minY )
				corners->minY = y;
			if ( y > corners->maxY )
				corners->maxY = y;
			if ( z < corners->minZ )
				corners->minZ = z;
			if ( z > corners->maxZ )
				corners->maxZ = z;
							 

			// CPW: s = abs(x*x) + abs(y*y) + abs(z*z);
			// CPW: since when can any number, squared be -ve?

			s = x*x + y*y + z*z;

			if (s>q)
			{
				q = s;
			}
		}
	}

  	j = utilSqrt(q) >> 16;

	return j;	
}


/**************************************************************************
	FUNCTION:	actorSetBounding
	PURPOSE:	as actorSetBoundingRotated but forces 0 rotation
	PARAMETERS:	
	RETURNS:	sets values in actor structure's bounding box
**************************************************************************/



void actorSetBounding(ACTOR *actor,int frame)
{
	actorSetBoundingRotated(actor,frame,0,0,0);
}

int actorNearEndOfAnimation(ACTOR *pActor, int animRange, int howNear)
{
/*	ANIMATION *pAnim = (ANIMATION *)(pActor->animSegments + (pActor->animation.currentAnimation*2));

	if(pActor->animation.currentAnimation!=animRange)
		return 0;

	if((pAnim->animEnd - pActor->animation.frame)<=howNear)
		return 1;
	else
*/
	if(pActor->animation.animData[0].segment!=animRange)
	{
		DB("Wrong range ...\n");
		return 0;
	}

	return(pActor->animation.endOfCurrentAnim);
}



int actorReachedFrame(ACTOR *pActor, int animRange, int checkFrame)
{
	int actFrame;

	if(animRange!=-1)
	{
		if(pActor->animation.animData[0].segment!=animRange)
			return(ACTORFRAME_BADRANGE);
	}

	actFrame = pActor->animation.animData[0].scaledFrameOffset/pActor->animation.animData[0].speed;

	if(actFrame<checkFrame)
		return ACTORFRAME_BEFORE;

	if(actFrame==checkFrame)
	{
//		DB("@\n");
		return ACTORFRAME_AT;
	}
	return ACTORFRAME_AFTER;
}

int actorReachedFrameRatio(ACTOR *pActor, int animRange, int checkRatio)
{
	int actRatio, oldActRatio;
	int scaledFrameOffset;

	if(animRange!=-1)
	{
		if(pActor->animation.animData[0].segment!=animRange)
			return(ACTORFRAME_BADRANGE);
	}

	scaledFrameOffset = pActor->animation.animData[0].scaledFrameOffset;

	oldActRatio = ((scaledFrameOffset - pActor->animation.animData[0].speed) * 16) / pActor->animation.pSegments[pActor->animation.animData[0].segment].actualAnimLength;
	actRatio =    (scaledFrameOffset * 16) / pActor->animation.pSegments[pActor->animation.animData[0].segment].actualAnimLength;

	if((oldActRatio < checkRatio) && (actRatio >= checkRatio))
	{
//		DB("#\n");
		return ACTORFRAME_AT;
	}

	if(actRatio<checkRatio)
		return ACTORFRAME_BEFORE;

	return ACTORFRAME_AFTER;
}

void actorGetBonePosition(ACTOR *actor, PSIOBJECT *obj, int frame, VECTOR *pos)
{
	int x,y,z;
	if(frame>=0)
	{
		actor->psiData.object->matrix.t[0] = 0;
		actor->psiData.object->matrix.t[1] = 0;
		actor->psiData.object->matrix.t[2] = 0;

		// set the model 'pose'
		actorSetAnimation(actor, frame);

		PSIrootScale = &actor->psiData.object->scale;
	
		psiCalcLocalMatrix(actor->psiData.object);
	}

	pos->vx = obj->matrixscale.t[0];
	pos->vy = obj->matrixscale.t[1];
	pos->vz = obj->matrixscale.t[2];
}
