/*
	Dog.c
	Formerly part of enemies.c
   Started by Chris Wilson 2/2000
*/



// SFX notes for dogs:-
//	Footsteps
//	Skidding round corner
//	Bark, BarkBark... (Chasing chicken. Possibly a couple of different ones)
//	Grrrrrr	(Growling at chicken. Again, a couple of different ones)
//	Whimper	(a) - when the chicken wakes 'em up by running into 'em. b) if they ram a wall, or something)
//	Sniff (Wondering if the chicken's there. "Huh? What's that noise?")
//	Snore (When it's asleep, obviously)
//	Yawn. The sleeping dog yawns. What noise it makes I don't know.
//	Scratch (For its scratching idle)

// Dual-Shock notes for dogs:-
// at...
//	void enemyDogStartPersuit(ENEMY *pE)		...the patrolling dog decides to chase the chicken
//	void enemyWakeUpSleeping(ENEMY *pE)			...the sleepy dog is alerted
// and a couple of points where I've temporarily got
//			DB("Chicken dies horribly!\n");	...you're stood right in front of their slavering maws





// SFX notes for Mr T
//	Footsteps (surprise!)

// SFX notes for Mrs T
//	Footsteps (surprise!)

// blitzgames
// lb's mega-zit?


#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 "sprouts.h"
#include "loadsnd.h"
#include "sound.h"
#include "dualshock.h"
#include "charactr.h"



//extern CHARACTERVARSTR characterVars;
void dogBeginTrekToNextPoint(ENEMY *pE);

#ifndef GINGER_WALKSPEED
 #define GINGER_WALKSPEED	(1500)
#endif
#ifndef GINGER_RUNSPEED
 #define GINGER_RUNSPEED		(2500)
#endif

#define DOG_BUZZ()	 {dsEvent_Add(1, 1, 1, 200, 0, 20, FALSE);}



// Note that the dog needs a sufficiently big sphere to roll over low-lying objects.


// This is the fast, skidding at corners, points navigator
// Actually, this won't be using the *points* points, but the *route-finding* points.
// (Not Used Yet, dude...)

void enemyDoPointsRun(ENEMY *pE)
{
	if(!(pE->turning) && enemyArrivedCheck(&pE->points.pointComingFrom, &pE->pos, &pE->points.pointGoingTo, 1100))
	{
		int d;
//		DB("Arrived pt %d\n",pE->points.pointTo);

		pE->points.pointFrom = pE->points.pointTo;
		pE->points.pointTo++;

		if(pE->points.pointTo >= pE->readData.pDef->nPoints)
			pE->points.pointTo = 0;

		pE->points.pointComingFrom = pE->pos;
		pE->points.pointGoingTo = pE->readData.pPoints[pE->points.pointTo].v;
		// CPW: DB("Got to point %d. Looking for %d\n", pE->points.pointFrom, pE->points.pointTo);

		d = enemySteerTowards(pE, &pE->points.pointGoingTo, 32);

//		pE->turning = 38;	// number of frames the turn anim takes
		pE->turning = 20;	// number of frames the turn anim takes

		if(d > 0)
		{
			int rv;
			rv = actorAnimate(pE->readData.pActor, DOG_SKIDRIGHT, 0, 0, 256, 0, 512);
			soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGSKIDS], 255, 0, (VECTOR *)&(pE->pos));

//			if(rv == ANIMATE_OK)
//				DB("Setting R\n");
//			else
//				DB("Setting R Fail %d\n",rv);

		}
		else
		{
			actorAnimate(pE->readData.pActor, DOG_SKIDLEFT, 0, 0, 256, 0, 512);
			soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGSKIDS], 255, 0, (VECTOR *)&(pE->pos));

//			DB("Setting L\n");
		}

	}
	else
	{
		if(pE->turning)
		{
			enemySteerTowards(pE, &pE->points.pointGoingTo, 60);
			pE->turning--;
		}
		else
		{
			enemySteerTowards(pE, &pE->points.pointGoingTo, 32);	// do the steering on a permanant basis
		}
	}
	enemyMove(pE, DOG_RUN_FORWARD_SPEED, ENEMYMOVE_SET, 0);	// Updates (pE->pos.vx and vz). enemy, desiredspeed, action (set,tendto), accel
}



void dogInitRoute(ENEMY *pE, VECTOR *target, int resetfrom)
{
//	DB("DogInitRoute\n");
	pE->route_len = routeFindRoute(&pE->pos, target , &pE->route[0], DOG_ROUTE_MAX);


	{
		int i;
// Make sure that the route itself is kept outside the routeboxes (!)
// as a "stop the dog from trying to run through things" measure
		for(i = 0; i < pE->route_len; i++)
		{
			routeKeepOutOfRouteboxes((int *)&pE->route[0].vx, (int *)&pE->route[0].vz, DOG_ROUTEFIND_MARGIN - 14);
		}
	}


	pE->route_target = *target;

	if(!pE->route_len)
	{
		DB("zero length route, reset to wandering\n");
		SETSTATE(STATE_WANDERING);
	}

	if(pE->route[pE->route_len-1].vx == target->vx
		&& pE->route[pE->route_len-1].vz == target->vz)
	{
		pE->route_truncated = 0;
	}
	else
	{
//		DB("truncated route\n");
		pE->route_truncated = 1;
	}

	if(resetfrom)
		pE->points.pointComingFrom = pE->pos;
	pE->points.pointGoingTo.vx = pE->route[0].vx;
	pE->points.pointGoingTo.vy = target->vy;	//pE->pos.vy;
	pE->points.pointGoingTo.vz = pE->route[0].vz;
}

// Starting from wherever the dog happens to be, go to the given patrol point
void enemyDogStartPatrolling(ENEMY *pE, int first_point)
{
	DB("Start Patrolling\n");
	if(pE->readData.pDef->nPoints < 2)
	{
// Persuit dog. The "Patrolling" state is actually "disabled".
		pE->runFlags &= ~RUNFLAGS_VISIBLE;
		SETSTATE(STATE_TURNEDOFF);
	}
	else
	{
// Make-believe we just arrived at the previous point,
// then start the trek to the next one
		pE->points.pointTo = (first_point + pE->readData.pDef->nPoints - 1) % pE->readData.pDef->nPoints;
		dogBeginTrekToNextPoint(pE);
	}
}

// Call on ARRIVAL at the corner, before waiting
void enemyBeginTrekToNextPoint(ENEMY *pE)
{
	int d;
	int a;
//		DB("Arrived pt %d\n",pE->points.pointTo);

	pE->points.pointFrom = pE->points.pointTo;
	pE->points.pointTo++;
	if(pE->points.pointTo >= pE->readData.pDef->nPoints)
		pE->points.pointTo = 0;
	pE->points.pointComingFrom = pE->pos;
	pE->points.pointGoingTo = pE->readData.pPoints[pE->points.pointTo].v;
}


void dogBeginTrekToNextPoint(ENEMY *pE)
{
	int d;
	int a;

	PATROLDOGSTR *pat;

	pat = (void *)pE->extras;

//	enemyBeginTrekToNextPoint(pE);	// done on ARRIVAL at the corner

	pat->corner_angle = enemyAngleCalc(pE,pE->points.pointTo);
//	DB("path angle %d\n",pat->corner_angle);

// Do bendy dog code based on the angle to target...
	pat->corner_time = pE->turning = 26;	// number of frames the turn anim takes
	pE->points.turnspeed_thiscorner = (ABS(pat->corner_angle)) * 60 / 1024;

	if(pE->state != STATE_WANDERING)
	{
		DB("reverting to wandering\n");
		actorAnimate(pE->readData.pActor, DOG_WALK, 1, 0, 256, 0, 512);	// yes loop, no queue. Blend in.
		SETSTATE(STATE_WANDERING);
	}
//		DB("angle at corner = %d. Turnspeed = %d\n",a,pE->points.turnspeed_thiscorner);
}





// ==================== The Sleepy Dog-Updating Function ========================

// The dog which is sleeping in the kennel, guarding his precious tennis racket.
// Also, the dog indoors that the rats need to sneak past.
// When alerted by:
//		Far-off loud Noises		** TBD **
//		Other Noises			** TBD **
//		Being walked into
//		Trying to pick up the tennis racket without using the bone		** TBD ** (& make into a timing puzzle, too?)
//	Wakes up. Barks. Brings on an angry dog from off-level.		** TBD **

