////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// doofus.cpp
// Project: Postal
//
// This module implements the CDoofus class which is the class of enemy
//	guys for the game.  
//
// History:
//		01/13/97 BRH	Started this file from CDude and modified it
//							to do some enemy logic using the same assets
//							as the sample 2D guy.  
//
//		01/15/97 BRH	Changed the render to draw the guy with his 
//							hotspot between his feet.
//
//		02/04/97	JMI	Changed LoadDib() call to Load() (which now supports
//							loading of DIBs).
//
//		02/05/97 BRH	Fixed problem loading Instance ID.  Also fixed some other
//							problems with the run routine.  He now will follow the 
//							bouys to get to the bouy nearest the CDude.
//
//		02/23/97 BRH	Changed coordinate system to x, -z in Find Direction.
//
//		02/25/97 BRH	Fixed problem with the Startup where the height was not
//							being multiplied by 4, so enemies would start too low
//							if placed on a roof.
//
//		03/04/97 BRH	Derived this from CCharacter instead of CThing
//
//		03/05/97 BRH	Added SelectRandomBouy function which picks a random
//							bouy number (a valid one) or returns 0 if there are
//							no bouys.
//
//		03/06/97 BRH	Added realignment timer and function so that the
//							direction to the bouy can be recalculated every
//							so often to avoid missing it.
//
//		03/13/97	JMI	Load now takes a version number.
//
//		03/14/97	JMI	SelectDude() now chooses the closest dude on the X/Z plane.
//
//		04/04/97	JMI	SelectDude() no longer chooses dead dudes.
//							Also, last update to make SelectDude() find the closest 
//							CDude was not comparing the distance to the CDude from this
//							guy, but instead was comparing the distance to the CDude from
//							(0, 0, 0).
//
//		04/16/97 BRH	Changed references to the realm's list of CThings to use
//							the new non-STL methods.
//
//		04/22/97 BRH	Moved common code and some variables like the animations
//							to the base class.  Put common logic routines like the
//							reactions to weapons in this base class.
//
//		04/24/97 BRH	Added TryClearDirection function that uses the 
//							IsPathClear() funciton to try 3 directions to see if they
//							are clear.
//
//		05/02/97	JMI	Added check to make sure not already in shot state or
//							writhing state before changing to shot state in OnShotMsg.
//							Also, now OnBurnMsg() sets him into the m_animOnFire in-
//							stead of m_animRun.
//
//		05/04/97 BRH	Removed #ifdef code sections referring to STL lists.
//
//		05/06/97 BRH	Added Popout logic, detection smash.
//
//		05/09/97 BRH	Added Writhing and Shot logic from CPerson.
//
//		05/11/97 BRH	Fixed problem with Logic_Guard.
//
//		05/15/97 BRH	In the popout and run & shoot wait states, changed from
//							the temporary timeout method to checking for the
//							triggered pylon before running the logic.
//
//		05/18/97 BRH	Added some logic functions for the victims to use.
//
//		05/20/97 BRH	Added the hiding states.  Also added calls to 
//							ReevaluateState() at times in the action cycles
//							where the action can be changed.  Changed Logic_PylonDetect
//							to set flags for use in ReevaluateState() and
//							in the logic table variables evaluation.
//
//		05/21/97 BRH	Added m_dAnimRot to specify the direction the guy is 
//							facing and separating that from the direction he is
//							moving.  This will be used for the run and shoot
//							so that he can run and face sideways.  
//
//		05/22/97 BRH	Fixed typo bug in Logic_Shoot.
//
//		05/25/97 BRH	Added the m_ShootAngle variable and an override for
//							ShootWeapon that uses this angle to aim the weapon.
//
//		05/26/97 BRH	Changed ShootWeapon so that CSmash bits are passed in
//							so that enemy bullets don't hit other enemies.
//
//		05/26/97 BRH	Added a timer for shooting to limit the number of shots
//							for each type of gun.  Also added some avoidance 
//							of obstacles during MoveNext in case the guy gets stuck
//							trying to get to the next bouy.
//
//		05/27/97 BRH	Fixed a few problems with logic table transitions.
//
//		05/27/97 BRH	Added some avoidance of obstacles to Logic_PopBegin in
//							case he gets stuck on walls while trying to find the 
//							first pylon.
//
//		05/31/97	JMI	Replaced m_pDude with m_idDude.  The problem was that, by
//							just using a pointer to the dude, we never found out when
//							the dude was gone (deleted).  Although this is rare for
//							CDudes, it does happen.  For example, in the beginning of
//							a level all CDudes that do not have an associated player
//							are sent a Delete msg.  They do not process this message
//							until their respective Update() calls.  If a CDoofus 
//							derived guy happened to be placed in the level before a 
//							CDude (that is, the CDoofus' Update() got called before the
//							CDude's), and the CDoofus happened to point its m_pDude at 
//							this CDude (that was destined to soon be deleted), later 
//							when referencing the pointer to the freed and/or reallocated
//							memory, the CDoofus could cause a protection	fault or	math 
//							overflow (due to invalid values returned by 
//							m_pDude->GetX, Y, Z() with the non-CDude 'this' pointer).
//							Also, in the case that SelectDude() did not select a dude,
//							SQDistanceToDude() was returning an unitialized double
//							which, in some cases, could be a totally illegal double
//							value.
//
//		06/02/97 BRH	Added AdvanceHold action and state so that once he reaches
//							the end of the advancement, he goes into this hold state
//							rather than Engage automatically.  This way the logic
//							table can have more control over the next state.
//
//		06/02/97 BRH	Changed TryClearShot to use the CRealm version of
//							IsPathClear which just checks for terrain obstacles.
//
//		06/05/97	JMI	Changed m_sHitPoints to m_stockpile.m_sHitPoints to 
//							accommodate new m_stockpile in base class, CThing3d (which
//							used to contain the m_sHitPoints).
//
//		06/10/97 BRH	Now sends messages to CDemon for Explosion, and burning.
//
//		06/10/97 BRH	Added RunIdleAnimation() function which monitors the
//							idle timer and controls which of 3 idle animations to
//							use.  This should be called when in some kind of wait
//							state.
//
//		06/10/97 BRH	Fixed "YMCA" bug where the enemy guy would cycle 
//							each frame between Advance and AdvanceHold.
//
//		06/11/97 BRH	Fixed the crouch and search animations since the
//							search is done from the crouch posiiton.  It was
//							previously backwards because I thought the search
//							animation was done from a standing position.
//
//		06/13/97	JMI	Changed FindDirection() to return m_dRot if we cannot find
//							a CDude.
//							Also, changed Logic_Shoot() over to using events.
//
//		06/17/97 BRH	Changed NEAR_DEATH_HITPOINTS to a higher value since the
//							machine gun bullets were increased, it was diffucult to
//							get a writhing person.
//
//		06/17/97 BRH	Attempted to make enemies stop shooting dead CDudes.
//
//		06/17/97	JMI	Now doubles smash radius when in writhing state.
//
//		06/18/97	JMI	Changed PlaySoundWrithing() to return the duration of the
//							played sample.
//
//		06/18/97 BRH	Changed over to using GetRandom()
//
//		06/24/97	JMI	Added intialization of m_sRotateDir.
//
//		06/26/97 BRH	Added a special case for Writhers who get burned. 
//							Previously they didn't react because their smash bits
//							were changed when they went into writhing.  Then we wanted
//							them to get killed by fire so included the AlmostDead bits
//							but they they would jump to their feet and run around
//							once they got burned.  So now they will just lie there
//							and die.
//
//		06/26/97 BRH	When a doofus is killed after preparing a weapon but 
//							before shooting it, he will drop it if it was a throwing
//							weapon, but will just delete it if it was a launched
//							weapon.
//
//		06/27/97 BRH	Added a flag for recently stuck so that when a character
//							gets stuck on a wall, he sets the flag, then when he gets
//							free of the obstacle, it will get the closest bouy rather
//							than trying to align to the one it was trying to get to
//							previously which many times caused him to get stuck in 
//							the same manner.
//
//		06/28/97 BRH	Changed the RegisterBirth and RegisterDeath calls to pass 
//							m_bCivilian so that the scoring can updated for hostiles
//							or civilians.
//
//		07/09/97 BRH	Added logic for walking and running around on a bouy
//							network - used for victims.
//
//		07/11/97 BRH	Added call to inline Cheater() to disable game if necessary
//
//		07/12/97 BRH	Added addional text strings for the new actions that were
//							added.  Also changed PylonDetect function to use 
//							QuickCheckClosest rather than QuickCheck so that enemies
//							will detect the pylon you put them closest to and not
//							just a marker pylon.
//
//		07/12/97 BRH	Changed macro MAX_STEPUP_THRESHOLD to use the CThing3d
//							definitino MaxStepUpThreshold so that the detecting and
//							ability to move will be the same.
//
//		07/15/97 BRH	Added a few more calls to Cheater.
//
//		07/20/97 BRH	Effectively canceled the DelayShoot state by setting
//							the timeout to zero since it caused the guys to
//							not shoot very often on levels that were difficult
//							to move in.
//
//		07/21/97	JMI	Now Update() calls base class version.
//
//		07/23/97 BRH	Added tunable values for different timeouts which will get
//							set in doofus to the default static values that they were
//							before, and can be set in Person to the personatorium values
//							so that different people have different traits.
//
//		08/01/97 BRH	Changed the aiming so that it is based on the game
//							difficulty setting.
//
//		08/02/97 BRH	Added YellForHelp funciton that enemies can call when they
//							get shot to alert others in the are that they need help.
//
//		08/05/97 BRH	Fixed the problem with the doofus leaving the Run&Shoot
//							state at higher difficulty settings due to re-aligning
//							the angles with the shoot angle.	  
//
//		08/06/97 BRH	Changed TryClearShot to first do the point translation
//							to the rigid body where the weapon is shot from, in order
//							to get the correct height.  This will give the guys more
//							opportunities to shoot, and will work when the guys
//							are scaled larger or smaller.
//
//		08/06/97	JMI	Added m_ptransExecutionTarget link point for execution
//							sphere.  Also, added PositionSmash() to provide overridable
//							method for updating the collision sphere.
//
//		08/06/97	JMI	Now TryClearPath() returns false if there's no weapon
//							link point.
//
//		08/07/97	JMI	Added ms_awdWeapons[], ms_apszWeaponResNames[], and 
//							GetResources() and FreeResources() for loading these anims.
//							Also, added ms_lWeaponResRefCount so we could know when the
//							weapons were no longer needed.
//
//		08/08/97	JMI	Now Logic_Shoot() handles flamer.
//
//		08/08/97	JMI	Now Logic_Shoot() handles AutoRifle, Uzi, and SmallPistol.
//
//		08/08/97 BRH	Added start and end bouy ID's for special cases like
//							marching.  Added these to load and save.  They are set
//							by the dialog in the CPerson.  Also added marching logic.
//
//		08/09/97 BRH	Changed panic to be vicinity based so only the nearby
//							people panic.  Also added checks for prepared weapons
//							when guys get blown up or burned, where they should
//							either randomly discard their weapon, or delete it.
//
//		08/10/97	JMI	Moved CDoofus() and ~CDoofus() into doofus.cpp from 
//							doofus.h.
//							Was going to move the registering of the birth into the
//							constructor but I realized that m_bCivilian probably won't
//							get set until Load() so I left it in Startup() (it could
//							probably also get set via EditModify() but this doesn't 
//							matter b/c that's in edit mode only).
//							Now Registers death with the realm in the destructor if 
//							m_bRegisteredBirth is true.
//							Also, now OnShotMsg() calls base class version even if
//							other states don't permit a state change b/c this gives
//							better feedback to the user (base class OnShotMsg() creates
//							blood).
//
//		08/10/97	JMI	Moved NoWeapon up to enum value 0 and created a new one
//							to take its -1 place as an invalid weapon (InvalidWeapon).
//							Also, added block in PrepareWeapon() for the NoWeapon case.
//							Also, moved prepare weapon from the .H to the .CPP.
//
//		08/11/97 BRH	Found the problem with the parade level victims where they
//							were flipping around one bouy.  The problem was that they
//							asked how to get to a particular bouy which was unreachable
//							because the network was not fully connected, so when it
//							got the unreachable flag, it kept retrying the same bouy.  
//							I fixed the problem so they will pick a new bouy, but also
//							we are deleting the unconnected nodes in the parade level.
//								
//		08/11/97	JMI	Now sets backup weapon to default 'none' and uses the 
//							backup weapon when there's no animation for the current
//							weapon type.
//							Also, changed incorrectly name ms_awtType2Id to 
//							ms_awtId2Type mapping.
//
//		08/11/97 BRH	Fixed problem with run & shoot not using the correct
//							angle.
//
//		08/12/97	JMI	Now hides weapon if current event is 10 or more.
//
//		08/12/97 BRH	Checks for events channel in render so that people
//							without animation events like the band guys will still
//							work.  Also made shooting timing based on game
//							difficulty.
//
//		08/13/97	JMI	Changed so OnShotMsg() calls base class to generate blood
//							no matter what.
//
//		08/14/97	JMI	Switched references to g_GameSettings.m_sDifficulty to
//							m_pRealm->m_flags.sDifficulty.
//
//		08/14/97 BRH	Added static default bits to pass to weapons for their
//							collision testing.  Changed call to WhileHoldingWeapon
//							to include these defaults.
//
//		08/15/97 BRH	Changed the check for available pylons to check for a
//							clear path to the pylon before saying it is available.
//							Some guys were getting stuck on fences between themselves
//							an an available pylon.  Also fixed the writhing to
//							executed transition so that the guys don't flip around
//							180 degrees.  Also fixed the smash bits that get passed
//							to the missile weapons.
//
//		08/16/97 BRH	Changed the person's PlaySound functions so that comments
//							are only made when the CDude is alive.  So in this module, I
//							made sure that the victims were also calling SelectDude()
//							for the logic that they were doing, just to keep track of
//							whether the CDude was alive or dead.  Also tuned the
//							shooting accuracy for the levels to make them more accurate
//							on the easier levels since the easy dudes were missing too
//							much.
//
//		08/18/97 BRH	Added virtual override for CCharacter's WhileHoldingWeapon
//							so that for higher difficulty settings where the enemies
//							re-aim after preparing the weapon, they could do it
//							every frame between PrepareWeapon and ShootWeapon so that
//							they turned smoothy and didn't just flip around when they
//							got to ShootWeapon.  This was a problem with the Rocketman
//							who had a long shoot animation.  If the target moved a
//							quite a bit between PrepareWeapon and ShootWeapon, he would
//							spin around just before releasing the shot.  Also fixed
//							a problem with the ShootWeapon adjustment of the
//							random shooting innacuracy when I changed the values 
//							last time, I adjusted the max left swing, but forgot to
//							adjust the random amount.
//
//		08/18/97	JMI	Now sends doofuses who are writhing to the back most sprite
//							layer.  As people pointed out this is another of two evils.
//							It is, however, a lessor.  It appears a little wierd in one
//							case but better in all others I've seen so far.
//
//		08/19/97	JMI	Now sends doofuses who are dying to the back as well.
//
//		08/19/97	JMI	No longer shoots CSmash::Misc items.
//
//		08/20/97	JMI	Now Logic_Shot() does not wait to the end of the animation
//							to check for death.  This way they don't seem to kick back
//							from the shot, shoot forward, and then die.
//
//		08/20/97 BRH	Trying a smaller tolerance on the Bouys so that the
//							enemies must get closer to the hotspot before
//							moving to the next one.  We wanted to see if this helps
//							certain situations where guys are getting stuck.  Changed
//							from 10 pixel radius to 5.
//
//		08/21/97	JMI	Now does deluxe reporting on whether this doofus finds his
//							NavNet. 
//
//		08/21/97	JMI	Now after reporting that the doofus did not find its NavNet
//							it sets him to the current NavNet.
//
//		08/21/97 BRH	Added a blooc counter so that the number of blood pools
//							created while writhing could be cut down a little bit.
//
//		08/24/97 BRH	Changed the TryClearShot to use the Character version of
//							IsPathClear which also checks for people in the way.  Added
//							an additional call to TryClearShot in ShootWeapon which is
//							after the weapon has been re-aimed to make sure that the
//							path is still clear.  It was a problem with the rocketman
//							who has the slowest animation and may turn completely
//							around before shooting.
//
//		08/24/97 BRH	Aborts sample that was playing when the guy gets executed
//							so that he doesn't keep making noises after he is dead.
//
//		08/25/97 BRH	Undid a bunch of changes made yesterday which changed the
//							logic too much which threw off the tuning.  Added two new
//							escape routes for the Engage mode instead.  Fixed the 
//							shoot timer which was being set to two different values
//							in two different places.  Made the shooting more accurate
//							and more of the difficulty tuning will be with the 
//							shooting times.
//
//		08/27/97 BRH	Victims now panic when they are shot.
//
//		09/03/97	JMI	Sentries now exclude CSmash::Bads and CSmash::Civilians.
//
//		09/07/97 BRH	As Steve requested, the medium difficulty will now
//							re-adjust aiming just like hard difficulty in ShootWeapon.
//
//		12/18/97	JMI	Changed SelectRandomBouy() to return 0 if a the number of
//							bouys is less than or equal to one.  It used to be less 
//							than one but, for some reason, GetNumNodes() returns 1 when
//							there's no bouys.  This caused it to lock up when no bouys
//							for some lgk files.
//
////////////////////////////////////////////////////////////////////////////////
#define DOOFUS_CPP

