/* Enemies.c
   Started by Chris Wilson 2/2000
*/








// SFX notes for Mr T
//	Footsteps (surprise!)
//	Huh? What's that noise?

// SFX notes for Mrs T
//	Footsteps (surprise!)
//	Huh? What's that noise?



#include <limits.h>

#include "global.h"
#include "maths.h"
#include "bff_load.h"
#include "lights.h"
#include "timer.h"
#include "collide.h"
#include "overlays.h"
#include "main.h"
#include "inventory.h"
#include "map_draw.h"
#include "puzzles.h"
#include "enemies.h"
#include "camera.h"
#include "debris.h"
#include "deth.h"
#include "demo.h"
#include "loadsnd.h"
#include "sound.h"




#include "charactr.h"
//extern CHARACTERVARSTR characterVars;


//	PSI name
//	Update routine
//	Collision routine
//	Init state
//	Flags,
//	Coll radius,
//	Y-Offset,
//	Extra Info Pointer
//	Capsule Radius
//	Capsule back end (+ve Z)
//	Capsule front end (-ve Z)
ENEMYINFO enemyInfo[]={
	{
		"GINGER",	// undefined
		NULL,
		NULL,
		STATE_NULL,
		0,
		(16*512)<<3,	// r
		15<<3,			// y off
		NULL,
		150,	// capsule r
		{0, 0, 0},
		{0,-500,0},
		ENEMY_UNDEFINED
	},
	{
		"DOG01",
		enemyUpdateDog,
		enemyDogCollide,
		STATE_NULL,
		0,
		(38*512)<<3,
		40<<3,
		NULL,
		300,	// capsule r
		{0,-300, 400},	// back. +ve z
		{0,-300,-280},	// front. -ve z
		ENEMY_DOG,
	},
	{
		"GINGER",
		enemyUpdateChicken,
		NULL,
		STATE_WANDERING,
		0,
		(16*512)<<3,
		18<<3,
		NULL,
		300,
		{0,-300, 400},
		{0,-300,-280},
		ENEMY_CHICKEN
	},
	{
		"DOG01",
		enemyUpdateSleeping,
		enemySleepyCollide,
		STATE_NULL,
		0,
		(38*512)<<3,
		40<<3,
		NULL,
		300,
		{0,-300, 400},
		{0,-300,-280},
		ENEMY_SLEEPYDOG
	},
	{
		"MRTWEEDY",
		enemyUpdateMrTweedyPatrol,
		enemyMrTweedyPatrolCollide,
		STATE_NULL,
		0,
		(38*512)<<3,
//		-82<<3,
		-72<<3,
		NULL,
		300,
		{0,700, 0},
		{0,-450,0},
		ENEMY_MRT_TORCH
	},
	{
		"MRTWEEDYNT",
		enemyUpdateMrTweedyPatrol,
		enemyMrTweedyPatrolCollide,
		STATE_NULL,
		0,
		(38*512)<<3,
//		-82<<3,
		-72<<3,
		NULL,
		300,
		{0,700, 0},
		{0,-450,0},
		ENEMY_MRT_PATROL
	},
	{
		"MRSTWEEDY",
		enemyUpdateMrsTweedy,
		enemyMrsTweedyCollide,
		STATE_NULL,
		0,
		(38*512)<<3,
		-100<<3,
		NULL,
		300,
		{0,800, 0},
		{0,-400,0},
		ENEMY_MRST_PATROL
	},
	{
		"MRSTWEEDYCLIP",
		enemyUpdateMrsTweedy,
		enemyMrsTweedyCollide,
		STATE_NULL,
		0,
		(38*512)<<3,
		-100<<3,
		NULL,
		300,
		{0,800, 0},
		{0,-400,0},
		ENEMY_MRST_CLIP_PATROL
	},
	{
		"MAC",	// undefined
		NULL,
		NULL,
		STATE_NULL,
		0,
		(16*512)<<3,	// r
		15<<3,			// y off
		NULL,
		150,	// capsule r
		{0, 0, 0},
		{0,-500,0},
		ENEMY_SCENIC_CHICKEN
	},
};
// Note that the dog needs a sufficiently big sphere to roll over low-lying objects.

int nme_replace_obe = 0;
char *nme_replace_name = NULL;
TextureType *hourglass_texes[8];
int hourglass_state = 0;
int hourglass_time = 0;
int hourglass_thistick = 0;
int hourglass_dog_persuit;
int hourglass_game_is_alert = 0;
int hourglass2_fadein = 0;
int hourglass2_max_this_frame = 0;
int hourglass2_sand = 0;
int hourglass2_in_alert_state = 0;

int hourglass2_repeat_this_amount = 0;
int hourglass2_repeat_this_time = 0;

int hourglass2_reason;
VECTOR hourglass2_pos;

#define MAX_MESSAGES	(5)


// CPW: The one and only enemy list
ENEMYLIST	enemyList;
ENEMYGLOBALS	enemyGlobals;

TextureType *enemy_markerspr;	// used for showing the path in debug mode

char enemyEndingLevelFlag = 0;
char enemyControllingLeadFlag = 0;


// CPW: Enemy functions
void enemyTellThemAboutCollision(int numCollBoxes, COLLBOX *pCollBoxes)
{
	enemyGlobals.numCollBoxes = numCollBoxes;
	enemyGlobals.pCollBoxes = pCollBoxes;
}

void enemyLinkBFF(ENEMYBFFLIST *list)
{
	int nme;
	ENEMYDEF *pED;
	PSIMODEL *model;
	ENEMY *pE;

	DB("Linking BFF ...\n");

	pED = ADD2POINTER(list, 12);


	do
	{
// The type numbers are those in egg's propdef & enemy type list
		switch(pED->type)
		{
		case 0x0000:// BP: dog 3 (the one from the dogchase sequence)
			DB("DOGCHASE DOG!\n");
			nme = ENEMY_DOG;	
			break;
		case 0xD090:// CPW: dog 0
			DB("PATROLLING DOG!\n");
			nme = ENEMY_DOG;	
			break;

		case 0xD091:// CPW: dog 1
			DB("GUARD DOG!\n");
			nme = ENEMY_DOG;	
			break;


		case 0xD092:
		case 2:		// Alternative version from the object lists.
			DB("SLEEPY DOG!!\n");
			nme = ENEMY_SLEEPYDOG;
			break;

		case 10:
			DB("MR TWEEDY\n");
			nme = ENEMY_MRT_TORCH;
			break;
		case 11:
			DB("MR TWEEDY PATROL\n");
			nme = ENEMY_MRT_PATROL;
			break;

		case 20:
			DB("MRS TWEEDY\n");
			nme = ENEMY_MRST_PATROL;
			break;
		case 21:
			DB("MRS TWEEDY CLIP\n");
			nme = ENEMY_MRST_CLIP_PATROL;
			break;


		case 30:
			nme = ENEMY_SCENIC_CHICKEN;
			break;


		default:
			DB("Unknown ENEMY type! (%d, &%x)\n", pED->type, pED->type);
			ASSERT(0);
		}

		if(pED->mesh_crc)
		{
			DB("Usng custom model\n");
			model = BFF_IsCRCModelLoaded(pED->mesh_crc);
			nme_replace_obe = pED->mesh_crc;
			nme_replace_name = &pED->mesh_name[0];
		}
		else
		{
			model = BFF_IsModelLoaded(enemyInfo[nme].sName);
			nme_replace_obe = 0;
			nme_replace_name = NULL;
		}


		if(model)
		{
			int i;

			pE = enemyAdd(&enemyInfo[nme]);

			pE->readData.pDef = pED;
			pE->readData.pPoints = ADD2POINTER(pED, sizeof(ENEMYDEF));


			if(pE->readData.pDef->nPoints)
			{
				pE->points.pointFrom = 0;
				pE->points.pointTo = 1;
				pE->pos.vx = pE->readData.pPoints[pE->points.pointFrom].v.vx;
				pE->pos.vy = pE->readData.pPoints[pE->points.pointFrom].v.vy - 1000;
				pE->pos.vz = pE->readData.pPoints[pE->points.pointFrom].v.vz;
			}
			else
			{
				pE->pos.vx = pE->readData.pDef->x;
				pE->pos.vy = pE->readData.pDef->y;
				pE->pos.vz = pE->readData.pDef->z;
			}

			pE->dir = pE->readData.pDef->ya;
			pE->desiredDir = pE->readData.pDef->ya;

//			for(i=0; i<pED->nPoints; i++)
//			{
//				DB("Point %d = (%d, %d)\n", i,  pE->readData.pPoints[i].v.vx/8, pE->readData.pPoints[i].v.vz/8);
//			}
		}
		else
		{
			DB("Can't find enemy '%s'\n", enemyInfo[nme].sName);
			ASSERT(0);
		}

		pE->readData.ppSamples = loadsndSetUpEnemy(pE->readData.pInfo->nme_type);
		ASSERT(pE->readData.ppSamples);

		switch(pE->readData.pInfo->nme_type)
		{
			case ENEMY_MRST_PATROL:
			case ENEMY_MRST_CLIP_PATROL:
				enemyMrsTInitPatrol(pE);
				break;
			
			case ENEMY_SLEEPYDOG:
				enemyDogInitSleepy(pE);
				break;

			case ENEMY_DOG:
				enemyDogInitPatrol(pE);
				break;

			case ENEMY_MRT_TORCH:
				enemyMrTInitTorch(pE);
				break;

			case ENEMY_MRT_PATROL:
				enemyMrTInitPatrol(pE);
				break;

		}

		if(pED->next)
		{
			pED->next = ADD2POINTER(list,(int)pED->next);
		}
		pED = pED->next;
	}
	while(pED);
}
/*
int enemySetSpecial(int id)
{
	int n=0;
	ENEMY *ptr;

	// CPW: step through the list
	for(ptr=enemyList.head.next; ptr != &enemyList.head; ptr = ptr->next)
	{
		if(ptr->header.;
	}
}
*/