// When the bone<->racket swap's done
//		Does a messing about with its arm animation.

// The other thing to note is that this dog does NOT need to do mesh collision... Coz it never moves.

void enemyWakeUpSleeping(ENEMY *pE)
{
	SETSTATE(STATE_GETTINGUP);
	actorAnimate(pE->readData.pActor, DOG_FASTGETUP, 0, 1, ENEMY_DEFAULTANIMSPEED, 0, 0);
	actorAnimate(pE->readData.pActor, DOG_STATICATTACK, 1, 1, ENEMY_DEFAULTANIMSPEED, 0, 0);

	DOG_BUZZ();


}
void enemyWakeUpWatching(ENEMY *pE)
{
	SETSTATE(STATE_GROWLING);
	actorAnimate(pE->readData.pActor, DOG_STATICATTACK, 1, 0, ENEMY_DEFAULTANIMSPEED, 0, 512);
	DOG_BUZZ();
}

// Freq = 16 for growl/bark, or 32 during persuit. Ish
void dogRandomBark(ENEMY *pE, int freq)
{
	if(!utilRandomInt(freq))
	{
// the first "dogbark" doesn't actually exist
		soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGBARK1+utilRandomInt(2)], 255, 0, (VECTOR *)&(pE->pos));
	}
}
void dogRandomSniff(ENEMY *pE, int freq)
{
	if(!utilRandomInt(freq))
	{
		soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGSNIFFS1+utilRandomInt(1)], 255, 0, (VECTOR *)&(pE->pos));
	}
}


void enemySleepyCollide(ENEMY *pE,void *pWith, int withtype)
{
// This is the same regardless of "with" is a chicken or a sprout
	if(pE->state == STATE_SLEEPING)
	{
		enemyWakeUpSleeping(pE);
	}
	else if(pE->state == STATE_WAITING)
	{
		enemyWakeUpWatching(pE);
	}

// If you're colliding with the head end, and it's barking, then you are one dead chicken
}

int enemyUpdateSleeping(ENEMY *pE)
{
	switch(pE->state)
	{
	case STATE_NULL:
		if(levinfo->level_number == LEVNUMBER_A4_LOUNGE)
		{
			actorAnimate(pE->readData.pActor, DOG_STATICBREATHING, 1, 0, ENEMY_DEFAULTANIMSPEED, 0, 0);
			SETSTATE(STATE_WAITING);
		}
		else
		{
			actorAnimate(pE->readData.pActor, DOG_FOOTONBONE, 1, 0, ENEMY_DEFAULTANIMSPEED, 0, 0);
			SETSTATE(STATE_SLEEPING);
		}
		break;

	case STATE_WAITING:		// Normal state for the A4_LOUNGE dog

// tbd - Check to see if a non-gnomed rat is visible... If so, go to bark mode...


		if(pE->stateCtr > 200 && utilRandomInt(200) > 20)
		{
			pE->stateCtr = 1;
			actorAnimate(pE->readData.pActor, DOG_EARSCRATCH, 0, 1, pE->animSpeed, 0, 0);
			soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGSCRATCH2], 255, 0, (VECTOR *)&(pE->pos));
			actorAnimate(pE->readData.pActor, DOG_STATICBREATHING, 1, 1, ENEMY_DEFAULTANIMSPEED, 0, 0);
		}

// For this dog, the range is irrelevant, but he shouldn't be able to see you on the windowsill
// (ie, upwards)
// Experiment shows that the rats can JUMP to 650 over dog height, and the windowsill's at 1152 over him.
//		DB("DY = %d\n",pE->pos.vy - characterVars.pMainChar->pEnemy->pos.vy);
		if(pE->pos.vy - 1000 < characterVars.pMainChar->pEnemy->pos.vy)
		{
			int seeable;

			seeable = 1;
//			if(characterVars.pMainChar->pVTable != hidingVTable)	// the table's static

			if(currentInv->selected_item == GNOME)
			{
				seeable = 0;
			}

// tbd - Check for equipped item = gnome (or check for character handler = gnome: The shared inventory may cause problems here)
			if(seeable)
			{
				if(enemyCanSeeCheck(pE, &characterVars.pMainChar->pEnemy->pos, WATCHING_DOG_SIGHT_FOV,4000) < 4000)
				{
					enemyWakeUpWatching(pE);
				}
			}

			pE->view_fov_current = WATCHING_DOG_SIGHT_FOV;
			pE->view_range_current = 4000;
		}
		break;



	case STATE_SLEEPING:		// Normal state for the A2_GARDEN_D dog
		pE->blinkOverride = 1;	// Keep your eyes shut, little doggie

		if(!(loop_ticker2 & 15))
		{
			// check if it's just reverting to sleep, or if it's checking for its racket/bone
			if(pE->readData.pActor->animation.animData->segment == DOG_FOOTONBONE)
			{
				// Note - volume set to 128, because the snore is actually louder than the barking (!)
				soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGSNORES1], 64, 0, (VECTOR *)&(pE->pos));
			}
		}

		break;

	case STATE_GETTINGUP:
		pE->blinkOverride = 2;	// Keep looking real mad, little doggie
		hourglass_game_is_alert = 1;
		hourglass2Inform(HOURGLASS_DOG_IN_PERSUIT_MODE,NULL);

		dogRandomBark(pE,16);

		if(pE->stateCtr == 0)
		{
			pE->view_range_rqd = SLEEPY_DOG_SIGHT_RANGE;
			pE->view_fov_rqd = SLEEPY_DOG_SIGHT_FOV;
		}


		// Time delay to allow the animations to run through.
		// (Actually, we could wait until the right anim's playing)
		if(pE->stateCtr >= 100)
		{
// Okay, we go "bark bark bark", and another dog runs in through the gate...
			enemyDogFindAndAlert(&characterVars.pMainChar->pEnemy->pos);
			SETSTATE(STATE_GROWLING);
		}
		break;

	case STATE_GROWLING:
		pE->blinkOverride = 2;	// Keep looking real mad, little doggie
		hourglass_game_is_alert = 1;
		hourglass2Inform(HOURGLASS_DOG_IN_GROWL_MODE,NULL);

		dogRandomBark(pE,16);

		if(levinfo->level_number == LEVNUMBER_A4_LOUNGE)
		{
			if(pE->stateCtr == 0 && !enemyEndingLevelFlag)
			{
				deathKill(characterVars.pMainChar, DEATH_REASON_GUARDDOG_SEEN, pE);
			}
			if(pE->stateCtr >= 200)	// Technically speaking this won't happen, coz you should be dead & re-spotted
			{
				actorAnimate(pE->readData.pActor, DOG_STATICBREATHING, 1, 0, ENEMY_DEFAULTANIMSPEED, 0, 0);
				SETSTATE(STATE_WAITING);
			}
		}
		else
		{
			// We've savaged the chicken. We only do this when we're growling...
			if(enemyCanSeeCheck(pE, &characterVars.pMainChar->pEnemy->pos, PATROL_DOG_KILLER_FOV,PATROL_DOG_KILLER_RANGE) < PATROL_DOG_KILLER_RANGE)
			{
				if(!enemyEndingLevelFlag)
				{
					deathKill(characterVars.pMainChar, DEATH_REASON_SLEEPY_DOG_MAUL, pE);
					DB("Chicken dies horribly by growling!\n");
				}
			}

			// If it's been growling for a while, and the chicken's managed to hide, go back to sleep.



			if(pE->stateCtr >= 100 && enemyCanSeeCheckWellyTest(pE, &characterVars.pMainChar->pEnemy->pos, SLEEPY_DOG_SIGHT_FOV,SLEEPY_DOG_SIGHT_RANGE) > SLEEPY_DOG_SIGHT_RANGE)
			{
				pE->view_range_rqd = 0;
				pE->view_fov_rqd = 0;

				actorAnimate(pE->readData.pActor, DOG_FASTGETUP, 0, 1, -ENEMY_DEFAULTANIMSPEED, 0, 0);	// Backwards

				actorAnimate(pE->readData.pActor, DOG_PUTFOOTONBONE, 0, 1, ENEMY_DEFAULTANIMSPEED, 0, 0);
				actorAnimate(pE->readData.pActor, DOG_FOOTONBONE, 1, 1, ENEMY_DEFAULTANIMSPEED, 0, 0);

				SETSTATE(STATE_SLEEPING);
			}
		}
		break;
	}

	if(pE->view_range_rqd != pE->view_range_current)
		pE->view_range_current += (pE->view_range_rqd - pE->view_range_current) / 16;

	if(pE->view_fov_rqd != pE->view_fov_current)
		pE->view_fov_current += (pE->view_fov_rqd - pE->view_fov_current) / 16;