#include "RSPiX.h"
#include <math.h>

#include "doofus.h"
#include "dude.h"
#include "SampleMaster.h"
#include "grenade.h"
#include "pylon.h"

////////////////////////////////////////////////////////////////////////////////
// Macros/types/etc.
////////////////////////////////////////////////////////////////////////////////

#define	NEAR_DEATH_HITPOINTS 31							// Below this, start writhing
#define  MS_BETWEEN_SAMPLES	100						// Time between pain groans.
#define  BURNT_BRIGHTNESS		-40						// -128 to 127.

////////////////////////////////////////////////////////////////////////////////
// Variables/data
////////////////////////////////////////////////////////////////////////////////

// These are default values -- actually values are set using the editor!
double CDoofus::ms_dAccUser     = 150.0;				// Acceleration due to user
double CDoofus::ms_dAccDrag     = 80.0;				// Acceleration due to drag

double CDoofus::ms_dMaxVelFore  = 80.0;				// Maximum forward velocity
double CDoofus::ms_dMaxVelBack  = -60.0;				// Maximum backward velocity

double CDoofus::ms_dDegPerSec   = 150.0;				// Degrees of rotation per second
double CDoofus::ms_dOffScreenDistance = 500*500;	// Square distance off screen
double CDoofus::ms_dGuardDistance = 300*300;			// Square distance before attacking
double CDoofus::ms_dThrowHorizVel = 200;				// Throw out at this velocity
double CDoofus::ms_dMinFightDistance = 80.0;			// Closest you want to get
double CDoofus::ms_dMedFightDistance = 200.0;		// Median distance for fighting
double CDoofus::ms_dMaxFightDistance = 400.0;		// Farthest distance for fighting
double CDoofus::ms_dMinFightDistanceSQ = 80.0*80.0;// Square distance for 
double CDoofus::ms_dMedFightDistanceSQ	= 200.0*200.0;
double CDoofus::ms_dMaxFightDistanceSQ = 400.0*400.0;	
double CDoofus::ms_dMarchVelocity = 20.0;				// Speed at which to march
int32_t CDoofus::ms_lDefaultAlignTime = 100; //2000;	// Time to realign to bouy position
int32_t CDoofus::ms_lGuardTimeoutMin = 4000;				// Time to check for CDudes again
int32_t CDoofus::ms_lGuardTimeoutInc = 500;				// Interval to add for each easier difficulty level
int32_t CDoofus::ms_lShootTimeoutMin = 1000;				// Min time between shots, adjused for difficulty
int32_t CDoofus::ms_lShootTimeoutInc = 200;				// Adjustment time for difficulty level between shots
int32_t CDoofus::ms_lDetectionRadius = 100;//80			// Radius of detection smash sphere
int32_t CDoofus::ms_lRunShootInterval = 2000;			// Time between shots while running
int32_t CDoofus::ms_lReseekTime = 15 * 1000;				// Seek the dude again after this time
int32_t CDoofus::ms_lShotTimeout = 3000;					// Time between full shot reaction animations
																	// which gives him a chance to attack or run
int32_t CDoofus::ms_lStuckRecoveryTime = 5000;			// Time to stay in recoverys state
int32_t CDoofus::ms_lAvoidRadius = 40;						// Radius of fire detection smash
int32_t CDoofus::ms_lYellRadius = 150;						// Yell for help in this vicinity
int32_t CDoofus::ms_lHelpTimeout = 3000;					// Time to react to a call for help.
int32_t CDoofus::ms_lDelayShootTimeout = 0; //2000;			// Time before shooting
int32_t CDoofus::ms_lHelpingTimeout = 1000;				// Only shoot every this often

// Note that these seem to apply to all weapons except bullet weapons.  That is, these are
// passed to WhileHoldingWeapon() which passes them on to ShootWeapon(), but WhileHoldingWeapon()
// is only used for non-bullet weapons.  In the bullet weapons case, it uses the default parameters
// to ShootWeapon().
U32 CDoofus::ms_u32CollideBitsInclude = CSmash::Character | CSmash::Barrel;
U32 CDoofus::ms_u32CollideBitsDontcare = CSmash::Good | CSmash::Bad;
U32 CDoofus::ms_u32CollideBitsExclude = CSmash::SpecialBarrel | CSmash::Ducking | CSmash::Bad | CSmash::Civilian;

int16_t CDoofus::ms_sStuckLimit = 3;						// Number of times to retry before attempting to
																	// get free of whatever you are stuck on.

// Weapon animations.
CAnim3D	CDoofus::ms_aanimWeapons[NumWeaponTypes];
// Current ref count on ms_aanimWeapons[].
int32_t		CDoofus::ms_lWeaponResRefCount	= 0;

// Weapon details (descriptions, res names, etc.).
CDoofus::WeaponDetails	CDoofus::ms_awdWeapons[NumWeaponTypes]	=
{
	//////// NoWeapon
	{	// pszName, pszResName, CThing ID
		"Verbal Abuse",
		NULL,
		TotalIDs,
	},

	//////// Rocket
	{	// pszName, pszResName, CThing ID
		"Rocket",
		"3d/launcher",
		CRocketID,
	},

	//////// Grenade
	{	// pszName, pszResName, CThing ID
		"Grenade",
		NULL,
		CGrenadeID,
	},

	//////// Napalm
	{	// pszName, pszResName, CThing ID
		"Napalm",
		"3d/napalmer",
		CNapalmID,
	},

	//////// Firebomb
	{	// pszName, pszResName, CThing ID
		"Cocktail",
		NULL,
		CFirebombID,
	},

	//////// ProximityMine
	{	// pszName, pszResName, CThing ID
		"ProximityMine",
		NULL,
		CProximityMineID,
	},

	//////// TimedMine
	{	// pszName, pszResName, CThing ID
		"TimedMine",
		NULL,
		CTimedMineID,
	},

	//////// RemoteMine
	{	// pszName, pszResName, CThing ID
		"RemoteMine",
		NULL,
		CRemoteControlMineID,
	},

	//////// BouncingBetty
	{	// pszName, pszResName, CThing ID
		"BouncingBetty",
		NULL,
		CBouncingBettyMineID,
	},

	//////// Flamer
	{	// pszName, pszResName, CThing ID
		"Flamer",
		"3d/flmthrower",
		CFirestreamID,
	},

	//////// Pistol
	{	// pszName, pszResName, CThing ID
		"Pistol",		
		"3d/bigpistol",
		CPistolID,
	},

	//////// MachineGun
	{	// pszName, pszResName, CThing ID
		"MachineGun",
		"3d/submachine",
		CMachineGunID,
	},

	//////// ShotGun
	{	// pszName, pszResName, CThing ID
		"ShotGun",
		"3d/ShotGun",
		CShotGunID,
	},

	//////// Heatseeker
	{	// pszName, pszResName, CThing ID
		"Heatseeker",
		"3d/launcher",
		CHeatseekerID,
	},

	//////// Assault
	{	// pszName, pszResName, CThing ID
		"Assault",
		"3d/SprayGun",
		CAssaultWeaponID,
	},

	//////// DeathWad
	{	// pszName, pszResName, CThing ID
		"DeathWad",
		"3d/napalmer",
		CDeathWadID,
	},

	//////// DoubleBarrel
	{	// pszName, pszResName, CThing ID
		"DoubleBarrel",
		"3d/shotgun",
		CDoubleBarrelID,
	},

	//////// Uzi
	{	// pszName, pszResName, CThing ID
		"Uzi",
		"3d/Uzi",
		CUziID,
	},

	//////// AutoRifle
	{	// pszName, pszResName, CThing ID
		"AutoRifle",
		"3d/AutoRifle",
		CAutoRifleID,
	},

	//////// SmallPistol
	{	// pszName, pszResName, CThing ID
		"SmallPistol",
		"3d/Pistol",
		CSmallPistolID,
	},

	//////// Dynamite
	{	// pszName, pszResName, CThing ID
		"Dynamite",
		NULL,
		CDynamiteID,
	},


};

// Maps a CThing ID to a WeaponType enum.
CDoofus::WeaponType	CDoofus::ms_awtId2Type[TotalIDs]	=	
{
	NoWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	Rocket,
	Grenade,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	Napalm,
	InvalidWeapon,
	InvalidWeapon,
	Firebomb,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	ProximityMine,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	Pistol,
	MachineGun,
	ShotGun,
	InvalidWeapon,
	TimedMine,
	BouncingBettyMine,
	RemoteControlMine,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	Heatseeker,
	InvalidWeapon,
	Assault,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	InvalidWeapon,
	Flamer,
	DeathWad,
	DoubleBarrel,
	Uzi,
	AutoRifle,
	SmallPistol,
	Dynamite,
};

char* CDoofus::ms_apszActionNames[] = 
{
	"Guard",
	"Advance",
	"Retreat",
	"Engage",
	"Popout",
	"Run&Shoot",
	"Hide",
	"Advance-Hold",
	"Walk",
	"Panic",
	"March",
	"Madness",
	"Help",
};

// Let this auto-init to 0
int16_t CDoofus::ms_sFileCount;


//
//

////////////////////////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////////////////////////
CDoofus::CDoofus(CRealm* pRealm, ClassIDType id)
	: CCharacter(pRealm, id)
	{
	m_sSuspend = 0;
	m_pNavNet = NULL;
	m_u16NavNetID = 0;
	m_idDude = CIdBank::IdNil;
	m_pNextBouy = NULL;
	m_sNextX = m_sNextZ = 0;
	m_ucDestBouyID = m_ucNextBouyID = 0;
	m_lAlignTimer = 0;
	m_lEvalTimer = 0;
	m_lShootTimer = 0;
	m_lShotTimeout = 0;
	m_pPylonStart = NULL;
	m_pPylonEnd = NULL;
	m_lCommentTimer = 0;
	m_usCommentCounter = 0;
	m_lLastHelpCallTime = 0;
	m_lSampleTimeIsPlaying = 0;
	m_bRecentlyStuck = false;
	m_bCivilian = false;
	m_ptransExecutionTarget	= NULL;
	m_spriteWeapon.m_pthing	= this;
	m_ucSpecialBouy0ID = 0;
	m_ucSpecialBouy1ID = 0;
	m_bPanic = false;
	m_bRegisteredBirth = false;
	// Default to no fallback weapon.
	m_eFallbackWeaponType	= TotalIDs;
	m_sStuckCounter = 0;
	m_usBloodCounter = 0;
	m_siPlaying = 0;

    // explicitly initialize to stop Valgrind whining. --ryan.
	m_lIdleTimer = pRealm->m_time.GetGameTime() + 2000 + GetRandom() % 2000;
	m_lStuckTimeout = 0;

	m_u16KillerId = 0;
	}

////////////////////////////////////////////////////////////////////////////////
// Destructor
////////////////////////////////////////////////////////////////////////////////
CDoofus::~CDoofus()
	{
	// If we've been born . . .
	if (m_bRegisteredBirth == true)
		{
		// See who killed us, if anyone
		bool bPlayerKill = false;
		CThing*	pthing;
		if (m_u16KillerId != 0 && m_pRealm->m_idbank.GetThingByID(&pthing, m_u16KillerId) == 0)
			if (pthing->GetClassID() == CDudeID)
				bPlayerKill = true; // Dude killed us.

		// Good bye.
		m_pRealm->RegisterDeath(m_bCivilian, bPlayerKill);
		}
	}

////////////////////////////////////////////////////////////////////////////////
// Load object (should call base class version!)
////////////////////////////////////////////////////////////////////////////////
int16_t CDoofus::Load(										// Returns 0 if successfull, non-zero otherwise
	RFile* pFile,											// In:  File to load from
	bool bEditMode,										// In:  True for edit mode, false otherwise
	int16_t sFileCount,										// In:  File count (unique per file, never 0)
	uint32_t	ulFileVersion)									// In:  Version of file format to load.
	{
	int16_t sResult = CCharacter::Load(pFile, bEditMode, sFileCount, ulFileVersion);

	if (sResult == SUCCESS)
	{
		// Load common data just once per file (not with each object)
		if (ms_sFileCount != sFileCount)
		{
			ms_sFileCount = sFileCount;

			// Load static data
			switch (ulFileVersion)
			{
				default:
				case 1:
					pFile->Read(&ms_dAccUser);
					pFile->Read(&ms_dAccDrag);
					pFile->Read(&ms_dMaxVelFore);
					pFile->Read(&ms_dMaxVelBack);
					pFile->Read(&ms_dDegPerSec);
					// Clear the panic flag at the load of a level.
					m_bPanic = false;
					break;
			}
		}

		// Load object data

		switch (ulFileVersion)
		{
			default:
			case 43:
				pFile->Read(&m_ucSpecialBouy0ID);
				pFile->Read(&m_ucSpecialBouy1ID);

			case 42:
			case 41:
			case 40:
			case 39:
			case 38:
			case 37:
			case 36:
			case 35:
			case 34:
			case 33:
			case 32:
			case 31:
			case 30:
			case 29:
			case 28:
			case 27:
			case 26:
			case 25:
			case 24:
			case 23:
			case 22:
			case 21:
			case 20:
			case 19:
			case 18:
			case 17:
			case 16:
			case 15:
			case 14:
			case 13:
			case 12:
			case 11:
			case 10:
			case 9:
			case 8:
			case 7:
			case 6:
			case 5:
			case 4:
			case 3:
			case 2:
			case 1:
				// Get the instance ID for the NavNet
				U16 u16Data;
				int16_t sres = pFile->Read(&u16Data);
				m_u16NavNetID = u16Data;
				break;
		}
		
		// Make sure there were no file errors
		if (!pFile->Error())
		{
			// Get resources
		}
		else
		{
			sResult = -1;
			TRACE("CDoofus::Load(): Error reading from file!\n");
		}
	}

	return sResult;
	}