ENEMY *enemyAdd(ENEMYINFO *pInf)
{
	ENEMY *ptr;
	ENEMY *pNew=MALLOC(sizeof(ENEMY));

	memset(pNew, 0, sizeof(ENEMY));

	ASSERT(pNew);
	ASSERT(pInf);
	
	ptr=enemyList.head.next;
	ASSERT(ptr);

	pNew->next=ptr;
	pNew->prev=ptr->prev;
	ptr->prev->next=pNew;
	ptr->prev=pNew;

	enemyList.numEntries++;

	// CPW: now set up the enemy specific stuff
	pNew->state = pInf->defaultState;
	pNew->oldState = pNew->state;

	pNew->readData.pInfo = pInf;

// CPW: 	pNew->ppSpecial = pInf->ppSpecial;
	pNew->stateCtr = 0;
// CPW: 	pNew->pHandler = pInf->pHandler;
	pNew->animSpeed = ENEMY_DEFAULTANIMSPEED;
// CPW: 	pNew->sName = pInf->sName;
// CPW: 	pNew->flags = pInf->flags;
	pNew->runFlags = RUNFLAGS_VISIBLE;
// CPW: 	pNew->collRadius = pInf->collRadius;
// CPW: 	pNew->collYoffset = pInf->collYoffset;

// CPW: 	pNew->messCount = 0;
// CPW: 	pNew->pMessList = MALLOC(sizeof(MESSAGE)*MAX_MESSAGES);


	// CPW: set up this enemy's collision info
	pNew->coll.collision.pPos = &pNew->coll.collPos;
	pNew->coll.collision.pVel = &pNew->coll.collVel;
	pNew->coll.collision.bounce = 4096;
	pNew->coll.collision.radius = pInf->collRadius;
	pNew->coll.collision.nBoxes = enemyGlobals.numCollBoxes;
	pNew->coll.collision.pCollBox = enemyGlobals.pCollBoxes;

	// CPW: zero the position rather than just letting it draw somewhere random, in case we forget to initialise it
	ZEROVECTOR(&pNew->pos);

	pNew->pos.vx = (enemyList.numEntries-1) * 256;

	// CPW: get pointer to MODEL

	if(!nme_replace_obe)
	{
		DB("Looking for '%s'\n", pInf->sName);
		pNew->readData.pModel=BFF_IsModelLoaded(pInf->sName);
		pNew->readData.nameCRC = utilStr2CRC(pInf->sName);
	}
	else
	{
		pNew->readData.pModel=BFF_IsCRCModelLoaded(nme_replace_obe);
		pNew->readData.nameCRC = nme_replace_obe;
		nme_replace_obe = 0;
	}
	ASSERT(pNew->readData.pModel);

	
	// CPW: create an instance
	pNew->readData.pActor=actorCreate(pNew->readData.pModel);
	ASSERT(pNew->readData.pActor);

	DB("Searching for head of '%s'\n", pInf->sName);
	// CPW: find head (rocky & ginger call it "head". The rats don't
	pNew->readData.pHead = psiObjectScan(pNew->readData.pActor->psiData.object, "HEAD"); // MUST be upper case, even if it isn't in the model
	if(!pNew->readData.pHead)
		pNew->readData.pHead = psiObjectScan(pNew->readData.pActor->psiData.object, "NICKHEAD");
	if(!pNew->readData.pHead)
		pNew->readData.pHead = psiObjectScan(pNew->readData.pActor->psiData.object, "FETCHHEAD");


	if(!pNew->readData.pHead)
		DB("WARNING: Can't find head!\n");


	{
		PSIOBJECT *body;
		body = psiObjectScan(pNew->readData.pActor->psiData.object, "GING_BODY_UPPER");
		if(!body)
			body = psiObjectScan(pNew->readData.pActor->psiData.object, "RKTORSO");
		if(body)
			actorSetupForThrowing(pNew->readData.pActor, body, GINGER_THROWING);
		DB("We've found a body!\n");
	}


	// CPW: do eye animations


	{
		char sTexName[32];
		int l;

		if(nme_replace_name)
		{
			strcpy(sTexName, nme_replace_name);
		}
		else
		{
			strcpy(sTexName, pNew->readData.pInfo->sName);
		}
		l = strlen(sTexName);
		sTexName[l++]='_';

		strcpy(sTexName + l, "EYES");


//		utilPrintf("target %s\n",sTexName);

// If there's multiple copies of the model, then we need to set the model up
// to allow for each instance to have a different target texture for the texture
// animations.
// (If this is the first instance of a model, then it's okay to just use the given texture)
		{
			ENEMY *test;
			int need_instance = 0;

			for(test=enemyList.head.next; test != &enemyList.head; test = test->next)
			{
				if(test != pNew
					&& test->readData.pModel == pNew->readData.pModel)
				{
					DB("This enemy needs a texture instance\n");
					need_instance = 1;
					break;
				}
			}

//				need_instance = 1;

			if(need_instance)
			{
				pNew->texinstance = texanimSetupInstanceModel(pNew->readData.pModel, utilStr2CRC(sTexName), 1);
				if(!test->texinstance)
				{
					DB("Telling the original enemy it needs to restore its source textures\n");
					test->texinstance = texanimSetupInstanceModel(pNew->readData.pModel, utilStr2CRC(sTexName), 0);
				}
			}
			else
			{
				pNew->texinstance = NULL;
			}


			if(pNew->texinstance)
			{
				pNew->readData.animeyes.pDummy = &pNew->texinstance->tex;
//				DB("Dummy = &%8x\n",pNew->readData.animeyes.pDummy);
			}
			else
			{
				pNew->readData.animeyes.pDummy = textureFindInAllBanks(sTexName);
//				DB("Dummy = &%8x %s\n",pNew->readData.animeyes.pDummy,sTexName);

			}
		}



		{
			int i;
			static char *names[] = {"EYEOPEN","EYECLOSED","EYEANGRY"};

			pNew->readData.animeyes.nFrames=0;

			for (i =0; i < 3; i++)
			{
				strcpy(sTexName + l, names[i]);

//				utilPrintf("source %s\n",sTexName);

				pNew->readData.animeyes.pFrames[pNew->readData.animeyes.nFrames] = textureFindInAllBanks(sTexName);

				if(pNew->readData.animeyes.pFrames[pNew->readData.animeyes.nFrames])
					pNew->readData.animeyes.nFrames++;
			}
		}




		if((pNew->readData.animeyes.pDummy))	// && (pNew->readData.animeyes.pFrames[0]) && (pNew->readData.animeyes.pFrames[1]))
		{
//			pNew->readData.animeyes.nFrames=2;

			ASSERT(pNew->readData.animeyes.pFrames[0]!=pNew->readData.animeyes.pFrames[1]);
			ASSERT(pNew->readData.animeyes.pFrames[0]!=pNew->readData.animeyes.pDummy);

			pNew->readData.animeyes.pAnim = textureCreateAnimation(pNew->readData.animeyes.pDummy,
																	pNew->readData.animeyes.pFrames,
																	pNew->readData.animeyes.nFrames);

			ASSERT(pNew->readData.animeyes.pAnim);
		}
		else
		{
			pNew->readData.animeyes.nFrames=0;
		}
	}

	DB("Created ENEMY '%s'. pModel = %p, pActor = %p\n", pNew->readData.pInfo->sName, pNew->readData.pModel, pNew->readData.pActor); 
	nme_replace_name = NULL;
	return pNew;
}

void enemyDelete(ENEMY *pE)
{
// Enemy-specific frees...
	switch(pE->readData.pInfo->nme_type)
	{
		case ENEMY_DOG:
		case ENEMY_MRT_PATROL:
		{
			FREE(pE->extras);
			break;
		}

		case ENEMY_MRT_TORCH:
		{
			enemyMrTReleaseTorch(pE);
			break;
		}
	}

	FREE(pE->readData.ppSamples);

// Deal with multiple sets of doggie eyes taking up Vram
	if(pE->texinstance)
	{
		textureUnload(&pE->texinstance->tex);
		FREE(pE->texinstance);
	}
	if(pE->readData.animeyes.pAnim)
	{
		textureDestroyAnimation(pE->readData.animeyes.pAnim);
	}

// Unhook from the enemy list, & free.
	pE->prev->next = pE->next;
	pE->next->prev = pE->prev;
	actorFree(pE->readData.pActor);
	FREE(pE);
}

void enemyDeleteAll(void)
{
	while(enemyList.head.next != &enemyList.head)
	{
		enemyDelete(enemyList.head.next);
	}
}


int enemyInitialiseList(void)
{
	enemyList.head.next=enemyList.head.prev=&enemyList.head;
	enemyList.numEntries=0;

	enemy_markerspr = textureFindInAllBanks("PSXBUB3");	//POINT");

	characterVars.pMainChar = NULL;

	hourglass_texes[0] = 0;
	hourglass_game_is_alert = 0;
	hourglass_dog_persuit = 0;
	hourglass2_fadein = 0;
	hourglass2_max_this_frame = 0;
	hourglass2_sand = 0;
	hourglass2_in_alert_state = 0;

	hourglass2_repeat_this_amount = 0;
	hourglass2_repeat_this_time = 0;



	pReadPad->shockEnable=1;

	enemyEndingLevelFlag = 0;
	enemyControllingLeadFlag = 0;

	return 0;
}