// Test Code... (yes, the dynamic FOV stuff works okay)
//	enemyCanSeeCheckWellyTest(pE, &characterVars.pMainChar->pEnemy->pos, pE->view_fov_current,pE->view_range_current);

	return 0;
}

// ==================== The Big Nasty Patrol'n'Chase Dog-Updating Function ========================

// The standard patrolling dog.
// Patrols around a list of points.
// Pauses and looks at things.

// When alerted by:-
//		Far-off loud Noises		** TBD **
//		Other Noises (chicken not tip-toing)			** TBD **
//		Being walked into
//		Seeing the chicken
//	Chases to...
//		If visible, the chicken
//		Or, the location of the noise	** TBD **

//	On arrival.
//		Mauls the chicken to death. Level ends. Dog gets respotted.				** TBD **
//		Barks at the unreachable chicken. Times out. Returns to route.			** TBD **
//		Sniffs, and looks around for the chicken. Times out. Returns to route.	** TBD **

// ...When the chicken's not tiptoing, the dog shouldn't go directly into chase mode, it should stop & look around
// first.


void enemyDogInitPatrol(ENEMY *pE)
{
	PATROLDOGSTR *ext;
	static char *names[] ={"SPINE","LEFTHIP","RIGHTHIP","TAIL","LEFTSHOULDER","RIGHTSHOULDER","HEAD"};
	int i;

	ext = MALLOC(sizeof(PATROLDOGSTR));
	FMEMZERO(ext,sizeof(PATROLDOGSTR));
	pE->extras = ext;

	utilPrintf("points = %d\n",pE->readData.pDef->nPoints);
	ASSERT(pE->readData.pDef->nPoints > 0);

	for(i = 0; i < 7; i++)
	{
		 ext->bend_objects[i] = psiObjectScan(pE->readData.pActor->psiData.object, names[i]);
	}

	nmeDogEnsureHourglass();
	pE->view_range_current = pE->view_range_rqd = PATROL_DOG_SIGHT_RANGE;
	pE->view_fov_current = pE->view_fov_rqd = PATROL_DOG_SIGHT_FOV;

}
void enemyDogInitSleepy(ENEMY *pE)
{
	pE->view_range_current = pE->view_range_rqd = 0;
	pE->view_fov_current = pE->view_fov_rqd = 0;
}





void enemyDogStartPersuit(ENEMY *pE,VECTOR *position, int buzzer)
{
	PATROLDOGSTR *pat;
	pat = (void *)pE->extras;

	DB("persuit start\n");
	pE->turning = 0;	// kill off any previous turn

	pat->alertpoint = *position;

	pE->view_range_rqd = PATROL_DOG_EXTRA_SIGHT_RANGE;
	pE->view_fov_rqd = PATROL_DOG_EXTRA_SIGHT_FOV;


	if(buzzer)
	{
		DOG_BUZZ();
	}
	SETSTATE(STATE_PERSUIT);
}





// Tell the dog that there was a noise at the given position
// It'll start by quick-turning to face that position.

// If the dog is already both alert and facing in roughly the right direction, then it'll go into chase mode.

void enemyDogStartAlert(ENEMY *pE, VECTOR *position, int buzzer)
{
	PATROLDOGSTR *pat;
	pat = (void *)pE->extras;

// technically difficult, but possible - triggering the dog before it's been initialised
// (need to init stuff in a different manner, methinks)

	switch(pE->state)
	{
	case STATE_NULL:
	case STATE_GROWLING:	// should we distinguish between giving up growling & killing chicken growling?
		return;

	case STATE_ALERTED:
// If we're already alert, hang around till we've got to the old angle...
// If we're at the old angle, and are facing roughly the right direction, go into persuit.
// Otherwise, restart alert
		return;
	case STATE_PERSUIT:
		pat->alertpoint = *position;
		return;

	default:
		DB("DOG ALERT START! ALARM! ALARM!\n");
		if(buzzer)
		{
			DOG_BUZZ();
		}

		SETSTATE(STATE_ALERTED);

		pE->view_range_rqd = PATROL_DOG_EXTRA_SIGHT_RANGE;
		pE->view_fov_rqd = PATROL_DOG_EXTRA_SIGHT_FOV;


	// Enable it if it's a persuit dog
		if(pE->readData.pDef->nPoints == 1 && !(pE->runFlags & RUNFLAGS_VISIBLE))
		{
			pE->runFlags |= RUNFLAGS_VISIBLE;
			pE->dir = pE->desiredDir = enemyFindAbsAngleTo(pE, position);
			pat->corner_time = pE->turning = 0;
			actorAnimate(pE->readData.pActor, DOG_WALK, 1, 0, 256, 0, 0);	// no blend
		}
		else
		{
			pat->corner_angle = enemyFindDeltaAngleTo(pE,position);
			DB("alert angle %d\n",pat->corner_angle);
	// Do bendy dog code based on the angle to target...
			pat->corner_time = pE->turning = 13;
	//		pE->turning = 26;	// number of frames the turn anim takes
	//		pE->points.turnspeed_thiscorner = (ABS(pat->corner_angle)) * 60 / 1024;
			pE->points.turnspeed_thiscorner = (ABS(pat->corner_angle)) * 120 / 1024;	// the "alert" turn is tighter than the normal one
			if(pE->readData.pActor->animation.animData->segment != DOG_WALK)
			{
				actorAnimate(pE->readData.pActor, DOG_WALK, 1, 0, 256, 0, 512);	// yes loop, no queue. Blend in.
			}
		}
		pat->alertpoint = *position;
		return;
	}
}


// Find the persuit dog, and alert it
void enemyDogFindAndAlert(VECTOR *position)
{
	ENEMY *pE;

// tbd - Find the closest ?
	for(pE=enemyList.head.next; pE != &enemyList.head; pE = pE->next)
	{
		if(pE->readData.pInfo->nme_type == ENEMY_DOG)
		{
			PATROLDOGSTR *pat;
			pat = (void *)pE->extras;

			if(pE->state == STATE_PERSUIT && pat->persuing_what == DOG_PERSUING_SPROUT)
			{
			// If we're persuing a sprout, don't be alerted by hourglass-chickens, coz we're busy chasing that there sprout
			}
			else
			{
				enemyDogStartAlert(pE, position,1);
				pat->persuing_what = DOG_PERSUING_CHICKEN;
			}
		}
		else if (pE->readData.pInfo->nme_type == ENEMY_SLEEPYDOG)
		{
			if(pE->state == STATE_SLEEPING)	// Otherwise this could get recursive, coz the sleepy dog calls here when it wakes up
				enemyWakeUpSleeping(pE);
		}
		else if(pE->readData.pInfo->nme_type == ENEMY_MRT_TORCH || pE->readData.pInfo->nme_type == ENEMY_MRT_PATROL)
		{
			enemyMrTStartPersuit(pE, position,1);
		}
	}
}

// Call here when you pick up the tennis racket
// (if you've got the bone equipped - you should then be allowed to pick the racket up. Also, we need to spawn a bone scenic, but the puzzle engine can do that itself)
// (if you've NOT got the bone equipped - you should NOT then be allowed to pick the racket up)
int enemyDogTennisRacketPickup(int item)
{
	ENEMY *pE;

	DB("Dog-vs-tennis racket interaction\n");

	for(pE=enemyList.head.next; pE != &enemyList.head; pE = pE->next)
	{
		if(pE->readData.pInfo->nme_type == ENEMY_SLEEPYDOG)
		{
			if(pE->state != STATE_SLEEPING)
			{
				return 0;
			}

			if(item == BONE)
			{
				inventoryRemoveItem(item);	// You're about to be given a tennis racket in exchange

				soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGYAWNS1], 255, 0, (VECTOR *)&(pE->pos));

				actorAnimate(pE->readData.pActor, DOG_PUTFOOTONBONE, 0, 1, -ENEMY_DEFAULTANIMSPEED, 0, 0);
				actorAnimate(pE->readData.pActor, DOG_PUTFOOTONBONE, 0, 1, ENEMY_DEFAULTANIMSPEED, 0, 0);
				return 1;
			}
			else
			{
				enemyWakeUpSleeping(pE);
				return 0;
			}
		}
	}
	return 1;	// never happens ;)
}