////////////////////////////////////////////////////////////////////////////////
// Save object (should call base class version!)
////////////////////////////////////////////////////////////////////////////////
int16_t CDoofus::Save(										// Returns 0 if successfull, non-zero otherwise
	RFile* pFile,											// In:  File to save to
	int16_t sFileCount)										// In:  File count (unique per file, never 0)
	{
	// Call the base class save to save the u16InstanceID
	int16_t sResult = CCharacter::Save(pFile, sFileCount);
	if (sResult == SUCCESS)
	{
		// Save common data just once per file (not with each object)
		if (ms_sFileCount != sFileCount)
			{
			ms_sFileCount = sFileCount;

			// Save static data
			pFile->Write(&ms_dAccUser);
			pFile->Write(&ms_dAccDrag);
			pFile->Write(&ms_dMaxVelFore);
			pFile->Write(&ms_dMaxVelBack);
			pFile->Write(&ms_dDegPerSec);
			}

		// Save object data

		// Save the instance ID for the parent NavNet so it can be connected
		// again after load
		pFile->Write(&m_ucSpecialBouy0ID);
		pFile->Write(&m_ucSpecialBouy1ID);
		U16 u16Data = CIdBank::IdNil;	// Safety.
		if (m_pNavNet)
			u16Data	= m_pNavNet->GetInstanceID();
		pFile->Write(&u16Data);

		sResult = pFile->Error();
	}

	return sResult;
	}


////////////////////////////////////////////////////////////////////////////////
// Startup object
////////////////////////////////////////////////////////////////////////////////
int16_t CDoofus::Startup(void)								// Returns 0 if successfull, non-zero otherwise
{
	CCharacter::Startup();

	// If we don't have a pointer to the Nav Net yet, get it from the ID
	if (m_pNavNet == NULL)
		{
		if (m_pRealm->m_idbank.GetThingByID((CThing**) &m_pNavNet, m_u16NavNetID) == 0)
			{
			// Verify it's a NavNet . . .
			if (m_pNavNet->GetClassID() == CNavigationNetID)
				{
				// Wahoo.
				}
			else
				{
				m_pNavNet	= NULL;
				}
			}

		// If still no nav net . . .
		if (m_pNavNet == NULL)
			{
			// Message depends on user mode.
			if (m_pRealm->m_flags.bEditing)
				{
				rspMsgBox(
					RSP_MB_ICN_STOP | RSP_MB_BUT_OK,
					g_pszAppName,
					g_pszDoofusCannotFindNavNet_EditMode_hu_hu,
					m_u16InstanceId,
					m_u16NavNetID);
				}
			else
				{
				rspMsgBox(
					RSP_MB_ICN_INFO | RSP_MB_BUT_OK,
					g_pszAppName,
					g_pszDoofusCannotFindNavNet_PlayMode_hu_hu,
					m_u16InstanceId,
					m_u16NavNetID);
				}

			// Use the current NavNet.
			m_pNavNet	= m_pRealm->GetCurrentNavNet();
			if (m_pNavNet)
				{
				// Remember to reset our ID so this doesn't happen again.
				m_u16NavNetID	= m_pNavNet->GetInstanceID();
				}
			}
		}

	// Clear stuck counter
	m_sStuckCounter = 0;

	// Init other stuff
	m_dVel = 0.0;
	m_dRot = 0.0;
	m_dAnimRot = 0.0;
	m_sPrevHeight = (int16_t) m_dY;
	m_sRotateDir = 0;

	// Set up the detection smash
	m_smashDetect.m_pThing = this;
	m_smashDetect.m_bits = 0;
	m_smashDetect.m_sphere.sphere.lRadius = ms_lDetectionRadius;

	// Set up the fire avoidance smash
	m_smashAvoid.m_sphere.sphere.X = m_dX + (rspCos(m_dRot) * ms_lAvoidRadius);
	m_smashAvoid.m_sphere.sphere.Y = m_dY;
	m_smashAvoid.m_sphere.sphere.Z = m_dZ - (rspSin(m_dRot) * ms_lAvoidRadius);
	m_smashAvoid.m_sphere.sphere.lRadius = ms_lAvoidRadius;
	m_smashAvoid.m_bits = 0;
	m_smashAvoid.m_pThing = this;

	// Setup weapon sprite.
	m_spriteWeapon.m_sInFlags	= CSprite::InHidden;
	m_sprite.AddChild(&m_spriteWeapon);


	// Start in a neutral state
	m_state = State_Idle;
	// Hello, world.
	m_pRealm->RegisterBirth(m_bCivilian);
	// Note that we've registered our birth so we know later that we need to
	// register our death.
	m_bRegisteredBirth	= true;

	// Set tunable values to their doofus defaults
	m_lShootTimeout = ms_lShootTimeoutMin + ((11-m_pRealm->m_flags.sDifficulty) * ms_lShootTimeoutInc);
	m_lRunShootInterval = ms_lRunShootInterval;
	m_lShotReactionTimeout = ms_lShotTimeout;

	return 0;
}