int enemyProcessAll(void)
{
	ENEMY *pE;

	if(HOURGLASS_NEEDS_UPDATING)
		hourglass2Cycle();
//		nmeDogCycleHourglass();

	// CPW: step through the list
	for(pE=enemyList.head.next; pE != &enemyList.head; pE = pE->next)
	{
#ifdef DEBUG
			switch(pE->readData.pInfo->nme_type)
			{
			case ENEMY_SLEEPYDOG:
			case ENEMY_DOG:
				TIMER_START(TIMER_DOG);
				break;

			case ENEMY_MRT_PATROL:
			case ENEMY_MRT_TORCH:
				TIMER_START(TIMER_MRT);
				break;
			}
#endif


		if(pE->runFlags & RUNFLAGS_VISIBLE)
		{
			int mY;
			VECTOR temp;

			ASSERT(pE);

//			pE->override.num = 0;
			ASSERT(pE->readData.pActor->psiData.object);
			psiCalcWorldMatrix(pE->readData.pActor->psiData.object);



			switch(pE->readData.pInfo->nme_type)
			{
// Sleepy dog just needs placing on the floor on the first gameframe, and then leaving alone.
// In fact, it may be worth doing this on the first frame for all bad guys.
// But the point is that the sleepy dog needn't do any background interaction from then onwards.
			case ENEMY_SLEEPYDOG:
				if(pE->state== STATE_NULL)
				{
					mY = getHeightAt(pE->pos.vx<<9,pE->pos.vy<<9,pE->pos.vz<<9);
					mY = mY >> 9;
					mY += pE->pos.vy;
					mY -= pE->readData.pInfo->collYoffset;
					mY += (pE->readData.pInfo->collRadius >>9);
					pE->pos.vy = mY;
				}
				break;

			
			case ENEMY_DOG:
				pE->vel.vy += GRAVITY;
				if(pE->vel.vy > 128)
					pE->vel.vy = 128;
				ADDTOVECTOR(&pE->pos, &pE->vel);

		// Keep dogs out of the route-finding boxes. Forcibly.
		// (The "10" safety margin is to ensure it can reach its routepoints (!))
				routeKeepOutOfRouteboxes((int *)&pE->pos.vx, (int *)&pE->pos.vz, DOG_ROUTEFIND_MARGIN - 10);

		// Stick dogs on top of the floor. Don't do full collision.
		// They need to be able to step over small obstacles like bricks.
		// The routefinding data should be enough to keep them out of solid objects.

				pE->runFlags &= ~RUNFLAGS_ONGROUND;
				mY = getHeightAt(pE->pos.vx<<9,pE->pos.vy<<9,pE->pos.vz<<9);

				if(mY == 0x7fffffff)
				{
					mY = 0;	// If the dog falls off the world for whatever reason, assuma a ground height of zero
				}
				else
				{
					mY = mY >> 9;
					mY += pE->pos.vy;
				}
				mY -= pE->readData.pInfo->collYoffset;
				mY += (pE->readData.pInfo->collRadius >>9);

				if(mY <= pE->pos.vy)
				{
					pE->runFlags|=RUNFLAGS_ONGROUND;
					pE->runFlags|=RUNFLAGS_BEENONGROUND;
					pE->vel.vy = 0;
				}

				pE->pos.vy = mY;
				break;


			case ENEMY_MRST_PATROL:
			case ENEMY_MRT_PATROL:
			case ENEMY_MRT_TORCH:

				pE->vel.vy += GRAVITY;
				if(pE->vel.vy > 128)
					pE->vel.vy = 128;
				ADDTOVECTOR(&pE->pos, &pE->vel);

		// Keep dogs out of the route-finding boxes. Forcibly.
		// (The "10" safety margin is to ensure it can reach its routepoints (!))
				routeKeepOutOfRouteboxes((int *)&pE->pos.vx, (int *)&pE->pos.vz, DOG_ROUTEFIND_MARGIN - 10);

				pE->coll.collPos.vx = pE->pos.vx * 512;
				pE->coll.collPos.vy = pE->pos.vy * 512 - 512 * (pE->readData.pInfo->collYoffset);
				pE->coll.collPos.vz = pE->pos.vz * 512;

				pE->runFlags &= ~RUNFLAGS_ONGROUND;
				mY = pE->coll.collPos.vy;

				pE->coll.collision.nBoxes = enemyGlobals.numCollBoxes;	// make sure the enemy's uptodate with the collision. Erk.
				pE->coll.collision.pCollBox = enemyGlobals.pCollBoxes;

				pE->coll.collision.nHits = collboxCheckSphere(&pE->coll.collision);

				// CPW: DB("before = %d, after = %d\n", mY, pE->collPos.vy);

				if(pE->coll.collPos.vy < mY)
				{
					// CPW: DB("(%p) HIT FLOOR!\n", pE);
					pE->runFlags|=RUNFLAGS_ONGROUND;
					pE->runFlags|=RUNFLAGS_BEENONGROUND;

					//pE->vel.vy = 0;
				}

				pE->pos.vx = pE->coll.collPos.vx / 512;
				pE->pos.vz = pE->coll.collPos.vz / 512;
				pE->pos.vy = pE->coll.collPos.vy / 512 + (pE->readData.pInfo->collYoffset);

				break;



		// Full-on-collision enemies.
		// Essentially, this is the chickens & rats
		// and (currently) Mr/Mrs T.
			default:
				if((pE->state != STATE_CLINGING) && (!(pE->runFlags & RUNFLAGS_NOGRAVITY)))
				{
					pE->vel.vy += GRAVITY;
					if(pE->vel.vy > 128)
						pE->vel.vy = 128;
				}

				ADDTOVECTOR(&pE->pos, &pE->vel);

				//DB("2nd check vel %d pos %d\n",pE->vel.vy,pE->pos.vy);

				if (pE->runFlags &RUNFLAGS_ONGROUND && pE->vel.vy>0) pE->vel.vy=0;

		// Keep chickens out of dogs
				temp.vx = pE->pos.vx;
				temp.vy = pE->pos.vy-100;
				temp.vz = pE->pos.vz;

				if(pE->flags & ENEMYFLAGS_MAINCHARACTER)
				{
					if(enemyCheckCollision(pE, COLLIDE_WITH_MAINCHAR, &temp,200))
					{
						pE->pos.vx = temp.vx;
						pE->pos.vy = temp.vy+100;
						pE->pos.vz = temp.vz;

						//DB("vel %d\n",pE->vel.vy);
						if (pE->vel.vy<0) pE->vel.vy=(pE->vel.vy*6)>>3;	// Try to stop char jumping on top of each other TD
						//DB("post vel %d\n",pE->vel.vy);
					}
				}

// Collision !
				pE->coll.collPos.vx = pE->pos.vx * 512;
				pE->coll.collPos.vy = pE->pos.vy * 512 - 512 * (pE->readData.pInfo->collYoffset);
				pE->coll.collPos.vz = pE->pos.vz * 512;

				pE->runFlags &= ~RUNFLAGS_ONGROUND;
				mY = pE->coll.collPos.vy;

				pE->coll.collision.nBoxes = enemyGlobals.numCollBoxes;	// make sure the enemy's uptodate with the collision. Erk.
				pE->coll.collision.pCollBox = enemyGlobals.pCollBoxes;

				if(pE->flags & ENEMYFLAGS_MAINCHARACTER)
				{
					platcoll_is_enemy = 0;
					if(!enemyControllingLeadFlag)
					{
						pE->coll.collision.nHits = targetCharCollTest(&pE->coll.collision);
					}
					else
					{
						DB("Not Checking Coll on lead\n");
						pE->coll.collision.nHits = 0;
					}
					platcoll_is_enemy = 1;


					// Like Glover, put a "safety net" consisting of a big pool of lava under the level...
					if((pE->pos.vy) >= (WorldBFFcoll->endY>>9) + 100 *8)
					{
						DB("Main Character Fell Out of World\n");
#ifndef _WINDOWS
						deathKill(characterVars.pMainChar, DEATH_REASON_FELL_OUT_OF_WORLD, NULL);
#endif
					}
				}
				else
				{
					pE->coll.collision.nHits = collboxCheckSphere(&pE->coll.collision);
				}

				// CPW: DB("before = %d, after = %d\n", mY, pE->collPos.vy);

				if(pE->coll.collPos.vy < mY)
				{
					// CPW: DB("(%p) HIT FLOOR!\n", pE);
					pE->runFlags|=RUNFLAGS_ONGROUND;
					pE->runFlags|=RUNFLAGS_BEENONGROUND;
					//pE->vel.vy = 0;
				}

				pE->pos.vx = pE->coll.collPos.vx / 512;
				pE->pos.vz = pE->coll.collPos.vz / 512;
				pE->pos.vy = pE->coll.collPos.vy / 512 + (pE->readData.pInfo->collYoffset);
				break;
			}

// Okay, general stuff that all enemies do...

			if(pE->readData.pInfo->pHandler)
				pE->readData.pInfo->pHandler(pE);	// CPW: call handler function (if there is one)

			pE->stateCtr++;

			ASSERT(pE->readData.pActor);
			actorUpdateAnimations(pE->readData.pActor);
		}
#ifdef DEBUG
			switch(pE->readData.pInfo->nme_type)
			{
			case ENEMY_SLEEPYDOG:
			case ENEMY_DOG:
				TIMER_STOP(TIMER_DOG);
				break;

			case ENEMY_MRT_PATROL:
			case ENEMY_MRT_TORCH:
				TIMER_STOP(TIMER_MRT);
				break;
			}
#endif
	}
	return 0;
}