// Dogstates
//	Wandering, Abandoning, Waiting
//	Null, TurnedOff, Persuit, Growling, Alerted, 
void enemyDogCollide(ENEMY *pE, void *pWith, int withtype)
{
	PATROLDOGSTR *pat;
	pat = (void *)pE->extras;
//		enemyDogStartPersuit(pE);
	switch(withtype)
	{
	case COLLIDE_WITH_MAINCHAR:
		pat->persuing_what = DOG_PERSUING_CHICKEN;
		switch(pE->state)
		{

		case STATE_WANDERING:
		case STATE_WAITING:	
		case STATE_ABANDONING:
		case STATE_THINKING:
			enemyDogStartAlert(pE, &((ENEMY *)pWith)->pos,1);
			break;

		default:
			pat->alertpoint = ((ENEMY *)pWith)->pos;
			break;
		}
		break;

	case COLLIDE_WITH_SPROUT:
// The dog decides to attack the thrower
// OR... if alert already, it gets stunned
//			overlayDebugMessage("Dog Hit By Sprout!", 25 * 2);
		pat->persuing_what = DOG_PERSUING_CHICKEN;
		switch(pE->state)
		{
		case STATE_WANDERING:
		case STATE_WAITING:
		case STATE_ABANDONING:
		case STATE_THINKING:
			enemyDogStartAlert(pE, &characterVars.pMainChar->pEnemy->pos,1);
			break;
		case STATE_PERSUIT:
			pat->alertpoint = characterVars.pMainChar->pEnemy->pos;
			SETSTATE(STATE_STUNNED);
			break;
		}
		break;
	}

// If you're colliding with the head end, and it's in persuit mode,
// then you are one dead chicken
}


int DogCanSeeCheck(ENEMY * pE)
{
	VECTOR v1;
	VECTOR v2;
	COLLBOX hitBox;
	int rv;

	if(!characterVars.pMainChar)
		return 0;
	if(enemyCanSeeCheckWellyTest(pE, &characterVars.pMainChar->pEnemy->pos, pE->view_fov_current,pE->view_range_current) >= pE->view_range_current)
		return 0;
	v1 = characterVars.pMainChar->pEnemy->pos;
	v2 = pE->pos;

	v1.vy -= 400;	// Ginger's head
	v2.vy -= 460;	// Dog's head

	v1.vx = v1.vx <<9;
	v1.vy = v1.vy <<9;
	v1.vz = v1.vz <<9;

	v2.vx = v2.vx <<9;
	v2.vy = v2.vy <<9;
	v2.vz = v2.vz <<9;

	rv = collideBoxIsectCheck(&v1,&v2);

	if(rv)
		return 0;

	return 1;
}

int DogCanHearCheck(ENEMY *pE)
{
	VECTOR temp;
	ENEMY *leade;
	CHARACTER *leadc;
	int range;
	int act;
	PATROLDOGSTR *pat;
	int anim;

	pat = (void *)pE->extras;

	leadc = characterVars.pMainChar;
	leade = characterVars.pMainChar->pEnemy;

/*
	if(leadc->movement.speed<=GINGER_WALKSPEED)
	{
		return 0;
	}
	range = PATROL_DOG_HEARS_RUN_RANGE;
	if(leadc->movement.speed<=GINGER_RUNSPEED)
	{
		range = PATROL_DOG_HEARS_WALK_RANGE;
	}
*/

	anim = leade->readData.pActor->animation.animData[0].segment;
	switch(anim)
	{
		case GINGER_WALK:
			range = PATROL_DOG_HEARS_WALK_RANGE;
			break;
		case GINGER_RUN:
			range = PATROL_DOG_HEARS_RUN_RANGE;
			break;
		default:
			return 0;
	}
	temp.vx = pE->pos.vx - leade->pos.vx;
	temp.vy = pE->pos.vy - leade->pos.vy;
	temp.vz = pE->pos.vz - leade->pos.vz;

	if(temp.vx > range || temp.vx < -range)
		return 0;
	if(temp.vz > range || temp.vz < -range)
		return 0;
	if(temp.vy > range || temp.vy < -range)
		return 0;


	if(Magnitude(&temp) > range)
	{
		return 0;
	}

	pat->alertpoint = leade->pos;
	return 1;
}

// Find the closest visible sprout
SPROUTSTR *DogSeesSproutCheck(ENEMY *pE)
{
	int i;
	SPROUTSTR *sprout;
	SPROUTSTR *closest = NULL;
	int close_range = pE->view_range_current;
	int range;
	VECTOR v1;
	VECTOR v2;
	int rv;

	if(!sproutman)
		return NULL;

	sprout = &sproutman->sprouts[0];
	for(i = 0; i < MAX_SPROUTS; i++,sprout++)
	{
		if(sprout->flags & SPROUT_ACTIVE)
		{
			range = enemyCanSeeCheck(pE, &sprout->pos, pE->view_fov_current,pE->view_range_current);
			if(range < close_range)
			{
				v1 = characterVars.pMainChar->pEnemy->pos;
				v2 = pE->pos;

				v2.vy -= 460;	// Dog's head

				v1.vx = v1.vx <<9;
				v1.vy = v1.vy <<9;
				v1.vz = v1.vz <<9;

				v2.vx = v2.vx <<9;
				v2.vy = v2.vy <<9;
				v2.vz = v2.vz <<9;

				rv = collideBoxIsectCheck(&v1,&v2);

				if(!rv)
				{
					close_range = range;
					closest = sprout;
				}
			}
		}
	}
	return closest;
}