////////////////////////////////////////////////////////////////////////////////
// Called by editor to init new object at specified position
////////////////////////////////////////////////////////////////////////////////
int16_t CDoofus::EditNew(									// Returns 0 if successfull, non-zero otherwise
	int16_t sX,												// In:  New x coord
	int16_t sY,												// In:  New y coord
	int16_t sZ)												// In:  New z coord
{
	int16_t sResult = 0;

	CCharacter::EditNew(sX, sY, sZ);	

	// Since we were just created in the editor, set our nav net to the
	// current one for this realm.
	m_pNavNet = m_pRealm->GetCurrentNavNet();
	m_u16NavNetID = m_pNavNet->GetInstanceID();

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Render - This override version temporarily replaces the m_dRot with the
//				m_dAnimRot so that it draws the person in the desired facing 
//				direction.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Render(void)
{
	double dSaveRotation = m_dRot;
	m_dRot = m_dAnimRot;

	CCharacter::Render();
	
	m_dRot = dSaveRotation;

	CAnim3D*		panimWeapon	= NULL;

	if (m_panimCur->m_pevent)
	{	
		// Get current event.
		U8	u8Event	= *( (U8*)(m_panimCur->m_pevent->GetAtTime(m_lAnimTime) ) );

		// If gun not hidden by Randy . . .
		if (u8Event < 10)
		{
			panimWeapon	= GetWeaponAnim(m_eWeaponType);
			// If we got an anim . . .
			if (panimWeapon)
			{
				// If this anim is empty (and not intentional) . . .
				if (panimWeapon->m_pmeshes == NULL && m_eWeaponType != TotalIDs)
				{
					// Get fallback weapon, if any.
					panimWeapon	= GetWeaponAnim(m_eFallbackWeaponType);
				}
			}
		}
	}

	// If we have a visible weapon . . .
	if (panimWeapon)
	{
		// If we have the necessary components . . .
		if (panimWeapon->m_pmeshes && m_panimCur->m_ptransWeapon)
		{
			// Show weapon sprite.
			m_spriteWeapon.m_sInFlags	&= ~CSprite::InHidden;

			m_spriteWeapon.m_pmesh		= panimWeapon->m_pmeshes->GetAtTime(m_lAnimTime);
			m_spriteWeapon.m_psop		= panimWeapon->m_psops->GetAtTime(m_lAnimTime);
			m_spriteWeapon.m_ptex		= panimWeapon->m_ptextures->GetAtTime(m_lAnimTime);
			m_spriteWeapon.m_psphere	= panimWeapon->m_pbounds->GetAtTime(m_lAnimTime);
			m_spriteWeapon.m_ptrans		= m_panimCur->m_ptransWeapon->GetAtTime(m_lAnimTime);
		}
		else
		{
			// Hide weapon sprite.
			m_spriteWeapon.m_sInFlags	|= CSprite::InHidden;
		}
	}
	else
	{
		// Hide weapon sprite.
		m_spriteWeapon.m_sInFlags	|= CSprite::InHidden;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Called by editor to render object
////////////////////////////////////////////////////////////////////////////////
void CDoofus::EditRender(void)
{
	// In some cases, object's might need to do a special-case render in edit
	// mode because Startup() isn't called.  In this case it doesn't matter, so
	// we can call the normal Render().
	Render();
}

////////////////////////////////////////////////////////////////////////////////
// SelectDude
////////////////////////////////////////////////////////////////////////////////

int16_t CDoofus::SelectDudeBouy(void)
{
	int16_t sReturn = SUCCESS;
//	CDude* pDude;
//	CBouy* pBouytest;

	if (SelectDude() == SUCCESS)
		{
		CDude*	pdude;
		if (m_pRealm->m_idbank.GetThingByID((CThing**)&pdude, m_idDude) == 0)
			{
			m_ucDestBouyID = m_pNavNet->FindNearestBouy(pdude->GetX(), pdude->GetZ());
			}
		}
	else
		sReturn = FAILURE;

/*
	if (m_pRealm->m_asClassNumThings[CThing::CDudeID] > 0)
	{
		pDude = (CDude*) m_pRealm->m_aclassHeads[CThing::CDudeID].GetNext();
		m_ucDestBouyID = m_pNavNet->FindNearestBouy(pDude->GetX(), pDude->GetZ());
	}
	else
	{
		if (m_pNavNet->GetNumNodes() < 1)
			sReturn = FAILURE;
		else
		{
			pBouytest = NULL;
			while (pBouytest == NULL)		
			{
				m_ucDestBouyID = GetRandom() % m_pNavNet->GetNumNodes();
				pBouytest = m_pNavNet->GetBouy(m_ucDestBouyID);		
			}
		}
	}
*/
	return sReturn;

}

////////////////////////////////////////////////////////////////////////////////
// SelectRandomBouy - make sure it exists before setting it
////////////////////////////////////////////////////////////////////////////////

uint8_t CDoofus::SelectRandomBouy(void)
{
	uint8_t ucSelect = 0;
	CBouy* pBouy = NULL;

	if (m_pNavNet->GetNumNodes() <= 1)
		return 0;

	while (pBouy == NULL)
	{
		ucSelect = GetRandom() % m_pNavNet->GetNumNodes();
		pBouy = m_pNavNet->GetBouy(ucSelect);
	}
	return ucSelect;
}

////////////////////////////////////////////////////////////////////////////////
// SelectDude - Picks the closest dude from the dude list and assignes it to
//					 this enemy's CDude pointer.
////////////////////////////////////////////////////////////////////////////////

int16_t CDoofus::SelectDude(void)
{
//	Things::iterator i;
//	Things* pDudes;

	m_idDude = CIdBank::IdNil;
	uint32_t	ulSqrDistance;
	uint32_t	ulCurSqrDistance	= 0xFFFFFFFF;
	uint32_t	ulDistX;
	uint32_t	ulDistZ;
//	pDudes = m_pRealm->m_apthings[CThing::CDudeID];
	CDude*	pdude;

	CListNode<CThing>* pDudeList = m_pRealm->m_aclassHeads[CThing::CDudeID].m_pnNext;

	while (pDudeList && pDudeList->m_powner)
	{
		pdude = (CDude*) pDudeList->m_powner;
		// If this dude is not dead . . .
		if (pdude->m_state != State_Dead)
		{
			ulDistX	= pdude->m_dX - m_dX;
			ulDistZ	= pdude->m_dZ - m_dZ;
			ulSqrDistance	= ulDistX * ulDistX + ulDistZ * ulDistZ;
			if (ulSqrDistance < ulCurSqrDistance)
			{
				// This one is closer.
				ulCurSqrDistance	= ulSqrDistance;
				m_idDude	= pdude->GetInstanceID();
			}
		}
		pDudeList = pDudeList->m_pnNext;
	}

	return (m_idDude != CIdBank::IdNil) ? SUCCESS : FAILURE;
}

////////////////////////////////////////////////////////////////////////////////
// FindDirection - gives the direction toward the cDude
////////////////////////////////////////////////////////////////////////////////

int16_t CDoofus::FindDirection()
{
	int16_t sAngle;
	double dDudeX;
	double dDudeZ;
	double dX;
	double dZ;

	if (m_idDude == CIdBank::IdNil)
		SelectDude();

	if (m_idDude != CIdBank::IdNil)
	{
		CDude*	pdude;
		if (m_pRealm->m_idbank.GetThingByID((CThing**)&pdude, m_idDude) == 0)
		{
			dDudeX = pdude->GetX();
			dDudeZ = pdude->GetZ();
			dX = dDudeX - m_dX;
			dZ = m_dZ - dDudeZ;
			sAngle = rspATan(dZ, dX);
		}
		else
			sAngle = m_dRot;
	}
	else
		sAngle = m_dRot;

	return sAngle;
}

////////////////////////////////////////////////////////////////////////////////
// Return the square of the distance to the guy (to test for proximity
////////////////////////////////////////////////////////////////////////////////

double CDoofus::SQDistanceToDude()
{
	double dSquareDistance	= 0.0;
	double dX;
	double dZ;
		
	if (m_idDude == CIdBank::IdNil)
		SelectDude();

	if (m_idDude != CIdBank::IdNil)
	{
		CDude*	pdude;
		if (m_pRealm->m_idbank.GetThingByID((CThing**)&pdude, m_idDude) == 0)
		{
			dX = pdude->GetX() - m_dX;
			dZ = pdude->GetZ() - m_dZ;
			dSquareDistance = (dX * dX) + (dZ * dZ);
		}
	}

	return dSquareDistance;
}


////////////////////////////////////////////////////////////////////////////////
// AlignToBouy - When timer is up, recalculate direction to bouy
////////////////////////////////////////////////////////////////////////////////

void CDoofus::AlignToBouy(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	if (lThisTime > m_lAlignTimer || m_bRecentlyStuck)
	{
		// If he got stuck and is now free, pick a new bouy
		if (m_bRecentlyStuck)
		{
			m_bRecentlyStuck = false;
			m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);

			if (m_ucNextBouyID > 0)
			{
				m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
				if (m_pNextBouy != NULL)
				{
					m_sNextX = m_pNextBouy->GetX();
					m_sNextZ = m_pNextBouy->GetZ();			
				}
			}
		}
		m_lAlignTimer = lThisTime + ms_lDefaultAlignTime;
		m_dAnimRot = m_dRot = rspATan(m_dZ - m_sNextZ, m_sNextX - m_dX);
	}
}

////////////////////////////////////////////////////////////////////////////////
// TryClearDirection - Given an angle, and a variance, checks the given angle
//							  to see if it is clear.  If it is blocked by walls or fire
//							  it will try the max variance in either direction for a 
//							  clear path and set the angle if it finds one.  If these
//							  three angles fail, it will return false.
//
// Enemy guys can use this to attempt several paths before moving.  If it fails
// then they can fall back on using a random direction, or they could try again
//	with a completely different angle and variance.
////////////////////////////////////////////////////////////////////////////////

bool CDoofus::TryClearDirection(double* pdRot, int16_t sVariance)
{
	bool bFoundPath = false;
	int16_t sTries = 0;
	int16_t sX, sY, sZ;
	double dRotAttempt = *pdRot;
	CThing* pthing;

	while (!bFoundPath && sTries < 2)
	{
		// Check clear path
		if (IsPathClear(							// Returns true, if the entire path is clear.
														// Returns false, if only a portion of the path is clear.
														// (see *psX, *psY, *psZ, *ppthing).
			m_dX,										// In:  Starting X.
			m_dY,										// In:  Starting Y.
			m_dZ,										// In:  Starting Z.
			dRotAttempt,							// In:  Rotation around y axis (direction on X/Z plane).
			10,										// In:  Rate at which to scan ('crawl') path in pixels per
														// iteration.
														// NOTE: We scan terrain using GetFloorAttributes()
														// so small values of sCrawl are not necessary.
														// NOTE: We could change this to a speed in pixels per second
														// where we'd assume a certain frame rate.
			200,										// In:  Range on X/Z plane.
			m_smash.m_sphere.sphere.lRadius,	// In:  Radius of path traverser.
			MaxStepUpThreshold,					// In:  Max traverser can step up.
			CSmash::Fire,							// In:  Mask of CSmash bits that would terminate path.
			CSmash::Bad | CSmash::Good,		// In:  Mask of CSmash bits that would not affect path.
			CSmash::Dead,							// In:  Mask of CSmash bits that cannot affect path.
			&sX,										// Out: Point of intercept, if any, on path.
			&sY,										// Out: Point of intercept, if any, on path.
			&sZ,										// Out: Point of intercept, if any, on path.
			&pthing,									// Out: Thing that intercepted us or NULL, if none.
			&m_smash)								// In:  Optional CSmash to exclude or NULL, if none.
			== false)
			{
				switch (sTries)
				{
					case 0:
						dRotAttempt = rspMod360(dRotAttempt + (sVariance/2));
						break;
					case 1:
						dRotAttempt = rspMod360(dRotAttempt - sVariance);
						break;
				}
			}
			else
			{
				bFoundPath = true;
				*pdRot = dRotAttempt;
			}

			sTries++;
	}

	return bFoundPath;
}

////////////////////////////////////////////////////////////////////////////////
// TryClearShot - Works like the TryClearDirection, but doesn't check for 
//						fire, only walls.  So it knows if it could hit the target
//						from here.
////////////////////////////////////////////////////////////////////////////////

bool CDoofus::TryClearShot(double dRot, int16_t sVariance)
{
	bool bFoundPath = false;

	// If we have a weapon transform . . .
	if (m_panimCur->m_ptransRigid)
	{
		int16_t sTries = 0;
		int16_t sX, sY, sZ;
		CThing* pthing = NULL;
		double dRotAttempt = dRot;

		// Do a translation to the weapon position so that it is at the correct
		// height for checking

		double	dMuzzleX, dMuzzleY, dMuzzleZ;
		GetLinkPoint(														// Returns nothing.
			m_panimCur->m_ptransRigid->GetAtTime(m_lAnimTime),	// In:  Transform specifying point.
			&dMuzzleX,														// Out: Point speicfied.
			&dMuzzleY,														// Out: Point speicfied.
			&dMuzzleZ);														// Out: Point speicfied.			// Update execution point via link point.

		while (!bFoundPath && sTries < 2)
		{
			// Check clear path
/*
				// This one checks terrain and for people in the way
				if (IsPathClear(
					(short) (m_dX + dMuzzleX),			// Start x position
					(short) (m_dY + dMuzzleY),			// Start y position
					(short) (m_dZ + dMuzzleZ),			// Start z position
					(short) dRotAttempt,					// Angle of shot
					4,											// Crawl rate
					rspSqrt(CDoofus::SQDistanceToDude()),
					2,											// radius of traverser
					2,											// vertical tolerance
					CSmash::Bad,							// Bits that would terminate path
					0,											// Bits that would not affect path
					0,											// Bits that cannot affect path
					&sX,										// pointer to terminating point
					&sY,										// pointer to terminating point
					&sZ,										// pointer to terminating point
					&pthing,									// handle to terminating target
					&m_smash)								// exclude your own smash
					== false)
*/
				// This one only checks terrain
				if (m_pRealm->IsPathClear(
					 m_dX + dMuzzleX,
					 m_dY + dMuzzleY,
					 m_dZ + dMuzzleZ,
					 (int16_t) dRotAttempt,
					 3,
					 rspSqrt(CDoofus::SQDistanceToDude()),
					 0,
					 &sX,
					 &sY,
					 &sZ)
					 == false)

				{
					switch (sTries)
					{
						case 0:
							dRotAttempt = rspMod360(dRotAttempt + (sVariance/2));
							break;
						case 1:
							dRotAttempt = rspMod360(dRotAttempt - sVariance);
							break;
					}
				}
				else
				{
					bFoundPath = true;
				}

				sTries++;
		}
	}

	return bFoundPath;
}

////////////////////////////////////////////////////////////////////////////////
// Update - default implementation of logic for enemy characters
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Update(void)
{
	if (!m_sSuspend)
	{
		int32_t lThisTime = m_pRealm->m_time.GetGameTime();

		// Check for new messages that may change the state
		ProcessMessages();

		// If he has no network, then he should stick to Guard mode
//		if (m_pNavNet == NULL)
//			m_state = State_Guard;

		// See if there are any pylons nearby that he wants to use.
		if (m_state == State_Idle || 
		    m_state == State_Wait ||
			 m_state == State_Hunt)
			Logic_PylonDetect();
	 
		switch (m_state)
		{
			case State_Guard:
				Logic_Guard();
				break;

			case State_PopBegin:
				Logic_PopBegin();
				break;

			case State_PopWait:
				Logic_PopWait();
				break;

			case State_Popout:
				Logic_Popout();
				break;

			case State_RunShootBegin:
				Logic_RunShootBegin();
				break;

			case State_RunShoot:
				Logic_RunShoot();
				break;

			case State_RunShootWait:
				Logic_RunShootWait();
				break;

			case State_Shoot:
				Logic_Shoot();
				break;

			case State_Hunt:
				Logic_Hunt();
				break;

			case State_HuntNext:
				Logic_MoveNext();
				break;

			case State_Shot:
				Logic_Shot();
				break;

			case State_BlownUp:
				Logic_BlownUp();
				break;

			case State_Burning:
				Logic_Burning();
				break;

			case State_Die:
				Logic_Die();
				break;

			case State_Dead:
				CCharacter::OnDead();
				delete this;
				return;
				break;

		}	

		// Determine appropriate position for main smash.
		PositionSmash();

		// Update the smash.
		m_pRealm->m_smashatorium.Update(&m_smash);
		
		m_lPrevTime = lThisTime;

		// Call base class //////////////////////////////////////////////////////

		CCharacter::Update();
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Shot - You got shot so check your damage
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Shot(void)
{
	if (m_stockpile.m_sHitPoints > NEAR_DEATH_HITPOINTS)
	// Restore previous state & go back to what you were doing
	{
		// If your shot animation is over...
		if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
		{
			// Add this check for low health -> retreat
			// if (m_stockpile.m_sHitPoints < DefHitPoints / 2)
			//{
			//	m_state = State_Retreat;
			//	m_panimCur = &m_animRun;
			//	m_lAnimTime = 0;
			//	m_dRot = rspMod360(CDoofus::FindDirection() + 180);
			//	if (CDoofus::TryClearDirection(&m_dRot, 90) == false)
			//		m_dRot = rspMod360(GetRandom());
			//}
			// 
			// else // go back to previous state
			m_state = m_ePreviousState;
			m_panimCur = m_panimPrev;
			m_lAnimTime = 0;
		}
		else
		// Else continue shot animation
		{
			m_lAnimTime += m_pRealm->m_time.GetGameTime() - m_lPrevTime;
		}

		Cheater();
	}
	else
	// else you are dead or writhing so die
	{
		m_state = State_Die;
		m_panimCur = &m_animDie;
		m_lAnimTime = 0;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_BlownUp - You were blown up so pop up into the air and come down again
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_BlownUp(void)
{
	// Make him animate
	m_lAnimTime += (m_pRealm->m_time.GetGameTime() - m_lPrevTime);

	if (!WhileBlownUp())
		m_state = State_Dead;
	else
		UpdateFirePosition();

	Cheater();
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Burning - You are on fire, so run aroudn burning until you are dead
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Burning(void)
{
	m_lAnimTime += m_pRealm->m_time.GetGameTime() - m_lPrevTime;

	if (!CCharacter::WhileBurning())
	{
		m_state = State_Die;
		m_lAnimTime = 0;
		m_panimCur = &m_animDie;
	}

	Cheater();
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Die - You are about to die, so look like it!
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Die(void)
{
	// Change our smashatorium bits
	// enable this if you want them to always writhe when dead - for testing
#if 0
	if ((m_smash.m_bits & CSmash::AlmostDead) == 0)
	{
		m_smash.m_bits |= CSmash::AlmostDead;
		m_stockpile.m_sHitPoints = 30;
	}
#else
	m_smash.m_bits |= CSmash::AlmostDead;
#endif

	// Send to back.
	m_sLayerOverride	= CRealm::LayerSprite1; 

	// When the die animation is finsihed, so are you.
	if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
	{
		if (m_stockpile.m_sHitPoints > 0)
		{
			m_state = State_Writhing;
			m_panimCur = &m_animWrithing;
			m_lAnimTime = 0;
			GameMessage msg;
			msg.msg_Writhing.eType = typeWrithing;
			msg.msg_Writhing.sPriority = 0;
			CThing* pDemon = m_pRealm->m_aclassHeads[CThing::CDemonID].GetNext();
			if (pDemon)
				SendThingMessage(&msg, pDemon);				
		}
		else
		{
			m_state = State_Dead;
		}
	}	
	else
	{
		CWeapon* pweapon;
		if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
		{
			// It should drop like a rock if its a throwing weapon or just delete it if it
			// is a launched weapon
			if (m_eWeaponType == CHeatseekerID || m_eWeaponType == CRocketID || m_eWeaponType == CNapalmID)
			{
				GameMessage msg;
				msg.msg_ObjectDelete.eType = typeObjectDelete;
				msg.msg_ObjectDelete.sPriority = 0;
				SendThingMessage(&msg, pweapon);
			}
			else
			{
				pweapon->m_dHorizVel = (GetRandom() % (int16_t) CGrenade::ms_dThrowHorizVel);
				m_dShootAngle = pweapon->m_dRot = GetRandom() % 360;
				ShootWeapon();
			}
		}
		m_lAnimTime += m_pRealm->m_time.GetGameTime() - m_lPrevTime;

#if 0  // causes people to spin when they die. --ryan.
		// Rotate him so that he doesn't fall the same way
		// Don't rotate again if he was writhing and has been executed
		if (m_panimCur != &m_animExecuted)
		{
			short sRot = GetRandom() % 2;
			if (sRot & 0x01)
				sRot++;

			if (((short) m_dRot) & 0x01)
				m_dAnimRot = m_dRot = rspMod360(m_dRot + sRot);
			else
				m_dAnimRot = m_dRot = rspMod360(m_dRot - sRot);
		}
#endif
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Writhing - Rolling around on the ground in pain, waiting to be
//						  executed or until the pain is no longer tolerable.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Writhing(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	int32_t lTimeDifference = lThisTime - m_lPrevTime;

	// Send to back.
	m_sLayerOverride	= CRealm::LayerSprite1; 

	if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
	{
		if (--m_stockpile.m_sHitPoints <= 0)
		{
			// Die. - Run the executed anim first to transition between the
			// writhing animation and being dead, otherwise some characters will
			// die on their knees.
			m_state = State_Die;
			m_panimCur = &m_animExecuted;
			m_lAnimTime = 0;
//			m_state = State_Dead;
		}
		else
		{
			// Whoa!  MOD EQUALS is deep, man!
			m_lAnimTime	%= m_panimCur->m_psops->TotalTime();
			// Now you can tune the amount of blood if it gets too thick.
			if ((++m_usBloodCounter % 2) == 0)
				MakeBloodPool();

			if (lThisTime >= m_lTimer)
			{
				// Launch a sample and get the duration of the sample.
				int32_t	lSampleDuration;
				PlaySoundWrithing(&lSampleDuration);

				m_lTimer	= lThisTime	+ lSampleDuration + MS_BETWEEN_SAMPLES;
			}
		}
	}
	else
	{	
		CWeapon*	pweapon;
		if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
		{
			// It should drop like a rock if its a throwing weapon or just delete it if it
			// is a launched weapon
			if (m_eWeaponType == CHeatseekerID || m_eWeaponType == CRocketID || m_eWeaponType == CNapalmID)
			{
				GameMessage msg;
				msg.msg_ObjectDelete.eType = typeObjectDelete;
				msg.msg_ObjectDelete.sPriority = 0;
				SendThingMessage(&msg, pweapon);
			}
			else
			{
				pweapon->m_dHorizVel = (GetRandom() % (int16_t) CGrenade::ms_dThrowHorizVel);
				m_dShootAngle = pweapon->m_dRot = GetRandom() % 360;
				ShootWeapon();
			}
		}
		m_lAnimTime += lTimeDifference;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Guard - Guard the area until a CDude is nearby
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Guard(void)
{
	int32_t lThisTime;
	int32_t lTimeDifference;
	double dSeconds;

	// Get new time
	lThisTime = m_pRealm->m_time.GetGameTime();
	lTimeDifference = lThisTime - m_lPrevTime;
	m_eCurrentAction = Action_Guard;

	// Calculate the elapsed time in seconds
	dSeconds = (double)(lThisTime - m_lPrevTime) / 1000.0;

//	if (m_panimCur != &m_animStand)
//	{
//		m_panimCur = &m_animStand;
//		m_lAnimTime = 0;
//	}
	m_dRot = m_dAnimRot = m_dShootAngle = FindDirection();
	RunIdleAnimation();

	// We assume at first that nothing much is going on in the area, so he
	// only evaluates CDude position every once in a while to cut down on
	// the processing.
	if (lThisTime > m_lTimer)
	{
		m_eCurrentAction = Action_Guard;
		m_lTimer = lThisTime + m_lGuardTimeout;
		if (SelectDude() == SUCCESS && SQDistanceToDude() < ms_dGuardDistance)
		{	
			if (CDoofus::TryClearShot(m_dRot, 20) == true)
			{
				CWeapon*	pweapon	= PrepareWeapon();
				if (pweapon != NULL)
				{
					// Keep it hidden, for now.
					pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
					pweapon->SetRangeToTarget(rspSqrt(SQDistanceToDude()));
				}
				m_panimCur = &m_animShoot;
				m_lAnimTime = 0;
				SelectDude();
				m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();	
				m_state = State_Shoot;
				m_eNextState = State_Guard;
				m_lTimer = lThisTime + m_lShootTimeout;
			}
			else
			{
				m_lTimer = lThisTime + m_lShootTimeout;
			}
		}
	}
	else
		ReevaluateState();
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Patrol - Keep an eye on the CDude and look like you're doing something
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Patrol(void)
{
	// Turn to face dude but do not attack yet
	m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Hunt - Find a CDude, use the bouy network to get to him wherever he is
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Hunt(void)
{
	// Find the destination bouy (one closest to the Dude)
	m_eDestinationState = State_HuntHold;
	SelectDudeBouy();
	m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
	if (m_ucNextBouyID > 0)
	{
		m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
		if (m_pNextBouy != NULL)
		{
			m_sNextX = m_pNextBouy->GetX();
			m_sNextZ = m_pNextBouy->GetZ();			
			m_state = State_HuntNext;
			m_lTimer = m_pRealm->m_time.GetGameTime() + ms_lReseekTime;
		}
		else
		{
			// Couldn't get bouy position, so stay put
			m_state = State_Guard;
			m_eCurrentAction = Action_Guard;
		}	
	}
	else
	{
		// Couldn't find a bouy so stay put
		m_state = State_Guard;
		m_eCurrentAction = Action_Guard;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_HuntHold - This state means that you were trying to advance to the
//						  Dude and have got to the bouy closest to the Dude.  The
//						  Logic table can either break you out into Engage or 
//						  whatever, or you will stay in this state where you will 
//						  be in a guard mode, but always looking to see if you should
//						  move to a closer bouy once the Dude changes his position.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_HuntHold(void)
{
	// Evaluate the logic table suggestion to see if you should change states
	m_eCurrentAction = Action_AdvanceHold;
	if (!ReevaluateState())
	{
		// Check to see if the dude has moved and you should move	
		int32_t lThisTime = m_pRealm->m_time.GetGameTime();

		if (lThisTime > m_lTimer)
		{
			m_lTimer = lThisTime + 1000;
			SelectDudeBouy();
			m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
			// See if he should move closer.
			if (m_ucNextBouyID != m_ucDestBouyID)
			{
				m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
				if (m_pNextBouy != NULL)
				{
					m_sNextX = m_pNextBouy->GetX();
					m_sNextZ = m_pNextBouy->GetZ();
					m_lTimer = lThisTime + ms_lReseekTime;
					m_state = State_HuntNext;
					m_eCurrentAction = Action_Advance;
				}
			}
			// Else see if he is close enough to take a shot.
			else
			{
				if (SelectDude() == SUCCESS && SQDistanceToDude() < ms_dGuardDistance)
				{	
					CWeapon*	pweapon	= PrepareWeapon();
					if (pweapon != NULL)
					{
						// Keep it hidden, for now.
						pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
						pweapon->SetRangeToTarget(rspSqrt(SQDistanceToDude()));
					}
					m_panimCur = &m_animShoot;
					m_lAnimTime = 0;
					SelectDude();
					m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();	
					m_state = State_Shoot;
					m_eNextState = State_HuntHold;
					m_lTimer = lThisTime + m_lGuardTimeout;
				}
			}
		}

		// Check to see if the dude is nearby and you should shoot at him.
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_HuntNext - going to next bouy in network en route to final destination
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_MoveNext(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	int32_t lElapsedTime = lThisTime - m_lPrevTime;
	double dSeconds = lElapsedTime / 1000.0;
	double dStartX = m_dX;
	double dStartZ = m_dZ;

	if (!ReevaluateState())
	{
		// Make sure its using the correct animation
		if (m_state == State_MarchNext || m_state == State_WalkNext)
		{
			if (m_panimCur != &m_animWalk)
			{
				m_panimCur = &m_animWalk;
				m_lAnimTime = 0;
			}
			else
				m_lAnimTime += lElapsedTime;

			// Cheat and make him walk slower by continuously
			// resetting the velocity.
			m_dVel = ms_dMarchVelocity;
		}
		// Normally use run
		else
		{
			if (m_panimCur != &m_animRun)
			{
				m_panimCur = &m_animRun;
				m_lAnimTime = 0;
			}
			else
				m_lAnimTime += lElapsedTime;
		}

		AlignToBouy();
		m_dAcc = ms_dAccUser;

		DeluxeUpdatePosVel(dSeconds);

		// If not moving when you are trying to, rotate 
		if (m_dX == dStartX && m_dZ == dStartZ)
		{
			if (m_sRotateDir)
				m_dAnimRot = m_dRot = rspMod360(m_dRot + 20);
			else
				m_dAnimRot = m_dRot = rspMod360(m_dRot - 20);
			// Reset alignment timer so he doesn't reseek the bouy direction immediately
			m_lAlignTimer = lThisTime + ms_lDefaultAlignTime;
			// Set a flag indicating that you were stuck, so we know what bouy to look
			// for once you get free
			m_bRecentlyStuck = true;
		}
		else
		{
			m_sRotateDir = GetRandom() % 2;
		}

		// See if we are at the next bouy yet
		double dX = m_dX - (double) m_sNextX;
		double dZ = m_dZ - (double) m_sNextZ;
		double dsq = (dX * dX) + (dZ * dZ);
		if (dsq < 5*5) // Was 10*10 for a long time, trying smaller to see if it keeps guys from getting stuck
		{
			uint8_t ucNext = m_pNextBouy->NextRouteNode(m_ucDestBouyID);
			if (ucNext == 0 || ucNext == 255) // you are here or you are lost
			{
				m_state = m_eDestinationState;
				switch (m_state)
				{
					case State_Engage:
						m_eCurrentAction = Action_Engage;
						break;

					case State_Guard:
						m_eCurrentAction = Action_Guard;
						break;

					case State_March:
						m_eCurrentAction = Action_March;
						break;

					case State_Walk:
						m_eCurrentAction = Action_Walk;
						break;
				}
			}
			else
			{
				// If the reseek timer has expired, find the Dude bouy 
				// again since he may have moved
				if (lThisTime > m_lTimer || ucNext == 255)
				{
					if (m_state == State_HuntNext)
						m_state = State_Hunt;
					else
						m_lTimer = lThisTime + ms_lReseekTime;
				}
				else
				{
					m_ucNextBouyID = ucNext;
					m_pNextBouy = m_pNavNet->GetBouy(ucNext);
					m_sNextX = m_pNextBouy->GetX();
					m_sNextZ = m_pNextBouy->GetZ();
				}
			}
		}
	}
	// Check for fire in your path so you don't keep moving
	AvoidFire();
}

////////////////////////////////////////////////////////////////////////////////
// Logic_PositionSet - Get yourself set up to get into range for shooting
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_PositionSet(void)
{
	if (!ReevaluateState())
	{
		int32_t lThisTime = m_pRealm->m_time.GetGameTime();
		int16_t sVarRot = GetRandom() % 40;
		double dTargetDist = SQDistanceToDude();
		double dTestAngle;
		double dAngleTurn;

		// If he is out of range now, reset desired range to the middle value.
		if (dTargetDist < ms_dMinFightDistanceSQ || dTargetDist > ms_dMaxFightDistanceSQ)
			dTargetDist = ms_dMedFightDistanceSQ;

		bool bFoundDirection = false;
		int16_t sAttempts = 0;

		while (!bFoundDirection && sAttempts < 8)
		{
			switch (sAttempts)
			{
				case 0:
					if (sVarRot & 0x01)
						dTestAngle = rspMod360(m_dRot + 40 + sVarRot);
					else
						dTestAngle = rspMod360(m_dRot - 40 - sVarRot);
					dAngleTurn = 40 + sVarRot;
					break;
					
				case 1:
					if (sVarRot & 0x01)
						dTestAngle = rspMod360(m_dRot - 40 - sVarRot);
					else
						dTestAngle = rspMod360(m_dRot + 40 + sVarRot);
					// Same dAngleTurn as last time
					break;
					
				case 2:
					if (sVarRot & 0x01)
						dTestAngle = rspMod360(m_dRot + 20 + sVarRot);
					else
						dTestAngle = rspMod360(m_dRot - 20 - sVarRot);
					dAngleTurn = 20 + sVarRot;
					break;
					
				case 3:
					if (sVarRot & 0x01)
						dTestAngle = rspMod360(m_dRot - 20 - sVarRot);
					else
						dTestAngle = rspMod360(m_dRot + 20 + sVarRot);
					// Same dAngleTurn as last time
					break;
					
				case 4:
					dTestAngle = rspMod360(m_dRot + 95);
					break;
					
				case 5:
					dTestAngle = rspMod360(m_dRot - 95);
					break;

				case 6:
					dTestAngle = m_dRot;
					break;

				case 7:
					dTestAngle = rspMod360(m_dRot + 180);
					break;
			}

			if (CDoofus::TryClearDirection(&dTestAngle, 30) == true)
			{
				bFoundDirection = true;
				if (sAttempts < 4)
				{
					m_sDistRemaining = 2 * 3.1415 * rspSqrt(dTargetDist) * ((180.0-(2.0*(dAngleTurn)))/360.0);
					m_lTimer = lThisTime + (1000 * (m_sDistRemaining / ms_dMaxVelFore));		
				}
				else
				{
					m_lTimer = 800 + GetRandom() % 400;
				}
			}
			else
			{
				sAttempts++;
			}
		}

		// If you found a clear direction, take it
		if (bFoundDirection)
		{
				m_dAnimRot = m_dRot = dTestAngle;
				// Set new animation
				m_dAcc = ms_dAccUser;
				m_state = State_PositionMove;
				m_panimCur = &m_animRun;
				m_lAnimTime = 0;
		}
		else
		// else stay where you are.
		{
			m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
			if (m_idDude != CIdBank::IdNil)
			{
				m_state = State_DelayShoot;
				m_lTimer = lThisTime + ms_lDelayShootTimeout;
				m_eNextState = State_PositionSet;
				m_sStuckCounter++;
				m_lAnimTime = 0;
				if (m_sStuckCounter > ms_sStuckLimit)
				{
					m_sStuckCounter = 0;
					m_lStuckTimeout = lThisTime + ms_lStuckRecoveryTime;
					ReevaluateState();
				}
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_DelayShoot - wait for timer to expire then shoot if clear
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_DelayShoot(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();

	// See if its time to shoot yet
	if (lThisTime > m_lTimer)
	{
		if (TryClearShot(m_dRot, 20) == true && SelectDude() == SUCCESS) 
		{
			CWeapon* pweapon = PrepareWeapon();
			if (pweapon != NULL)
			{
				pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
				pweapon->SetRangeToTarget(rspSqrt(SQDistanceToDude()));
			}
			m_panimCur = &m_animShoot;
			m_lAnimTime = 0;
			m_state = State_Shoot;
		}
		else
		// Skip the shot and go back to the correct state
		{
			m_state = m_eNextState;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_PositionMove - Move into position then shoot
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_PositionMove(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	int32_t lTimeDifference = lThisTime - m_lPrevTime;
	double dSeconds = lTimeDifference / 1000.0;

	if (lThisTime > m_lTimer)
	{
		m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
		// Check for clear shot before shooting.  If not clear, go back to moving
		if (CDoofus::TryClearShot(m_dRot, 20) == true && SelectDude() == SUCCESS)
		{
			CWeapon*	pweapon	= PrepareWeapon();
			if (pweapon != NULL)
			{
				// Keep it hidden, for now.
				pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
				pweapon->SetRangeToTarget(rspSqrt(SQDistanceToDude()));
			}
			m_state = State_Shoot;
			m_eNextState = State_PositionSet;
			m_panimCur = &m_animShoot;
			m_lAnimTime = 0;
		}
		// If path is blocked, go on the move again and don't shoot
		else
		{
			m_state = State_PositionSet;	
		}
	}
	else
	{
		double dLastPosX = m_dX;
		double dLastPosZ = m_dZ;

		DeluxeUpdatePosVel(dSeconds);
		m_lAnimTime += lTimeDifference;

		// If not moving when intending to, rotate one way or the other
		// to try to avoid the obstacle
		if (m_dVel != 0.0 && dLastPosX == m_dX && dLastPosZ == m_dZ)
		{
			if (((int16_t) m_dRot) & 0x01)
				m_dAnimRot = m_dRot = rspMod360(m_dRot + 20);
			else
				m_dAnimRot = m_dRot = rspMod360(m_dRot - 20);
		}
		// Avoid fire in your path
		AvoidFire();
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Engage - engage the Dude and start fighting at close range
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Engage(void)
{
	m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();
	m_state = State_PositionSet;
	m_sStuckCounter = 0;
}

////////////////////////////////////////////////////////////////////////////////
// Logic_BeSafe - Stay behind the SafeSpot bouy if the Dude is facing you
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_BeSafe(void)
{

}

////////////////////////////////////////////////////////////////////////////////
// Logic_Firefight - Shoot and move
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Firefight(void)
{

}

////////////////////////////////////////////////////////////////////////////////
// Logic_PylonDetect - If you detect that a pylon is near, find out what kind
//						     it is and decide if you will use it based on the
//							  suggested action.  This function is only called when
//							  the suggested action requires the use of a pylon.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_PylonDetect(void)
{
	CSmash* pSmashPylon;
	CPylon* pPylon = NULL;		  
	double dSmashedX = 0.0;
	double dSmashedZ = 0.0;

	m_bPylonSafeAvailable = false;
	m_bPylonPopoutAvailable = false;
	m_bPylonRunShootAvailable = false;

	// Update the detection smash.
	m_smashDetect.m_sphere.sphere.X = m_dX;
	m_smashDetect.m_sphere.sphere.Y = m_dY;
	m_smashDetect.m_sphere.sphere.Z = m_dZ;

	// For now when we detect a pylon we are assuming that this
	// character should use that logic.  Later we will have to 
	// decide based on the personality numbers and current state.
	if (m_pRealm->m_smashatorium.QuickCheckClosest(&m_smashDetect,
																  CSmash::Pylon,
																  0,
																  0,
																  &pSmashPylon))
	{
		ASSERT(pSmashPylon);
		pPylon = (CPylon*) pSmashPylon->m_pThing;
		ASSERT(pPylon);

		if (pSmashPylon->m_pThing != this)
		{
			dSmashedX = pSmashPylon->m_sphere.sphere.X;
			dSmashedZ = pSmashPylon->m_sphere.sphere.Z;
			// Check IsPathClear to that thing
			if (m_pRealm->IsPathClear(	// Returns true, if the entire path is clear.                 
													// Returns false, if only a portion of the path is clear.     
													// (see *psX, *psY, *psZ).                                    
					(int16_t) m_dX, 				// In:  Starting X.                                           
					(int16_t) m_dY, 				// In:  Starting Y.                                           
					(int16_t) m_dZ, 				// In:  Starting Z.                                           
					3.0, 							// In:  Rate at which to scan ('crawl') path in pixels per    
													// iteration.                                                 
													// NOTE: Values less than 1.0 are inefficient.                
													// NOTE: We scan terrain using GetHeight()                    
													// at only one pixel.                                         
													// NOTE: We could change this to a speed in pixels per second 
													// where we'd assume a certain frame rate.                    
					(int16_t) dSmashedX,		// In:  Destination X.                                        
					(int16_t) dSmashedZ,		// In:  Destination Z.                                        
					0,								// In:  Max traverser can step up.                      
					NULL,							// Out: If not NULL, last clear point on path.                
					NULL,							// Out: If not NULL, last clear point on path.                
					NULL,							// Out: If not NULL, last clear point on path.                
					true) )						// In:  If true, will consider the edge of the realm a path
													// inhibitor.  If false, reaching the edge of the realm    
													// indicates a clear path. 
			{                                
				switch (pPylon->m_msg.msg_Generic.eType)
				{
					case typeSafeSpot:
						m_bPylonSafeAvailable = true;
						m_pPylonStart = pPylon;
						break;

					case typePopout:
						m_bPylonPopoutAvailable = true;
						m_pPylonStart = pPylon;
						break;

					case typeShootCycle:
						m_bPylonRunShootAvailable = true;
						m_pPylonStart = pPylon;
						break;

					default:
						break;
				}
			}
		}
	}
	else
	{
		// If no pylons were nearby where he started, and he was just idle,
		// then set him up as a stationary guard for now.
		if (m_state == State_Idle || m_state == State_Wait)
		{	
			// See if there are bouys, if there aren't any, then stay in Guard mode
			if (m_pNavNet->GetNumNodes() > 1)
			{
				m_state = State_Hunt;
				m_eCurrentAction = Action_Advance;
			}
			else
			{
				m_state = State_Guard;
				m_eCurrentAction = Action_Guard;
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_HideBegin - Go to the hiding pylon and then wait once you get there
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_HideBegin(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	// If we'renot using the run animation yet then switch to it
	if (m_panimCur != &m_animRun)
	{
		m_panimCur = &m_animRun;
		m_lAnimTime = 0;
	}
	else
		m_lAnimTime += lThisTime - m_lPrevTime;

	// Set angle to pylon.
	m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
	m_dAcc = ms_dAccUser;

	int32_t lElapsedTime = m_pRealm->m_time.GetGameTime() - m_lPrevTime;
	double dSeconds = lElapsedTime / 1000.0;
	DeluxeUpdatePosVel(dSeconds);

	//	if close to pylon, go to next state
	// for now just check the square distance, but later, probably use
	// QuickCheckCloses in smash to see if you are there yet.
	double dX = m_dX - (double) m_sNextX;
	double dZ = m_dZ - (double) m_sNextZ;
	double dsq = (dX * dX) + (dZ * dZ);
	if (dsq < 300)
	{
		m_state = State_Hide;
	}
	// Check for fire in your path
	AvoidFire();
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Hide - Wait here and hope nobody sees you and blows you away.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Hide(void)
{
	ReevaluateState();
}

////////////////////////////////////////////////////////////////////////////////
// Logic_PopBegin - Once you have detected a nearby Pylon, begin the popout
//						  phase here by going to that pylon.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_PopBegin(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	double dStartX = m_dX;
	double dStartZ = m_dZ;

	// If we're not using the run animation yet then switch to it
	if (m_panimCur != &m_animRun)
	{
		m_panimCur = &m_animRun;
		m_lAnimTime = 0;
	}
	else
		m_lAnimTime += lThisTime - m_lPrevTime;

	m_eCurrentAction = Action_Popout;
	// Set angle to pylon.
	if (lThisTime > m_lAlignTimer)
	{
		m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
		m_lAlignTimer = lThisTime + 100;
		m_sRotateDir = GetRandom() % 2;
	}
	m_dAcc = ms_dAccUser;

	int32_t lElapsedTime = m_pRealm->m_time.GetGameTime() - m_lPrevTime;
	double dSeconds = lElapsedTime / 1000.0;
	DeluxeUpdatePosVel(dSeconds);

	// Check for fire in your path
	AvoidFire();

	// If not moving when meaning to, rotate to avoid obstacle
	if (m_dX == dStartX && m_dZ == dStartZ)
	{
		if (m_sRotateDir)
			m_dAnimRot = m_dRot = rspMod360(m_dRot + 20);
		else
			m_dAnimRot = m_dRot = rspMod360(m_dRot - 20);
		m_lAlignTimer = lThisTime + 100;
	}

	//	if close to pylon, go to next state
	// for now just check the square distance, but later, probably use
	// QuickCheckCloses in smash to see if you are there yet.
	double dX = m_dX - (double) m_sNextX;
	double dZ = m_dZ - (double) m_sNextZ;
	double dsq = (dX * dX) + (dZ * dZ);
	if (dsq < 300)
	{
		m_state = State_PopWait;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_PopWait - Wait at covered pylon until a CDude steps on the trigger
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_PopWait(void)
{
	// If the stand animation is not being used yet then switch to it.
//	if (m_panimCur != &m_animStand)
//	{
//		m_panimCur = &m_animStand;
//		m_lAnimTime = 0;
//	}
	RunIdleAnimation();

	if (!ReevaluateState())
	{
		m_eCurrentAction = Action_Popout;
		if (m_pRealm->m_time.GetGameTime() > m_lTimer && m_pPylonStart->Triggered())
		{
			// Set next pylon to go to 
			m_sNextX = m_pPylonEnd->GetX();
			m_sNextZ = m_pPylonEnd->GetZ();
			m_state = State_Popout;

			m_panimCur = &m_animRun;
			m_lAnimTime = 0;
		}
	}
	else
	{
		m_pPylonStart = NULL;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Popout - When at a popout bouy, if the guy is close, popout, shoot, 
//						and duck back behind the bouy.  If he is not in range, stay
//						behind the bouy and wait for some amount of time.  Reevaluate
//						logic choice based on enemy attributes.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Popout(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	int32_t lElapsedTime = lThisTime - m_lPrevTime;
	double dSeconds = lElapsedTime / 1000.0;
	m_lAnimTime += lElapsedTime;
	m_eCurrentAction = Action_Popout;

	// Go to next pylon
	m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
	m_dAcc = ms_dAccUser;
	DeluxeUpdatePosVel(dSeconds);

	// Check for fire in your path
	AvoidFire();

	// If you are at the pylon, then go to the next state
	//	if close to pylon, go to next state
	// for now just check the square distance, but later, probably use
	// QuickCheckCloses in smash to see if you are there yet.
	double dX = m_dX - (double) m_sNextX;
	double dZ = m_dZ - (double) m_sNextZ;
	double dsq;
	dsq = (dX * dX) + (dZ * dZ);
	if (dsq < 300)
	{
		m_state = State_Shoot;
		// After shooting, go back to beginning
		m_eNextState = State_PopBegin;
		m_lTimer = lThisTime + 2000 + GetRandom() % 2000;
		m_sNextX = m_pPylonStart->GetX();
		m_sNextZ = m_pPylonStart->GetZ();
		CWeapon*	pweapon	= PrepareWeapon();
		if (pweapon != NULL)
		{
			// Keep it hidden, for now.
			pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
		}
		m_panimCur = &m_animShoot;
		m_lAnimTime = 0;
		SelectDude();
		m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();	
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Shoot - run the animation to the proper time and then shoot the weapon
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Shoot(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	int32_t lTimeDifference = lThisTime - m_lPrevTime;

	// Switch behavior on weapon type.
	switch (m_eWeaponType)
	{
		case CThing::CFirestreamID:
			// If out of fire streams . . .
			if (m_u16IdWeapon == CIdBank::IdNil)
				{
				// Must prepare more fire.
				if (PrepareWeapon() == NULL)
					break;
				}
			// Intentional fall through.
		case CThing::CShotGunID:
		case CThing::CMachineGunID:
		case CThing::CPistolID:
		case CThing::CAssaultWeaponID:
		case CThing::CDoubleBarrelID:
		case CUziID:
		case CAutoRifleID:
		case CSmallPistolID:
		{
			// Get event.
			U8	u8Event	= *( (U8*)(m_panimCur->m_pevent->GetAtTime(m_lAnimTime) ) );
			// We don't care about show point for these non-object weapon types.
			// If it's time to fire the weapon . . .
			if (u8Event > 0 && lThisTime > m_lShootTimer)
			{
				// Fire!
				ShootWeapon();

				switch (m_eWeaponType)
				{
					case CThing::CShotGunID:
					case CThing::CDoubleBarrelID:
						m_lShootTimer = lThisTime + 2000;
						break;

					case CUziID:
					case CAutoRifleID:
					case CThing::CMachineGunID:
					case CThing::CAssaultWeaponID:
					case CThing::CFirestreamID:
						m_lShootTimer = lThisTime + 100;
						break;

					case CSmallPistolID:
					case CThing::CPistolID:
						m_lShootTimer = lThisTime + 300;
						break;
				}
			}
			break;
		}
		
		// This is the "I don't have a weapon" case (i.e., NoWeapon).
		case TotalIDs:
			break;

		default:
			if (m_u16IdWeapon != CIdBank::IdNil)
			{
				if (WhileHoldingWeapon(ms_u32CollideBitsInclude, ms_u32CollideBitsDontcare, ms_u32CollideBitsExclude) == true)
				{
					// Note we are done with the child.  Now we're just finishing the anim.
					ASSERT(m_u16IdWeapon == CIdBank::IdNil);
				}
			}
			break;
	}

	// See if we are at the end of the animation or we have no weapon . . .
	if (m_lAnimTime > m_panimCur->m_psops->TotalTime() || m_eWeaponType == TotalIDs)
	{
		m_state = m_eNextState;
	}
	// Else advance the animation timer
	else
	{
		m_lAnimTime += lTimeDifference;					
	}
	
}

////////////////////////////////////////////////////////////////////////////////
// Logic_ShootRun - Shoot while running - use the Shoot logic to control
//						  the firing of the shot and the animation, and just add on
//						  the motion update
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_ShootRun(void)
{
	double dSeconds = (m_pRealm->m_time.GetGameTime() - m_lPrevTime) / 1000.0;

	Logic_Shoot();

	DeluxeUpdatePosVel(dSeconds);

	// Check for fire in your path
	AvoidFire();
}

////////////////////////////////////////////////////////////////////////////////
// Logic_RunShootBegin - Set up the destination pylon and shooting interval
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_RunShootBegin(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	int32_t lElapsedTime = lThisTime - m_lPrevTime;
	double dStartX = m_dX;
	double dStartZ = m_dZ;

	// If we're not using the run animation yet, then switch to it
	if (m_panimCur != &m_animRun)
	{
		m_panimCur = &m_animRun;
		m_lAnimTime = 0;
	}						 
	else
		m_lAnimTime += lElapsedTime;

	// Set angle to pylon.
	if (lThisTime > m_lAlignTimer)
	{
		m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
		m_lAlignTimer = lThisTime + 100;
		m_sRotateDir = GetRandom() % 2;
	}
	m_dAcc = ms_dAccUser;

	double dSeconds = lElapsedTime / 1000.0;
	DeluxeUpdatePosVel(dSeconds);

	// Check for fire in your path
	AvoidFire();

	// If not moving when you meant to, start to rotate to avoid obstacles
	if (m_dX == dStartX && m_dZ == dStartZ)
	{
		if (m_sRotateDir)
			m_dAnimRot = m_dRot = rspMod360(m_dRot + 20);
		else
			m_dAnimRot = m_dRot = rspMod360(m_dRot - 20);
		m_lAlignTimer = lThisTime + 100;
	}

	// If close to pylon, go to nex state
	double dX = m_dX - (double) m_sNextX;
	double dZ = m_dZ - (double) m_sNextZ;
	double dsq = (dX * dX) + (dZ * dZ);
	if (dsq < 300)
	{
		// Find position of destinaiton pylon
		m_sNextX = m_pPylonEnd->GetX();
		m_sNextZ = m_pPylonEnd->GetZ();
		m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
		m_lTimer = lThisTime + m_lRunShootInterval;
//		m_state = State_RunShoot;
		m_state = State_RunShootWait;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_RunShoot - Run and Shoot logic.  When near a ShootCycle bouy, hide, 
//						  run out and shoot at a Dude, run towards other bouy, stop
//						  and shoot again, then duck behind the second bouy.  Repeat
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_RunShoot(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	int32_t lElapsedTime = lThisTime - m_lPrevTime;

	// If its not using the run animation, then switch to it
	if (m_panimCur != &m_animRun)
	{
		m_panimCur = &m_animRun;
//		m_lAnimTime = 0;
		m_dAnimRot = m_dRot;
	}

	if (!ReevaluateState())
	{
		// Advance the run animation
		m_lAnimTime += lElapsedTime;

		// If its time to shoot then do it.
		if (lThisTime > m_lTimer)
		{
			m_state = State_ShootRun;
			// After shooting, go back to beginning
			m_eNextState = State_RunShoot;
			CWeapon*	pweapon	= PrepareWeapon();
			if (pweapon != NULL)
			{
				// Keep it hidden, for now.
				pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
			}
			SelectDude();

			int16_t sTargetAngle = FindDirection();
			m_dShootAngle = sTargetAngle;
			int16_t sAngleCCL = rspMod360(sTargetAngle - m_dRot);
			int16_t sAngleCL  = rspMod360((360 - sTargetAngle) + m_dRot);
			int16_t sAngleDistance = MIN(sAngleCCL, sAngleCL);
			if (sAngleCCL < sAngleCL)
			// Rotate Counter Clockwise - Use left animations
			{
				if (sAngleDistance > 150)
				{
					m_panimCur = &m_animShootRunBack;
					m_dAnimRot = rspMod360(m_dRot + (sAngleDistance - 180));
				}
				else
				{
					if (sAngleDistance > 68)
					{
						m_panimCur = &m_animShootRunL1;
						m_dAnimRot = rspMod360(m_dRot + (sAngleDistance - 90));
					}
					else
					{
						if (sAngleDistance > 23)
						{
							m_panimCur = &m_animShootRunL0;
							m_dAnimRot = rspMod360(m_dRot + (sAngleDistance - 45));
						}
						else
						{
							m_panimCur = &m_animShootRun;
							m_dAnimRot = rspMod360(m_dRot + sAngleDistance);
						}
					}
				}
			}
			else
			// Rotate Clockwise - Use right animations
			{
				if (sAngleDistance > 150)
				{
					m_panimCur = &m_animShootRunBack;
					m_dAnimRot = rspMod360(m_dRot - (sAngleDistance - 180));
				}
				else
				{
					if (sAngleDistance > 68)
					{
						m_panimCur = &m_animShootRunR1;
						m_dAnimRot = rspMod360(m_dRot - (sAngleDistance - 90));
					}
					else
					{
						if (sAngleDistance > 23)
						{
							m_panimCur = &m_animShootRunR0;
							m_dAnimRot = rspMod360(m_dRot - (sAngleDistance - 45));
						}
						else
						{
							m_panimCur = &m_animShootRun;
							m_dAnimRot = rspMod360(m_dRot - sAngleDistance);
						}
					}
				}
			}

			m_lTimer = lThisTime + m_lRunShootInterval;
			m_lAnimTime = 0;
		}
		else
		{
			// Set angle to pylon.
			m_dAnimRot = m_dRot = FindAngleTo(m_sNextX, m_sNextZ);
			m_dAcc = ms_dAccUser;

			double dSeconds = lElapsedTime / 1000.0;
			DeluxeUpdatePosVel(dSeconds);

			// Check for fire in your path
			AvoidFire();

			//	if close to pylon, go to next state
			// for now just check the square distance, but later, probably use
			// QuickCheckCloses in smash to see if you are there yet.
			double dX = m_dX - (double) m_sNextX;
			double dZ = m_dZ - (double) m_sNextZ;
			double dsq = (dX * dX) + (dZ * dZ);
			if (dsq < 300)
			{
				m_state = State_RunShootWait;
				m_lTimer = lThisTime + m_lGuardTimeout;
				// Make him face the other way.
				m_dAnimRot = m_dRot = FindAngleTo(m_pPylonEnd->GetX(), m_pPylonEnd->GetZ());
			}
		}
	}
	else
	{
		m_pPylonStart = NULL;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_RunShootWait - Wait at the endpoint before going again.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_RunShootWait(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();
	
	RunIdleAnimation();

	// If the waiting is over and its triggered
	if (lThisTime > m_lTimer && m_pPylonStart->Triggered())
	{
		CPylon* pPylonTemp = m_pPylonStart;
		m_pPylonStart = m_pPylonEnd;
		m_pPylonEnd = pPylonTemp;
		m_state = State_RunShoot;
		m_sNextX = m_pPylonStart->GetX();
		m_sNextZ = m_pPylonStart->GetZ();
		m_dVel = 0.0;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Retreat - Once you are low on health, you may choose this logic to
//						 run away from the Dude, taking shelter wherever you can.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Retreat(void)
{
	// Get your closest bouy, pick a random destination bouy, then
	// start traversing 3 away from your current one and set that
	// as the destination bouy, then switch to State_MoveNext

	m_eDestinationState = State_Guard;
	m_ucDestBouyID = SelectRandomBouy();
	m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
	if (m_ucNextBouyID > 0)
	{
		m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
		if (m_pNextBouy != NULL)
		{
			m_sNextX = m_pNextBouy->GetX();
			m_sNextZ = m_pNextBouy->GetZ();
			m_state = State_MoveNext;
			m_eCurrentAction = Action_Retreat;
		}
		else
		{
			// Couldn't find bouy so stay engaged
			m_state = State_Engage;
			m_eCurrentAction = Action_Engage;
		}
	}
	else
	{
		// Couldn't find a bouy so just stay in engage.
		m_state = State_Engage;
		m_eCurrentAction = Action_Engage;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_PanicBegin - Pick a random bouy to run to and set animation
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_PanicBegin(void)
{
	// See if dude is still alive
	SelectDude();
	// Switch to correct animation if not already.
	if (m_panimCur != &m_animRun)
	{
		m_panimCur = &m_animRun;
		m_lAnimTime = 0;
	}

	m_eDestinationState = State_PanicContinue;
	m_bPanic = true;
	m_ucDestBouyID = SelectRandomBouy();
	m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
	if (m_ucNextBouyID > 0)
	{
		m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
		if (m_pNextBouy != NULL)
		{
			m_sNextX = m_pNextBouy->GetX();
			m_sNextZ = m_pNextBouy->GetZ();
			m_state = State_MoveNext;
		}
		else
		{
			// Couldn't find bouy so stay engaged
			m_state = State_PanicBegin;
		}
	}
	else
	{
		// Couldn't find a bouy so just stay in engage.
		m_state = State_PanicBegin;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_PanicContinue - End of panic state - pick another bouy
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_PanicContinue(void)
{
	m_state = State_PanicBegin;
}

////////////////////////////////////////////////////////////////////////////////
// Logic_MarchBegin - Pick an endpoint and march to it.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_MarchBegin(void)
{
	// See if Dude is still alive
	SelectDude();

	// Switch to correct animation if not already.
	if (m_panimCur != &m_animWalk)
	{
		m_panimCur = &m_animWalk;
		m_lAnimTime = 0;
	}

	m_eDestinationState = State_March;
	m_eCurrentAction = Action_March;
	// Pick an endpoint that we are not already at.
	if (m_ucDestBouyID == m_ucSpecialBouy0ID)
		m_ucDestBouyID = m_ucSpecialBouy1ID;
	else
		m_ucDestBouyID = m_ucSpecialBouy0ID;

	m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
	if (m_ucNextBouyID > 0)
	{
		m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
		if (m_pNextBouy != NULL)
		{
			m_sNextX = m_pNextBouy->GetX();
			m_sNextZ = m_pNextBouy->GetZ();
			m_state = State_MarchNext;
		}
		// Couldn't find a bouy - try guard mode
		else
		{
			m_state = State_Guard;
			m_eCurrentAction = Action_Guard;
		}
	}
	// Couldn't find a bouy - try guard mode
	else
	{
		m_state = State_Guard;
		m_eCurrentAction = Action_Guard;
	}

}

////////////////////////////////////////////////////////////////////////////////
// Logic_WalkBegin - Pick a random bouy to run to and set animation
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_WalkBegin(void)
{
	// See if dude is still alive
	SelectDude();
	m_eDestinationState = State_WalkContinue;
	m_ucDestBouyID = SelectRandomBouy();
	m_ucNextBouyID = m_pNavNet->FindNearestBouy(m_dX, m_dZ);
	if (m_ucNextBouyID > 0)
	{
		m_pNextBouy = m_pNavNet->GetBouy(m_ucNextBouyID);
		if (m_pNextBouy != NULL)
		{
			m_sNextX = m_pNextBouy->GetX();
			m_sNextZ = m_pNextBouy->GetZ();
			m_state = State_WalkNext;
		}
		else
		{
			// Couldn't find bouy so stay engaged
			m_state = State_Walk;
		}
	}
	else
	{
		// Couldn't find a bouy so just stay in engage.
		m_state = State_Walk;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Logic_WalkContinue - End of walk state - pick another bouy
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_WalkContinue(void)
{
	m_state = State_WalkBegin;
}

////////////////////////////////////////////////////////////////////////////////
// Logic_Helping - Help out your buddies without moving around and screwing
//						 up your positioning
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_Helping(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();

	if (lThisTime > m_lTimer)
	{
		m_eCurrentAction = Action_Help;
		m_lTimer = lThisTime + ms_lHelpingTimeout;
		if (SelectDude() == SUCCESS && SQDistanceToDude() <= ms_lYellRadius)
		{	
			if (CDoofus::TryClearShot(m_dRot, 20) == true)
			{
				CWeapon*	pweapon	= PrepareWeapon();
				if (pweapon != NULL)
				{
					// Keep it hidden, for now.
					pweapon->GetSprite()->m_sInFlags |= CSprite::InHidden;
					pweapon->SetRangeToTarget(rspSqrt(SQDistanceToDude()));
				}
				m_panimCur = &m_animShoot;
				m_lAnimTime = 0;
				SelectDude();
				m_dShootAngle = m_dAnimRot = m_dRot = FindDirection();	
				m_state = State_Shoot;
				m_eNextState = State_Helping;
				m_lTimer = lThisTime + ms_lHelpingTimeout;
			}
			else
			{
				m_lTimer = lThisTime + ms_lHelpingTimeout;
			}
		}
	}
	else
		ReevaluateState();
}

////////////////////////////////////////////////////////////////////////////////
// Logic_AvoidFire - Wait for fire danger to pass before going back to your
//						   previous state.  This state is set by AvoidFire function
//							when there is a fire in your path.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::Logic_AvoidFire(void)
{
	if (AvoidFire())
	{
		// Check for a different state
		ReevaluateState();
	}
	else
	// Go back to your previous state
	{
		m_state = m_ePreviousState;
		m_panimCur = m_panimPrev;
	}
}

////////////////////////////////////////////////////////////////////////////////
// AvoidFire - checks for fire in your path, returns true if there is a fire
//					danger.  If you are not already in the fire avoidance state,
//					it will save your previous state info and change your state.
////////////////////////////////////////////////////////////////////////////////

bool CDoofus::AvoidFire(void)
{
	bool bFireDanger = false;

	CSmash* pSmashed = NULL;

	// Change this to quick check closest
	if (m_pRealm->m_smashatorium.QuickCheck(&m_smashAvoid, 
														 CSmash::Fire,	
														 CSmash::Good | CSmash::Bad, 
														 0, 
														 &pSmashed))
	{
		bFireDanger = true;

		// If its not already avoiding fire, switch to this safe state
		// and save the previous state
		if (m_state != State_AvoidFire)
		{
			m_ePreviousState = m_state;
			m_panimPrev = m_panimCur;
			m_state = State_AvoidFire;
			m_panimCur = &m_animStand;
		}
	}


	return bFireDanger;	
}

////////////////////////////////////////////////////////////////////////////////
// YellForHelp - Call this when you get shot and it will alert other enemies in
//					  the area within line of sight that you are in trouble.  Then 
//					  they can choose to react.
////////////////////////////////////////////////////////////////////////////////

void CDoofus::YellForHelp(void)
{
	CSmash* pSmashed = NULL;
	CSmash smashYell;
	double dSmashedX;
	double dSmashedZ;
	GameMessage msg;
	msg.msg_Help.eType = typeHelp;
	msg.msg_Help.sPriority = 0;

	// Set up the yell smash
	smashYell.m_sphere.sphere.X = m_dX;
	smashYell.m_sphere.sphere.Y = m_dY;
	smashYell.m_sphere.sphere.Z = m_dZ;
	smashYell.m_sphere.sphere.lRadius = ms_lYellRadius;
	smashYell.m_bits = 0;
	smashYell.m_pThing = this;

	m_pRealm->m_smashatorium.QuickCheckReset(&smashYell,
														  CSmash::Character | CSmash::Bad,
														  0,
														  CSmash::Good);
	while (m_pRealm->m_smashatorium.QuickCheckNext(&pSmashed))
	{
		ASSERT(pSmashed->m_pThing);
		if (pSmashed->m_pThing != this)
		{
			dSmashedX = pSmashed->m_sphere.sphere.X;
			dSmashedZ = pSmashed->m_sphere.sphere.Z;
			// Check IsPathClear to that thing
			if (m_pRealm->IsPathClear(	// Returns true, if the entire path is clear.                 
													// Returns false, if only a portion of the path is clear.     
													// (see *psX, *psY, *psZ).                                    
					(int16_t) m_dX, 				// In:  Starting X.                                           
					(int16_t) m_dY, 				// In:  Starting Y.                                           
					(int16_t) m_dZ, 				// In:  Starting Z.                                           
					3.0, 							// In:  Rate at which to scan ('crawl') path in pixels per    
													// iteration.                                                 
													// NOTE: Values less than 1.0 are inefficient.                
													// NOTE: We scan terrain using GetHeight()                    
													// at only one pixel.                                         
													// NOTE: We could change this to a speed in pixels per second 
													// where we'd assume a certain frame rate.                    
					(int16_t) dSmashedX,		// In:  Destination X.                                        
					(int16_t) dSmashedZ,		// In:  Destination Z.                                        
					0,								// In:  Max traverser can step up.                      
					NULL,							// Out: If not NULL, last clear point on path.                
					NULL,							// Out: If not NULL, last clear point on path.                
					NULL,							// Out: If not NULL, last clear point on path.                
					true) )						// In:  If true, will consider the edge of the realm a path
													// inhibitor.  If false, reaching the edge of the realm    
													// indicates a clear path. 
			{                                
				SendThingMessage(&msg, pSmashed->m_pThing);				
			}
		}
	}

}

////////////////////////////////////////////////////////////////////////////////
// ReevaluateState - Based on parameters, some randomness, current state etc,
//							come up with a new state.
//
//							Returns true if the state changed
////////////////////////////////////////////////////////////////////////////////

bool CDoofus::ReevaluateState(void)
{
	bool bChanged = false;

	// If its not already doing the suggested Action, then attempt to switch to it.
	// and if its not in one of the final death states
	if (m_eSuggestedAction != m_eCurrentAction &&
	    m_state != State_Burning &&
		 m_state != State_BlownUp &&
		 m_state != State_Die &&
		 m_state != State_Dead &&
		 m_state != State_Writhing)
	{
		switch (m_eSuggestedAction)
		{
			case Action_Guard:	
				if (m_panimCur != &m_animStand &&
				    m_panimCur != &m_animCrouch &&
					 m_panimCur != &m_animSearch)
				{
					m_panimCur = &m_animStand;
					m_lAnimTime = 0;
				}
				m_state = State_Guard;
				m_eCurrentAction = Action_Guard;
				bChanged = true;
				break;

			case Action_Advance:
				if (m_eCurrentAction != Action_AdvanceHold)
				{
					m_state = State_Hunt;
					m_eCurrentAction = Action_Advance;
					bChanged = true;
				}
				break;

			case Action_Retreat:
				m_state = State_Retreat;
				m_eCurrentAction = Action_Retreat;
				bChanged = true;
				break;

			case Action_Engage:
				m_state = State_Engage;
				m_eCurrentAction = Action_Engage;
				bChanged = true;
				break;

			case Action_Walk:
				m_state = State_WalkBegin;
				m_eCurrentAction = Action_Walk;
				bChanged = true;
				break;

			case Action_March:
				m_state = State_March;
				m_eCurrentAction = Action_March;
				bChanged = true;
				break;

			case Action_Panic:
				m_state = State_PanicBegin;
				m_eCurrentAction = Action_Panic;
				bChanged = true;
				break;

			case Action_Help:
				m_state = State_Helping;
				m_eCurrentAction = Action_Help;
				bChanged = true;
				// Force it to shoot right away.
				m_lTimer = 0;
				break;

			// In these cases, if a pylon of their type is detected, it
			// will switch them to the correct state, otherwise it will leave
			// the action and the state unchanged.
			case Action_Popout:
				Logic_PylonDetect();
				if (m_bPylonPopoutAvailable)
				{
					m_pPylonEnd = m_pPylonStart->GetPylon(m_pPylonStart->m_msg.msg_Popout.ucIDNext);
					ASSERT(m_pPylonEnd != NULL);
					m_sNextX = m_pPylonStart->GetX();
					m_sNextZ = m_pPylonStart->GetZ();
					m_state = State_PopBegin;								
					m_eCurrentAction = Action_Popout;
					bChanged = true;
				}
				break;

			case Action_RunShoot:
				Logic_PylonDetect();
				if (m_bPylonRunShootAvailable)
				{
					m_pPylonEnd = m_pPylonStart->GetPylon(m_pPylonStart->m_msg.msg_ShootCycle.ucIDNext);
					ASSERT(m_pPylonEnd != NULL);
					m_sNextX = m_pPylonStart->GetX();
					m_sNextZ = m_pPylonStart->GetZ();
					m_state = State_RunShootBegin;
					m_eCurrentAction = Action_RunShoot;
					bChanged = true;
				}
				break;

			case Action_Hide:
				Logic_PylonDetect();
				if (m_bPylonSafeAvailable)
				{
					m_sNextX = m_pPylonStart->GetX();
					m_sNextZ = m_pPylonStart->GetZ();
					m_state = State_HideBegin;
					m_eCurrentAction = Action_Hide;
					bChanged = true;
				}
				break;
		}
	}
	return bChanged;
}

////////////////////////////////////////////////////////////////////////////////
// Message handlers
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Shot Message
////////////////////////////////////////////////////////////////////////////////

void CDoofus::OnShotMsg(Shot_Message* pMessage)
{
	// If he is already shot, just deduct hit points for
	// the additional bullets
	m_stockpile.m_sHitPoints -= pMessage->sDamage;

	if (m_state != State_Burning	&& 
	    m_state != State_BlownUp	&&
		 m_state != State_Die		&& 
		 m_state != State_Dead)
	{
		// Alert other in the area that you are being attacked.
		YellForHelp();

		// Set panic so that victims will panic after being shot
		m_bPanic = true;

		// If he got shot while he was writhing, then he was executed
		// so switch to that animation.
		if (m_state == State_Writhing)
		{
			m_stockpile.m_sHitPoints = 0;
			m_panimCur = &m_animExecuted;
			m_lAnimTime = 0;
			AbortSample(m_siPlaying);
		}
		else
		{
			int32_t lThisTime = m_pRealm->m_time.GetGameTime();
			if (lThisTime > m_lShotTimeout)
			{
				m_lShotTimeout = lThisTime + m_lShotReactionTimeout;

				PlaySoundShot();

				if (	m_state != State_Shot		&&
						m_state != State_Writhing)
				{
					m_ePreviousState = m_state;
					m_panimPrev = m_panimCur;
					m_state = State_Shot;
					m_panimCur = &m_animShot;
					m_lAnimTime = 0;
				}
			}
			// Give him time to escape and move without starting the full 
			// shot animation again
			else
			{
				PlaySoundShot();
			}
		}
	}

	// Let's have some blood no matter what!
	CCharacter::OnShotMsg(pMessage);
}

////////////////////////////////////////////////////////////////////////////////
// Explosion message
////////////////////////////////////////////////////////////////////////////////

void CDoofus::OnExplosionMsg(Explosion_Message* pMessage)
{
	if (
	    m_state != State_BlownUp	&&
		 m_state != State_Die		&& 
		 m_state != State_Dead)
	{
		CCharacter::OnExplosionMsg(pMessage);
		
//		PlaySample(g_smidBlownupYell);
		PlaySoundBlownup();
		m_ePreviousState = m_state;
		m_state = State_BlownUp;
		m_panimPrev = m_panimCur;
		m_panimCur = &m_animDie;
		m_lAnimTime = 0;
		m_stockpile.m_sHitPoints = 0;
		m_lTimer = m_pRealm->m_time.GetGameTime() + 6000;
		GameMessage msg;
		msg.msg_Explosion.eType = typeExplosion;
		msg.msg_Explosion.sPriority = 0;
		CThing* pDemon = m_pRealm->m_aclassHeads[CThing::CDemonID].GetNext();
		if (pDemon)
			SendThingMessage(&msg, pDemon);				
		// If he has a weapon ready, either get rid of it or shoot it off randomly
		CWeapon* pweapon;
		if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
		{
			// It should drop like a rock if its a throwing weapon or just delete it if it
			// is a launched weapon
			if (m_eWeaponType == CHeatseekerID || m_eWeaponType == CRocketID || m_eWeaponType == CNapalmID)
			{
				GameMessage msg;
				msg.msg_ObjectDelete.eType = typeObjectDelete;
				msg.msg_ObjectDelete.sPriority = 0;
				SendThingMessage(&msg, pweapon);
			}
			else
			{
				pweapon->m_dHorizVel = (GetRandom() % (int16_t) CGrenade::ms_dThrowHorizVel);
				m_dShootAngle = pweapon->m_dRot = GetRandom() % 360;
				ShootWeapon();
			}
		}

	}
}

////////////////////////////////////////////////////////////////////////////////
// Burning message
////////////////////////////////////////////////////////////////////////////////

void CDoofus::OnBurnMsg(Burn_Message* pMessage)
{
	CCharacter::OnBurnMsg(pMessage);
	m_stockpile.m_sHitPoints -= pMessage->sDamage;

	if (m_state != State_Burning	&& 
	    m_state != State_BlownUp	&&
		 m_state != State_Die		&& 
		 m_state != State_Dead)
	{
		PlaySoundBurning();

		if (m_state == State_Writhing)
		{
			m_sBrightness = BURNT_BRIGHTNESS;
			// May also have to create a small fire at this position
			m_stockpile.m_sHitPoints = 0;
			m_state = State_Dead;
		}
		else
		{
			m_ePreviousState = m_state;
			m_state = State_Burning;

			// If he's not on fire already, switch to on fire animation
			if (m_panimCur != &m_animOnfire)
			{
				m_panimPrev = m_panimCur;
				m_panimCur = &m_animOnfire;
				m_lAnimTime = 0;
				GameMessage msg;
				msg.msg_Burn.eType = typeBurn;
				msg.msg_Burn.sPriority = 0;
				CThing* pDemon = m_pRealm->m_aclassHeads[CThing::CDemonID].GetNext();
				if (pDemon)
					SendThingMessage(&msg, pDemon);				
			}
		}
		// If he has a weapon ready, either get rid of it or shoot it off randomly.
		CWeapon* pweapon;
		if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
		{
			// It should drop like a rock if its a throwing weapon or just delete it if it
			// is a launched weapon
			if (m_eWeaponType == CHeatseekerID || m_eWeaponType == CRocketID || m_eWeaponType == CNapalmID)
			{
				GameMessage msg;
				msg.msg_ObjectDelete.eType = typeObjectDelete;
				msg.msg_ObjectDelete.sPriority = 0;
				SendThingMessage(&msg, pweapon);
			}
			else
			{
				pweapon->m_dHorizVel = (GetRandom() % (int16_t) CGrenade::ms_dThrowHorizVel);
				m_dShootAngle = pweapon->m_dRot = GetRandom() % 360;
				ShootWeapon();
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Handles an ObjectDelete_Message.
// (virtual).
////////////////////////////////////////////////////////////////////////////////
void CDoofus::OnDeleteMsg(					// Returns nothing.
	ObjectDelete_Message* pdeletemsg)	// In:  Message to handle.
	{
	CCharacter::OnDeleteMsg(pdeletemsg);
	}

////////////////////////////////////////////////////////////////////////////////
// Handles a Help_Message
// (virtual)
////////////////////////////////////////////////////////////////////////////////
void CDoofus::OnHelpMsg(					// Returns nothing
	Help_Message* phelpmsg)					// In:  Message to handle
	{
	m_lLastHelpCallTime = m_pRealm->m_time.GetGameTime();
	// Victium take the yell for help as a indication to panic.
	m_bPanic = true;							
						
	}

////////////////////////////////////////////////////////////////////////////////
// OnDead - override of CCharacter version
////////////////////////////////////////////////////////////////////////////////

void CDoofus::OnDead(void)
{
	CCharacter::OnDead();
}

////////////////////////////////////////////////////////////////////////////////
// Prepare current weapon (ammo).
// This should be done when the character starts its shoot animation.
// (virtual (overriden here) ).
////////////////////////////////////////////////////////////////////////////////
CWeapon* CDoofus::PrepareWeapon(void)	// Returns the weapon ptr or NULL.
{
	// Play sound even if we have no weapon...seems like they're threatening him.
	PlaySoundShooting();

	CWeapon*	pweapon;
	// If we have a weapon . . .
	if (m_eWeaponType != TotalIDs)
	{
		pweapon	= CCharacter::PrepareWeapon();
	}
	else
	{
		pweapon	= NULL;
	}

	return pweapon;
};

////////////////////////////////////////////////////////////////////////////////
// ShootWeapon - Overloaded version to use the Shooting Angle
////////////////////////////////////////////////////////////////////////////////

CWeapon* CDoofus::ShootWeapon(CSmash::Bits bitsInclude, 
										CSmash::Bits bitsDontcare,
										CSmash::Bits bitsExclude)
{
	CWeapon* pWeapon = NULL;
	double dSaveDirection = m_dRot;

	// Don't adjust shooting angle if shooting on the run
	if (m_state != State_ShootRun)
	{
		// Tuned for difficulty level - if the game is in the harder
		// levels, the enemies will tune their aim at the last second
		// before shooting, and then also depending on the difficulty,
		// they will add different amounts of random misses
		// Easy levels   = 1, 2, 3
		// Medium levels = 4, 5, 6
		// Hard levels   = 7, 8, 9, 10, 11
		switch (m_pRealm->m_flags.sDifficulty)
		{
			case 0:
			case 1:
				m_dRot = rspMod360(m_dShootAngle - 8 + (GetRandom() % 17));
				break;

			case 2:
				m_dRot = rspMod360(m_dShootAngle - 7 + (GetRandom() % 15));
				break;

			case 3:
				m_dRot = rspMod360(m_dShootAngle - 6 + (GetRandom() % 13));
				break;

			case 4:
				m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 6 + (GetRandom() % 13));
				break;

			case 5:
				m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 5 + (GetRandom() % 11));
				break;

			case 6:
				m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 4 + (GetRandom() % 9));
				break;

			case 7:
				m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 6 + (GetRandom() % 13));
				break;

			case 8:
				m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 5 + (GetRandom() % 11));
				break;

			case 9:
				m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 4 + (GetRandom() % 9));
				break;

			case 10:
				m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = rspMod360(FindDirection() - 3 + (GetRandom() % 7));
				break;

			case 11:
			default:
				m_dRot = m_dAnimRot = m_dShootAngle = dSaveDirection = FindDirection();
				break;
		}
	}
	else
	{
		m_dRot = m_dShootAngle;
	}
/*
	// Allow rockets to hit other enemies, but still not the special barrel type
	if (m_eWeaponType == CHeatseekerID || m_eWeaponType == CRocketID)
	{
		bitsExclude &= ~CSmash::Bad;
	}
*/
	pWeapon = CCharacter::ShootWeapon(bitsInclude, bitsDontcare, bitsExclude);
	if (pWeapon)
		pWeapon->SetDetectionBits(CSmash::Character | CSmash::Fire,
									  0,
									  CSmash::Bad | CSmash::Ducking | CSmash::AlmostDead);

	m_dRot = dSaveDirection;

	return pWeapon;
}

////////////////////////////////////////////////////////////////////////////////
// RunIdleAnimation - Check the idle timer and pick an idle animation to run
////////////////////////////////////////////////////////////////////////////////

void CDoofus::RunIdleAnimation(void)
{
	int32_t lThisTime = m_pRealm->m_time.GetGameTime();

	if (m_panimCur == &m_animStand)
	{
		if (lThisTime > m_lIdleTimer)
		{
			if (GetRandom() % 3 == 0)
			{
				// Stay with current animation
				m_lIdleTimer = lThisTime + 2000 + GetRandom() % 2000;
			}
			else
			{
				// Go to the Crouch animaiton
				m_panimCur = &m_animCrouch;
				m_lAnimTime = 0;
				m_bAnimUp = true;
				m_lIdleTimer = lThisTime + 3000 + GetRandom() % 2000;
			}
		}
	}
	else
	{
		if (m_panimCur == &m_animCrouch)
		{
			// Animate forward or backward depending on if he is standing up or
			if (m_bAnimUp)
			{
				m_lAnimTime += lThisTime - m_lPrevTime;
				// See if it is time to switch yet
				if (lThisTime > m_lIdleTimer)
				{
					if ((GetRandom() % 3 ) == 0)
					// Start standing up to use the stand animation
					{
						// Start to stand up - run the animation backwards
						m_lAnimTime = m_panimCur->m_psops->TotalTime();
						m_bAnimUp = false;
					}
					else
					{
						// Pick either crouch still or search
						if (GetRandom() % 2)
						{
							// Start search animation
							m_panimCur = &m_animSearch;
							m_lAnimTime = 0;
							m_lIdleTimer = lThisTime + 1000 + GetRandom() % 1500;
						}
						else
						{
							// Stay with still crouch animation
							m_lIdleTimer = lThisTime + 2000 + GetRandom() % 2000;
						}
					}
				}
			}
			else
			{
				m_lAnimTime -= lThisTime - m_lPrevTime;
				// See if it is done standing yet.  If so, you have
				// already previously decided to end the crouch animation
				// so now pick the stand animation
				if (m_lAnimTime < 0)
				{
					m_panimCur = &m_animStand;
					m_lAnimTime = 0;
					m_lIdleTimer = lThisTime + 3000 + GetRandom() % 2000;
				}
			}
		}
		else
		{
			if (m_panimCur == &m_animSearch)
			{
				// Animate the search 
				m_lAnimTime += lThisTime - m_lPrevTime;
				// See if its time to switch yet
				if (lThisTime > m_lIdleTimer)
				{
					switch (GetRandom() % 3)
					{
						case 0:	// Stay with current animation
							m_lIdleTimer = lThisTime + 1000 + GetRandom() % 1000;
							break;

						case 1:	// Go to Crouch animation
							m_panimCur = &m_animCrouch;
							m_bAnimUp = true;
							m_lIdleTimer = lThisTime + 3000 + GetRandom() % 2000;
							break;

						case 2:	// Go to stand - use Crouch backwards to get up
							m_panimCur = &m_animCrouch;
							m_bAnimUp = false;
							m_lAnimTime = m_panimCur->m_psops->TotalTime();
							m_lIdleTimer = lThisTime + 3000 + GetRandom() % 2500;
							break;
					}					
				}
			}
			// Start the stand animation since it isn't currently using any idle animations.
			else
			{
				m_panimCur = &m_animStand;
				m_lIdleTimer = lThisTime + 3000;
				m_lAnimTime = 0;			
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Position our smash approriately.
// (virtual).
////////////////////////////////////////////////////////////////////////////////
void CDoofus::PositionSmash(void)
	{
	// If not in writhing state . . .
	if (m_state != State_Writhing)
		{
		// Update sphere.
		m_smash.m_sphere.sphere.X			= m_dX;
		m_smash.m_sphere.sphere.Y			= m_dY + m_sprite.m_sRadius;
		m_smash.m_sphere.sphere.Z			= m_dZ;
		m_smash.m_sphere.sphere.lRadius	= m_sprite.m_sRadius;
		}
	else
		{
		// If we have the execution points . . .
		if (m_ptransExecutionTarget)
			{
			// Compute link point for execution target.
			double	dVitalOrganX;
			double	dVitalOrganY;
			double	dVitalOrganZ;
			GetLinkPoint(														// Returns nothing.
				m_ptransExecutionTarget->GetAtTime(m_lAnimTime),	// In:  Transform specifying point.
				&dVitalOrganX,													// Out: Point speicfied.
				&dVitalOrganY,													// Out: Point speicfied.
				&dVitalOrganZ);												// Out: Point speicfied.			// Update execution point via link point.

			// Offset from hotspot to set collision sphere position.
			m_smash.m_sphere.sphere.X			= m_dX + dVitalOrganX;              
			m_smash.m_sphere.sphere.Y			= m_dY + dVitalOrganY;
			m_smash.m_sphere.sphere.Z			= m_dZ + dVitalOrganZ;
			m_smash.m_sphere.sphere.lRadius	= m_sprite.m_sRadius;
			}
		else
			{
			ASSERT(m_dRot >= 0);
			ASSERT(m_dRot < 360);
			// Try to find center.
			// Let's go a radius up their torso.  Say... .
			// This only looks decent if m_dRot is the direction they fell which is
			// not always the case.
			int16_t	sPseudoCenter	= m_sprite.m_sRadius;
			m_smash.m_sphere.sphere.X			= m_dX + COSQ[int16_t(m_dRot)] * sPseudoCenter;
			m_smash.m_sphere.sphere.Y			= m_dY + m_sprite.m_sRadius;              
			m_smash.m_sphere.sphere.Z			= m_dZ - SINQ[int16_t(m_dRot)] * sPseudoCenter;              
			m_smash.m_sphere.sphere.lRadius	= m_sprite.m_sRadius;
			}
		}
	}

////////////////////////////////////////////////////////////////////////////////
//	WhileHoldingWeapon
//
// Override for the character version which will just re-aim the guy during the
// shoot-prepare frames.  Otherwise, guys like the rocketman which has a long
// prepare animation, quickly flip around in ShootWeapon if the target has moved
// between PrepareWeapon and ShootWeapon.
//
////////////////////////////////////////////////////////////////////////////////

bool CDoofus::WhileHoldingWeapon(	// Returns true when weapon is released.
				U32 u32BitsInclude,		// In:  Collision bits passed to ShootWeapon
				U32 u32BitsDontcare,		// In:  Collision bits passed to ShootWeapon
				U32 u32BitsExclude)		// In:  Collision bits passed to ShootWeapon
{
	bool bResult = true;

	// If they haven't shot the weapon yet, adjust their aiming if the difficulty
	// level allows them to re-aim after preparing the weapon
	if (!(CCharacter::WhileHoldingWeapon(u32BitsInclude, u32BitsDontcare, u32BitsExclude)))
	{
		bResult = false;
		// Don't adjust shooting angle if shooting on the run
		if (m_state != State_ShootRun)
		{
			// Tuned for difficulty level - if the game is in the harder
			// levels, the enemies will tune their aim at the last second
			// before shooting, and then also depending on the difficulty,
			// they will add different amounts of random misses
			// Easy levels   = 1, 2, 3
			// Medium levels = 4, 5, 6
			// Hard levels   = 7, 8, 9, 10, 11
			switch (m_pRealm->m_flags.sDifficulty)
			{
				// Easy levels don't re-aim after preparing weapon, so leve it alone
				case 0:
				case 1:
				case 2:
				case 3:
				case 4:
				case 5:
				case 6:
					break;

				// More difficult levels allow constant re-aiming.
				case 7:
				case 8:
				case 9:
				case 10:
				case 11:
				default:
					m_dRot = m_dAnimRot = m_dShootAngle = rspMod360(FindDirection());
					break;
			}
		}
	}
	return bResult;
}


////////////////////////////////////////////////////////////////////////////////
// A way for the base class to get resources.  If you are going to use
// any of this class's resources (e.g., ms_aanimWeapons[]), call this
// when getting your resources.
////////////////////////////////////////////////////////////////////////////////
int16_t CDoofus::GetResources(void)
	{
	int16_t	sResult	= 0;

	// If the ref count was 0 . . .
	if (ms_lWeaponResRefCount++ == 0)
		{
		// Get the actual resources.
		int16_t	i;
		int16_t	sLoadResult;
		for (i = 0; i < NumWeaponTypes; i++)
			{
			// If this weapon has a visible resource . . .
			if (ms_awdWeapons[i].pszResName)
				{
				sLoadResult	= ms_aanimWeapons[i].Get(
					ms_awdWeapons[i].pszResName, 
					NULL, 
					NULL, 
					NULL, 
					RChannel_LoopAtStart | RChannel_LoopAtEnd);

				if (sLoadResult != 0)
					{
					TRACE("GetResources(): Failed to load weapon resource \"%s\".\n",
						ms_awdWeapons[i].pszResName);
					sResult	= -1;
					}
				}
			}
		}

	return sResult;
	}

////////////////////////////////////////////////////////////////////////////////
// A way for the base class to release resources.  If you are going to use
// any of this class's resources (e.g., ms_aanimWeapons[]), call this
// when releasing your resources.
////////////////////////////////////////////////////////////////////////////////
void CDoofus::ReleaseResources(void)
	{
	ASSERT(ms_lWeaponResRefCount > 0);

	// If the ref count hits zero with this release . . .
	if (--ms_lWeaponResRefCount == 0)
		{
		// Release the actual resources.
		int16_t	i;
		for (i = 0; i < NumWeaponTypes; i++)
			{
			if (ms_aanimWeapons[i].m_pmeshes)
				{
				ms_aanimWeapons[i].Release();
				}
			}
		}
	}

////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////