int enemyDrawAll(void)
{
	ENEMY *pE;

	if(HOURGLASS_NEEDS_UPDATING)
	{
//		nmeDogDrawHourglass();
		hourglass2Draw();
	}


// Equipped inventory item display.
// (Double check that the sprites are actually there)
// BP: Changed so that it depends on the level which texture set to look for
	if((currentInv->selected_item != EMPTY)	&& (streamed_bank))
	{
		if(((levinfo->level_number<=LEVNUMBER_A1_DOGCHASE) && (streamed_bankno == STREAMBANK_INVENTORYACT1)) ||
		((levinfo->level_number<=LEVNUMBER_A2_COGCHASE) && (streamed_bankno == STREAMBANK_INVENTORYACT2)) ||
		((levinfo->level_number<=LEVNUMBER_A4_FLYMACHINE) && (streamed_bankno == STREAMBANK_INVENTORYACT4)));
		{
			inventoryEquippedItemOverlayDraw();
		}
	}


/*
	if(test_instance)
	{
		overlaySprite(&test_instance->tex, 0, 0, 0, 0xff808080);
	}
*/
	// CPW: step through the list
	for(pE=enemyList.head.next; pE != &enemyList.head; pE = pE->next)
	{
		ASSERT(pE);

		if( (pE->runFlags & RUNFLAGS_VISIBLE))
		{

			enemyBlinkHandler(pE);	// Needs to be part of the draw routine, not the cycle one

			switch(pE->readData.pInfo->nme_type)
			{
				case ENEMY_MRT_TORCH:
					enemyMrTweedyDrawTorch(pE);
					break;
			}
		}

		// RFW - visibility check
		if( (pE->runFlags & RUNFLAGS_VISIBLE)
			&& mapdrawIsSphereVisible(&pE->pos, pE->readData.pActor->radius))
		{
			switch(overlay_show_collision)
			{
			case DEBUG_OVERLAY_ENEMY_INFO:


				if(!(pE->flags & ENEMYFLAGS_MAINCHARACTER))
				{
					// A silly little light that bobs up & down, and is green when the enemy's asleep
					int h;
					h = rcos((loop_ticker<<7) & 4095)/16;

					if(pE->runFlags & RUNFLAGS_NOTLOOKING)
					{
						overlayShowSphere(pE->pos.vx, pE->pos.vy - (1000 + h), pE->pos.vz, 100,0x00ff00);
					}
					else
					{
						overlayShowSphere(pE->pos.vx, pE->pos.vy - (1000 + h), pE->pos.vz, 100,0x00ffff);
					}
				}
				// ... dropthru
			case DEBUG_OVERLAY_LEVEL_COLLISION:
			case DEBUG_OVERLAY_PLATFORM_COLLISION:
			case DEBUG_OVERLAY_PUZZLE_BOXES:
				// (show the enemy's collision sphere pretty much on all the debugs
				overlayShowSphere(pE->pos.vx, pE->pos.vy - (pE->readData.pInfo->collYoffset), pE->pos.vz, pE->coll.collision.radius>>9,0x0000ff);
				break;
			}

			// CPW: copy actor position into model position
			COPYVECTOR(&pE->readData.pActor->position, &pE->pos);

			// CPW: set model rotation to enemy rotation
			if(pE->flags & RUNFLAGS_FORCEFACE)
			{
				pE->flags&=(~RUNFLAGS_FORCEFACE);
			}
			else
			{
				pE->readData.pActor->psiData.object->rotate.vy = pE->dir;
			}

			ASSERT(pE->readData.pActor);

// Things to do between the anim set and the world-matrix calcs
// "position" parts of the bendy dog.
// "rotate" is done earlier, in the dog's own routine
			switch(pE->readData.pInfo->nme_type)
			{
				case ENEMY_DOG:
				{
					PATROLDOGSTR *pat = (void *)pE->extras;
					int angle = pat->angle & 4095;

					if(angle > 2048)
						angle = angle - 4096;


					pat->bend_objects[4]->matrix.t[0] = - rsin(angle / 4) * 19 / 4096  + rcos(angle / 4) * 104 / 4096;
					pat->bend_objects[4]->matrix.t[2] = - rcos(angle / 4) * 19 / 4096  - rsin(angle / 4) * 104 / 4096;

					pat->bend_objects[5]->matrix.t[0] = - rsin(angle / 4) * 19 / 4096  + rcos(angle / 4) * -104 / 4096;
					pat->bend_objects[5]->matrix.t[2] = - rcos(angle / 4) * 19 / 4096  - rsin(angle / 4) * -104 / 4096;

					pat->bend_objects[6]->matrix.t[0] = - rsin(angle / 4) * 162 / 4096;
					pat->bend_objects[6]->matrix.t[2] = - rcos(angle / 4) * 162 / 4096;

					break;
				}
			}



			if(pE->flags & ENEMYFLAGS_MAINCHARACTER)
			{
				EXLIGHT_SetupActorLightsAlert(pE->readData.pActor);
			}
			else
			{
				EXLIGHT_SetupActorLights(pE->readData.pActor);
			}

			actorMove(pE->readData.pActor);

// The "WithOverride" is the full-blown "matrix" version.
// If you're just hitting the "rotate.vy", then actorDraw works fine
			// CPW: if(pE->override.num)
			// CPW: 	actorDrawWithOverride(pE->pActor, pE->override.num, pE->override.data);
			// CPW: else

			if(pE->texinstance)
			{
				texanimInstanceModel(pE->texinstance);
			}


			if(pE->runFlags & RUNFLAGS_FORCETOFRONT)	// For the death-fade
				PSImodelctrl.depthoverride = ENEMIES_FORCEFRONT_SORTDEPTH;

#ifndef _WINDOWS
			actorDraw(pE->readData.pActor);
#else

			d2mActorDraw(pE->readData.pActor);
#endif


			if(pE->runFlags & RUNFLAGS_FORCETOFRONT)
				PSImodelctrl.depthoverride = 0;

			if(pE->readData.pInfo->nme_type != ENEMY_SCENIC_CHICKEN)
			{
				if(pE->readData.pInfo->nme_type == ENEMY_MRT_PATROL || pE->readData.pInfo->nme_type == ENEMY_MRT_TORCH)
					EXLIGHT_Data->shadow_width = 200;

				TIMER_START(TIMER_SHADOW);
				EXLIGHT_DrawShadow(pE->readData.pActor);
				TIMER_STOP(TIMER_SHADOW);

				if(pE->readData.pInfo->nme_type == ENEMY_MRT_PATROL || pE->readData.pInfo->nme_type == ENEMY_MRT_TORCH)
					EXLIGHT_Data->shadow_width = 80;
			}


		}

		
		if(overlay_show_collision == DEBUG_OVERLAY_ENEMY_INFO && pE->readData.pPoints)
		{
			if(pE->readData.pDef->nPoints > 2)
			{
				int i;
				VERT v;

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

				for(i=0; i<pE->readData.pDef->nPoints; i++)
				{
					int colour;

					if(i != pE->readData.pDef->nPoints-1)
					{
						overlayShowDebugLine(
							&pE->readData.pPoints[i].v,
							&pE->readData.pPoints[i+1].v,
							0xff808080,
							DEBUGPOLY_SUBDIV+DEBUGPOLY_USEDEPTH);
					}
					else
					{
						overlayShowDebugLine(
							&pE->readData.pPoints[i].v,
							&pE->readData.pPoints[0].v,
							0xff404040,
							DEBUGPOLY_SUBDIV+DEBUGPOLY_USEDEPTH);
					}

					v.vx = pE->readData.pPoints[i].v.vx - mapCamData.gameCam.vpx;
					v.vy = pE->readData.pPoints[i].v.vy - mapCamData.gameCam.vpy;
					v.vz = pE->readData.pPoints[i].v.vz - mapCamData.gameCam.vpz;

					if(i == pE->points.pointFrom)
						colour = 0x000080;
					else
					{
						if(i == pE->points.pointTo)
							colour = 0x008000;
						else
							colour = 0x808080;
					}
					Print3DSprite(enemy_markerspr, &v, 512, colour);
				}
			}
		}
	}
	return 0;
}

/*
void enemySendMessage(ENEMY *pE, int messageCode, void *pMessBlk)
{
	ASSERT(pE->pMessList);

	pE->pMessList[pE->messCount].messageCode = messageCode;
	pE->pMessList[pE->messCount].messageBuff = pMessBlk;

	pE->messCount++;
	
	ASSERT(pE->messCount<MAX_MESSAGES);
}
*/

void enemyBlinkHandler(ENEMY *pE)
{
	if(pE->readData.animeyes.nFrames)
	{
		if(pE->blinkOverride != -1)	// Used by sleepy and angry dogs to fix the eyes instead of random blinking
		{
			if(pE->readData.animeyes.nFrames > pE->blinkOverride)
			{
				textureSetAnimation(pE->readData.animeyes.pAnim, pE->blinkOverride);
			}
		}
		else
		{
			if(pE->readData.animeyes.nFrames)
			{
				if(pE->blinkCtr==0)
				{
					if(utilRandomInt(32)==0)
					{
						pE->blinkCtr++;
						textureSetAnimation(pE->readData.animeyes.pAnim, 1);
					}
				}
				else
				{
					pE->blinkCtr++;

					if(pE->blinkCtr>=4)
					{
						textureSetAnimation(pE->readData.animeyes.pAnim, 0);
						pE->blinkCtr = 0;
					}
				}
			}
		}
	}

	pE->blinkOverride = -1;

}

void enemyMove(ENEMY *pE, int desiredSpeed, int action, int accel)
{
	static int actualSpeed=0;

	accel=abs(accel);

	switch(action)
	{
	case ENEMYMOVE_SET:
		actualSpeed = desiredSpeed;
		break;

	case ENEMYMOVE_TENDTO:
		if(desiredSpeed == actualSpeed)
			break;

		if(actualSpeed<desiredSpeed)
		{
			actualSpeed+=accel;
			if(actualSpeed>desiredSpeed)
				actualSpeed = desiredSpeed;
		}
		else
		{
			actualSpeed-=accel;
			if(actualSpeed<desiredSpeed)
				actualSpeed = desiredSpeed;
		}

		break;

	default:
		ASSERT(0);
	}

	// CPW: signs may be incorrect
//	pE->pos.vx-=(rsin(pE->dir)*actualSpeed)>>12;
//	pE->pos.vz-=(rcos(pE->dir)*actualSpeed)>>12;
	pE->vel.vx = -(rsin(pE->dir)*actualSpeed)>>12;
	pE->vel.vz = -(rcos(pE->dir)*actualSpeed)>>12;
}