int enemyUpdateDog(ENEMY *pE)
{	// CPW: one might call this function a dog handler! Ho ho.
	int i;
	int d;
	PATROLDOGSTR *pat;
	SPROUTSTR *sprout;


	ASSERT(pE);

//	DB("state %d turning %d. dirn = %d\n", pE->state, pE->turning,pE->dir);
	pat = (void *)pE->extras;

	if(overlay_show_collision == DEBUG_OVERLAY_ENEMY_INFO)
	{
		if(pE->state == STATE_PERSUIT || pE->state == STATE_ABANDONING)
			routeShowRoute(&pE->pos, &pE->route[0], pE->route_len);
	}


	switch(pE->state)
	{

	case STATE_NULL:

		if(pE->readData.pDef->nPoints == 1)
		{
// summonable persuit dog. Hang around disabled, in state_null, until required
			pE->points.pointGoingTo = pE->readData.pPoints[0].v;
			actorAnimate(pE->readData.pActor, DOG_WALK, 1, 0, 256, 0, 0);	// yes loop, no queue. no blend.
			pE->runFlags &= ~RUNFLAGS_VISIBLE;
			SETSTATE(STATE_TURNEDOFF);
		}
		else
		{
			pE->points.pointGoingTo = pE->readData.pPoints[pE->points.pointTo].v;	// from & to are initialised to 0 & 1 in the bff linker
			actorAnimate(pE->readData.pActor, DOG_WALK, 1, 0, 256, 0, 0);	// yes loop, no queue. no blend.
			SETSTATE(STATE_WANDERING);
		}
		break;


	case STATE_TURNEDOFF:
		pE->runFlags &= ~RUNFLAGS_VISIBLE;
		break;


	case STATE_PERSUIT:

		if(pat->persuing_what == DOG_PERSUING_CHICKEN)
		{
			hourglass2Inform(HOURGLASS_DOG_IN_PERSUIT_MODE,NULL);
			hourglass_dog_persuit = 2;
			hourglass_game_is_alert = 1;
		}
		pE->blinkOverride = 2;	// Keep looking real mad, little doggie

		dogRandomBark(pE,24);
		if(actorReachedFrameRatio(pE->readData.pActor, -1, 1024) == ACTORFRAME_AT)
		{
			enemyPlayEnvironmentFootstepNoDogCheck((VECTOR *)&(pE->pos), 64);	// ENEMY_SFX_DOGWALKS1
		}
		if(actorReachedFrameRatio(pE->readData.pActor, -1, 3072) == ACTORFRAME_AT)
		{
			enemyPlayEnvironmentFootstepNoDogCheck((VECTOR *)&(pE->pos), 64);	// ENEMY_SFX_DOGWALKS2
		}

		pat->angle = enemyTendTowards(0, pat->angle, 128);	// make sure the dog unbends

		sprout = DogSeesSproutCheck(pE);
		if(sprout)
		{
			pat->alertpoint = sprout->pos;
			pat->alertvel.vx = sprout->collvel.vx>>9;
			pat->alertvel.vy = sprout->collvel.vy>>9;
			pat->alertvel.vz = sprout->collvel.vz>>9;
			pat->alertcountdown = PATROL_DOG_SPROUT_PREDICT_TIME;
			pat->persuing_what = DOG_PERSUING_SPROUT;
		}
		else if(pat->alertcountdown)
		{
			pat->alertcountdown--;
			pat->alertpoint.vx += pat->alertvel.vx;
			pat->alertpoint.vy += pat->alertvel.vy;
			pat->alertpoint.vz += pat->alertvel.vz;
		}
		else if(DogCanSeeCheck(pE))
		{
			pat->alertpoint = characterVars.pMainChar->pEnemy->pos;
			pat->persuing_what = DOG_PERSUING_CHICKEN;
		}
		enemyLookAtPos(pE, &pat->alertpoint);



		if(enemyCanSeeCheck(pE, &characterVars.pMainChar->pEnemy->pos, PATROL_DOG_KILLER_FOV,PATROL_DOG_KILLER_RANGE) < PATROL_DOG_KILLER_RANGE)
		{
			if(!enemyEndingLevelFlag)
			{
				DB("Chicken dies horribly by persuit success!\n");
				deathKill(characterVars.pMainChar, DEATH_REASON_PERSUIT_DOG_MAUL, pE);
				SETSTATE(STATE_GROWLING);
				break;
			}
		}




		if(pE->readData.pActor->animation.animData->segment != DOG_RUN)
		{
			int anim;

			if(
				pE->readData.pActor->animation.blendCounter == 4096
				&&
				(pE->readData.pActor->animation.queueSize == 0
				|| pE->readData.pActor->animation.animQueue[pE->readData.pActor->animation.queueSize-1].segment != DOG_RUN)
				)
			{
				anim = DOG_RUN;
//				actorAnimate(pE->readData.pActor, anim, 1, 1, pE->animSpeed, 0, 0);	// yes loop it, yes queue it Don't blend
				actorAnimate(pE->readData.pActor, anim, 1, 1, pE->animSpeed, 0, 512);	// Try yes to blending it
			}
		}





		if(!(pE->turning) && enemyArrivedCheck(&pE->points.pointComingFrom, &pE->pos, &pE->points.pointGoingTo, 0))
		{
			int d;
//			utilPrintf("Arrived\n");

			pE->points.pointComingFrom = pE->pos;

			// Grab the next point in the existing route as a first bet for the corner estimate
			// (We'll revise our route once we're round the corner)
			if(pE->route_len > 1)
			{
				pE->points.pointGoingTo.vx = pE->route[1].vx;
				pE->points.pointGoingTo.vy = pE->pos.vy;
				pE->points.pointGoingTo.vz = pE->route[1].vz;


				d = enemySteerTowards(pE, &pE->points.pointGoingTo, 32);
				pE->turning = 20;	// number of frames the turn anim takes
				if(d > 0)
				{
					int rv;
					rv = actorAnimate(pE->readData.pActor, DOG_SKIDRIGHT, 0, 0, 256, 0, 512);
				}
				else
				{
					actorAnimate(pE->readData.pActor, DOG_SKIDLEFT, 0, 0, 256, 0, 512);
				}
			}
			else
			{
// We've arrived at a 1-length route target. Or, to put it another way, we're there.
// Time to switch to growl mode, or something.
//	Moved to be a range check. See above.

				pE->points.pointGoingTo.vx = pE->route[0].vx;
				pE->points.pointGoingTo.vy = pE->pos.vy;
				pE->points.pointGoingTo.vz = pE->route[0].vz;

// If the route was truncated due to the chicken being outside the valid area, then we need to go into growl mode
// without killing the chicken.
// After a while, give up, and head back to the level entrance or the patrol route

// Problem: the dog's not actually arriving at it's last route point for collision reasons. Bother.

// Solution - we need to see if the dog's tring to run forwards into a collision box.
// If it is, then * Can it jump over (bricks problem) * Should it sit and bark?

				if(pE->route_truncated)
				{
					SETSTATE(STATE_GROWLING);
					break;	// don't do the moving bits of the code
				}

// We've arrived at the target, yet we've not managed to kill the chicken
// TBD - This would be a really great time to skid to a halt and look confused.
// (We need a delay interval if we're going to use the headshake here)
				actorAnimate(pE->readData.pActor, DOG_STOP, 0, 0, ENEMY_DEFAULTANIMSPEED, 0, 256);
//				actorAnimate(pE->readData.pActor, DOG_HEADSHAKE, 0, 1, ENEMY_DEFAULTANIMSPEED, 0, 0);
				actorAnimate(pE->readData.pActor, DOG_WALK, 1, 1, pE->animSpeed, 0, 0);
				SETSTATE(STATE_ABANDONING);

			}
		}
		else
		{
			if(pE->turning)
			{
				enemySteerTowards(pE, &pE->points.pointGoingTo, 128);
				pE->turning--;
				if(!pE->turning)	// Once we've rounded the corner, establish the route
				{

// Okay. We've rounded the corner. Now check to see if we can see the chicken.
// If we *can*, then use the chicken as the point to navigate to.
// If we *can't*, then keep using the alert point as the point to navigate to.
// (Actually, we're now revising the alert point in that manner all the time whilst in persuit)
					dogInitRoute(pE,&pat->alertpoint,1);
				}
			}
			else
			{
//				utilPrintf("Home Run\n");
				if(pE->route_len < 3)	// If we've got direct line of sight, recalculate continually (is this wise?)
				{
					dogInitRoute(pE,&pat->alertpoint,1);
				}
				enemySteerTowards(pE, &pE->points.pointGoingTo, 128);	// do the steering on a permanant basis
			}
		}
		enemyMove(pE, DOG_RUN_FORWARD_SPEED, ENEMYMOVE_SET, 0);	// Updates (pE->pos.vx and vz). enemy, desiredspeed, action (set,tendto), accel
		break;



// Even when growling, we keep the hourglass on screen if it's enabled...
// Growling at chicken after a successful persuit. The screen fades out. The dog keeps growling. Ginger looks scared. Reset to hut17.
// No timeout. It keeps growling. Grrr!

// Growling at chicken after an unsuccessful persuit. The chicken sits above or whatever, giggling happily at the chicken...
// Eventually, the dog gives up. Shakes head, decides to return to patrol route. Or (if there's no route), back to
// original location (which is outside the level).

	case STATE_GROWLING:
	{
		hourglass_dog_persuit = 2;
		if(pat->persuing_what == DOG_PERSUING_CHICKEN)
		{
			hourglass_game_is_alert = 1;
			hourglass2Inform(HOURGLASS_DOG_IN_GROWL_MODE,NULL);
		}

		pat->angle = enemyTendTowards(0, pat->angle, 64);	// make sure the dog unbends

		dogRandomBark(pE,16);

		if(pE->stateCtr == 0)
		{
			soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGGROWLS2], 255, 0, (VECTOR *)&(pE->pos));
			actorAnimate(pE->readData.pActor, DOG_STATICATTACK, 1, 0, pE->animSpeed, 0, 256);	// loop, no queue, quick blend
		}
		if(pE->stateCtr == 70 && !deathDying)	// If there's a dying anim going on, don't stop growling
		{
			actorAnimate(pE->readData.pActor, DOG_HEADSHAKE, 0, 1, ENEMY_DEFAULTANIMSPEED, 0, 0);
			actorAnimate(pE->readData.pActor, DOG_WALK, 1, 1, pE->animSpeed, 0, 0);
		}

		if(pE->stateCtr >= 138 && !deathDying)	// Don't stop growling while the death sequence is going on
		{
// Time to abandon the chase. The chicken's escaped. Bother.
// The dog gives up, and retires to a safe distance.
// Either it's going to return to it's original patrol route, or if not, then it'll return to it's original location...

			DB("abandoning\n");
			SETSTATE(STATE_ABANDONING);
		}
		else
		{
			pE->blinkOverride = 2;	// Keep looking real mad, little doggie

		}

		pE->vel.vx = 0;
		pE->vel.vz = 0;
		break;
	}

// Hit by a sprout in persuit...
	case STATE_STUNNED:
		pat->angle = enemyTendTowards(0, pat->angle, 128);	// make sure the dog unbends

		if(pE->stateCtr == 0)
		{
			pE->view_range_rqd = PATROL_DOG_SIGHT_RANGE;
			pE->view_fov_rqd = PATROL_DOG_SIGHT_FOV;

			soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGWHIMPER1], 255, 0, (VECTOR *)&(pE->pos));
			actorAnimate(pE->readData.pActor, DOG_STOP, 0, 0, ENEMY_DEFAULTANIMSPEED, 0, 256);
			actorAnimate(pE->readData.pActor, DOG_HEADSHAKE, 0, 1, ENEMY_DEFAULTANIMSPEED, 0, 0);
		}
		if(pE->stateCtr >= 10)
		{
			pE->vel.vx = 0;
			pE->vel.vz = 0;
		}
		if(pE->stateCtr >= 75)
		{
			if(DogCanSeeCheck(pE))
			{
//				overlayDebugMessage("Dog reverts to persuit!", 25 * 2);
				enemyDogStartPersuit(pE,&characterVars.pMainChar->pEnemy->pos,1);
				soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGGROWLS1], 255, 0, (VECTOR *)&(pE->pos));
				actorAnimate(pE->readData.pActor, DOG_RUN, 1, 1, ENEMY_DEFAULTANIMSPEED, 0, 0);
				pat->persuing_what = DOG_PERSUING_CHICKEN;
			}
			else
			{
//				overlayDebugMessage("Dog reverts to abandon!", 25 * 2);
				actorAnimate(pE->readData.pActor, DOG_WALK, 1, 1, pE->animSpeed, 0, 0);
				SETSTATE(STATE_ABANDONING);
			}
		}
		break;


// Abandon the chase
// Stop running, start walking
// Sort out a route back to somewhere on our actual path
// (Note - we'll need a complete route, remember)
// Routefind along it.
// When we get there, switch back to patrolling

// tbd - the "created" type of dog needs to navigate its way out of the level.

	case STATE_ABANDONING:
	{
		int dc;


		if(actorReachedFrameRatio(pE->readData.pActor, -1, 1024) == ACTORFRAME_AT)
		{
// CPW: 			soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGWALKS1], 255, 0, (VECTOR *)&(pE->pos));
			enemyPlayEnvironmentFootstepNoDogCheck((VECTOR *)&(pE->pos), 64);
		}
		if(actorReachedFrameRatio(pE->readData.pActor, -1, 3072) == ACTORFRAME_AT)
		{
// CPW: 			soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGWALKS2], 255, 0, (VECTOR *)&(pE->pos));
			enemyPlayEnvironmentFootstepNoDogCheck((VECTOR *)&(pE->pos), 64);
		}

// Don't recalculate the route if we've already got a direct one
//		if(pE->route_len > 1 || pE->stateCtr < 1)
		if(pE->stateCtr < 1)
		{
			pE->view_range_rqd = PATROL_DOG_SIGHT_RANGE;	// If it's been persuing, reset to normal
			pE->view_fov_rqd = PATROL_DOG_SIGHT_FOV;

			pE->points.pointComingFrom = pE->pos;
			pE->points.pointTo = routeFindNearestPoint(&pE->pos, &pE->readData.pPoints[0], pE->readData.pDef->nPoints, &pE->route_target);

			dogInitRoute(pE,&pE->route_target,1);

//			if(pE->stateCtr < 1)
			{
				pE->runFlags&=(~RUNFLAGS_FORCETOFRONT);	// set the dog so it's not part of the death sequence any more

				pat->corner_angle = enemyFindDeltaAngleTo(pE,&pE->points.pointGoingTo);
				DB("1st abandon corner angle %d\n",pat->corner_angle);
				pat->corner_time = pE->turning = 26;
				pE->points.turnspeed_thiscorner = (ABS(pat->corner_angle)) * 60 / 1024;
			}

		}

		enemyLookAtPos(pE, &pE->route_target);

		if(pE->readData.pDef->nPoints == 1)
			dc = 0;
		else
			dc = 400;

		if(!pE->turning && enemyArrivedCheck(&pE->points.pointComingFrom, &pE->pos, &pE->points.pointGoingTo, dc))
		{
			int d;
			DB("abandon arrived\n");

			pE->points.pointComingFrom = pE->pos;

// Grab the next point in the existing route as a first bet for the corner estimate
// (We'll revise our route once we're round the corner)
			if(pE->route_len > 1)
			{
				pE->points.pointGoingTo.vx = pE->route[1].vx;
				pE->points.pointGoingTo.vy = pE->pos.vy;
				pE->points.pointGoingTo.vz = pE->route[1].vz;
			}
			else
			{
//				DB("abandon arrived at 1-length. Start patrolling\n");
// We've arrived at a 1-length route target. Or, to put it another way, we're there.

				DB("Time to return to patrol!\n");

				enemyDogStartPatrolling(pE, (pE->points.pointTo+1) % pE->readData.pDef->nPoints);
			}

			d = enemySteerTowards(pE, &pE->points.pointGoingTo, 32);

			pat->corner_angle = enemyFindDeltaAngleTo(pE,&pE->points.pointGoingTo);
			DB("abandon-to-patrol angle %d\n",pat->corner_angle);
			pat->corner_time = pE->turning = 26;
			pE->points.turnspeed_thiscorner = (ABS(pat->corner_angle)) * 60 / 1024;

		}
		else
		{
			if(pE->turning)
			{
				enemySteerTowards(pE, &pE->points.pointGoingTo, pE->points.turnspeed_thiscorner);
				pE->turning--;
//				DB("abandon - post-turn route establish\n");
				// Once we've rounded the corner, establish the route
				if(!pE->turning)
				{
					dogInitRoute(pE,&pE->route_target,1);
				}
			}
			else
			{
				if(pE->route_len < 3)	// If we've got direct line of sight, recalculate continually
				{
//					DB("abandon - contunial route recalc\n");
					dogInitRoute(pE,&pE->route_target,0);
				}
				enemySteerTowards(pE, &pE->points.pointGoingTo, 32);
			}
		}
		enemyMove(pE, DOG_FORWARD_SPEED, ENEMYMOVE_SET, 0);	// Updates (pE->pos.vx and vz). enemy, desiredspeed, action (set,tendto), accel


		sprout = DogSeesSproutCheck(pE);	// Abandoning
		if(sprout)
		{
			enemyDogStartPersuit(pE,&sprout->pos,0);
			pat->persuing_what = DOG_PERSUING_SPROUT;
		}
		else if(pE->stateCtr > 50 && DogCanSeeCheck(pE))	// Don't start looking straight away if we're abandoning persuit
		{
			enemyDogStartPersuit(pE,&characterVars.pMainChar->pEnemy->pos,1);
			pat->persuing_what = DOG_PERSUING_CHICKEN;
		}
		else if(pE->stateCtr > 50 && DogCanHearCheck(pE))
		{
			SETSTATE(STATE_THINKING);
		}
		break;
	}

// The dog is turning rapidly to a point of interest
	case STATE_ALERTED:
		hourglass_dog_persuit = 2;