int	enemyTendTowards(int desired, int actual, int maxturn)
{
	int dangle;

	if(desired==actual)
		return actual;

	dangle = (desired - actual) & 4095;
	if(dangle >= 2048) dangle -= 4096;

	if(dangle<-maxturn)
		dangle=-maxturn;

	if(dangle>maxturn)
		dangle=maxturn; 

	actual+=dangle;

	return actual & 4095;
}

int	enemyTendTowards2(int desired, int actual, int maxturn, int tolerance)
{
	int dangle;

	if(desired==actual)
		return actual;

	dangle = (desired - actual) & 4095;
	if(dangle >= 2048) dangle -= 4096;

	if(dangle > -tolerance && dangle < tolerance)
		return actual;

	if(dangle<-maxturn)
		dangle=-maxturn;

	if(dangle>maxturn)
		dangle=maxturn; 

	actual+=dangle;

	return actual & 4095;
}


/*
int enemyDistanceBetween(ACTOR *pA1, ACTOR *pA2)
{
	int dsq;
	VECTOR v;

	if((!pA1) || (!pA2))
		return 0x7fffffff;

	SUBVECTOR(&v, &pA1->position, &pA2->position);

	//dsq = v.vx*v.vx + v.vy*v.vy +v.vz*v.vz;

	return Magnitude(&v);
}
*/
/*
int enemyFacingAnother(ENEMY *pE)
{
	ENEMY *ptr;
	int facing = ENEMYFACING_NO;

	// CPW: step through the list
	for(ptr=enemyList.head.next; ptr != &enemyList.head; ptr = ptr->next)
	{
		if((ptr!=pE))// CPW:  && (!(ptr->flags&ENEMYFLAGS_NOTANENEMY)))
		{	// CPW: this is RIGHT now ...
			if(enemyDistanceBetween(pE->readData.pActor, ptr->readData.pActor)<1000)
			{
				// CPW: there's a chance we're going to collide here
				int imFacing, theyreFacing;
				int otherAng;
				int myAng = utilCalcAngle(	pE->pos.vx - ptr->pos.vx,
											pE->pos.vz - ptr->pos.vz);

				otherAng = myAng+2048;

				myAng -= pE->dir;
				myAng &=4095;

				otherAng -=ptr->dir;
				otherAng &=4095;

				imFacing = (myAng<512) || (myAng>3584);
				theyreFacing = (otherAng<512) || (otherAng>3584);

// CPW: 		DB("myAng = %d, theirAng = %d\n", myAng, otherAng);

				if(facing!=ENEMYFACING_MUTUALLY)
				{
					if(imFacing)
					{
						facing=ENEMYFACING_YES;

						if(theyreFacing)
							facing=ENEMYFACING_MUTUALLY;
					}
				}
			}
		}
	}

// CPW: 	DB("facing = %d\n", facing);
	return facing;
}
*/
void enemyMakeMain(ENEMY *pME)
{
	ENEMY *ptr;

	for(ptr=enemyList.head.next; ptr != &enemyList.head; ptr = ptr->next)
	{
		ptr->flags&=(~ENEMYFLAGS_MAINCHARACTER);
	}

	pME->flags|=ENEMYFLAGS_MAINCHARACTER;
	pME->runFlags|=RUNFLAGS_VISIBLE;
}

void enemyFollow(ENEMY *pE, ACTOR *pA, int turning)
{
	int dangle;
	int dx = pE->pos.vx - pA->position.vx;
	int dz = pE->pos.vz - pA->position.vz;

	ASSERT(pE);

	if(!pA)
		return;

	pE->desiredDir = utilCalcAngle(dx, dz) & 4095;
	pE->dir = enemyTendTowards(pE->desiredDir, pE->dir, turning);
}

void enemySteer(ENEMY *pE, int desiredDir)
{
	pE->desiredDir += desiredDir;
	pE->desiredDir &= 4095;

	pE->dir = enemyTendTowards(pE->desiredDir, pE->dir, 12);
}


int enemySteerTowards(ENEMY *pE, VECTOR *pPos, int turning)
{
	int dangle;
	int dx = pE->pos.vx - pPos->vx;
	int dz = pE->pos.vz - pPos->vz;

	pE->desiredDir = utilCalcAngle(dx, dz) & 4095;
	pE->dir = enemyTendTowards2(pE->desiredDir, pE->dir, turning, 3);
//	pE->dir = enemyTendTowards(pE->desiredDir, pE->dir, turning);

	dangle = (pE->desiredDir - pE->dir) & 4095;
	if(dangle >= 2048) dangle -= 4096;


	// CPW: DB("desired = %d, dir = %d, dangle = %d, turning = %d\n", pE->desiredDir, pE->dir, dangle, turning);

	if(abs(dangle)<turning)
		return 0;
	else
		return ((dangle<0)?-1:1);
}

/* RFW - Now in scenics
ENEMY *enemyIsThereAnythingToPickUp(ENEMY *pE)
{
	ENEMY *ptr;

	ASSERT(pE);
	// CPW: step through the list
	for(ptr=enemyList.head.next; ptr != &enemyList.head; ptr = ptr->next)
	{
		if((ptr!=pE) && (ptr->runFlags & RUNFLAGS_VISIBLE))
		{	// CPW: this is RIGHT now ...
			if(enemyDistanceBetween(pE->readData.pActor, ptr->readData.pActor)<1000)
			{
				return ptr;
			}
		}
	}

	return(NULL);
}
*/



// Checks angle. Returns range.
int enemyCanSeeCheck(ENEMY *pE, VECTOR *vec, int fov, int range)
{
	int angle;
	int dx,dz;
	int dist;

//	return INT_MAX;
// Range is only used for debug purposes

	if(pE->runFlags & RUNFLAGS_NOTLOOKING)
	{
		return INT_MAX;
	}

	if(enemyEndingLevelFlag)
	{
		return INT_MAX;
	}


//	if(!(loop_ticker & 7))
//	{
//		DB("check (tag %d): looking. flags = &%8x\n",pE->readData.pDef->tag,pE->runFlags);
//	}

	fov = fov>>1;

	if(overlay_show_collision == DEBUG_OVERLAY_ENEMY_INFO)
	{
		VECTOR temp[8];
		int i;
		int a;
		for(i = 0; i < 8; i++)
		{
			temp[i] = pE->pos;
			if(i != 7)
			{
				a = pE->dir + pE->head.actualAngle - fov + (2*fov) * i/6;

				temp[i].vx -= range * rsin(a) /4096;
				temp[i].vz -= range * rcos(a) /4096;
			}
		}
		overlayShowDebugPolygon(&temp[0], 8, 0xff0000, DEBUGPOLY_USEDEPTH | DEBUGPOLY_DUBSIDED | DEBUGPOLY_SUBDIV);
	}


	dx = pE->pos.vx - vec->vx;
	dz = pE->pos.vz - vec->vz;
	angle = utilCalcAngle(dx, dz);
	angle -= (pE->dir + pE->head.actualAngle);
	angle &=4095;

	if(angle < fov || angle > 4096 - fov)
	{
		dist = Magnitude2D(dx,dz);
/*
// Hmmm. This don't work none
		if(dist < range)
		{
			unsigned int rv;
			COLLBOX hitBox;
			rv = findClosestBox(
				vec->vx<<9,vec->vy<<9,vec->vz<<9,
				pE->pos.vx<<9, pE->pos.vy<<9,pE->pos.vz<<9,
				&hitBox,TRUE);
			if(rv != 0x7fffffff)
			{
				DB("collbox intersect\n");
				return INT_MAX;
			}
		}
*/
		return dist;

	}
	return INT_MAX;
}

int enemyCanSeeCheckWellyTest(ENEMY *pE, VECTOR *vec, int fov, int range)
{

// If the welly's running around, the enemy can't see you
// tbd - needs to deal with "which rat's got the bloody gnome on"?
// tbd2 - there need to be some conditional checks for tiptoe & hide, here.

	if(characterVars.pHideChar)
	{
	if(characterVars.pHideChar->pEnemy->runFlags & RUNFLAGS_VISIBLE)
	{
		return INT_MAX;
	}
	}


	return enemyCanSeeCheck(pE,vec,fov,range);
}




void enemyLookAtPos(ENEMY *pE, VECTOR *vec)
{
	int des;
	ASSERT(pE);

	if(!vec)
	{
		des = 0;
	}
	else
	{
		des = utilCalcAngle(pE->pos.vx - vec->vx, pE->pos.vz - vec->vz);
		des -= pE->dir;
		des &=4095;
	}

	if(!pE->readData.pHead)
		return;
	if((des<900) || (des>4096 - 900))
	{
		pE->head.desiredAngle = des;
		pE->head.actualAngle = enemyTendTowards(pE->head.desiredAngle, pE->head.actualAngle, 48);	//128);
		if(pE->readData.pHead)
		{
			pE->readData.pHead->rotate.vy = pE->head.actualAngle;
		}
	}
	else
	{
// Reset to forwards
		pE->head.desiredAngle = 0;
		pE->head.actualAngle = enemyTendTowards(pE->head.desiredAngle, pE->head.actualAngle, 128);
		if(pE->readData.pHead)
		{
			pE->readData.pHead->rotate.vy = pE->head.actualAngle;
		}
	}
}