//		hourglass_game_is_alert = 1;
		hourglass2Inform(HOURGLASS_DOG_IN_THINKING_MODE,NULL);

		if(pE->turning)
		{
			enemySteerTowards(pE, &pat->alertpoint, pE->points.turnspeed_thiscorner);
			enemyMove(pE, DOG_FORWARD_SPEED, ENEMYMOVE_SET, 0);

			pE->turning--;
		}
		else
		{
//			enemySteerTowards(pE, &pE->points.pointGoingTo, 32);	// do the steering on a permanant basis
// Boom. End of alert. Enter persuit, or look confused. The choice is yours.
			DB("post-alert persuit\n");
			enemyDogStartPersuit(pE,&pat->alertpoint,0);	// retain the same point that we were alerted to
			enemyMove(pE, DOG_FORWARD_SPEED, ENEMYMOVE_SET, 0);

		}

		break;

// The dog is plodding round its path...
	case STATE_WANDERING:

		if((actorReachedFrameRatio(pE->readData.pActor, -1, 1310) == ACTORFRAME_AT) || (actorReachedFrameRatio(pE->readData.pActor, -1, 3440) == ACTORFRAME_AT))
		{
			enemyPlayEnvironmentFootstepNoDogCheck((VECTOR *)&(pE->pos), 64);
		}

		if((actorReachedFrameRatio(pE->readData.pActor, -1, 2130) == ACTORFRAME_AT) || (actorReachedFrameRatio(pE->readData.pActor, -1, 4095) == ACTORFRAME_AT))
		{
			enemyPlayEnvironmentFootstepNoDogCheck((VECTOR *)&(pE->pos), 64);
		}

		enemyLookAtPos(pE, NULL);

		if(pE->points.timer)
			pE->points.timer = 0;	// used as a "enemy just at point" flag

#if GOLDCD == 0
		if(padData.debounce[4] & PAD_CIRCLE)
		{
			enemyDogStartAlert(pE, &characterVars.pMainChar->pEnemy->pos,1);
			break;
		}
#endif

		sprout = DogSeesSproutCheck(pE);
		if(sprout)
		{
			DB("Wandering sprout persuit\n");
			enemyDogStartPersuit(pE,&sprout->pos,0);
			pat->persuing_what = DOG_PERSUING_SPROUT;
		}
		else if(DogCanSeeCheck(pE))
		{
			DB("Wandering sight persuit\n");
			enemyDogStartPersuit(pE,&characterVars.pMainChar->pEnemy->pos,1);
			pat->persuing_what = DOG_PERSUING_CHICKEN;
		}
		else if(DogCanHearCheck(pE))
		{
			SETSTATE(STATE_THINKING);
		}




		// If the anim isn't walk, then stick a walk anim in the queue right after whatever we're in the middle of doing

		if(pE->readData.pActor->animation.animData->segment != DOG_WALK)
		{
			int anim;

			if(pE->readData.pActor->animation.queueSize == 0
				|| pE->readData.pActor->animation.animQueue[pE->readData.pActor->animation.queueSize-1].segment != DOG_WALK)
			{
			//	if((utilRandomInt(8)&1)==0)
			//		anim = DOG_WALKANDSNIFF;	// Walk & sniff will need blending
			//	else
					anim = DOG_WALK;
				actorAnimate(pE->readData.pActor, anim, 1, 1, pE->animSpeed, 0, 0);	// yes loop it, yes queue it Don't blend
			}
		}

		




		// Next up. If the point is a WAIT type point, we want to actually go all the way up to it.
		// Then we need to decide what to do when we get there.
		// One thing is "looking in boxes"

		if(pE->readData.pPoints[pE->points.pointTo].pause)
			d = 0;
		else
			d = 400;

		if(enemyArrivedCheck(&pE->points.pointComingFrom, &pE->pos, &pE->points.pointGoingTo, d))
		{
			enemyBeginTrekToNextPoint(pE);
			if(pE->readData.pPoints[pE->points.pointFrom].pause)
			{
				pE->points.timer = pE->readData.pPoints[pE->points.pointFrom].pause;
				DB("set to waiting\n");
				SETSTATE(STATE_WAITING);
			}
			else
			{
				dogBeginTrekToNextPoint(pE);
				pE->points.timer = 1;
			}
		}
		else
		{
			if(pE->turning)
			{
				enemySteerTowards(pE, &pE->points.pointGoingTo, pE->points.turnspeed_thiscorner);
				pE->turning--;
			}
			else
			{
				enemySteerTowards(pE, &pE->points.pointGoingTo, 32);
			}

		}


		enemyMove(pE, DOG_FORWARD_SPEED, ENEMYMOVE_SET, 0);	// Updates (pE->pos.vx and vz). enemy, desiredspeed, action (set,tendto), accel
		break;


// It was wandering, and you've interrupted it. So it pauses and looks around at random
// If you do any more attention grabbing noise-making, it's persuit time.

// (tbd - trigger this state)


	case STATE_THINKING:
		pE->vel.vx = 0;
		pE->vel.vz = 0;
//		hourglass2Inform(HOURGLASS_DOG_IN_THINKING_MODE,NULL);	// tbd: only do this if you're also making a noise

		dogRandomSniff(pE, 16);

		if(pE->stateCtr==0)
		{
			int dx,dz,angle;


			actorAnimate(pE->readData.pActor, DOG_STATICBREATHING, 1, 0, pE->animSpeed, 0, 512);


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

			if(angle > 2048)
			{
				pE->head.desiredAngle = -THINKING_DOG_LOOKANGLE;
			}
			else
			{
				pE->head.desiredAngle = THINKING_DOG_LOOKANGLE;
			}
			pE->view_range_rqd = PATROL_DOG_EXTRA_SIGHT_RANGE;
			pE->view_fov_rqd = PATROL_DOG_EXTRA_SIGHT_FOV;
		}

		if(pE->stateCtr == 16)
		{
			pE->head.desiredAngle = -pE->head.desiredAngle;
		}


// Once we've looked both ways, be random
		if(pE->stateCtr > 32 && (!utilRandomInt(16)))
		{
			if(!(utilRandomInt(256) & 1))
			{
				pE->head.desiredAngle = -THINKING_DOG_LOOKANGLE;
			}
			else
			{
				pE->head.desiredAngle = THINKING_DOG_LOOKANGLE;
			}
		}


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


		sprout = DogSeesSproutCheck(pE);
		if(sprout)
		{
			enemyDogStartPersuit(pE,&sprout->pos,0);
			pat->persuing_what = DOG_PERSUING_SPROUT;
			break;
		}
		else if(DogCanSeeCheck(pE))
		{
//			DB("waiting sight persuit\n");
			enemyDogStartPersuit(pE,&characterVars.pMainChar->pEnemy->pos,1);
			pat->persuing_what = DOG_PERSUING_CHICKEN;
			break;
		}
/*
		else if (pE->stateCtr > 16 && DogCanHearCheck(pE))
		{
			// Once he's done his one-way look, if you make any more racket, he'll turn to see if you're there
			enemyDogStartAlert(pE, &pat->alertpoint,1);
		}
*/
		else if(pE->stateCtr > 100)
		{
			pE->view_range_rqd = PATROL_DOG_SIGHT_RANGE;
			pE->view_fov_rqd = PATROL_DOG_SIGHT_FOV;
			actorAnimate(pE->readData.pActor, DOG_WALK, 1, 0, 256, 0, 512);	// yes loop, no queue. Blend in.

			pE->view_range_rqd = PATROL_DOG_SIGHT_RANGE;	// If it's been look-at-pointing, reset to normal
			pE->view_fov_rqd = PATROL_DOG_SIGHT_FOV;

			SETSTATE(pE->oldState);	// Revert to either waiting, wandering, or abandoning (the only states which switch to this one)
		}
		break;


// The dog has been told to wait at this corner...
	case STATE_WAITING:
		pE->vel.vx = 0;
		pE->vel.vz = 0;

#if GOLDCD == 0
		if(padData.debounce[4] & PAD_CIRCLE)
		{
			enemyDogStartAlert(pE, &characterVars.pMainChar->pEnemy->pos,1);
			break;
		}