// Only use on those enemies that have well-defined heads
void enemyLookAtPos3D(ENEMY *pE, VECTOR *vec)
{
	int des;
	int desx;
	int m;
	ASSERT(pE);

	if(!vec)
	{
		des = 0;
		desx = 0;
	}
	else
	{
		m = Magnitude2D(pE->pos.vx - vec->vx, pE->pos.vz - vec->vz);

		des = utilCalcAngle(pE->pos.vx - vec->vx, pE->pos.vz - vec->vz);
		des -= pE->dir;
		des &=4095;

		desx = -utilCalcAngle((pE->pos.vy - 400) - vec->vy,m);	// ginger's head height
		desx &= 4095;
	}

	if(!pE->readData.pHead)
		return;
	if((des<900) || (des>4096 - 900))
	{
		pE->head.desiredAngle = des;
		pE->head.actualAngle = enemyTendTowards(pE->head.desiredAngle, pE->head.actualAngle, 48);	//128);

		if(desx >= 700 && desx < 2048)
			desx = 700;
		if(desx <= 4096 - 700 && desx >= 2048)
			desx = 4096 - 700;

		pE->head.desiredElevation = desx;
		pE->head.actualElevation= enemyTendTowards(pE->head.desiredElevation, pE->head.actualElevation, 48);


		if(pE->readData.pHead)
		{
			pE->readData.pHead->rotate.vy = pE->head.actualAngle;
			pE->readData.pHead->rotate.vx = pE->head.actualElevation;
		}
	}
	else
	{
// Reset to forwards
		pE->head.desiredAngle = 0;
		pE->head.desiredElevation = 0;

		pE->head.actualAngle = enemyTendTowards(pE->head.desiredAngle, pE->head.actualAngle, 128);
		pE->head.actualElevation= enemyTendTowards(pE->head.desiredElevation, pE->head.actualElevation, 128);
		if(pE->readData.pHead)
		{
			pE->readData.pHead->rotate.vy = pE->head.actualAngle;
			pE->readData.pHead->rotate.vx = pE->head.actualElevation;
		}
	}

}




// We need to calculate the angle in order to determine the turning speed...
// (and determine whether to bend or just turn whilst walking)
int enemyAngleCalc(ENEMY *pE, int pointno)
{
	VECTOR *pnext;
	int dot;
	VECTOR d1,d2;
	int ang1,ang2;

	pnext = &pE->readData.pPoints[pointno].v;

	SUBVECTOR(&d2,pnext,&pE->pos);

	ang1 = pE->dir & 4095;
	ang2 = utilCalcAngle(-d2.vx,-d2.vz) & 4095;

	ang2 = findShortestAngle(ang2,ang1);
	return (ang2);
}


int enemyFindAbsAngleTo(ENEMY *pE, VECTOR *vec)
{
	VECTOR d2;
	int ang2;
	SUBVECTOR(&d2,vec,&pE->pos);
	ang2 = utilCalcAngle(-d2.vx,-d2.vz) & 4095;
	return ang2;
}
int enemyFindDeltaAngleTo(ENEMY *pE, VECTOR *vec)
{
	VECTOR d2;
	int ang1,ang2;

	SUBVECTOR(&d2,vec,&pE->pos);
	ang2 = utilCalcAngle(-d2.vx,-d2.vz) & 4095;
	ang1 = pE->dir & 4095;
	ang2 = findShortestAngle(ang2,ang1);
	return ang2;
}

// Check whether an enemy is
// (1) Within a given range of a point
// (2) Has just passed a line (distance) away from the point, perpendicular to the enemy's direction of motion.
// [tbd - if we'd used source points, rather than dirn of motion checks, that latter check wouldn't be needed.
// tsk]
int enemyArrivedCheck(VECTOR *comingfrom, VECTOR *pos, VECTOR *target, int distance)
{
	VECTOR next;
	VECTOR diff;
	VECTOR unitvel;
	int dot;
	int mag;

	SUBVECTOR(&diff,target, pos);
	diff.vy = 0;

	SUBVECTOR(&unitvel,target, comingfrom);
	unitvel.vy = 0;
	MakeUnit(&unitvel);	// 0...4096, remember


	dot = DotProduct(&diff,&unitvel);	// /4096;	//>> 12;

//	utilPrintf("arrivedcheck dot %d\n",dot);

	if(dot < distance)
		return 1;

	return 0;
}





// ============ Enemy -> Chicken interaction ============
// Glover did multiple enemy spheres, colliding with the single glove sphere
// Now here, we can represent pretty much all of the enemies quite nicely with a capsule,
// rotated to the enemy's y angle.
// (Exception: Sitting dog)

// we need a debug overlay capsule plotter.
// (For now, two spheres)

// Acutally. Given that we need the endpoints of bones for the shadow calculations,
// why not use 'em here?
// (answer - because we're not calculating the shadows when the enemy's offscreen)

// One prob - if you touch the head of an angry dog, you should be deaded

int enemyCapsuleInteraction(void *source, int sourcetype, ENEMY *pE, VECTOR *chickencentre, int chickenradius)
{
	VECTOR p1,p2;
	VECTOR outpoint;
	int dist;
	int r;
	int rc,rs;
	int defr = pE->readData.pInfo->capsule_r;
	VECTOR *coll1 = &pE->readData.pInfo->capsule_back;
	VECTOR *coll2 = &pE->readData.pInfo->capsule_front;

	if(sourcetype == COLLIDE_WITH_SPROUT)	// you don't collide with things you're throwing
	{
		if(pE==characterVars.pMainChar->pEnemy)
			return 0;
	}

	r = chickenradius + defr;

	rc = rcos(pE->dir);
	rs = rsin(pE->dir);

	p1.vx = ( coll1->vx * rc + coll1->vz * rs) >> 12;
	p1.vy = coll1->vy;
	p1.vz = (-coll1->vx * rs + coll1->vz * rc) >> 12;

	p2.vx = ( coll2->vx * rc + coll2->vz * rs) >> 12;
	p2.vy =  coll2->vy;
	p2.vz = (-coll2->vx * rs + coll2->vz * rc) >> 12;

	p1.vx += pE->pos.vx;
	p1.vy += pE->pos.vy;
	p1.vz += pE->pos.vz;

	p2.vx += pE->pos.vx;
	p2.vy += pE->pos.vy;
	p2.vz += pE->pos.vz;

	if(overlay_show_collision == DEBUG_OVERLAY_ENEMY_INFO)
	{
		overlayShowSphere(p1.vx,p1.vy,p1.vz, defr, 0x800080);
		overlayShowSphere(p2.vx,p2.vy,p2.vz, defr, 0xff00ff);
	}

// Do a quick bit of knocking out before we dive into the (unitvector,dotproduct,magnitude) of point-to-line code
	if(p1.vx > chickencentre->vx+r && p2.vx > chickencentre->vx+r)
		return 0;
	if(p1.vx < chickencentre->vx-r && p2.vx < chickencentre->vx-r)
		return 0;
	if(p1.vy > chickencentre->vy+r && p2.vy > chickencentre->vy+r)
		return 0;
	if(p1.vy < chickencentre->vy-r && p2.vy < chickencentre->vy-r)
		return 0;
	if(p1.vz > chickencentre->vz+r && p2.vz > chickencentre->vz+r)
		return 0;
	if(p1.vz < chickencentre->vz-r && p2.vz < chickencentre->vz-r)
		return 0;


	dist = mathsDistancePointToLine3D(&p1, &p2, chickencentre, &outpoint);
	if(dist < r)
	{
//		static int temp=0;
//		utilPrintf(" nme hit %d!\n",temp++);

// Outpoint is the point on the line. push to point on exterior.
		chickencentre->vx = chickencentre->vx - outpoint.vx;
		chickencentre->vy = chickencentre->vy - outpoint.vy;
		chickencentre->vz = chickencentre->vz - outpoint.vz;
		MakeUnit(chickencentre);
		chickencentre->vx = ((chickencentre->vx * r)>> 12) + outpoint.vx;
		chickencentre->vy = ((chickencentre->vy * r)>> 12) + outpoint.vy;
		chickencentre->vz = ((chickencentre->vz * r)>> 12) + outpoint.vz;

		if(pE->readData.pInfo->pCollide)
		{
			pE->readData.pInfo->pCollide(pE,source,sourcetype);
		}
		return 1;
	}
	return 0;
}

// Call with a position, before sending that "new" position to the box collision routines
int enemyCheckCollision(void *source, int sourcetype, VECTOR *chickencentre, int radius)
{
	ENEMY *ptr;
	VECTOR pos;
	int hit = 0;

	if(overlay_show_collision == DEBUG_OVERLAY_ENEMY_INFO)
	{
		overlayShowSphere(chickencentre->vx,chickencentre->vy,chickencentre->vz, radius, 0xffff00);
	}

	for(ptr=enemyList.head.next; ptr != &enemyList.head; ptr = ptr->next)
	{
// CPW: 		if(ptr != source && (ptr->runFlags & RUNFLAGS_VISIBLE) && (ptr->readData.pInfo->nme_type !=ENEMY_UNDEFINED && ptr->readData.pInfo->nme_type != ENEMY_CHICKEN))

		if(ptr != source && (ptr->runFlags & RUNFLAGS_VISIBLE) && (ptr->readData.pInfo->nme_type != ENEMY_CHICKEN))
		{
			hit |= enemyCapsuleInteraction(source, sourcetype, ptr, chickencentre, radius);
		}
	}
	return hit;
}

// For calling from the puzzle engine with set_scenic_anim
void enemySetAnim(ENEMY *pE, int anim, int flags)
{
	int loop;
	int queue;
	int blend;
	int speed;


	loop = (flags & SCENIC_LOOP)!=0;
	queue =(flags & SCENIC_QUEUE) != 0;
	blend = flags & 255;

	if(flags & SCENIC_REVERSE)
	{
		speed = -(1<<ANIMSHIFT);
	}
	else
	{
		speed = (1<<ANIMSHIFT);
	}

	actorAnimate(pE->readData.pActor,
		anim,
		loop,
		queue,
		speed,
		0,
		blend);

}


void enemyFindInteresting(VECTOR *character_pos, int character_dir, VECTOR **best, int *max_range)
{
	ENEMY *pE;
	ENEMY *nearest_interesting;
	static VECTOR temp;	// Note static, coz a pointer to it's passed back
	int m;
	int a;

	nearest_interesting = NULL;

	for(pE = enemyList.head.next; pE != &enemyList.head; pE=pE->next)
	{
		if(pE->runFlags & RUNFLAGS_VISIBLE && pE != characterVars.pMainChar->pEnemy)
		{
			VECTOR t;
			int m;

			t.vx = character_pos->vx - pE->readData.pActor->position.vx;
			t.vy = character_pos->vy - pE->readData.pActor->position.vy;
			t.vz = character_pos->vz - pE->readData.pActor->position.vz;
			m = Magnitude(&t);
			if (m < *max_range)
			{
// Check it's in front of us
				a = utilCalcAngle(
					character_pos->vx - pE->readData.pActor->position.vx,
					character_pos->vz - pE->readData.pActor->position.vz);
				a -= character_dir;
				a &=4095;
				if((a<1024) || (a>4096 - 1024))
				{
//					DB("near = %d\n",pE->readData.pInfo->nme_type);
					nearest_interesting = pE;
					*max_range = m;
					temp = nearest_interesting->readData.pActor->position;
					temp.vy -= 50 * 8;	// Actually, this works out okay for tweedies and dogs
					*best = &temp;
				}
			}
		}
	}
}


// ==================== The Minimalist, and unused, wandering chicken ========================

int enemyUpdateChicken(ENEMY *pE)
{
	ENEMY *ptr;

	ASSERT(pE);

	switch(pE->state)
	{
		case STATE_WANDERING:
		// CPW: Just wander around doing chickeny things
		if(pE->stateCtr==0)
		{
			DB("Wandering\n");
			actorAnimate(pE->readData.pActor, GINGER_WALK, 1, 0, pE->animSpeed, 0, 0);
		}


// CPW: 		if(pE->collision.nHits>1)
// CPW: 			enemySteer(pE, 24);

		// CPW: follow next chicken in the list

		for(ptr=pE->next; ; ptr = ptr->next)
		{
			if(ptr->readData.pInfo->sName==pE->readData.pInfo->sName)
			{
				enemyFollow(pE, ptr->readData.pActor, 24);
				break;
			}
		}

		enemyMove(pE, 32, ENEMYMOVE_SET, 0);


/*		switch(enemyFacingAnother(pE))
		{
		case ENEMYFACING_NO:
			// CPW: make the chicken walk forwards in the direction it is facing
			break;

		case ENEMYFACING_YES:
		case ENEMYFACING_MUTUALLY:
			enemySteer(pE, 24);
		}
*/

		break;
	}

	return 0;
}


ENEMY *enemyFind(int tag)
{
	ENEMY *ptr;

	for(ptr=enemyList.head.next; ptr != &enemyList.head; ptr = ptr->next)
	{
		if(ptr->readData.pDef->tag == tag)
			return ptr;
	}
	return NULL;
}

#define HOUR_DEPTH (PANEL_OVERLAYS_SORTDEPTH)


int HOUR_SAND_DX = 38;
int HOUR_SAND_DY = 31;

// pos goes from 0 to 20
void nmeDrawHourglass(int x, int y, int pos, int sand)
{
// Solid Backdrop spr
// Top sand poly portion (2 frame anim) - tricky one.
// Bottom sand poly portion (2 frame anim) - variable height plotted. Rising cone over cylinder - base masked by overlay
// Semitrans glass overlay
// +ittive glass overlay?
// Solid front spr

	static int count=0;

	count++;
	count = count%3;


	ASSERT(hourglass_texes[0]);	// If we need an hourglass, then it had better have been in the level's textures

	overlaySprite(hourglass_texes[4],x,y,HOUR_DEPTH,0xff808080);

	overlayTranspSprite(hourglass_texes[1], x,y,HOUR_DEPTH,0x808080 | OV_TRANSSPR_SEMI);

	hourglassBotSprite(hourglass_texes[3], x, y+(22-pos), y + 70-10);	//15);
	hourglassTopSprite(hourglass_texes[2], x,y,pos + 14 + 2);

	overlaySprite(hourglass_texes[0],x,y,HOUR_DEPTH,0xff808080);
	
	if(sand)
		overlayTranspSprite(hourglass_texes[5+count], x+HOUR_SAND_DX, y+HOUR_SAND_DY, HOUR_DEPTH-1, 0x808080 | OV_TRANSSPR_SEMI);

}




// Puzzle-controlled Ginger for the end-of-level stuff
// Needs to be anim-controlled, and face & move in a given direction
// (via the puzzle engine?)
int enemyUpdateRemoteEnemy(ENEMY *pE)
{
	return 0;
}

void *nme_oldEHandler = NULL;
void *nme_oldCHandler = NULL;


int characterRemoteHandler(CHARACTER *pC, MOVEDATA *pMove)
{
	ENEMY *pE;
	ENEMYINFO *pEnemyInfo;

	pE = pC->pEnemy;
	pEnemyInfo = &pC->enemyInfo;

	pE->vel.vx = 0;
	pE->vel.vy = 0;
	pE->vel.vz = 0;

	pE->dir = enemyTendTowards(pE->desiredDir, pE->dir, 256);	// fast turn
	if(VectorMoveTo(&pE->pos, &pE->route_target, pE->moveSpeed))
	{

// Check the flags, and restore control if rqd
		if(pE->runFlags & RUNFLAGS_RESTORECTRL)
		{
			enemyControllingLeadFlag = 0;
			pC->pHandler = nme_oldCHandler;
			pEnemyInfo->pHandler = nme_oldEHandler;
			pE->runFlags &= ~RUNFLAGS_RESTORECTRL;
			pE->runFlags &= ~RUNFLAGS_NOGRAVITY;
		}
		else
		{
		}
	}
	return 0;
}

void enemySendLeadTo(POSMARK *target, int anim, int speed, int flags)
{
	CHARACTER *pC;
	ENEMY *pE;
	ENEMYINFO *pEnemyInfo;

	pC = characterVars.pMainChar;
	pE = pC->pEnemy;

	pE->route_target.vx = target->x;
	pE->route_target.vy = target->y;
	pE->route_target.vz = target->z;

	pE->moveSpeed = speed;

	pE->desiredDir = enemyFindAbsAngleTo(pE, &pE->route_target);

	actorAnimate(pE->readData.pActor, anim, 1, 0 , 256, 0, 2048);

	if(flags & LEADACTIONFLAG_RESTORE)
	{
		pE->runFlags |= RUNFLAGS_RESTORECTRL;
	}
	pE->runFlags |= RUNFLAGS_NOGRAVITY;

	pC = characterVars.pMainChar;
	pEnemyInfo = &pC->enemyInfo;

//characterGingerAndRockyHandler

	nme_oldEHandler = pEnemyInfo->pHandler;
	nme_oldCHandler = pC->pHandler;
	pC->pHandler = characterRemoteHandler;
	pEnemyInfo->pHandler = enemyUpdateRemoteEnemy;

	pC->pEnemy->vel.vx = 0;
	pC->pEnemy->vel.vz = 0;
	enemyControllingLeadFlag = 1;

}

void enemyInformEndingLevel()
{
	enemyEndingLevelFlag = 1;	// Render the chicken invincible
}

int enemyPlayEnvironmentFootstep(VECTOR *pPos, int volume)
{
	SfxSampleType *pFootStep;
	int flags;

	pFootStep = platformCheckForSampleTriggers(pPos, &flags);

	if(pFootStep)
	{
//		DB("Playing footstep sample! (%p)\n", pFootStep);
		soundPlaySamplePanFade(pFootStep, volume, 0, pPos);

		if(flags & TRIGGER_SAMPLE_DOGCALL )
		{
//			nmeDogStartHourglass(1);
			hourglass2Inform(HOURGLASS_MAKING_LOUD_NOISE, pPos);
		}

		return 1;
	}

	return 0;
}

int enemyPlayEnvironmentFootstepNoDogCheck(VECTOR *pPos, int volume)
{
	SfxSampleType *pFootStep;

	pFootStep = platformCheckForSampleTriggers(pPos, NULL);

	if(pFootStep)
	{
//		DB("Playing footstep sample! (%p)\n", pFootStep);
		soundPlaySamplePanFade(pFootStep, volume, 0, pPos);
		return 1;
	}

	return 0;
}


void nmeDogEnsureHourglass()
{
//	char *hg_names[5] = {"HG_BACK","HG_GLASS","HG_TOP1","HG_BOT1","HG_FRONT"};
	static char *hg_names[8] = {"WOODBOTTOMX16","TIMERGLASSX16","SANDTOPX16","SANDBOTTOMX16","WOODTOPX16","SAND01","SAND02","SAND03"};
	int i;

	for(i = 0; i < 8; i++)
	{
		hourglass_texes[i] = textureFindInAllBanks(hg_names[i]);
	}

	ASSERT(sizeof(hourglass_texes) == i * sizeof(void *));	// Tsk. Took a while to find that one.

	hourglass_time = 0;
	hourglass_dog_persuit = 0;
}