#endif



		if(pE->stateCtr==0)
		{
			actorAnimate(pE->readData.pActor, DOG_STATICBREATHING, 1, 0, pE->animSpeed, 0, 512);
		}
		if(pE->points.timer>0)
		{
			// If we've got something that needs looking at, we'd better look at it.
			if(pE->readData.pPoints[pE->points.pointFrom].lookAtTag)
			{
				POSMARK *pMarker;
				pMarker = levinfoFindMarker(pE->readData.pPoints[pE->points.pointFrom].lookAtTag);
				if(pMarker)
				{
					dogRandomSniff(pE, 16);	// sound

					enemyLookAtPos(pE, (VECTOR *)&pMarker->x);

					// A dog who's been told specifically to look at something is always alert
					pE->view_range_rqd = PATROL_DOG_LOOKINGAT_RANGE;
					pE->view_fov_rqd = PATROL_DOG_LOOKINGAT_FOV;

					if(pE->runFlags & RUNFLAGS_NOTLOOKING)
					{
						pE->view_range_current = pE->view_range_rqd;
						pE->view_fov_current = pE->view_fov_rqd;
					}

					pE->runFlags &= ~RUNFLAGS_NOTLOOKING;
				}
			}
			else
			{
			// Otherwise, hang around, and maybe do something silly if we've got time
				if(pE->points.timer ==70)	// If we've got time for an idle, let's do an idle
				{
					switch(utilRandomInt(1))	// utilRI is 0-n inclusive
					{
					case 0:
					case 1:
						soundPlaySamplePanFade(pE->readData.ppSamples[ENEMY_SFX_DOGSCRATCH1], 255, 0, (VECTOR *)&(pE->pos));
						actorAnimate(pE->readData.pActor, DOG_EARSCRATCH, 0, 0, pE->animSpeed, 0, 512);
						actorAnimate(pE->readData.pActor, DOG_STATICBREATHING, 1, 1, pE->animSpeed, 0, 0);
						break;

	/*
	// Sadly, this anim is built to be mid-sniffy-walk
					case 1:
						actorAnimate(pE->readData.pActor, DOG_WEE, 0, 1, pE->animSpeed, 0, 512);
						actorAnimate(pE->readData.pActor, DOG_STATICBREATHING, 1, 1, pE->animSpeed, 0, 0);
						break;
	*/

					}
				}
				enemyLookAtPos(pE, NULL);
			}
			pE->points.timer--;
		}


		sprout = DogSeesSproutCheck(pE);
		if(sprout)
		{
			enemyDogStartPersuit(pE,&sprout->pos,0);
			pat->persuing_what = DOG_PERSUING_SPROUT;
			break;
		}
		else if(DogCanSeeCheck(pE))
		{
//			DB("waiting sight persuit\n");

// tbd - if the dog is actually looking AT a point, and that's where you are, then we need to up the killer range
// (so the dog gets you, inside those boxes when it's checking them, rather than sitting outside, barking & never going away)

			if(pE->readData.pPoints[pE->points.pointFrom].lookAtTag && pE->points.timer > 25)
			{
				if(enemyCanSeeCheck(pE, &characterVars.pMainChar->pEnemy->pos, pE->view_fov_current,PATROL_DOG_LOOKINGAT_KILLER_RANGE))
				{
					DOG_BUZZ();
					deathKill(characterVars.pMainChar, DEATH_REASON_PERSUIT_DOG_MAUL, pE);
					SETSTATE(STATE_GROWLING);
					break;
				}
			}
			enemyDogStartPersuit(pE,&characterVars.pMainChar->pEnemy->pos,1);
			pat->persuing_what = DOG_PERSUING_CHICKEN;
			break;
		}
		else if(DogCanHearCheck(pE))
		{
			SETSTATE(STATE_THINKING);
		}



		if(!pE->points.timer)
		{
//			DB("Return to Wander\n");
			pE->view_range_rqd = PATROL_DOG_SIGHT_RANGE;	// If it's been look-at-pointing, reset to normal
			pE->view_fov_rqd = PATROL_DOG_SIGHT_FOV;

			dogBeginTrekToNextPoint(pE);
		}
		break;
	}



// Actually, we could just check for anim = walk
	if(pE->state == STATE_WANDERING || pE->state == STATE_ALERTED || pE->state == STATE_ABANDONING )
	{
		if(pE->turning)
		{
			int max;
			int ang;

			if(!pat->corner_time)
			{
				pat->angle = enemyTendTowards(0, pat->angle, 128);	// unbend
			}
			else
			{
				max = pat->corner_angle;	// / 2;
				ang = pE->turning * 4096 / pat->corner_time;	//26;
				pat->angle = (-rcos(ang) + 4096) * max / 8192;
			}

	//		DB("ang = %d (%d / %d turn). maxangle = %d\n",pat->angle,pE->turning,pat->corner_time, pat->corner_angle);

		}
		else
		{
			pE->turning = 0;
			if(pat->angle)
			{
				pat->angle = enemyTendTowards(0, pat->angle, 128);	// unbend as fast as is feasable
	//			DB("ret-to-zero %d\n",pat->angle);
			}
		}
	}

	{
		int ang;	// ensure in -2048...+2048 range

		ang = pat->angle & 4095;
		if(ang > 2048)
			ang = ang - 4096;

//		if(pat->angle)
//			DB("bend ang =  %d\n",ang);

		pat->bend_objects[0]->rotate.vy = -ang / 4;
		pat->bend_objects[1]->rotate.vy = -ang / 2;
		pat->bend_objects[2]->rotate.vy = -ang / 2;
		pat->bend_objects[3]->rotate.vy = -ang / 2;
		pat->bend_objects[4]->rotate.vy = ang / 2;
		pat->bend_objects[5]->rotate.vy = ang / 2;
		pat->bend_objects[6]->rotate.vy = (ang * 3 / 5) + pE->head.actualAngle;
	}


	if(pE->view_range_rqd != pE->view_range_current)
		pE->view_range_current += (pE->view_range_rqd - pE->view_range_current) / 16;

	if(pE->view_fov_rqd != pE->view_fov_current)
		pE->view_fov_current += (pE->view_fov_rqd - pE->view_fov_current) / 16;


	return 0;
}



// A couple of special case sprite plotters with clipped tops and bottoms
#define HOUR_DEPTH (PANEL_OVERLAYS_SORTDEPTH)

int hourglassBotSprite(TextureType *spr, int x, int y, int yb)
{
	POLY_FT4 *si;
	
	BEGINPRIM(si, POLY_FT4);

	si->x0 = si->x2 = x;
	si->x1 = si->x3 = x + spr->w;
	si->y0 = si->y1 = y;
	si->y2 = si->y3 = yb;	// y + spr->h - t;

	*(ULONG *)(&si->u0) = ((unsigned long)*(USHORT *)&spr->u0)
		                  | (((unsigned long)(spr->clut)) <<16);


	*(ULONG *)(&si->u2) = ((unsigned long)*(USHORT *)&spr->u2);
	*(ULONG *)(&si->u3) = ((unsigned long)*(USHORT *)&spr->u3);

	*(ULONG *)(&si->u1) = ((unsigned long)*(USHORT *)&spr->u1)
						  | (((unsigned long)(spr->tpage)) <<16);

	si->v2 = si->v0 + si->y2 - si->y0;
	si->v3 = si->v1 + si->y2 - si->y0;

	*(ULONG*)&si->r0 = (0x808080) | ((GPU_COM_TF4)<<24);
	setlen(si, 9);

	ENDPRIM(si, HOUR_DEPTH, POLY_FT4);
	return 1;
}




int hourglassTopSprite(TextureType *spr, int x, int y, int t)
{
	POLY_FT4 *si;
	
	BEGINPRIM(si, POLY_FT4);

	si->x0 = si->x2 = x;
	si->x1 = si->x3 = x + spr->w;
	si->y0 = si->y1 = y + t;
	si->y2 = si->y3 = y + spr->h;	// - t;

	*(ULONG *)(&si->u0) = ((unsigned long)*(USHORT *)&spr->u0)
		                  | (((unsigned long)(spr->clut)) <<16);


	*(ULONG *)(&si->u2) = ((unsigned long)*(USHORT *)&spr->u2);
	*(ULONG *)(&si->u3) = ((unsigned long)*(USHORT *)&spr->u3);

	*(ULONG *)(&si->u1) = ((unsigned long)*(USHORT *)&spr->u1)
						  | (((unsigned long)(spr->tpage)) <<16);

	si->v0 += t;
	si->v1 += t;

	*(ULONG*)&si->r0 = (0x808080) | ((GPU_COM_TF4)<<24);
	setlen(si, 9);

	ENDPRIM(si, HOUR_DEPTH, POLY_FT4);
	return 1;
}