/*


#define HGLASS_TIME	(25 * 10 * 4096)
//#define HGLASS_DARK_SPEED	(4096)
//#define HGLASS_LITE_SPEED	(8192)

#define HGLASS_DARK_SPEED	(4096 * 2)
#define HGLASS_LITE_SPEED	(8192 * 2)
#define HGLASS_INSTANT_SPEED (20000 * 2)
// state = 1 for a normal "you've alerted the dogs" type thing,
// state = 2 for a more immediate trigger

void nmeDogStartHourglass(int state)
{
// If it's already on, leave it on.
// Similarly, if the persuit dog's in persuit, we need to leave it on, too (or, indeed, leave it off it was off)

	if(!hourglass_texes[0])	// If there's not an hourglass, don't alert the dogs
		return;

	if(state > hourglass_state)
		hourglass_state = state;
	if(hourglass_time || hourglass_dog_persuit)
	{
		hourglass_thistick = HGLASS_LITE_SPEED;
		return;
	}
	hourglass_time = HGLASS_TIME;
	hourglass_thistick = HGLASS_LITE_SPEED;
}

void nmeDogCycleHourglass()
{
	if(hourglass_dog_persuit)	// alert, persuing, and growling dogs set it to 2
		hourglass_dog_persuit--;
	hourglass_game_is_alert = 0;

	if(!hourglass_time)
	{
		return;
	}

	if(hourglass_state > 1)
		hourglass_thistick = HGLASS_INSTANT_SPEED;
	hourglass_time -= hourglass_thistick;
	if(hourglass_time <= 0)
	{
		hourglass_time = 0;
		hourglass_state = 0;
		// Bring on the doggie!
		enemyDogFindAndAlert(&characterVars.pMainChar->pEnemy->pos);

	}

	hourglass_thistick = HGLASS_DARK_SPEED;	// allow it to be overwritten by any lights

}

void nmeDogDrawHourglass()
{
	int	pos;

	if(!hourglass_time)
	{
		return;
	}
	pos = (HGLASS_TIME - hourglass_time) * (22-3) / HGLASS_TIME;
	nmeDrawHourglass(HGLASS_X, HGLASS_Y, pos, 1);

}
*/

// =====================================================================
//                            Hourglass Mk II
// =====================================================================

// Specifically, if you go into a light, and there's a dog heading where you were, this brings it uptodate
void enemiesInformAboutPosition(VECTOR *position)
{
	ENEMY *pE;
// Scan for any alert patrol dogs, and keep 'em uptodate as to where the chicken is
	for(pE=enemyList.head.next; pE != &enemyList.head; pE = pE->next)
	{
		if((   pE->readData.pInfo->nme_type == ENEMY_DOG
			)
			&& pE->state == STATE_PERSUIT)
		{
			PATROLDOGSTR *pat;

			pat = (void *)pE->extras;

			pat->alertpoint = *position;
		}
		else if((
			pE->readData.pInfo->nme_type == ENEMY_MRT_TORCH
			|| pE->readData.pInfo->nme_type == ENEMY_MRT_PATROL

			)
			&& pE->state == STATE_PERSUIT)
		{
			MRT_TORCH_STR *ext;
			ext = pE->extras;
			ext->alertpoint = *position;
		}
// If it's abandoning, or whatever, but the game's in alert state, set it into persuit mode?
	}
}


#define HGLASS2_MAX_TIME		(25 * 10 * 4096)
#define HOURGLASS_LIGHT_TIME	(4096 * 2)
#define HOURGLASS_SEARCH_TIME	(8192 * 2)	
#define HOURGLASS_LOUD_TIME		(8192 * 2)	
#define HOURGLASS_QUIET_TIME	(4096)	
#define HOURGLASS_MRT_FOV_TIME		(8192 * 2)
#define HOURGLASS_ALERT_TIME		(8192 * 2)	// If the level's alert, then the hourglass doesn't ever stand still half-full

#define HOURGLASS_DECAY_TIME	(2048)

#define HOURGLASS_FADEIN_SPEED		16
#define HOURGLASS_FADEOVER_SPEED	16
#define HOURGLASS_FADEOUT_SPEED		8


void hourglass2Inform(int event, VECTOR *pos)
{
	int amount;

	amount = 0;
	switch(event)
	{
		case HOURGLASS_NOWT:
			break;
		case HOURGLASS_IN_LIGHT:
			amount = HOURGLASS_LIGHT_TIME;
			break;
		case HOURGLASS_IN_SEARCHLIGHT:
			amount = HOURGLASS_SEARCH_TIME;
			break;
		case HOURGLASS_MAKING_LOUD_NOISE:
			amount = HOURGLASS_LOUD_TIME;
#ifndef DOG_INSTANT_PERSUIT
			hourglass2_repeat_this_amount = amount;
			hourglass2_repeat_this_time = 10;
#endif
			break;
		case HOURGLASS_MAKING_QUIET_NOISE:
		case HOURGLASS_DOG_IN_THINKING_MODE:
#ifndef DOG_INSTANT_PERSUIT	// If we HAVE got instant persuit, we don't want it happening when the dog's just thinking
			amount = HOURGLASS_QUIET_TIME;	
#endif
			break;

		case HOURGLASS_IN_MRT_FOV:
			amount = HOURGLASS_MRT_FOV_TIME;
			break;

		case HOURGLASS_IN_DOG_FOV:
		case HOURGLASS_DOG_IN_PERSUIT_MODE:
		case HOURGLASS_DOG_IN_GROWL_MODE:
		case HOURGLASS_MRT_IN_PERSUIT_MODE:
			hourglass2_in_alert_state = 2;
			break;
	}
	if(amount > hourglass2_max_this_frame)
	{
		hourglass2_max_this_frame = amount;
		hourglass2_reason = event;
	}

	if(pos)
	{
//		enemiesInformAboutPosition(pos);
		hourglass2_pos = *pos;
	}
}

void hourglass2Draw()
{
	int	pos;

	if(hourglass2_fadein == 0 || hourglass2_fadein == 512)
		return;

//	pos = (HGLASS2_MAX_TIME - hourglass2_sand) * (22-3) / HGLASS2_MAX_TIME;
	pos = (hourglass2_sand) * (22-3) / HGLASS2_MAX_TIME;

	if(hourglass2_fadein == 256)
	{
		nmeDrawHourglass(HGLASS_X, HGLASS_Y, pos, 1);
		return;
	}

	if(hourglass2_fadein < 256)
	{
		nmeDrawHourglass(HGLASS_X, HGLASS_Y - 64 + hourglass2_fadein/4, pos, 1);
		return;
	}
	
	if(hourglass2_fadein > 256)
	{
		nmeDrawHourglass(HGLASS_X, HGLASS_Y - (hourglass2_fadein-256)/4, pos, 1);
		return;
	}

}

void hourglass2Cycle()
{

#ifndef DOG_INSTANT_PERSUIT
	if(hourglass2_repeat_this_time)
	{
		if(hourglass2_fadein >= 256)
		{
			hourglass2_repeat_this_time--;
		}
		if(hourglass2_repeat_this_amount > hourglass2_max_this_frame)
		{
			hourglass2_max_this_frame = hourglass2_repeat_this_amount;
		}
	}
// This is for keeping the display red & stopping the hourglass from going down
// when the dog's growling
	if(hourglass2_max_this_frame == 0 && hourglass_game_is_alert)
	{
		hourglass2_max_this_frame = HOURGLASS_ALERT_TIME;
		hourglass2_reason = HOURGLASS_OLD_ALERT;
	}
#endif


	if(hourglass2_max_this_frame > 0)
	{
#ifdef DOG_INSTANT_PERSUIT
//		enemyDogFindAndAlert(&characterVars.pMainChar->pEnemy->pos);
		enemyDogFindAndAlert(&hourglass2_pos);
		{
			char temp[128];
			sprintf(temp,"HG reason %d\n",hourglass2_reason);
			overlayDebugMessage(temp,2);
		}
#else
		if(hourglass2_fadein == 256)
		{
			// If it's faded all the way in
			//	up the timer by the biggest of 'em
			hourglass2_sand += hourglass2_max_this_frame;

			if(hourglass2_sand >= HGLASS2_MAX_TIME)
			{
				// Hourglass has hit top...
				// Set the game to red alert
				// Start doing something with the hourglass to grab the attention
				hourglass2_sand = HGLASS2_MAX_TIME;
				enemyDogFindAndAlert(&characterVars.pMainChar->pEnemy->pos);
				hourglass2_fadein = 257;
			}
		}
		else if(hourglass2_fadein < 256)
		{
			// fade hourglass in without upping the sand count
			hourglass2_fadein += HOURGLASS_FADEIN_SPEED;
			if(hourglass2_fadein >= 256)
			{
				hourglass2_fadein = 256;
			}
		}
#endif
	}
#ifndef DOG_INSTANT_PERSUIT
	else
	{
		if(hourglass2_fadein <= 256)
		{
//			fade hourglass out without changing the sand count
			hourglass2_fadein -= HOURGLASS_FADEOUT_SPEED;
			if(hourglass2_fadein < 0)
			{
				hourglass2_fadein = 0;
			}
		}

		if(hourglass2_fadein >= 512)
		{
// If there's no alert, set fade = 0, so we start to relax again
			if(!hourglass2_in_alert_state)
			{
				hourglass2_fadein = 0;
			}
		}

	}


	if(hourglass2_fadein > 256)
	{
		// Expand & transparent the hourglass, or do somthing else to grab some attention
		hourglass2_fadein += HOURGLASS_FADEOVER_SPEED;
		if(hourglass2_fadein > 512)
			hourglass2_fadein = 512;
	}
	else if(hourglass2_fadein > 0)
	{
//		If the hourglass is on screen (or maxed and off)

	}
	else 
	{
		// = 0
		// otherwise relax the amount of sand in the hourglass whilst it's offscreen
		hourglass2_sand -= HOURGLASS_DECAY_TIME;
		if(hourglass2_sand < 0)
		{
			hourglass2_sand = 0;
		}

//		{
//			char temp[80];
//			sprintf(temp,"sand %d\n",hourglass2_sand);
//			overlayDebugMessage(temp,20);
//		}
	}
#endif
	hourglass2_max_this_frame = 0;

	// 2,1,0 counter
	if(hourglass2_in_alert_state)
	{
		hourglass2_in_alert_state--;
	}

	// Old version.
	hourglass_game_is_alert = 0;

}


// Still called from Charactr when you're on a noisy platform
void nmeDogStartHourglass(int test)
{
	hourglass2Inform(HOURGLASS_MAKING_LOUD_NOISE, &characterVars.pMainChar->pEnemy->pos);
}

