text: /***
  *
  *	Copyright (c) 1996-2001, Valve LLC. All rights reserved.
  *	
  *	This product contains software technology licensed from Id 
  *	Software, Inc. ("Id Technology").  Id Technology (c) 1996 Id Software, Inc. 
  *	All Rights Reserved.
  *
  *   This source code contains proprietary and confidential information of
  *   Valve LLC and its suppliers.  Access to this code is restricted to
  *   persons who have executed a written SDK license with Valve.  Any access,
  *   use or distribution of this code by or to any unlicensed person is illegal.
  *
  ****/
  /*
  
  ===== monsters.cpp ========================================================
  
    Monster-related utility code
  
  */
  
  #include "extdll.h"
  #include "util.h"
  #include "cbase.h"
  #include "nodes.h"
  #include "monsters.h"
  #include "animation.h"
  #include "saverestore.h"
  #include "weapons.h"
  #include "scripted.h"
  #include "squadmonster.h"
  #include "decals.h"
  #include "soundent.h"
  #include "gamerules.h"
  
  #define MONSTER_CUT_CORNER_DIST		8 // 8 means the monster's bounding box is contained without the box of the node in WC
  
  
  Vector VecBModelOrigin( entvars_t* pevBModel );
  
  extern DLL_GLOBAL	BOOL	g_fDrawLines;
  extern DLL_GLOBAL	short	g_sModelIndexLaser;// holds the index for the laser beam
  extern DLL_GLOBAL	short	g_sModelIndexLaserDot;// holds the index for the laser beam dot
  
  extern CGraph WorldGraph;// the world node graph
  
  
  
  // Global Savedata for monster
  // UNDONE: Save schedule data?  Can this be done?  We may
  // lose our enemy pointer or other data (goal ent, target, etc)
  // that make the current schedule invalid, perhaps it's best
  // to just pick a new one when we start up again.
  TYPEDESCRIPTION	CBaseMonster::m_SaveData[] = 
  {
  	DEFINE_FIELD( CBaseMonster, m_hEnemy, FIELD_EHANDLE ),
  	DEFINE_FIELD( CBaseMonster, m_hTargetEnt, FIELD_EHANDLE ),
  	DEFINE_ARRAY( CBaseMonster, m_hOldEnemy, FIELD_EHANDLE, MAX_OLD_ENEMIES ),
  	DEFINE_ARRAY( CBaseMonster, m_vecOldEnemy, FIELD_POSITION_VECTOR, MAX_OLD_ENEMIES ),
  	DEFINE_FIELD( CBaseMonster, m_flFieldOfView, FIELD_FLOAT ),
  	DEFINE_FIELD( CBaseMonster, m_flWaitFinished, FIELD_TIME ),
  	DEFINE_FIELD( CBaseMonster, m_flMoveWaitFinished, FIELD_TIME ),
  
  	DEFINE_FIELD( CBaseMonster, m_Activity, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_IdealActivity, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_LastHitGroup, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_MonsterState, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_IdealMonsterState, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_iTaskStatus, FIELD_INTEGER ),
  
  	//Schedule_t			*m_pSchedule;
  
  	DEFINE_FIELD( CBaseMonster, m_iScheduleIndex, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_afConditions, FIELD_INTEGER ),
  	//WayPoint_t			m_Route[ ROUTE_SIZE ];
  //	DEFINE_FIELD( CBaseMonster, m_movementGoal, FIELD_INTEGER ),
  //	DEFINE_FIELD( CBaseMonster, m_iRouteIndex, FIELD_INTEGER ),
  //	DEFINE_FIELD( CBaseMonster, m_moveWaitTime, FIELD_FLOAT ),
  
  	DEFINE_FIELD( CBaseMonster, m_vecMoveGoal, FIELD_POSITION_VECTOR ),
  	DEFINE_FIELD( CBaseMonster, m_movementActivity, FIELD_INTEGER ),
  
  	//		int					m_iAudibleList; // first index of a linked list of sounds that the monster can hear.
  //	DEFINE_FIELD( CBaseMonster, m_afSoundTypes, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_vecLastPosition, FIELD_POSITION_VECTOR ),
  	DEFINE_FIELD( CBaseMonster, m_iHintNode, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_afMemory, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_iMaxHealth, FIELD_INTEGER ),
  
  	DEFINE_FIELD( CBaseMonster, m_vecEnemyLKP, FIELD_POSITION_VECTOR ),
  	DEFINE_FIELD( CBaseMonster, m_cAmmoLoaded, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_afCapability, FIELD_INTEGER ),
  
  	DEFINE_FIELD( CBaseMonster, m_flNextAttack, FIELD_TIME ),
  	DEFINE_FIELD( CBaseMonster, m_bitsDamageType, FIELD_INTEGER ),
  	DEFINE_ARRAY( CBaseMonster, m_rgbTimeBasedDamage, FIELD_CHARACTER, CDMG_TIMEBASED ),
  	DEFINE_FIELD( CBaseMonster, m_bloodColor, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_failSchedule, FIELD_INTEGER ),
  
  	DEFINE_FIELD( CBaseMonster, m_flHungryTime, FIELD_TIME ),
  	DEFINE_FIELD( CBaseMonster, m_flDistTooFar, FIELD_FLOAT ),
  	DEFINE_FIELD( CBaseMonster, m_flDistLook, FIELD_FLOAT ),
  	DEFINE_FIELD( CBaseMonster, m_iTriggerCondition, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_iszTriggerTarget, FIELD_STRING ),
  
  	DEFINE_FIELD( CBaseMonster, m_HackedGunPos, FIELD_VECTOR ),
  
  	DEFINE_FIELD( CBaseMonster, m_scriptState, FIELD_INTEGER ),
  	DEFINE_FIELD( CBaseMonster, m_pCine, FIELD_CLASSPTR ),
  };
  
  //IMPLEMENT_SAVERESTORE( CBaseMonster, CBaseToggle );
  int CBaseMonster::Save( CSave &save )
  {
  	if ( !CBaseToggle::Save(save) )
  		return 0;
  	return save.WriteFields( "CBaseMonster", this, m_SaveData, ARRAYSIZE(m_SaveData) );
  }
  
  int CBaseMonster::Restore( CRestore &restore )
  {
  	if ( !CBaseToggle::Restore(restore) )
  		return 0;
  	int status = restore.ReadFields( "CBaseMonster", this, m_SaveData, ARRAYSIZE(m_SaveData) );
  	
  	// We don't save/restore routes yet
  	RouteClear();
  
  	// We don't save/restore schedules yet
  	m_pSchedule = NULL;
  	m_iTaskStatus = TASKSTATUS_NEW;
  	
  	// Reset animation
  	m_Activity = ACT_RESET;
  
  	// If we don't have an enemy, clear conditions like see enemy, etc.
  	if ( m_hEnemy == NULL )
  		m_afConditions = 0;
  
  	return status;
  }
  
  
  //=========================================================
  // Eat - makes a monster full for a little while.
  //=========================================================
  void CBaseMonster :: Eat ( float flFullDuration )
  {
  	m_flHungryTime = gpGlobals->time + flFullDuration;
  }
  
  //=========================================================
  // FShouldEat - returns true if a monster is hungry.
  //=========================================================
  BOOL CBaseMonster :: FShouldEat ()
  {
  	if ( m_flHungryTime > gpGlobals->time )
  	{
  		return FALSE;
  	}
  
  	return TRUE;
  }
  
  //=========================================================
  // BarnacleVictimBitten - called
  // by Barnacle victims when the barnacle pulls their head
  // into its mouth
  //=========================================================
  void CBaseMonster :: BarnacleVictimBitten ( entvars_t *pevBarnacle )
  {
  	Schedule_t	*pNewSchedule;
  
  	pNewSchedule = GetScheduleOfType( SCHED_BARNACLE_VICTIM_CHOMP );
  
  	if ( pNewSchedule )
  	{
  		ChangeSchedule( pNewSchedule );
  	}
  }
  
  //=========================================================
  // BarnacleVictimReleased - called by barnacle victims when
  // the host barnacle is killed.
  //=========================================================
  void CBaseMonster :: BarnacleVictimReleased ()
  {
  	m_IdealMonsterState = MONSTERSTATE_IDLE;
  
  	pev->velocity = g_vecZero;
  	pev->movetype = MOVETYPE_STEP;
  }
  
  //=========================================================
  // Listen - monsters dig through the active sound list for
  // any sounds that may interest them. (smells, too!)
  //=========================================================
  void CBaseMonster :: Listen ()
  {
  	int		iSound;
  	int		iMySounds;
  	float	hearingSensitivity;
  	CSound	*pCurrentSound;
  
  	m_iAudibleList = SOUNDLIST_EMPTY; 
  	ClearConditions(bits_COND_HEAR_SOUND | bits_COND_SMELL | bits_COND_SMELL_FOOD);
  	m_afSoundTypes = 0;
  
  	iMySounds = ISoundMask();
  
  	if ( m_pSchedule )
  	{
  		//!!!WATCH THIS SPOT IF YOU ARE HAVING SOUND RELATED BUGS!
  		// Make sure your schedule AND personal sound masks agree!
  		iMySounds &= m_pSchedule->iSoundMask;
  	}
  
  	iSound = CSoundEnt::ActiveList();
  
  	// UNDONE: Clear these here?
  	ClearConditions( bits_COND_HEAR_SOUND | bits_COND_SMELL_FOOD | bits_COND_SMELL );
  	hearingSensitivity = HearingSensitivity( );
  
  	while ( iSound != SOUNDLIST_EMPTY )
  	{
  		pCurrentSound = CSoundEnt::SoundPointerForIndex( iSound );
  
  		if ( pCurrentSound	&& 
  			 ( pCurrentSound->m_iType & iMySounds )	&& 
  			 ( pCurrentSound->m_vecOrigin - EarPosition() ).Length() <= pCurrentSound->m_iVolume * hearingSensitivity )
  
  		//if ( ( g_pSoundEnt->m_SoundPool[ iSound ].m_iType & iMySounds ) && ( g_pSoundEnt->m_SoundPool[ iSound ].m_vecOrigin - EarPosition()).Length () <= g_pSoundEnt->m_SoundPool[ iSound ].m_iVolume * hearingSensitivity ) 
  		{
   			// the monster cares about this sound, and it's close enough to hear.
  			//g_pSoundEnt->m_SoundPool[ iSound ].m_iNextAudible = m_iAudibleList;
  			pCurrentSound->m_iNextAudible = m_iAudibleList;
  			
  			if ( pCurrentSound->FIsSound() )
  			{
  				// this is an audible sound.
  				SetConditions( bits_COND_HEAR_SOUND );
  			}
  			else
  			{
  				// if not a sound, must be a smell - determine if it's just a scent, or if it's a food scent
  //				if ( g_pSoundEnt->m_SoundPool[ iSound ].m_iType & ( bits_SOUND_MEAT | bits_SOUND_CARCASS ) )
  				if ( pCurrentSound->m_iType & ( bits_SOUND_MEAT | bits_SOUND_CARCASS ) )
  				{
  					// the detected scent is a food item, so set both conditions.
  					// !!!BUGBUG - maybe a virtual function to determine whether or not the scent is food?
  					SetConditions( bits_COND_SMELL_FOOD );
  					SetConditions( bits_COND_SMELL );
  				}
  				else
  				{
  					// just a normal scent. 
  					SetConditions( bits_COND_SMELL );
  				}
  			}
  
  //			m_afSoundTypes |= g_pSoundEnt->m_SoundPool[ iSound ].m_iType;
  			m_afSoundTypes |= pCurrentSound->m_iType;
  
  			m_iAudibleList = iSound;
  		}
  
  //		iSound = g_pSoundEnt->m_SoundPool[ iSound ].m_iNext;
  		iSound = pCurrentSound->m_iNext;
  	}
  }
  
  //=========================================================
  // FLSoundVolume - subtracts the volume of the given sound
  // from the distance the sound source is from the caller, 
  // and returns that value, which is considered to be the 'local' 
  // volume of the sound. 
  //=========================================================
  float CBaseMonster :: FLSoundVolume ( CSound *pSound )
  {
  	return ( pSound->m_iVolume - ( ( pSound->m_vecOrigin - pev->origin ).Length() ) );
  }
  
  //=========================================================
  // FValidateHintType - tells use whether or not the monster cares
  // about the type of Hint Node given
  //=========================================================
  BOOL CBaseMonster :: FValidateHintType ( short sHint )
  {
  	return FALSE;
  }
  
  //=========================================================
  // Look - Base class monster function to find enemies or 
  // food by sight. iDistance is distance ( in units ) that the 
  // monster can see.
  //
  // Sets the sight bits of the m_afConditions mask to indicate
  // which types of entities were sighted.
  // Function also sets the Looker's m_pLink 
  // to the head of a link list that contains all visible ents.
  // (linked via each ent's m_pLink field)
  //
  //=========================================================
  void CBaseMonster :: Look ( int iDistance )
  {
  	int	iSighted = 0;
  
  	// DON'T let visibility information from last frame sit around!
  	ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT);
  
  	m_pLink = NULL;
  
  	CBaseEntity	*pSightEnt = NULL;// the current visible entity that we're dealing with
  
  	// See no evil if prisoner is set
  	if ( !FBitSet( pev->spawnflags, SF_MONSTER_PRISONER ) )
  	{
  		CBaseEntity *pList[100];
  
  		Vector delta = Vector( iDistance, iDistance, iDistance );
  
  		// Find only monsters/clients in box, NOT limited to PVS
  		int count = UTIL_EntitiesInBox( pList, 100, pev->origin - delta, pev->origin + delta, FL_CLIENT|FL_MONSTER );
  		for ( int i = 0; i < count; i++ )
  		{
  			pSightEnt = pList[i];
  			// !!!temporarily only considering other monsters and clients, don't see prisoners
  			if ( pSightEnt != this												&& 
  				 !FBitSet( pSightEnt->pev->spawnflags, SF_MONSTER_PRISONER )	&& 
  				 pSightEnt->pev->health > 0 )
  			{
  				// the looker will want to consider this entity
  				// don't check anything else about an entity that can't be seen, or an entity that you don't care about.
  				if ( IRelationship( pSightEnt ) != R_NO && FInViewCone( pSightEnt ) && !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && FVisible( pSightEnt ) )
  				{
  					if ( pSightEnt->IsPlayer() )
  					{
  						if ( pev->spawnflags & SF_MONSTER_WAIT_TILL_SEEN )
  						{
  							CBaseMonster *pClient;
  
  							pClient = pSightEnt->MyMonsterPointer();
  							// don't link this client in the list if the monster is wait till seen and the player isn't facing the monster
  							if ( pSightEnt && !pClient->FInViewCone( this ) )
  							{
  								// we're not in the player's view cone. 
  								continue;
  							}
  							else
  							{
  								// player sees us, become normal now.
  								pev->spawnflags &= ~SF_MONSTER_WAIT_TILL_SEEN;
  							}
  						}
  
  						// if we see a client, remember that (mostly for scripted AI)
  						iSighted |= bits_COND_SEE_CLIENT;
  					}
  
  					pSightEnt->m_pLink = m_pLink;
  					m_pLink = pSightEnt;
  
  					if ( pSightEnt == m_hEnemy )
  					{
  						// we know this ent is visible, so if it also happens to be our enemy, store that now.
  						iSighted |= bits_COND_SEE_ENEMY;
  					}
  
  					// don't add the Enemy's relationship to the conditions. We only want to worry about conditions when
  					// we see monsters other than the Enemy.
  					switch ( IRelationship ( pSightEnt ) )
  					{
  					case	R_NM:
  						iSighted |= bits_COND_SEE_NEMESIS;		
  						break;
  					case	R_HT:		
  						iSighted |= bits_COND_SEE_HATE;		
  						break;
  					case	R_DL:
  						iSighted |= bits_COND_SEE_DISLIKE;
  						break;
  					case	R_FR:
  						iSighted |= bits_COND_SEE_FEAR;
  						break;
  					case    R_AL:
  						break;
  					default:
  						ALERT ( at_aiconsole, "%s can't assess %s\n", STRING(pev->classname), STRING(pSightEnt->pev->classname ) );
  						break;
  					}
  				}
  			}
  		}
  	}
  	
  	SetConditions( iSighted );
  }
  
  //=========================================================
  // ISoundMask - returns a bit mask indicating which types
  // of sounds this monster regards. In the base class implementation,
  // monsters care about all sounds, but no scents.
  //=========================================================
  int CBaseMonster :: ISoundMask ()
  {
  	return	bits_SOUND_WORLD	|
  			bits_SOUND_COMBAT	|
  			bits_SOUND_PLAYER;
  }
  
  //=========================================================
  // PBestSound - returns a pointer to the sound the monster 
  // should react to. Right now responds only to nearest sound.
  //=========================================================
  CSound* CBaseMonster :: PBestSound ()
  {	
  	int iThisSound; 
  	int	iBestSound = -1;
  	float flBestDist = 8192;// so first nearby sound will become best so far.
  	float flDist;
  	CSound *pSound;
  
  	iThisSound = m_iAudibleList; 
  
  	if ( iThisSound == SOUNDLIST_EMPTY )
  	{
  		ALERT ( at_aiconsole, "ERROR! monster %s has no audible sounds!\n", STRING(pev->classname) );
  #if _DEBUG
  		ALERT( at_error, "NULL Return from PBestSound\n" );
  #endif
  		return NULL;
  	}
  
  	while ( iThisSound != SOUNDLIST_EMPTY )
  	{
  		pSound = CSoundEnt::SoundPointerForIndex( iThisSound );
  
  		if ( pSound && pSound->FIsSound() )
  		{
  			flDist = ( pSound->m_vecOrigin - EarPosition()).Length();
  
  			if ( flDist < flBestDist )
  			{
  				iBestSound = iThisSound;
  				flBestDist = flDist;
  			}
  		}
  
  		iThisSound = pSound->m_iNextAudible;
  	}
  	if ( iBestSound >= 0 )
  	{
  		pSound = CSoundEnt::SoundPointerForIndex( iBestSound );
  		return pSound;
  	}
  #if _DEBUG
  	ALERT( at_error, "NULL Return from PBestSound\n" );
  #endif
  	return NULL;
  }
  
  //=========================================================
  // PBestScent - returns a pointer to the scent the monster 
  // should react to. Right now responds only to nearest scent
  //=========================================================
  CSound* CBaseMonster :: PBestScent ()
  {	
  	int iThisScent; 
  	int	iBestScent = -1;
  	float flBestDist = 8192;// so first nearby smell will become best so far.
  	float flDist;
  	CSound *pSound;
  
  	iThisScent = m_iAudibleList;// smells are in the sound list.
  
  	if ( iThisScent == SOUNDLIST_EMPTY )
  	{
  		ALERT ( at_aiconsole, "ERROR! PBestScent() has empty soundlist!\n" );
  #if _DEBUG
  		ALERT( at_error, "NULL Return from PBestSound\n" );
  #endif
  		return NULL;
  	}
  
  	while ( iThisScent != SOUNDLIST_EMPTY )
  	{
  		pSound = CSoundEnt::SoundPointerForIndex( iThisScent );
  
  		if ( pSound->FIsScent() )
  		{
  			flDist = ( pSound->m_vecOrigin - pev->origin ).Length();
  
  			if ( flDist < flBestDist )
  			{
  				iBestScent = iThisScent;
  				flBestDist = flDist;
  			}
  		}
  
  		iThisScent = pSound->m_iNextAudible;
  	}
  	if ( iBestScent >= 0 )
  	{
  		pSound = CSoundEnt::SoundPointerForIndex( iBestScent );
  
  		return pSound;
  	}
  #if _DEBUG
  	ALERT( at_error, "NULL Return from PBestScent\n" );
  #endif
  	return NULL;
  }
  
  
  
  //=========================================================
  // Monster Think - calls out to core AI functions and handles this
  // monster's specific animation events
  //=========================================================
  void CBaseMonster :: MonsterThink ()
  {
  	pev->nextthink = gpGlobals->time + 0.1;// keep monster thinking.
  
  
  	RunAI();
  
  	float flInterval = StudioFrameAdvance( ); // animate
  // start or end a fidget
  // This needs a better home -- switching animations over time should be encapsulated on a per-activity basis
  // perhaps MaintainActivity() or a ShiftAnimationOverTime() or something.
  	if ( m_MonsterState != MONSTERSTATE_SCRIPT && m_MonsterState != MONSTERSTATE_DEAD && m_Activity == ACT_IDLE && m_fSequenceFinished )
  	{
  		int iSequence;
  
  		if ( m_fSequenceLoops )
  		{
  			// animation does loop, which means we're playing subtle idle. Might need to 
  			// fidget.
  			iSequence = LookupActivity ( m_Activity );
  		}
  		else
  		{
  			// animation that just ended doesn't loop! That means we just finished a fidget
  			// and should return to our heaviest weighted idle (the subtle one)
  			iSequence = LookupActivityHeaviest ( m_Activity );
  		}
  		if ( iSequence != ACTIVITY_NOT_AVAILABLE )
  		{
  			pev->sequence = iSequence;	// Set to new anim (if it's there)
  			ResetSequenceInfo( );
  		}
  	}
  
  	DispatchAnimEvents( flInterval );
  
  	if ( !MovementIsComplete() )
  	{
  		Move( flInterval );
  	}
  #if _DEBUG	
  	else 
  	{
  		if ( !TaskIsRunning() && !TaskIsComplete() )
  			ALERT( at_error, "Schedule stalled!!\n" );
  	}
  #endif
  }
  
  //=========================================================
  // CBaseMonster - USE - will make a monster angry at whomever
  // activated it.
  //=========================================================
  void CBaseMonster :: MonsterUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  {
  	//Don't do this because it can resurrect dying monsters
  	//m_IdealMonsterState = MONSTERSTATE_ALERT;
  }
  
  //=========================================================
  // Ignore conditions - before a set of conditions is allowed
  // to interrupt a monster's schedule, this function removes
  // conditions that we have flagged to interrupt the current
  // schedule, but may not want to interrupt the schedule every
  // time. (Pain, for instance)
  //=========================================================
  int CBaseMonster :: IgnoreConditions ()
  {
  	int iIgnoreConditions = 0;
  
  	if ( !FShouldEat() )
  	{
  		// not hungry? Ignore food smell.
  		iIgnoreConditions |= bits_COND_SMELL_FOOD;
  	}
  
  	if ( m_MonsterState == MONSTERSTATE_SCRIPT && m_pCine )
  		iIgnoreConditions |= m_pCine->IgnoreConditions();
  
  	return iIgnoreConditions;
  }
  
  //=========================================================
  // 	RouteClear - zeroes out the monster's route array and goal
  //=========================================================
  void CBaseMonster :: RouteClear ()
  {
  	RouteNew();
  	m_movementGoal = MOVEGOAL_NONE;
  	m_movementActivity = ACT_IDLE;
  	Forget( bits_MEMORY_MOVE_FAILED );
  }
  
  //=========================================================
  // Route New - clears out a route to be changed, but keeps
  //				goal intact.
  //=========================================================
  void CBaseMonster :: RouteNew ()
  {
  	m_Route[ 0 ].iType		= 0;
  	m_iRouteIndex			= 0;
  }
  
  //=========================================================
  // FRouteClear - returns TRUE is the Route is cleared out
  // ( invalid )
  //=========================================================
  BOOL CBaseMonster :: FRouteClear ()
  {
  	if ( m_Route[ m_iRouteIndex ].iType == 0 || m_movementGoal == MOVEGOAL_NONE )
  		return TRUE;
  
  	return FALSE;
  }
  
  //=========================================================
  // FRefreshRoute - after calculating a path to the monster's
  // target, this function copies as many waypoints as possible
  // from that path to the monster's Route array
  //=========================================================
  BOOL CBaseMonster :: FRefreshRoute ()
  {
  	CBaseEntity	*pPathCorner;
  	int			i;
  	BOOL		returnCode;
  
  	RouteNew();
  
  	returnCode = FALSE;
  
  	switch( m_movementGoal )
  	{
  		case MOVEGOAL_PATHCORNER:
  			{
  				// monster is on a path_corner loop
  				pPathCorner = m_pGoalEnt;
  				i = 0;
  
  				while ( pPathCorner && i < ROUTE_SIZE )
  				{
  					m_Route[ i ].iType = bits_MF_TO_PATHCORNER;
  					m_Route[ i ].vecLocation = pPathCorner->pev->origin;
  
  					pPathCorner = pPathCorner->GetNextTarget();
  
  					// Last path_corner in list?
  					if ( !pPathCorner )
  						m_Route[i].iType |= bits_MF_IS_GOAL;
  					
  					i++;
  				}
  			}
  			returnCode = TRUE;
  			break;
  
  		case MOVEGOAL_ENEMY:
  			returnCode = BuildRoute( m_vecEnemyLKP, bits_MF_TO_ENEMY, m_hEnemy );
  			break;
  
  		case MOVEGOAL_LOCATION:
  			returnCode = BuildRoute( m_vecMoveGoal, bits_MF_TO_LOCATION, NULL );
  			break;
  
  		case MOVEGOAL_TARGETENT:
  			if (m_hTargetEnt != NULL)
  			{
  				returnCode = BuildRoute( m_hTargetEnt->pev->origin, bits_MF_TO_TARGETENT, m_hTargetEnt );
  			}
  			break;
  
  		case MOVEGOAL_NODE:
  			returnCode = FGetNodeRoute( m_vecMoveGoal );
  //			if ( returnCode )
  //				RouteSimplify( NULL );
  			break;
  	}
  
  	return returnCode;
  }
  
  
  BOOL CBaseMonster::MoveToEnemy( Activity movementAct, float waitTime )
  {
  	m_movementActivity = movementAct;
  	m_moveWaitTime = waitTime;
  	
  	m_movementGoal = MOVEGOAL_ENEMY;
  	return FRefreshRoute();
  }
  
  
  BOOL CBaseMonster::MoveToLocation( Activity movementAct, float waitTime, const Vector &goal )
  {
  	m_movementActivity = movementAct;
  	m_moveWaitTime = waitTime;
  	
  	m_movementGoal = MOVEGOAL_LOCATION;
  	m_vecMoveGoal = goal;
  	return FRefreshRoute();
  }
  
  
  BOOL CBaseMonster::MoveToTarget( Activity movementAct, float waitTime )
  {
  	m_movementActivity = movementAct;
  	m_moveWaitTime = waitTime;
  	
  	m_movementGoal = MOVEGOAL_TARGETENT;
  	return FRefreshRoute();
  }
  
  
  BOOL CBaseMonster::MoveToNode( Activity movementAct, float waitTime, const Vector &goal )
  {
  	m_movementActivity = movementAct;
  	m_moveWaitTime = waitTime;
  
  	m_movementGoal = MOVEGOAL_NODE;
  	m_vecMoveGoal = goal;
  	return FRefreshRoute();
  }
  
  
  #ifdef _DEBUG
  void DrawRoute( entvars_t *pev, WayPoint_t *m_Route, int m_iRouteIndex, int r, int g, int b )
  {
  	int			i;
  
  	if ( m_Route[m_iRouteIndex].iType == 0 )
  	{
  		ALERT( at_aiconsole, "Can't draw route!\n" );
  		return;
  	}
  
  //	UTIL_ParticleEffect ( m_Route[ m_iRouteIndex ].vecLocation, g_vecZero, 255, 25 );
  
  	MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
  		WRITE_BYTE( TE_BEAMPOINTS);
  		WRITE_COORD( pev->origin.x );
  		WRITE_COORD( pev->origin.y );
  		WRITE_COORD( pev->origin.z );
  		WRITE_COORD( m_Route[ m_iRouteIndex ].vecLocation.x );
  		WRITE_COORD( m_Route[ m_iRouteIndex ].vecLocation.y );
  		WRITE_COORD( m_Route[ m_iRouteIndex ].vecLocation.z );
  
  		WRITE_SHORT( g_sModelIndexLaser );
  		WRITE_BYTE( 0 ); // frame start
  		WRITE_BYTE( 10 ); // framerate
  		WRITE_BYTE( 1 ); // life
  		WRITE_BYTE( 16 );  // width
  		WRITE_BYTE( 0 );   // noise
  		WRITE_BYTE( r );   // r, g, b
  		WRITE_BYTE( g );   // r, g, b
  		WRITE_BYTE( b );   // r, g, b
  		WRITE_BYTE( 255 );	// brightness
  		WRITE_BYTE( 10 );		// speed
  	MESSAGE_END();
  
  	for ( i = m_iRouteIndex ; i < ROUTE_SIZE - 1; i++ )
  	{
  		if ( (m_Route[ i ].iType & bits_MF_IS_GOAL) || (m_Route[ i+1 ].iType == 0) )
  			break;
  
  		
  		MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
  			WRITE_BYTE( TE_BEAMPOINTS );
  			WRITE_COORD( m_Route[ i ].vecLocation.x );
  			WRITE_COORD( m_Route[ i ].vecLocation.y );
  			WRITE_COORD( m_Route[ i ].vecLocation.z );
  			WRITE_COORD( m_Route[ i + 1 ].vecLocation.x );
  			WRITE_COORD( m_Route[ i + 1 ].vecLocation.y );
  			WRITE_COORD( m_Route[ i + 1 ].vecLocation.z );
  			WRITE_SHORT( g_sModelIndexLaser );
  			WRITE_BYTE( 0 ); // frame start
  			WRITE_BYTE( 10 ); // framerate
  			WRITE_BYTE( 1 ); // life
  			WRITE_BYTE( 8 );  // width
  			WRITE_BYTE( 0 );   // noise
  			WRITE_BYTE( r );   // r, g, b
  			WRITE_BYTE( g );   // r, g, b
  			WRITE_BYTE( b );   // r, g, b
  			WRITE_BYTE( 255 );	// brightness
  			WRITE_BYTE( 10 );		// speed
  		MESSAGE_END();
  
  //		UTIL_ParticleEffect ( m_Route[ i ].vecLocation, g_vecZero, 255, 25 );
  	}
  }
  #endif
  
  
  int ShouldSimplify( int routeType )
  {
  	routeType &= ~bits_MF_IS_GOAL;
  
  	if ( (routeType == bits_MF_TO_PATHCORNER) || (routeType & bits_MF_DONT_SIMPLIFY) )
  		return FALSE;
  	return TRUE;
  }
  
  //=========================================================
  // RouteSimplify
  //
  // Attempts to make the route more direct by cutting out
  // unnecessary nodes & cutting corners.
  //
  //=========================================================
  void CBaseMonster :: RouteSimplify( CBaseEntity *pTargetEnt )
  {
  	// BUGBUG: this doesn't work 100% yet
  	int			i, count, outCount;
  	Vector		vecStart;
  	WayPoint_t	outRoute[ ROUTE_SIZE * 2 ];	// Any points except the ends can turn into 2 points in the simplified route
  
  	count = 0;
  
  	for ( i = m_iRouteIndex; i < ROUTE_SIZE; i++ )
  	{
  		if ( !m_Route[i].iType )
  			break;
  		else
  			count++;
  		if ( m_Route[i].iType & bits_MF_IS_GOAL )
  			break;
  	}
  	// Can't simplify a direct route!
  	if ( count < 2 )
  	{
  //		DrawRoute( pev, m_Route, m_iRouteIndex, 0, 0, 255 );
  		return;
  	}
  
  	outCount = 0;
  	vecStart = pev->origin;
  	for ( i = 0; i < count-1; i++ )
  	{
  		// Don't eliminate path_corners
  		if ( !ShouldSimplify( m_Route[m_iRouteIndex+i].iType ) )
  		{
  			outRoute[outCount] = m_Route[ m_iRouteIndex + i ];
  			outCount++;
  		}
  		else if ( CheckLocalMove ( vecStart, m_Route[m_iRouteIndex+i+1].vecLocation, pTargetEnt, NULL ) == LOCALMOVE_VALID )
  		{
  			// Skip vert
  			continue;
  		}
  		else
  		{
  			Vector vecTest, vecSplit;
  
  			// Halfway between this and next
  			vecTest = (m_Route[m_iRouteIndex+i+1].vecLocation + m_Route[m_iRouteIndex+i].vecLocation) * 0.5;
  
  			// Halfway between this and previous
  			vecSplit = (m_Route[m_iRouteIndex+i].vecLocation + vecStart) * 0.5;
  
  			int iType = (m_Route[m_iRouteIndex+i].iType | bits_MF_TO_DETOUR) & ~bits_MF_NOT_TO_MASK;
  			if ( CheckLocalMove ( vecStart, vecTest, pTargetEnt, NULL ) == LOCALMOVE_VALID )
  			{
  				outRoute[outCount].iType = iType;
  				outRoute[outCount].vecLocation = vecTest;
  			}
  			else if ( CheckLocalMove ( vecSplit, vecTest, pTargetEnt, NULL ) == LOCALMOVE_VALID )
  			{
  				outRoute[outCount].iType = iType;
  				outRoute[outCount].vecLocation = vecSplit;
  				outRoute[outCount+1].iType = iType;
  				outRoute[outCount+1].vecLocation = vecTest;
  				outCount++; // Adding an extra point
  			}
  			else
  			{
  				outRoute[outCount] = m_Route[ m_iRouteIndex + i ];
  			}
  		}
  		// Get last point
  		vecStart = outRoute[ outCount ].vecLocation;
  		outCount++;
  	}
  	ASSERT( i < count );
  	outRoute[outCount] = m_Route[ m_iRouteIndex + i ];
  	outCount++;
  	
  	// Terminate
  	outRoute[outCount].iType = 0;
  	ASSERT( outCount < (ROUTE_SIZE*2) );
  
  // Copy the simplified route, disable for testing
  	m_iRouteIndex = 0;
  	for ( i = 0; i < ROUTE_SIZE && i < outCount; i++ )
  	{
  		m_Route[i] = outRoute[i];
  	}
  
  	// Terminate route
  	if ( i < ROUTE_SIZE )
  		m_Route[i].iType = 0;
  
  // Debug, test movement code
  #if 0
  //	if ( CVAR_GET_FLOAT( "simplify" ) != 0 )
  		DrawRoute( pev, outRoute, 0, 255, 0, 0 );
  //	else
  		DrawRoute( pev, m_Route, m_iRouteIndex, 0, 255, 0 );
  #endif
  }
  
  //=========================================================
  // FBecomeProne - tries to send a monster into PRONE state.
  // right now only used when a barnacle snatches someone, so 
  // may have some special case stuff for that.
  //=========================================================
  BOOL CBaseMonster :: FBecomeProne ()
  {
  	if ( FBitSet ( pev->flags, FL_ONGROUND ) )
  	{
  		pev->flags -= FL_ONGROUND;
  	}
  
  	m_IdealMonsterState = MONSTERSTATE_PRONE;
  	return TRUE;
  }
  
  //=========================================================
  // CheckRangeAttack1
  //=========================================================
  BOOL CBaseMonster :: CheckRangeAttack1 ( float flDot, float flDist )
  {
  	if ( flDist > 64 && flDist <= 784 && flDot >= 0.5 )
  	{
  		return TRUE;
  	}
  	return FALSE;
  }
  
  //=========================================================
  // CheckRangeAttack2
  //=========================================================
  BOOL CBaseMonster :: CheckRangeAttack2 ( float flDot, float flDist )
  {
  	if ( flDist > 64 && flDist <= 512 && flDot >= 0.5 )
  	{
  		return TRUE;
  	}
  	return FALSE;
  }
  
  //=========================================================
  // CheckMeleeAttack1
  //=========================================================
  BOOL CBaseMonster :: CheckMeleeAttack1 ( float flDot, float flDist )
  {
  	// Decent fix to keep folks from kicking/punching hornets and snarks is to check the onground flag(sjb)
  	if ( flDist <= 64 && flDot >= 0.7 && m_hEnemy != NULL && FBitSet ( m_hEnemy->pev->flags, FL_ONGROUND ) )
  	{
  		return TRUE;
  	}
  	return FALSE;
  }
  
  //=========================================================
  // CheckMeleeAttack2
  //=========================================================
  BOOL CBaseMonster :: CheckMeleeAttack2 ( float flDot, float flDist )
  {
  	if ( flDist <= 64 && flDot >= 0.7 )
  	{
  		return TRUE;
  	}
  	return FALSE;
  }
  
  //=========================================================
  // CheckAttacks - sets all of the bits for attacks that the
  // monster is capable of carrying out on the passed entity.
  //=========================================================
  void CBaseMonster :: CheckAttacks ( CBaseEntity *pTarget, float flDist )
  {
  	Vector2D	vec2LOS;
  	float		flDot;
  
  	UTIL_MakeVectors ( pev->angles );
  
  	vec2LOS = ( pTarget->pev->origin - pev->origin ).Make2D();
  	vec2LOS = vec2LOS.Normalize();
  
  	flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() );
  
  	// we know the enemy is in front now. We'll find which attacks the monster is capable of by
  	// checking for corresponding Activities in the model file, then do the simple checks to validate
  	// those attack types.
  	
  	// Clear all attack conditions
  	ClearConditions( bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK1 |bits_COND_CAN_MELEE_ATTACK2 );
  
  	if ( m_afCapability & bits_CAP_RANGE_ATTACK1 )
  	{
  		if ( CheckRangeAttack1 ( flDot, flDist ) )
  			SetConditions( bits_COND_CAN_RANGE_ATTACK1 );
  	}
  	if ( m_afCapability & bits_CAP_RANGE_ATTACK2 )
  	{
  		if ( CheckRangeAttack2 ( flDot, flDist ) )
  			SetConditions( bits_COND_CAN_RANGE_ATTACK2 );
  	}
  	if ( m_afCapability & bits_CAP_MELEE_ATTACK1 )
  	{
  		if ( CheckMeleeAttack1 ( flDot, flDist ) )
  			SetConditions( bits_COND_CAN_MELEE_ATTACK1 );
  	}
  	if ( m_afCapability & bits_CAP_MELEE_ATTACK2 )
  	{
  		if ( CheckMeleeAttack2 ( flDot, flDist ) )
  			SetConditions( bits_COND_CAN_MELEE_ATTACK2 );
  	}
  }
  
  //=========================================================
  // CanCheckAttacks - prequalifies a monster to do more fine
  // checking of potential attacks. 
  //=========================================================
  BOOL CBaseMonster :: FCanCheckAttacks ()
  {
  	if ( HasConditions(bits_COND_SEE_ENEMY) && !HasConditions( bits_COND_ENEMY_TOOFAR ) )
  	{
  		return TRUE;
  	}
  
  	return FALSE;
  }
  
  //=========================================================
  // CheckEnemy - part of the Condition collection process,
  // gets and stores data and conditions pertaining to a monster's
  // enemy. Returns TRUE if Enemy LKP was updated.
  //=========================================================
  int CBaseMonster :: CheckEnemy ( CBaseEntity *pEnemy )
  {
  	float	flDistToEnemy;
  	int		iUpdatedLKP;// set this to TRUE if you update the EnemyLKP in this function.
  
  	iUpdatedLKP = FALSE;
  	ClearConditions ( bits_COND_ENEMY_FACING_ME );
  	
  	if ( !FVisible( pEnemy ) )
  	{
  		ASSERT(!HasConditions(bits_COND_SEE_ENEMY));
  		SetConditions( bits_COND_ENEMY_OCCLUDED );
  	}
  	else
  		ClearConditions( bits_COND_ENEMY_OCCLUDED );
  
  	if ( !pEnemy->IsAlive() )
  	{
  		SetConditions ( bits_COND_ENEMY_DEAD );
  		ClearConditions( bits_COND_SEE_ENEMY | bits_COND_ENEMY_OCCLUDED );
  		return FALSE;
  	}
  
  	Vector vecEnemyPos = pEnemy->pev->origin;
  	// distance to enemy's origin
  	flDistToEnemy = ( vecEnemyPos - pev->origin ).Length();
  	vecEnemyPos.z += pEnemy->pev->size.z * 0.5;
  	// distance to enemy's head
  	float flDistToEnemy2 = (vecEnemyPos - pev->origin).Length();
  	if (flDistToEnemy2 < flDistToEnemy)
  		flDistToEnemy = flDistToEnemy2;
  	else
  	{
  		// distance to enemy's feet
  		vecEnemyPos.z -= pEnemy->pev->size.z;
  		float flDistToEnemy2 = (vecEnemyPos - pev->origin).Length();
  		if (flDistToEnemy2 < flDistToEnemy)
  			flDistToEnemy = flDistToEnemy2;
  	}
  
  	if ( HasConditions( bits_COND_SEE_ENEMY ) )
  	{
  		CBaseMonster *pEnemyMonster;
  
  		iUpdatedLKP = TRUE;
  		m_vecEnemyLKP = pEnemy->pev->origin;
  
  		pEnemyMonster = pEnemy->MyMonsterPointer();
  
  		if ( pEnemyMonster )
  		{
  			if ( pEnemyMonster->FInViewCone ( this ) )
  			{
  				SetConditions ( bits_COND_ENEMY_FACING_ME );
  			}
  			else
  				ClearConditions( bits_COND_ENEMY_FACING_ME );
  		}
  
  		if (pEnemy->pev->velocity != Vector( 0, 0, 0))
  		{
  			// trail the enemy a bit
  			m_vecEnemyLKP = m_vecEnemyLKP - pEnemy->pev->velocity * RANDOM_FLOAT( -0.05, 0 );
  		}
  		else
  		{
  			// UNDONE: use pev->oldorigin?
  		}
  	}
  	else if ( !HasConditions(bits_COND_ENEMY_OCCLUDED|bits_COND_SEE_ENEMY) && ( flDistToEnemy <= 256 ) )
  	{
  		// if the enemy is not occluded, and unseen, that means it is behind or beside the monster.
  		// if the enemy is near enough the monster, we go ahead and let the monster know where the
  		// enemy is. 
  		iUpdatedLKP = TRUE;
  		m_vecEnemyLKP = pEnemy->pev->origin;
  	}
  
  	if ( flDistToEnemy >= m_flDistTooFar )
  	{
  		// enemy is very far away from monster
  		SetConditions( bits_COND_ENEMY_TOOFAR );
  	}
  	else
  		ClearConditions( bits_COND_ENEMY_TOOFAR );
  
  	if ( FCanCheckAttacks() )	
  	{
  		CheckAttacks ( m_hEnemy, flDistToEnemy );
  	}
  
  	if ( m_movementGoal == MOVEGOAL_ENEMY )
  	{
  		for ( int i = m_iRouteIndex; i < ROUTE_SIZE; i++ )
  		{
  			if ( m_Route[ i ].iType == (bits_MF_IS_GOAL|bits_MF_TO_ENEMY) )
  			{
  				// UNDONE: Should we allow monsters to override this distance (80?)
  				if ( (m_Route[ i ].vecLocation - m_vecEnemyLKP).Length() > 80 )
  				{
  					// Refresh
  					FRefreshRoute();
  					return iUpdatedLKP;
  				}
  			}
  		}
  	}
  
  	return iUpdatedLKP;
  }
  
  //=========================================================
  // PushEnemy - remember the last few enemies, always remember the player
  //=========================================================
  void CBaseMonster :: PushEnemy( CBaseEntity *pEnemy, Vector &vecLastKnownPos )
  {
  	int i;
  
  	if (pEnemy == NULL)
  		return;
  
  	// UNDONE: blah, this is bad, we should use a stack but I'm too lazy to code one.
  	for (i = 0; i < MAX_OLD_ENEMIES; i++)
  	{
  		if (m_hOldEnemy[i] == pEnemy)
  			return;
  		if (m_hOldEnemy[i] == NULL) // someone died, reuse their slot
  			break;
  	}
  	if (i >= MAX_OLD_ENEMIES)
  		return;
  
  	m_hOldEnemy[i] = pEnemy;
  	m_vecOldEnemy[i] = vecLastKnownPos;
  }
  
  //=========================================================
  // PopEnemy - try remembering the last few enemies
  //=========================================================
  BOOL CBaseMonster :: PopEnemy( )
  {
  	// UNDONE: blah, this is bad, we should use a stack but I'm too lazy to code one.
  	for (int i = MAX_OLD_ENEMIES - 1; i >= 0; i--)
  	{
  		if (m_hOldEnemy[i] != NULL)
  		{
  			if (m_hOldEnemy[i]->IsAlive( )) // cheat and know when they die
  			{
  				m_hEnemy = m_hOldEnemy[i];
  				m_vecEnemyLKP = m_vecOldEnemy[i];
  				// ALERT( at_console, "remembering\n");
  				return TRUE;
  			}
  			else
  			{
  				m_hOldEnemy[i] = NULL;
  			}
  		}
  	}
  	return FALSE;
  }
  
  //=========================================================
  // SetActivity 
  //=========================================================
  void CBaseMonster :: SetActivity ( Activity NewActivity )
  {
  	int	iSequence;
  
  	iSequence = LookupActivity ( NewActivity );
  
  	// Set to the desired anim, or default anim if the desired is not present
  	if ( iSequence > ACTIVITY_NOT_AVAILABLE )
  	{
  		if ( pev->sequence != iSequence || !m_fSequenceLoops )
  		{
  			// don't reset frame between walk and run
  			if ( !(m_Activity == ACT_WALK || m_Activity == ACT_RUN) || !(NewActivity == ACT_WALK || NewActivity == ACT_RUN))
  				pev->frame = 0;
  		}
  
  		pev->sequence		= iSequence;	// Set to the reset anim (if it's there)
  		ResetSequenceInfo( );
  		SetYawSpeed();
  	}
  	else
  	{
  		// Not available try to get default anim
  		ALERT ( at_aiconsole, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity );
  		pev->sequence		= 0;	// Set to the reset anim (if it's there)
  	}
  
  	m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present
  	
  	// In case someone calls this with something other than the ideal activity
  	m_IdealActivity = m_Activity;
  
  
  }
  
  //=========================================================
  // SetSequenceByName
  //=========================================================
  void CBaseMonster :: SetSequenceByName ( const char *szSequence )
  {
  	int	iSequence;
  
  	iSequence = LookupSequence ( szSequence );
  
  	// Set to the desired anim, or default anim if the desired is not present
  	if ( iSequence > ACTIVITY_NOT_AVAILABLE )
  	{
  		if ( pev->sequence != iSequence || !m_fSequenceLoops )
  		{
  			pev->frame = 0;
  		}
  
  		pev->sequence		= iSequence;	// Set to the reset anim (if it's there)
  		ResetSequenceInfo( );
  		SetYawSpeed();
  	}
  	else
  	{
  		// Not available try to get default anim
  		ALERT ( at_aiconsole, "%s has no sequence named:%f\n", STRING(pev->classname), szSequence );
  		pev->sequence		= 0;	// Set to the reset anim (if it's there)
  	}
  }
  
  //=========================================================
  // CheckLocalMove - returns TRUE if the caller can walk a 
  // straight line from its current origin to the given 
  // location. If so, don't use the node graph!
  //
  // if a valid pointer to a int is passed, the function
  // will fill that int with the distance that the check 
  // reached before hitting something. THIS ONLY HAPPENS
  // IF THE LOCAL MOVE CHECK FAILS!
  //
  // !!!PERFORMANCE - should we try to load balance this?
  // DON"T USE SETORIGIN! 
  //=========================================================
  #define	LOCAL_STEP_SIZE	16
  int CBaseMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist )
  {
  	Vector	vecStartPos;// record monster's position before trying the move
  	float	flYaw;
  	float	flDist;
  	float	flStep, stepSize;
  	int		iReturn;
  
  	vecStartPos = pev->origin;
  	
  	
  	flYaw = UTIL_VecToYaw ( vecEnd - vecStart );// build a yaw that points to the goal.
  	flDist = ( vecEnd - vecStart ).Length2D();// get the distance.
  	iReturn = LOCALMOVE_VALID;// assume everything will be ok.
  
  	// move the monster to the start of the local move that's to be checked.
  	UTIL_SetOrigin( pev, vecStart );// !!!BUGBUG - won't this fire triggers? - nope, SetOrigin doesn't fire
  
  	if ( !(pev->flags & (FL_FLY|FL_SWIM)) )
  	{
  		DROP_TO_FLOOR( ENT( pev ) );//make sure monster is on the floor!
  	}
  
  	//pev->origin.z = vecStartPos.z;//!!!HACKHACK
  
  //	pev->origin = vecStart;
  
  /*
  	if ( flDist > 1024 )
  	{
  		// !!!PERFORMANCE - this operation may be too CPU intensive to try checks this large.
  		// We don't lose much here, because a distance this great is very likely
  		// to have something in the way.
  
  		// since we've actually moved the monster during the check, undo the move.
  		pev->origin = vecStartPos;
  		return FALSE;
  	}
  */
  	// this loop takes single steps to the goal.
  	for ( flStep = 0 ; flStep < flDist ; flStep += LOCAL_STEP_SIZE )
  	{
  		stepSize = LOCAL_STEP_SIZE;
  
  		if ( (flStep + LOCAL_STEP_SIZE) >= (flDist-1) )
  			stepSize = (flDist - flStep) - 1;
  		
  //		UTIL_ParticleEffect ( pev->origin, g_vecZero, 255, 25 );
  
  		if ( !WALK_MOVE( ENT(pev), flYaw, stepSize, WALKMOVE_CHECKONLY ) )
  		{// can't take the next step, fail!
  
  			if ( pflDist != NULL )
  			{
  				*pflDist = flStep;
  			}
  			if ( pTarget && pTarget->edict() == gpGlobals->trace_ent )
  			{
  				// if this step hits target ent, the move is legal.
  				iReturn = LOCALMOVE_VALID;
  				break;
  			}
  			else
  			{
  				// If we're going toward an entity, and we're almost getting there, it's OK.
  //				if ( pTarget && fabs( flDist - iStep ) < LOCAL_STEP_SIZE )
  //					fReturn = TRUE;
  //				else
  				iReturn = LOCALMOVE_INVALID;
  				break;
  			}
  
  		}
  	}
  
  	if ( iReturn == LOCALMOVE_VALID && 	!(pev->flags & (FL_FLY|FL_SWIM) ) && (!pTarget || (pTarget->pev->flags & FL_ONGROUND)) )
  	{
  		// The monster can move to a spot UNDER the target, but not to it. Don't try to triangulate, go directly to the node graph.
  		// UNDONE: Magic # 64 -- this used to be pev->size.z but that won't work for small creatures like the headcrab
  		if ( fabs(vecEnd.z - pev->origin.z) > 64 )
  		{
  			iReturn = LOCALMOVE_INVALID_DONT_TRIANGULATE;
  		}
  	}
  	/*
  	// uncommenting this block will draw a line representing the nearest legal move.
  	WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY);
  	WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE);
  	WRITE_COORD(MSG_BROADCAST, pev->origin.x);
  	WRITE_COORD(MSG_BROADCAST, pev->origin.y);
  	WRITE_COORD(MSG_BROADCAST, pev->origin.z);
  	WRITE_COORD(MSG_BROADCAST, vecStart.x);
  	WRITE_COORD(MSG_BROADCAST, vecStart.y);
  	WRITE_COORD(MSG_BROADCAST, vecStart.z);
  	*/
  
  	// since we've actually moved the monster during the check, undo the move.
  	UTIL_SetOrigin( pev, vecStartPos );
  
  	return iReturn;
  }
  
  
  float CBaseMonster :: OpenDoorAndWait( entvars_t *pevDoor )
  {
  	float flTravelTime = 0;
  
  	//ALERT(at_aiconsole, "A door. ");
  	CBaseEntity *pcbeDoor = CBaseEntity::Instance(pevDoor);
  	if (pcbeDoor && !pcbeDoor->IsLockedByMaster())
  	{
  		//ALERT(at_aiconsole, "unlocked! ");
  		pcbeDoor->Use(this, this, USE_ON, 0.0);
  		//ALERT(at_aiconsole, "pevDoor->nextthink = %d ms\n", (int)(1000*pevDoor->nextthink));
  		//ALERT(at_aiconsole, "pevDoor->ltime = %d ms\n", (int)(1000*pevDoor->ltime));
  		//ALERT(at_aiconsole, "pev-> nextthink = %d ms\n", (int)(1000*pev->nextthink));
  		//ALERT(at_aiconsole, "pev->ltime = %d ms\n", (int)(1000*pev->ltime));
  		flTravelTime = pevDoor->nextthink - pevDoor->ltime;
  		//ALERT(at_aiconsole, "Waiting %d ms\n", (int)(1000*flTravelTime));
  		if ( pcbeDoor->pev->targetname )
  		{
  			edict_t *pentTarget = NULL;
  			for (;;)
  			{
  				pentTarget = FIND_ENTITY_BY_TARGETNAME( pentTarget, STRING(pcbeDoor->pev->targetname));
  
  				if ( VARS( pentTarget ) != pcbeDoor->pev )
  				{
  					if (FNullEnt(pentTarget))
  						break;
  
  					if ( FClassnameIs ( pentTarget, STRING(pcbeDoor->pev->classname) ) )
  					{
  						CBaseEntity *pDoor = Instance(pentTarget);
  						if ( pDoor )
  							pDoor->Use(this, this, USE_ON, 0.0);
  					}
  				}
  			}
  		}
  	}
  
  	return gpGlobals->time + flTravelTime;
  }
  
  
  //=========================================================
  // AdvanceRoute - poorly named function that advances the 
  // m_iRouteIndex. If it goes beyond ROUTE_SIZE, the route 
  // is refreshed. 
  //=========================================================
  void CBaseMonster :: AdvanceRoute ( float distance )
  {
  
  	if ( m_iRouteIndex == ROUTE_SIZE - 1 )
  	{
  		// time to refresh the route.
  		if ( !FRefreshRoute() )
  		{
  			ALERT ( at_aiconsole, "Can't Refresh Route!!\n" );
  		}
  	}
  	else
  	{
  		if ( ! (m_Route[ m_iRouteIndex ].iType & bits_MF_IS_GOAL) )
  		{
  			// If we've just passed a path_corner, advance m_pGoalEnt
  			if ( (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK) == bits_MF_TO_PATHCORNER )
  				m_pGoalEnt = m_pGoalEnt->GetNextTarget();
  
  			// IF both waypoints are nodes, then check for a link for a door and operate it.
  			//
  			if (  (m_Route[m_iRouteIndex].iType   & bits_MF_TO_NODE) == bits_MF_TO_NODE
  			   && (m_Route[m_iRouteIndex+1].iType & bits_MF_TO_NODE) == bits_MF_TO_NODE)
  			{
  				//ALERT(at_aiconsole, "SVD: Two nodes. ");
  
  				int iSrcNode  = WorldGraph.FindNearestNode(m_Route[m_iRouteIndex].vecLocation, this );
  				int iDestNode = WorldGraph.FindNearestNode(m_Route[m_iRouteIndex+1].vecLocation, this );
  
  				int iLink;
  				WorldGraph.HashSearch(iSrcNode, iDestNode, iLink);
  
  				if ( iLink >= 0 && WorldGraph.m_pLinkPool[iLink].m_pLinkEnt != NULL )
  				{
  					//ALERT(at_aiconsole, "A link. ");
  					if ( WorldGraph.HandleLinkEnt ( iSrcNode, WorldGraph.m_pLinkPool[iLink].m_pLinkEnt, m_afCapability, CGraph::NODEGRAPH_DYNAMIC ) )
  					{
  						//ALERT(at_aiconsole, "usable.");
  						entvars_t *pevDoor = WorldGraph.m_pLinkPool[iLink].m_pLinkEnt;
  						if (pevDoor)
  						{
  							m_flMoveWaitFinished = OpenDoorAndWait( pevDoor );
  //							ALERT( at_aiconsole, "Wating for door %.2f\n", m_flMoveWaitFinished-gpGlobals->time );
  						}
  					}
  				}
  				//ALERT(at_aiconsole, "\n");
  			}
  			m_iRouteIndex++;
  		}
  		else	// At goal!!!
  		{
  			if ( distance < m_flGroundSpeed * 0.2 /* FIX */ )
  			{
  				MovementComplete();
  			}
  		}
  	}
  }
  
  
  int CBaseMonster :: RouteClassify( int iMoveFlag )
  {
  	int movementGoal;
  
  	movementGoal = MOVEGOAL_NONE;
  
  	if ( iMoveFlag & bits_MF_TO_TARGETENT )
  		movementGoal = MOVEGOAL_TARGETENT;
  	else if ( iMoveFlag & bits_MF_TO_ENEMY )
  		movementGoal = MOVEGOAL_ENEMY;
  	else if ( iMoveFlag & bits_MF_TO_PATHCORNER )
  		movementGoal = MOVEGOAL_PATHCORNER;
  	else if ( iMoveFlag & bits_MF_TO_NODE )
  		movementGoal = MOVEGOAL_NODE;
  	else if ( iMoveFlag & bits_MF_TO_LOCATION )
  		movementGoal = MOVEGOAL_LOCATION;
  
  	return movementGoal;
  }
  
  //=========================================================
  // BuildRoute
  //=========================================================
  BOOL CBaseMonster :: BuildRoute ( const Vector &vecGoal, int iMoveFlag, CBaseEntity *pTarget )
  {
  	float	flDist;
  	Vector	vecApex;
  	int		iLocalMove;
  
  	RouteNew();
  	m_movementGoal = RouteClassify( iMoveFlag );
  
  // so we don't end up with no moveflags
  	m_Route[ 0 ].vecLocation = vecGoal;
  	m_Route[ 0 ].iType = iMoveFlag | bits_MF_IS_GOAL;
  
  // check simple local move
  	iLocalMove = CheckLocalMove( pev->origin, vecGoal, pTarget, &flDist );
  
  	if ( iLocalMove == LOCALMOVE_VALID )
  	{
  		// monster can walk straight there!
  		return TRUE;
  	}
  // try to triangulate around any obstacles.
  	else if ( iLocalMove != LOCALMOVE_INVALID_DONT_TRIANGULATE && FTriangulate( pev->origin, vecGoal, flDist, pTarget, &vecApex ) )
  	{
  		// there is a slightly more complicated path that allows the monster to reach vecGoal
  		m_Route[ 0 ].vecLocation = vecApex;
  		m_Route[ 0 ].iType = (iMoveFlag | bits_MF_TO_DETOUR);
  
  		m_Route[ 1 ].vecLocation = vecGoal;
  		m_Route[ 1 ].iType = iMoveFlag | bits_MF_IS_GOAL;
  
  			/*
  			WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY);
  			WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE);
  			WRITE_COORD(MSG_BROADCAST, vecApex.x );
  			WRITE_COORD(MSG_BROADCAST, vecApex.y );
  			WRITE_COORD(MSG_BROADCAST, vecApex.z );
  			WRITE_COORD(MSG_BROADCAST, vecApex.x );
  			WRITE_COORD(MSG_BROADCAST, vecApex.y );
  			WRITE_COORD(MSG_BROADCAST, vecApex.z + 128 );
  			*/
  
  		RouteSimplify( pTarget );
  		return TRUE;
  	}
  
  // last ditch, try nodes
  	if ( FGetNodeRoute( vecGoal ) )
  	{
  //		ALERT ( at_console, "Can get there on nodes\n" );
  		m_vecMoveGoal = vecGoal;
  		RouteSimplify( pTarget );
  		return TRUE;
  	}
  
  	// b0rk
  	return FALSE;
  }
  
  
  //=========================================================
  // InsertWaypoint - Rebuilds the existing route so that the
  // supplied vector and moveflags are the first waypoint in
  // the route, and fills the rest of the route with as much
  // of the pre-existing route as possible
  //=========================================================
  void CBaseMonster :: InsertWaypoint ( Vector vecLocation, int afMoveFlags )
  {
  	int			i, type;
  
  	
  	// we have to save some Index and Type information from the real
  	// path_corner or node waypoint that the monster was trying to reach. This makes sure that data necessary 
  	// to refresh the original path exists even in the new waypoints that don't correspond directy to a path_corner
  	// or node. 
  	type = afMoveFlags | (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK);
  
  	for ( i = ROUTE_SIZE-1; i > 0; i-- )
  		m_Route[i] = m_Route[i-1];
  
  	m_Route[ m_iRouteIndex ].vecLocation = vecLocation;
  	m_Route[ m_iRouteIndex ].iType = type;
  }
  
  //=========================================================
  // FTriangulate - tries to overcome local obstacles by 
  // triangulating a path around them.
  //
  // iApexDist is how far the obstruction that we are trying
  // to triangulate around is from the monster.
  //=========================================================
  BOOL CBaseMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex )
  {
  	Vector		vecDir;
  	Vector		vecForward;
  	Vector		vecLeft;// the spot we'll try to triangulate to on the left
  	Vector		vecRight;// the spot we'll try to triangulate to on the right
  	Vector		vecTop;// the spot we'll try to triangulate to on the top
  	Vector		vecBottom;// the spot we'll try to triangulate to on the bottom
  	Vector		vecFarSide;// the spot that we'll move to after hitting the triangulated point, before moving on to our normal goal.
  	int			i;
  	float		sizeX, sizeZ;
  
  	// If the hull width is less than 24, use 24 because CheckLocalMove uses a min of
  	// 24.
  	sizeX = pev->size.x;
  	if (sizeX < 24.0)
  		sizeX = 24.0;
  	else if (sizeX > 48.0)
  		sizeX = 48.0;
  	sizeZ = pev->size.z;
  	//if (sizeZ < 24.0)
  	//	sizeZ = 24.0;
  
  	vecForward = ( vecEnd - vecStart ).Normalize();
  
  	Vector vecDirUp(0,0,1);
  	vecDir = CrossProduct ( vecForward, vecDirUp);
  
  	// start checking right about where the object is, picking two equidistant starting points, one on
  	// the left, one on the right. As we progress through the loop, we'll push these away from the obstacle, 
  	// hoping to find a way around on either side. pev->size.x is added to the ApexDist in order to help select
  	// an apex point that insures that the monster is sufficiently past the obstacle before trying to turn back
  	// onto its original course.
  
  	vecLeft = pev->origin + ( vecForward * ( flDist + sizeX ) ) - vecDir * ( sizeX * 3 );
  	vecRight = pev->origin + ( vecForward * ( flDist + sizeX ) ) + vecDir * ( sizeX * 3 );
  	if (pev->movetype == MOVETYPE_FLY)
  	{
  		vecTop = pev->origin + (vecForward * flDist) + (vecDirUp * sizeZ * 3);
  		vecBottom = pev->origin + (vecForward * flDist) - (vecDirUp *  sizeZ * 3);
  	}
  
  	vecFarSide = m_Route[ m_iRouteIndex ].vecLocation;
  	
  	vecDir = vecDir * sizeX * 2;
  	if (pev->movetype == MOVETYPE_FLY)
  		vecDirUp = vecDirUp * sizeZ * 2;
  
  	for ( i = 0 ; i < 8; i++ )
  	{
  // Debug, Draw the triangulation
  #if 0
  		MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
  			WRITE_BYTE( TE_SHOWLINE);
  			WRITE_COORD( pev->origin.x );
  			WRITE_COORD( pev->origin.y );
  			WRITE_COORD( pev->origin.z );
  			WRITE_COORD( vecRight.x );
  			WRITE_COORD( vecRight.y );
  			WRITE_COORD( vecRight.z );
  		MESSAGE_END();
  
  		MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
  			WRITE_BYTE( TE_SHOWLINE );
  			WRITE_COORD( pev->origin.x );
  			WRITE_COORD( pev->origin.y );
  			WRITE_COORD( pev->origin.z );
  			WRITE_COORD( vecLeft.x );
  			WRITE_COORD( vecLeft.y );
  			WRITE_COORD( vecLeft.z );
  		MESSAGE_END();
  #endif
  
  #if 0
  		if (pev->movetype == MOVETYPE_FLY)
  		{
  			MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
  				WRITE_BYTE( TE_SHOWLINE );
  				WRITE_COORD( pev->origin.x );
  				WRITE_COORD( pev->origin.y );
  				WRITE_COORD( pev->origin.z );
  				WRITE_COORD( vecTop.x );
  				WRITE_COORD( vecTop.y );
  				WRITE_COORD( vecTop.z );
  			MESSAGE_END();
  
  			MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
  				WRITE_BYTE( TE_SHOWLINE );
  				WRITE_COORD( pev->origin.x );
  				WRITE_COORD( pev->origin.y );
  				WRITE_COORD( pev->origin.z );
  				WRITE_COORD( vecBottom.x );
  				WRITE_COORD( vecBottom.y );
  				WRITE_COORD( vecBottom.z );
  			MESSAGE_END();
  		}
  #endif
  
  		if ( CheckLocalMove( pev->origin, vecRight, pTargetEnt, NULL ) == LOCALMOVE_VALID )
  		{
  			if ( CheckLocalMove ( vecRight, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID )
  			{
  				if ( pApex )
  				{
  					*pApex = vecRight;
  				}
  
  				return TRUE;
  			}
  		}
  		if ( CheckLocalMove( pev->origin, vecLeft, pTargetEnt, NULL ) == LOCALMOVE_VALID )
  		{
  			if ( CheckLocalMove ( vecLeft, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID )
  			{
  				if ( pApex )
  				{
  					*pApex = vecLeft;
  				}
  
  				return TRUE;
  			}
  		}
  
  		if (pev->movetype == MOVETYPE_FLY)
  		{
  			if ( CheckLocalMove( pev->origin, vecTop, pTargetEnt, NULL ) == LOCALMOVE_VALID)
  			{
  				if ( CheckLocalMove ( vecTop, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID )
  				{
  					if ( pApex )
  					{
  						*pApex = vecTop;
  						//ALERT(at_aiconsole, "triangulate over\n");
  					}
  
  					return TRUE;
  				}
  			}
  #if 1
  			if ( CheckLocalMove( pev->origin, vecBottom, pTargetEnt, NULL ) == LOCALMOVE_VALID )
  			{
  				if ( CheckLocalMove ( vecBottom, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID )
  				{
  					if ( pApex )
  					{
  						*pApex = vecBottom;
  						//ALERT(at_aiconsole, "triangulate under\n");
  					}
  
  					return TRUE;
  				}
  			}
  #endif
  		}
  
  		vecRight = vecRight + vecDir;
  		vecLeft = vecLeft - vecDir;
  		if (pev->movetype == MOVETYPE_FLY)
  		{
  			vecTop = vecTop + vecDirUp;
  			vecBottom = vecBottom - vecDirUp;
  		}
  	}
  
  	return FALSE;
  }
  
  //=========================================================
  // Move - take a single step towards the next ROUTE location
  //=========================================================
  #define DIST_TO_CHECK	200
  
  void CBaseMonster :: Move ( float flInterval ) 
  {
  	float		flWaypointDist;
  	float		flCheckDist;
  	float		flDist;// how far the lookahead check got before hitting an object.
  	Vector		vecDir;
  	Vector		vecApex;
  	CBaseEntity	*pTargetEnt;
  
  	// Don't move if no valid route
  	if ( FRouteClear() )
  	{
  		// If we still have a movement goal, then this is probably a route truncated by SimplifyRoute()
  		// so refresh it.
  		if ( m_movementGoal == MOVEGOAL_NONE || !FRefreshRoute() )
  		{
  			ALERT( at_aiconsole, "Tried to move with no route!\n" );
  			TaskFail();
  			return;
  		}
  	}
  	
  	if ( m_flMoveWaitFinished > gpGlobals->time )
  		return;
  
  // Debug, test movement code
  #if 0
  //	if ( CVAR_GET_FLOAT("stopmove" ) != 0 )
  	{
  		if ( m_movementGoal == MOVEGOAL_ENEMY )
  			RouteSimplify( m_hEnemy );
  		else
  			RouteSimplify( m_hTargetEnt );
  		FRefreshRoute();
  		return;
  	}
  #else
  // Debug, draw the route
  //	DrawRoute( pev, m_Route, m_iRouteIndex, 0, 200, 0 );
  #endif
  
  	// if the monster is moving directly towards an entity (enemy for instance), we'll set this pointer
  	// to that entity for the CheckLocalMove and Triangulate functions.
  	pTargetEnt = NULL;
  
  	// local move to waypoint.
  	vecDir = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Normalize();
  	flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length2D();
  	
  	MakeIdealYaw ( m_Route[ m_iRouteIndex ].vecLocation );
  	ChangeYaw ( pev->yaw_speed );
  
  	// if the waypoint is closer than CheckDist, CheckDist is the dist to waypoint
  	if ( flWaypointDist < DIST_TO_CHECK )
  	{
  		flCheckDist = flWaypointDist;
  	}
  	else
  	{
  		flCheckDist = DIST_TO_CHECK;
  	}
  	
  	if ( (m_Route[ m_iRouteIndex ].iType & (~bits_MF_NOT_TO_MASK)) == bits_MF_TO_ENEMY )
  	{
  		// only on a PURE move to enemy ( i.e., ONLY MF_TO_ENEMY set, not MF_TO_ENEMY and DETOUR )
  		pTargetEnt = m_hEnemy;
  	}
  	else if ( (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK) == bits_MF_TO_TARGETENT )
  	{
  		pTargetEnt = m_hTargetEnt;
  	}
  
  	// !!!BUGBUG - CheckDist should be derived from ground speed.
  	// If this fails, it should be because of some dynamic entity blocking this guy.
  	// We've already checked this path, so we should wait and time out if the entity doesn't move
  	flDist = 0;
  	if ( CheckLocalMove ( pev->origin, pev->origin + vecDir * flCheckDist, pTargetEnt, &flDist ) != LOCALMOVE_VALID )
  	{
  		CBaseEntity *pBlocker;
  
  		// Can't move, stop
  		Stop();
  		// Blocking entity is in global trace_ent
  		pBlocker = CBaseEntity::Instance( gpGlobals->trace_ent );
  		if (pBlocker)
  		{
  			DispatchBlocked( edict(), pBlocker->edict() );
  		}
  
  		if ( pBlocker && m_moveWaitTime > 0 && pBlocker->IsMoving() && !pBlocker->IsPlayer() && (gpGlobals->time-m_flMoveWaitFinished) > 3.0 )
  		{
  			// Can we still move toward our target?
  			if ( flDist < m_flGroundSpeed )
  			{
  				// No, Wait for a second
  				m_flMoveWaitFinished = gpGlobals->time + m_moveWaitTime;
  				return;
  			}
  			// Ok, still enough room to take a step
  		}
  		else 
  		{
  			// try to triangulate around whatever is in the way.
  			if ( FTriangulate( pev->origin, m_Route[ m_iRouteIndex ].vecLocation, flDist, pTargetEnt, &vecApex ) )
  			{
  				InsertWaypoint( vecApex, bits_MF_TO_DETOUR );
  				RouteSimplify( pTargetEnt );
  			}
  			else
  			{
  //				ALERT ( at_aiconsole, "Couldn't Triangulate\n" );
  				Stop();
  				// Only do this once until your route is cleared
  				if ( m_moveWaitTime > 0 && !(m_afMemory & bits_MEMORY_MOVE_FAILED) )
  				{
  					FRefreshRoute();
  					if ( FRouteClear() )
  					{
  						TaskFail();
  					}
  					else
  					{
  						// Don't get stuck
  						if ( (gpGlobals->time - m_flMoveWaitFinished) < 0.2 )
  							Remember( bits_MEMORY_MOVE_FAILED );
  
  						m_flMoveWaitFinished = gpGlobals->time + 0.1;
  					}
  				}
  				else
  				{
  					TaskFail();
  					ALERT( at_aiconsole, "%s Failed to move (%d)!\n", STRING(pev->classname), HasMemory( bits_MEMORY_MOVE_FAILED ) );
  					//ALERT( at_aiconsole, "%f, %f, %f\n", pev->origin.z, (pev->origin + (vecDir * flCheckDist)).z, m_Route[m_iRouteIndex].vecLocation.z );
  				}
  				return;
  			}
  		}
  	}
  
  	// close enough to the target, now advance to the next target. This is done before actually reaching
  	// the target so that we get a nice natural turn while moving.
  	if ( ShouldAdvanceRoute( flWaypointDist ) )///!!!BUGBUG- magic number
  	{
  		AdvanceRoute( flWaypointDist );
  	}
  
  	// Might be waiting for a door
  	if ( m_flMoveWaitFinished > gpGlobals->time )
  	{
  		Stop();
  		return;
  	}
  
  	// UNDONE: this is a hack to quit moving farther than it has looked ahead.
  	if (flCheckDist < m_flGroundSpeed * flInterval)
  	{
  		flInterval = flCheckDist / m_flGroundSpeed;
  		// ALERT( at_console, "%.02f\n", flInterval );
  	}
  	MoveExecute( pTargetEnt, vecDir, flInterval );
  
  	if ( MovementIsComplete() )
  	{
  		Stop();
  		RouteClear();
  	}
  }
  
  
  BOOL CBaseMonster:: ShouldAdvanceRoute( float flWaypointDist )
  {
  	if ( flWaypointDist <= MONSTER_CUT_CORNER_DIST )
  	{
  		// ALERT( at_console, "cut %f\n", flWaypointDist );
  		return TRUE;
  	}
  
  	return FALSE;
  }
  
  
  void CBaseMonster::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval )
  {
  //	float flYaw = UTIL_VecToYaw ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin );// build a yaw that points to the goal.
  //	WALK_MOVE( ENT(pev), flYaw, m_flGroundSpeed * flInterval, WALKMOVE_NORMAL );
  	if ( m_IdealActivity != m_movementActivity )
  		m_IdealActivity = m_movementActivity;
  
  	float flTotal = m_flGroundSpeed * pev->framerate * flInterval;
  	float flStep;
  	while (flTotal > 0.001)
  	{
  		// don't walk more than 16 units or stairs stop working
  		flStep = V_min( 16.0, flTotal );
  		UTIL_MoveToOrigin ( ENT(pev), m_Route[ m_iRouteIndex ].vecLocation, flStep, MOVE_NORMAL );
  		flTotal -= flStep;
  	}
  	// ALERT( at_console, "dist %f\n", m_flGroundSpeed * pev->framerate * flInterval );
  }
  
  
  //=========================================================
  // MonsterInit - after a monster is spawned, it needs to 
  // be dropped into the world, checked for mobility problems,
  // and put on the proper path, if any. This function does
  // all of those things after the monster spawns. Any
  // initialization that should take place for all monsters
  // goes here.
  //=========================================================
  void CBaseMonster :: MonsterInit ()
  {
  	if (!g_pGameRules->FAllowMonsters())
  	{
  		pev->flags |= FL_KILLME;		// Post this because some monster code modifies class data after calling this function
  //		REMOVE_ENTITY(ENT(pev));
  		return;
  	}
  
  	// Set fields common to all monsters
  	pev->effects		= 0;
  	pev->takedamage		= DAMAGE_AIM;
  	pev->ideal_yaw		= pev->angles.y;
  	pev->max_health		= pev->health;
  	pev->deadflag		= DEAD_NO;
  	m_IdealMonsterState	= MONSTERSTATE_IDLE;// Assume monster will be idle, until proven otherwise
  
  	m_IdealActivity = ACT_IDLE;
  
  	SetBits (pev->flags, FL_MONSTER);
  	if ( pev->spawnflags & SF_MONSTER_HITMONSTERCLIP )
  		pev->flags |= FL_MONSTERCLIP;
  	
  	ClearSchedule();
  	RouteClear();
  	InitBoneControllers( ); // FIX: should be done in Spawn
  
  	m_iHintNode			= NO_NODE;
  
  	m_afMemory			= MEMORY_CLEAR;
  
  	m_hEnemy			= NULL;
  
  	m_flDistTooFar		= 1024.0;
  	m_flDistLook		= 2048.0;
  
  	// set eye position
  	SetEyePosition();
  
  	SetThink( &CBaseMonster::MonsterInitThink );
  	pev->nextthink = gpGlobals->time + 0.1;
  	SetUse ( &CBaseMonster::MonsterUse );
  }
  
  //=========================================================
  // MonsterInitThink - Calls StartMonster. Startmonster is 
  // virtual, but this function cannot be 
  //=========================================================
  void CBaseMonster :: MonsterInitThink ()
  {
  	StartMonster();
  }
  
  //=========================================================
  // StartMonster - final bit of initization before a monster 
  // is turned over to the AI. 
  //=========================================================
  void CBaseMonster :: StartMonster ()
  {
  	// update capabilities
  	if ( LookupActivity ( ACT_RANGE_ATTACK1 ) != ACTIVITY_NOT_AVAILABLE )
  	{
  		m_afCapability |= bits_CAP_RANGE_ATTACK1;
  	}
  	if ( LookupActivity ( ACT_RANGE_ATTACK2 ) != ACTIVITY_NOT_AVAILABLE )
  	{
  		m_afCapability |= bits_CAP_RANGE_ATTACK2;
  	}
  	if ( LookupActivity ( ACT_MELEE_ATTACK1 ) != ACTIVITY_NOT_AVAILABLE )
  	{
  		m_afCapability |= bits_CAP_MELEE_ATTACK1;
  	}
  	if ( LookupActivity ( ACT_MELEE_ATTACK2 ) != ACTIVITY_NOT_AVAILABLE )
  	{
  		m_afCapability |= bits_CAP_MELEE_ATTACK2;
  	}
  
  	// Raise monster off the floor one unit, then drop to floor
  	if ( pev->movetype != MOVETYPE_FLY && !FBitSet( pev->spawnflags, SF_MONSTER_FALL_TO_GROUND ) )
  	{
  		pev->origin.z += 1;
  		DROP_TO_FLOOR ( ENT(pev) );
  		// Try to move the monster to make sure it's not stuck in a brush.
  		if (!WALK_MOVE ( ENT(pev), 0, 0, WALKMOVE_NORMAL ) )
  		{
  			ALERT(at_error, "Monster %s stuck in wall--level design error", STRING(pev->classname));
  			pev->effects = EF_BRIGHTFIELD;
  		}
  	}
  	else 
  	{
  		pev->flags &= ~FL_ONGROUND;
  	}
  	
  	if ( !FStringNull(pev->target) )// this monster has a target
  	{
  		// Find the monster's initial target entity, stash it
  		m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME ( NULL, STRING( pev->target ) ) );
  
  		if ( !m_pGoalEnt )
  		{
  			ALERT(at_error, "ReadyMonster()--%s couldn't find target %s", STRING(pev->classname), STRING(pev->target));
  		}
  		else
  		{
  			// Monster will start turning towards his destination
  			MakeIdealYaw ( m_pGoalEnt->pev->origin );
  
  			// JAY: How important is this error message?  Big Momma doesn't obey this rule, so I took it out.
  #if 0
  			// At this point, we expect only a path_corner as initial goal
  			if (!FClassnameIs( m_pGoalEnt->pev, "path_corner"))
  			{
  				ALERT(at_warning, "ReadyMonster--monster's initial goal '%s' is not a path_corner", STRING(pev->target));
  			}
  #endif
  
  			// set the monster up to walk a path corner path. 
  			// !!!BUGBUG - this is a minor bit of a hack.
  			// JAYJAY
  			m_movementGoal = MOVEGOAL_PATHCORNER;
  			
  			if ( pev->movetype == MOVETYPE_FLY )
  				m_movementActivity = ACT_FLY;
  			else
  				m_movementActivity = ACT_WALK;
  
  			if ( !FRefreshRoute() )
  			{
  				ALERT ( at_aiconsole, "Can't Create Route!\n" );
  			}
  			SetState( MONSTERSTATE_IDLE );
  			ChangeSchedule( GetScheduleOfType( SCHED_IDLE_WALK ) );
  		}
  	}
  	
  	//SetState ( m_IdealMonsterState );
  	//SetActivity ( m_IdealActivity );
  
  	// Delay drop to floor to make sure each door in the level has had its chance to spawn
  	// Spread think times so that they don't all happen at the same time (Carmack)
  	SetThink ( &CBaseMonster::CallMonsterThink );
  	pev->nextthink += RANDOM_FLOAT(0.1, 0.4); // spread think times.
  	
  	if ( !FStringNull(pev->targetname) )// wait until triggered
  	{
  		SetState( MONSTERSTATE_IDLE );
  		// UNDONE: Some scripted sequence monsters don't have an idle?
  		SetActivity( ACT_IDLE );
  		ChangeSchedule( GetScheduleOfType( SCHED_WAIT_TRIGGER ) );
  	}
  }
  
  
  void CBaseMonster :: MovementComplete() 
  { 
  	switch( m_iTaskStatus )
  	{
  	case TASKSTATUS_NEW:
  	case TASKSTATUS_RUNNING:
  		m_iTaskStatus = TASKSTATUS_RUNNING_TASK;
  		break;
  
  	case TASKSTATUS_RUNNING_MOVEMENT:
  		TaskComplete();
  		break;
  	
  	case TASKSTATUS_RUNNING_TASK:
  		ALERT( at_error, "Movement completed twice!\n" );
  		break;
  
  	case TASKSTATUS_COMPLETE:		
  		break;
  	}
  	m_movementGoal = MOVEGOAL_NONE;
  }
  
  
  int CBaseMonster::TaskIsRunning()
  {
  	if ( m_iTaskStatus != TASKSTATUS_COMPLETE && 
  		 m_iTaskStatus != TASKSTATUS_RUNNING_MOVEMENT )
  		 return 1;
  
  	return 0;
  }
  
  //=========================================================
  // IRelationship - returns an integer that describes the 
  // relationship between two types of monster.
  //=========================================================
  int CBaseMonster::IRelationship ( CBaseEntity *pTarget )
  {
  	static int iEnemy[14][14] =
  	{			 //   NONE	 MACH	 PLYR	 HPASS	 HMIL	 AMIL	 APASS	 AMONST	APREY	 APRED	 INSECT	PLRALY	PBWPN	ABWPN
  	/*NONE*/		{ R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO,	R_NO,	R_NO	},
  	/*MACHINE*/		{ R_NO	,R_NO	,R_DL	,R_DL	,R_NO	,R_DL	,R_DL	,R_DL	,R_DL	,R_DL	,R_NO	,R_DL,	R_DL,	R_DL	},
  	/*PLAYER*/		{ R_NO	,R_DL	,R_NO	,R_NO	,R_DL	,R_DL	,R_DL	,R_DL	,R_DL	,R_DL	,R_NO	,R_NO,	R_DL,	R_DL	},
  	/*HUMANPASSIVE*/{ R_NO	,R_NO	,R_AL	,R_AL	,R_HT	,R_FR	,R_NO	,R_HT	,R_DL	,R_FR	,R_NO	,R_AL,	R_NO,	R_NO	},
  	/*HUMANMILITAR*/{ R_NO	,R_NO	,R_HT	,R_DL	,R_NO	,R_HT	,R_DL	,R_DL	,R_DL	,R_DL	,R_NO	,R_HT,	R_NO,	R_NO	},
  	/*ALIENMILITAR*/{ R_NO	,R_DL	,R_HT	,R_DL	,R_HT	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_DL,	R_NO,	R_NO	},
  	/*ALIENPASSIVE*/{ R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO,	R_NO,	R_NO	},
  	/*ALIENMONSTER*/{ R_NO	,R_DL	,R_DL	,R_DL	,R_DL	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_NO	,R_DL,	R_NO,	R_NO	},
  	/*ALIENPREY   */{ R_NO	,R_NO	,R_DL	,R_DL	,R_DL	,R_NO	,R_NO	,R_NO	,R_NO	,R_FR	,R_NO	,R_DL,	R_NO,	R_NO	},
  	/*ALIENPREDATO*/{ R_NO	,R_NO	,R_DL	,R_DL	,R_DL	,R_NO	,R_NO	,R_NO	,R_HT	,R_DL	,R_NO	,R_DL,	R_NO,	R_NO	},
  	/*INSECT*/		{ R_FR	,R_FR	,R_FR	,R_FR	,R_FR	,R_NO	,R_FR	,R_FR	,R_FR	,R_FR	,R_NO	,R_FR,	R_NO,	R_NO	},
  	/*PLAYERALLY*/	{ R_NO	,R_DL	,R_AL	,R_AL	,R_DL	,R_DL	,R_DL	,R_DL	,R_DL	,R_DL	,R_NO	,R_NO,	R_NO,	R_NO	},
  	/*PBIOWEAPON*/	{ R_NO	,R_NO	,R_DL	,R_DL	,R_DL	,R_DL	,R_DL	,R_DL	,R_DL	,R_DL	,R_NO	,R_DL,	R_NO,	R_DL	},
  	/*ABIOWEAPON*/	{ R_NO	,R_NO	,R_DL	,R_DL	,R_DL	,R_AL	,R_NO	,R_DL	,R_DL	,R_NO	,R_NO	,R_DL,	R_DL,	R_NO	}
  	};
  
  	return iEnemy[ Classify() ][ pTarget->Classify() ];
  }
  
  //=========================================================
  // FindCover - tries to find a nearby node that will hide
  // the caller from its enemy. 
  //
  // If supplied, search will return a node at least as far
  // away as MinDist, but no farther than MaxDist. 
  // if MaxDist isn't supplied, it defaults to a reasonable 
  // value
  //=========================================================
  // UNDONE: Should this find the nearest node?
  
  //float CGraph::PathLength( int iStart, int iDest, int iHull, int afCapMask )
  
  BOOL CBaseMonster :: FindCover ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist )
  {
  	int i;
  	int iMyHullIndex;
  	int iMyNode;
  	int iThreatNode;
  	float flDist;
  	Vector	vecLookersOffset;
  	TraceResult tr;
  
  	if ( !flMaxDist )
  	{
  		// user didn't supply a MaxDist, so work up a crazy one.
  		flMaxDist = 784;
  	}
  
  	if ( flMinDist > 0.5 * flMaxDist)
  	{
  #if _DEBUG
  		ALERT ( at_console, "FindCover MinDist (%.0f) too close to MaxDist (%.0f)\n", flMinDist, flMaxDist );
  #endif
  		flMinDist = 0.5 * flMaxDist;
  	}
  
  	if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet )
  	{
  		ALERT ( at_aiconsole, "Graph not ready for findcover!\n" );
  		return FALSE;
  	}
  
  	iMyNode = WorldGraph.FindNearestNode( pev->origin, this );
  	iThreatNode = WorldGraph.FindNearestNode ( vecThreat, this );
  	iMyHullIndex = WorldGraph.HullIndex( this );
  
  	if ( iMyNode == NO_NODE )
  	{
  		ALERT ( at_aiconsole, "FindCover() - %s has no nearest node!\n", STRING(pev->classname));
  		return FALSE;
  	}
  	if ( iThreatNode == NO_NODE )
  	{
  		// ALERT ( at_aiconsole, "FindCover() - Threat has no nearest node!\n" );
  		iThreatNode = iMyNode;
  		// return FALSE;
  	}
  
  	vecLookersOffset = vecThreat + vecViewOffset;// calculate location of enemy's eyes
  
  	// we'll do a rough sample to find nodes that are relatively nearby
  	for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ )
  	{
  		int nodeNumber = (i + WorldGraph.m_iLastCoverSearch) % WorldGraph.m_cNodes;
  
  		CNode &node = WorldGraph.Node( nodeNumber );
  		WorldGraph.m_iLastCoverSearch = nodeNumber + 1; // next monster that searches for cover node will start where we left off here.
  
  		// could use an optimization here!!
  		flDist = ( pev->origin - node.m_vecOrigin ).Length();
  
  		// DON'T do the trace check on a node that is farther away than a node that we've already found to 
  		// provide cover! Also make sure the node is within the mins/maxs of the search.
  		if ( flDist >= flMinDist && flDist < flMaxDist )
  		{
  			UTIL_TraceLine ( node.m_vecOrigin + vecViewOffset, vecLookersOffset, ignore_monsters, ignore_glass,  ENT(pev), &tr );
  
  			// if this node will block the threat's line of sight to me...
  			if ( tr.flFraction != 1.0 )
  			{
  				// ..and is also closer to me than the threat, or the same distance from myself and the threat the node is good.
  				if ( ( iMyNode == iThreatNode ) || WorldGraph.PathLength( iMyNode, nodeNumber, iMyHullIndex, m_afCapability ) <= WorldGraph.PathLength( iThreatNode, nodeNumber, iMyHullIndex, m_afCapability ) )
  				{
  					if ( FValidateCover ( node.m_vecOrigin ) && MoveToLocation( ACT_RUN, 0, node.m_vecOrigin ) )
  					{
  						/*
  						MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
  							WRITE_BYTE( TE_SHOWLINE);
  							
  							WRITE_COORD( node.m_vecOrigin.x );
  							WRITE_COORD( node.m_vecOrigin.y );
  							WRITE_COORD( node.m_vecOrigin.z );
  
  							WRITE_COORD( vecLookersOffset.x );
  							WRITE_COORD( vecLookersOffset.y );
  							WRITE_COORD( vecLookersOffset.z );
  						MESSAGE_END();
  						*/
  
  						return TRUE;
  					}
  				}
  			}
  		}
  	}
  	return FALSE;
  }
  
  
  //=========================================================
  // BuildNearestRoute - tries to build a route as close to the target
  // as possible, even if there isn't a path to the final point.
  //
  // If supplied, search will return a node at least as far
  // away as MinDist from vecThreat, but no farther than MaxDist. 
  // if MaxDist isn't supplied, it defaults to a reasonable 
  // value
  //=========================================================
  BOOL CBaseMonster :: BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist )
  {
  	int i;
  	int iMyHullIndex;
  	int iMyNode;
  	float flDist;
  	Vector	vecLookersOffset;
  	TraceResult tr;
  
  	if ( !flMaxDist )
  	{
  		// user didn't supply a MaxDist, so work up a crazy one.
  		flMaxDist = 784;
  	}
  
  	if ( flMinDist > 0.5 * flMaxDist)
  	{
  #if _DEBUG
  		ALERT ( at_console, "FindCover MinDist (%.0f) too close to MaxDist (%.0f)\n", flMinDist, flMaxDist );
  #endif
  		flMinDist = 0.5 * flMaxDist;
  	}
  
  	if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet )
  	{
  		ALERT ( at_aiconsole, "Graph not ready for BuildNearestRoute!\n" );
  		return FALSE;
  	}
  
  	iMyNode = WorldGraph.FindNearestNode( pev->origin, this );
  	iMyHullIndex = WorldGraph.HullIndex( this );
  
  	if ( iMyNode == NO_NODE )
  	{
  		ALERT ( at_aiconsole, "BuildNearestRoute() - %s has no nearest node!\n", STRING(pev->classname));
  		return FALSE;
  	}
  
  	vecLookersOffset = vecThreat + vecViewOffset;// calculate location of enemy's eyes
  
  	// we'll do a rough sample to find nodes that are relatively nearby
  	for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ )
  	{
  		int nodeNumber = (i + WorldGraph.m_iLastCoverSearch) % WorldGraph.m_cNodes;
  
  		CNode &node = WorldGraph.Node( nodeNumber );
  		WorldGraph.m_iLastCoverSearch = nodeNumber + 1; // next monster that searches for cover node will start where we left off here.
  
  		// can I get there?
  		if (WorldGraph.NextNodeInRoute( iMyNode, nodeNumber, iMyHullIndex, 0 ) != iMyNode)
  		{
  			flDist = ( vecThreat - node.m_vecOrigin ).Length();
  
  			// is it close?
  			if ( flDist > flMinDist && flDist < flMaxDist)
  			{
  				// can I see where I want to be from there?
  				UTIL_TraceLine( node.m_vecOrigin + pev->view_ofs, vecLookersOffset, ignore_monsters, edict(), &tr );
  
  				if (tr.flFraction == 1.0)
  				{
  					// try to actually get there
  					if ( BuildRoute ( node.m_vecOrigin, bits_MF_TO_LOCATION, NULL ) )
  					{
  						flMaxDist = flDist;
  						m_vecMoveGoal = node.m_vecOrigin;
  						return TRUE; // UNDONE: keep looking for something closer!
  					}
  				}
  			}
  		}
  	}
  
  	return FALSE;
  }
  
  
  
  //=========================================================
  // BestVisibleEnemy - this functions searches the link
  // list whose head is the caller's m_pLink field, and returns
  // a pointer to the enemy entity in that list that is nearest the 
  // caller.
  //
  // !!!UNDONE - currently, this only returns the closest enemy.
  // we'll want to consider distance, relationship, attack types, back turned, etc.
  //=========================================================
  CBaseEntity *CBaseMonster :: BestVisibleEnemy ()
  {
  	CBaseEntity	*pReturn;
  	CBaseEntity	*pNextEnt;
  	int			iNearest;
  	int			iDist;
  	int			iBestRelationship;
  
  	iNearest = 8192;// so first visible entity will become the closest.
  	pNextEnt = m_pLink;
  	pReturn = NULL;
  	iBestRelationship = R_NO;
  
  	while ( pNextEnt != NULL )
  	{
  		if ( pNextEnt->IsAlive() )
  		{
  			if ( IRelationship( pNextEnt) > iBestRelationship )
  			{
  				// this entity is disliked MORE than the entity that we 
  				// currently think is the best visible enemy. No need to do 
  				// a distance check, just get mad at this one for now.
  				iBestRelationship = IRelationship ( pNextEnt );
  				iNearest = ( pNextEnt->pev->origin - pev->origin ).Length();
  				pReturn = pNextEnt;
  			}
  			else if ( IRelationship( pNextEnt) == iBestRelationship )
  			{
  				// this entity is disliked just as much as the entity that
  				// we currently think is the best visible enemy, so we only
  				// get mad at it if it is closer.
  				iDist = ( pNextEnt->pev->origin - pev->origin ).Length();
  				
  				if ( iDist <= iNearest )
  				{
  					iNearest = iDist;
  					iBestRelationship = IRelationship ( pNextEnt );
  					pReturn = pNextEnt;
  				}
  			}
  		}
  
  		pNextEnt = pNextEnt->m_pLink;
  	}
  
  	return pReturn;
  }
  
  
  //=========================================================
  // MakeIdealYaw - gets a yaw value for the caller that would
  // face the supplied vector. Value is stuffed into the monster's
  // ideal_yaw
  //=========================================================
  void CBaseMonster :: MakeIdealYaw( Vector vecTarget )
  {
  	Vector	vecProjection;
  	
  	// strafing monster needs to face 90 degrees away from its goal
  	if ( m_movementActivity == ACT_STRAFE_LEFT )
  	{
  		vecProjection.x = -vecTarget.y;
  		vecProjection.y = vecTarget.x;
  
  		pev->ideal_yaw = UTIL_VecToYaw( vecProjection - pev->origin );
  	}
  	else if ( m_movementActivity == ACT_STRAFE_RIGHT )
  	{
  		vecProjection.x = vecTarget.y;
  		vecProjection.y = vecTarget.x;
  
  		pev->ideal_yaw = UTIL_VecToYaw( vecProjection - pev->origin );
  	}
  	else
  	{
  		pev->ideal_yaw = UTIL_VecToYaw ( vecTarget - pev->origin );
  	}
  }
  
  //=========================================================
  // FlYawDiff - returns the difference ( in degrees ) between
  // monster's current yaw and ideal_yaw
  //
  // Positive result is left turn, negative is right turn
  //=========================================================
  float	CBaseMonster::FlYawDiff ()
  {
  	float	flCurrentYaw;
  
  	flCurrentYaw = UTIL_AngleMod( pev->angles.y );
  
  	if ( flCurrentYaw == pev->ideal_yaw )
  	{
  		return 0;
  	}
  
  
  	return UTIL_AngleDiff( pev->ideal_yaw, flCurrentYaw );
  }
  
  
  //=========================================================
  // Changeyaw - turns a monster towards its ideal_yaw
  //=========================================================
  float CBaseMonster::ChangeYaw ( int yawSpeed )
  {
  	float		ideal, current, move, speed;
  
  	current = UTIL_AngleMod( pev->angles.y );
  	ideal = pev->ideal_yaw;
  	if (current != ideal)
  	{
  		float delta = gpGlobals->time - m_flLastYawTime;
  
  		m_flLastYawTime = gpGlobals->time;
  
  		if (delta > 0.25)
  		{
  			delta = 0.25;
  		}
  
  		speed = yawSpeed * delta * 2;
  		move = ideal - current;
  
  		if (ideal > current)
  		{
  			if (move >= 180)
  				move = move - 360;
  		}
  		else
  		{
  			if (move <= -180)
  				move = move + 360;
  		}
  
  		if (move > 0)
  		{// turning to the monster's left
  			if (move > speed)
  				move = speed;
  		}
  		else
  		{// turning to the monster's right
  			if (move < -speed)
  				move = -speed;
  		}
  		
  		pev->angles.y = UTIL_AngleMod (current + move);
  
  		// turn head in desired direction only if they have a turnable head
  		if (m_afCapability & bits_CAP_TURN_HEAD)
  		{
  			float yaw = pev->ideal_yaw - pev->angles.y;
  			if (yaw > 180) yaw -= 360;
  			if (yaw < -180) yaw += 360;
  			// yaw *= 0.8;
  			SetBoneController( 0, yaw );
  		}
  	}
  	else
  		move = 0;
  
  	return move;
  }
  
  //=========================================================
  // VecToYaw - turns a directional vector into a yaw value
  // that points down that vector.
  //=========================================================
  float	CBaseMonster::VecToYaw ( Vector vecDir )
  {
  	if (vecDir.x == 0 && vecDir.y == 0 && vecDir.z == 0)
  		return pev->angles.y;
  
  	return UTIL_VecToYaw( vecDir );
  }
  
  
  //=========================================================
  // SetEyePosition
  //
  // queries the monster's model for $eyeposition and copies
  // that vector to the monster's view_ofs
  //
  //=========================================================
  void CBaseMonster :: SetEyePosition ()
  {
  	Vector  vecEyePosition;
  	void	*pmodel = GET_MODEL_PTR( ENT(pev) );
  
  	GetEyePosition( pmodel, vecEyePosition );
  
  	pev->view_ofs = vecEyePosition;
  
  	if ( pev->view_ofs == g_vecZero )
  	{
  		ALERT ( at_aiconsole, "%s has no view_ofs!\n", STRING ( pev->classname ) );
  	}
  }
  
  void CBaseMonster :: HandleAnimEvent( MonsterEvent_t *pEvent )
  {
  	switch( pEvent->event )
  	{
  	case SCRIPT_EVENT_DEAD:
  		if ( m_MonsterState == MONSTERSTATE_SCRIPT )
  		{
  			pev->deadflag = DEAD_DYING;
  			// Kill me now! (and fade out when CineCleanup() is called)
  #if _DEBUG
  			ALERT( at_aiconsole, "Death event: %s\n", STRING(pev->classname) );
  #endif
  			pev->health = 0;
  		}
  #if _DEBUG
  		else
  			ALERT( at_aiconsole, "INVALID death event:%s\n", STRING(pev->classname) );
  #endif
  		break;
  	case SCRIPT_EVENT_NOT_DEAD:
  		if ( m_MonsterState == MONSTERSTATE_SCRIPT )
  		{
  			pev->deadflag = DEAD_NO;
  			// This is for life/death sequences where the player can determine whether a character is dead or alive after the script 
  			pev->health = pev->max_health;
  		}
  		break;
  
  	case SCRIPT_EVENT_SOUND:			// Play a named wave file
  		EMIT_SOUND( edict(), CHAN_BODY, pEvent->options, 1.0, ATTN_IDLE );
  		break;
  
  	case SCRIPT_EVENT_SOUND_VOICE:
  		EMIT_SOUND( edict(), CHAN_VOICE, pEvent->options, 1.0, ATTN_IDLE );
  		break;
  
  	case SCRIPT_EVENT_SENTENCE_RND1:		// Play a named sentence group 33% of the time
  		if (RANDOM_LONG(0,2) == 0)
  			break;
  		// fall through...
  	case SCRIPT_EVENT_SENTENCE:			// Play a named sentence group
  		SENTENCEG_PlayRndSz( edict(), pEvent->options, 1.0, ATTN_IDLE, 0, 100 );
  		break;
  
  	case SCRIPT_EVENT_FIREEVENT:		// Fire a trigger
  		FireTargets( pEvent->options, this, this, USE_TOGGLE, 0 );
  		break;
  
  	case SCRIPT_EVENT_NOINTERRUPT:		// Can't be interrupted from now on
  		if ( m_pCine )
  			m_pCine->AllowInterrupt( FALSE );
  		break;
  
  	case SCRIPT_EVENT_CANINTERRUPT:		// OK to interrupt now
  		if ( m_pCine )
  			m_pCine->AllowInterrupt( TRUE );
  		break;
  
  #if 0
  	case SCRIPT_EVENT_INAIR:			// Don't DROP_TO_FLOOR()
  	case SCRIPT_EVENT_ENDANIMATION:		// Set ending animation sequence to
  		break;
  #endif
  
  	case MONSTER_EVENT_BODYDROP_HEAVY:
  		if ( pev->flags & FL_ONGROUND )
  		{
  			if ( RANDOM_LONG( 0, 1 ) == 0 )
  			{
  				EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, "common/bodydrop3.wav", 1, ATTN_NORM, 0, 90 );
  			}
  			else
  			{
  				EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, "common/bodydrop4.wav", 1, ATTN_NORM, 0, 90 );
  			}
  		}
  		break;
  
  	case MONSTER_EVENT_BODYDROP_LIGHT:
  		if ( pev->flags & FL_ONGROUND )
  		{
  			if ( RANDOM_LONG( 0, 1 ) == 0 )
  			{
  				EMIT_SOUND( ENT(pev), CHAN_BODY, "common/bodydrop3.wav", 1, ATTN_NORM );
  			}
  			else
  			{
  				EMIT_SOUND( ENT(pev), CHAN_BODY, "common/bodydrop4.wav", 1, ATTN_NORM );
  			}
  		}
  		break;
  
  	case MONSTER_EVENT_SWISHSOUND:
  		{
  			// NO MONSTER may use this anim event unless that monster's precache precaches this sound!!!
  			EMIT_SOUND( ENT(pev), CHAN_BODY, "zombie/claw_miss2.wav", 1, ATTN_NORM );
  			break;
  		}
  
  	default:
  		ALERT( at_aiconsole, "Unhandled animation event %d for %s\n", pEvent->event, STRING(pev->classname) );
  		break;
  
  	}
  }
  
  
  // Combat
  
  Vector CBaseMonster :: GetGunPosition( )
  {
  	UTIL_MakeVectors(pev->angles);
  
  	// Vector vecSrc = pev->origin + gpGlobals->v_forward * 10;
  	//vecSrc.z = pevShooter->absmin.z + pevShooter->size.z * 0.7;
  	//vecSrc.z = pev->origin.z + (pev->view_ofs.z - 4);
  	Vector vecSrc = pev->origin 
  					+ gpGlobals->v_forward * m_HackedGunPos.y 
  					+ gpGlobals->v_right * m_HackedGunPos.x 
  					+ gpGlobals->v_up * m_HackedGunPos.z;
  
  	return vecSrc;
  }
  
  
  
  
  
  //=========================================================
  // NODE GRAPH
  //=========================================================
  
  
  
  
  
  //=========================================================
  // FGetNodeRoute - tries to build an entire node path from
  // the callers origin to the passed vector. If this is 
  // possible, ROUTE_SIZE waypoints will be copied into the
  // callers m_Route. TRUE is returned if the operation 
  // succeeds (path is valid) or FALSE if failed (no path 
  // exists )
  //=========================================================
  BOOL CBaseMonster :: FGetNodeRoute ( Vector vecDest )
  {
  	int iPath[ MAX_PATH_SIZE ];
  	int iSrcNode, iDestNode;
  	int iResult;
  	int i;
  	int iNumToCopy;
  
  	iSrcNode = WorldGraph.FindNearestNode ( pev->origin, this );
  	iDestNode = WorldGraph.FindNearestNode ( vecDest, this );
  
  	if ( iSrcNode == -1 )
  	{
  		// no node nearest self
  //		ALERT ( at_aiconsole, "FGetNodeRoute: No valid node near self!\n" );
  		return FALSE;
  	}
  	else if ( iDestNode == -1 )
  	{
  		// no node nearest target
  //		ALERT ( at_aiconsole, "FGetNodeRoute: No valid node near target!\n" );
  		return FALSE;
  	}
  
  	// valid src and dest nodes were found, so it's safe to proceed with
  	// find shortest path
  	int iNodeHull = WorldGraph.HullIndex( this ); // make this a monster virtual function
  	iResult = WorldGraph.FindShortestPath ( iPath, iSrcNode, iDestNode, iNodeHull, m_afCapability );
  
  	if ( !iResult )
  	{
  #if 1
  		ALERT ( at_aiconsole, "No Path from %d to %d!\n", iSrcNode, iDestNode );
  		return FALSE;
  #else
  		BOOL bRoutingSave = WorldGraph.m_fRoutingComplete;
  		WorldGraph.m_fRoutingComplete = FALSE;
  		iResult = WorldGraph.FindShortestPath(iPath, iSrcNode, iDestNode, iNodeHull, m_afCapability);
  		WorldGraph.m_fRoutingComplete = bRoutingSave;
  		if ( !iResult )
  		{
  			ALERT ( at_aiconsole, "No Path from %d to %d!\n", iSrcNode, iDestNode );
  			return FALSE;
  		}
  		else
  		{
  			ALERT ( at_aiconsole, "Routing is inconsistent!" );
  		}
  #endif
  	}
  
  	// there's a valid path within iPath now, so now we will fill the route array
  	// up with as many of the waypoints as it will hold.
  	
  	// don't copy ROUTE_SIZE entries if the path returned is shorter
  	// than ROUTE_SIZE!!!
  	if ( iResult < ROUTE_SIZE )
  	{
  		iNumToCopy = iResult;
  	}
  	else
  	{
  		iNumToCopy = ROUTE_SIZE;
  	}
  	
  	for ( i = 0 ; i < iNumToCopy; i++ )
  	{
  		m_Route[ i ].vecLocation = WorldGraph.m_pNodes[ iPath[ i ] ].m_vecOrigin;
  		m_Route[ i ].iType = bits_MF_TO_NODE;
  	}
  	
  	if ( iNumToCopy < ROUTE_SIZE )
  	{
  		m_Route[ iNumToCopy ].vecLocation = vecDest;
  		m_Route[ iNumToCopy ].iType |= bits_MF_IS_GOAL;
  	}
  
  	return TRUE;
  }
  
  //=========================================================
  // FindHintNode
  //=========================================================
  int CBaseMonster :: FindHintNode ()
  {
  	int i;
  	TraceResult tr;
  
  	if ( !WorldGraph.m_fGraphPresent )
  	{
  		ALERT ( at_aiconsole, "find_hintnode: graph not ready!\n" );
  		return NO_NODE;
  	}
  
  	if ( WorldGraph.m_iLastActiveIdleSearch >= WorldGraph.m_cNodes )
  	{
  		WorldGraph.m_iLastActiveIdleSearch = 0;
  	}
  
  	for ( i = 0; i < WorldGraph.m_cNodes ; i++ )
  	{
  		int nodeNumber = (i + WorldGraph.m_iLastActiveIdleSearch) % WorldGraph.m_cNodes;
  		CNode &node = WorldGraph.Node( nodeNumber );
  
  		if ( node.m_sHintType )
  		{
  			// this node has a hint. Take it if it is visible, the monster likes it, and the monster has an animation to match the hint's activity.
  			if ( FValidateHintType ( node.m_sHintType ) )
  			{
  				if ( !node.m_sHintActivity || LookupActivity ( node.m_sHintActivity ) != ACTIVITY_NOT_AVAILABLE )
  				{
  					UTIL_TraceLine ( pev->origin + pev->view_ofs, node.m_vecOrigin + pev->view_ofs, ignore_monsters, ENT(pev), &tr );
  
  					if ( tr.flFraction == 1.0 )
  					{
  						WorldGraph.m_iLastActiveIdleSearch = nodeNumber + 1; // next monster that searches for hint nodes will start where we left off.
  						return nodeNumber;// take it!
  					}
  				}
  			}
  		}
  	}
  
  	WorldGraph.m_iLastActiveIdleSearch = 0;// start at the top of the list for the next search.
  
  	return NO_NODE;
  }
  			
  
  void CBaseMonster::ReportAIState()
  {
  	ALERT_TYPE level = at_console;
  
  	static const char *pStateNames[] = { "None", "Idle", "Combat", "Alert", "Hunt", "Prone", "Scripted", "Dead" };
  
  	ALERT( level, "%s: ", STRING(pev->classname) );
  	if ( (int)m_MonsterState < ARRAYSIZE(pStateNames) )
  		ALERT( level, "State: %s, ", pStateNames[m_MonsterState] );
  	int i = 0;
  	while ( activity_map[i].type != 0 )
  	{
  		if ( activity_map[i].type == (int)m_Activity )
  		{
  			ALERT( level, "Activity %s, ", activity_map[i].name );
  			break;
  		}
  		i++;
  	}
  
  	if ( m_pSchedule )
  	{
  		const char *pName = NULL;
  		pName = m_pSchedule->pName;
  		if ( !pName )
  			pName = "Unknown";
  		ALERT( level, "Schedule %s, ", pName );
  		Task_t *pTask = GetTask();
  		if ( pTask )
  			ALERT( level, "Task %d (#%d), ", pTask->iTask, m_iScheduleIndex );
  	}
  	else
  		ALERT( level, "No Schedule, " );
  
  	if ( m_hEnemy != NULL )
  		ALERT( level, "\nEnemy is %s", STRING(m_hEnemy->pev->classname) );
  	else
  		ALERT( level, "No enemy" );
  
  	if ( IsMoving() )
  	{
  		ALERT( level, " Moving " );
  		if ( m_flMoveWaitFinished > gpGlobals->time )
  			ALERT( level, ": Stopped for %.2f. ", m_flMoveWaitFinished - gpGlobals->time );
  		else if ( m_IdealActivity == GetStoppedActivity() )
  			ALERT( level, ": In stopped anim. " );
  	}
  
  	CSquadMonster *pSquadMonster = MySquadMonsterPointer();
  
  	if ( pSquadMonster )
  	{
  		if ( !pSquadMonster->InSquad() )
  		{
  			ALERT ( level, "not " );
  		}
  
  		ALERT ( level, "In Squad, " );
  
  		if ( !pSquadMonster->IsLeader() )
  		{
  			ALERT ( level, "not " );
  		}
  
  		ALERT ( level, "Leader." );
  	}
  
  	ALERT( level, "\n" );
  	ALERT( level, "Yaw speed:%3.1f,Health: %3.1f\n", pev->yaw_speed, pev->health );
  	if ( pev->spawnflags & SF_MONSTER_PRISONER )
  		ALERT( level, " PRISONER! " );
  	if ( pev->spawnflags & SF_MONSTER_PREDISASTER )
  		ALERT( level, " Pre-Disaster! " );
  	ALERT( level, "\n" );
  }
  
  //=========================================================
  // KeyValue
  //
  // !!! netname entvar field is used in squadmonster for groupname!!!
  //=========================================================
  void CBaseMonster :: KeyValue( KeyValueData *pkvd )
  {
  	if (FStrEq(pkvd->szKeyName, "TriggerTarget"))
  	{
  		m_iszTriggerTarget = ALLOC_STRING( pkvd->szValue );
  		pkvd->fHandled = TRUE;
  	}
  	else if (FStrEq(pkvd->szKeyName, "TriggerCondition") )
  	{
  		m_iTriggerCondition = atoi( pkvd->szValue );
  		pkvd->fHandled = TRUE;
  	}
  	else
  	{
  		CBaseToggle::KeyValue( pkvd );
  	}
  }
  
  //=========================================================
  // FCheckAITrigger - checks the monster's AI Trigger Conditions,
  // if there is a condition, then checks to see if condition is 
  // met. If yes, the monster's TriggerTarget is fired.
  //
  // Returns TRUE if the target is fired.
  //=========================================================
  BOOL CBaseMonster :: FCheckAITrigger ()
  {
  	BOOL fFireTarget;
  
  	if ( m_iTriggerCondition == AITRIGGER_NONE )
  	{
  		// no conditions, so this trigger is never fired.
  		return FALSE; 
  	}
  
  	fFireTarget = FALSE;
  
  	switch ( m_iTriggerCondition )
  	{
  	case AITRIGGER_SEEPLAYER_ANGRY_AT_PLAYER:
  		if ( m_hEnemy != NULL && m_hEnemy->IsPlayer() && HasConditions ( bits_COND_SEE_ENEMY ) )
  		{
  			fFireTarget = TRUE;
  		}
  		break;
  	case AITRIGGER_SEEPLAYER_UNCONDITIONAL:
  		if ( HasConditions ( bits_COND_SEE_CLIENT ) )
  		{
  			fFireTarget = TRUE;
  		}
  		break;
  	case AITRIGGER_SEEPLAYER_NOT_IN_COMBAT:
  		if ( HasConditions ( bits_COND_SEE_CLIENT ) && 
  			 m_MonsterState != MONSTERSTATE_COMBAT	&& 
  			 m_MonsterState != MONSTERSTATE_PRONE	&& 
  			 m_MonsterState != MONSTERSTATE_SCRIPT)
  		{
  			fFireTarget = TRUE;
  		}
  		break;
  	case AITRIGGER_TAKEDAMAGE:
  		if ( m_afConditions & ( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) )
  		{
  			fFireTarget = TRUE;
  		}
  		break;
  	case AITRIGGER_DEATH:
  		if ( pev->deadflag != DEAD_NO )
  		{
  			fFireTarget = TRUE;
  		}
  		break;
  	case AITRIGGER_HALFHEALTH:
  		if ( IsAlive() && pev->health <= ( pev->max_health / 2 ) )
  		{
  			fFireTarget = TRUE;
  		}
  		break;
  /*
  
    // !!!UNDONE - no persistant game state that allows us to track these two. 
  
  	case AITRIGGER_SQUADMEMBERDIE:
  		break;
  	case AITRIGGER_SQUADLEADERDIE:
  		break;
  */
  	case AITRIGGER_HEARWORLD:
  		if ( m_afConditions & bits_COND_HEAR_SOUND && m_afSoundTypes & bits_SOUND_WORLD )
  		{
  			fFireTarget = TRUE;
  		}
  		break;
  	case AITRIGGER_HEARPLAYER:
  		if ( m_afConditions & bits_COND_HEAR_SOUND && m_afSoundTypes & bits_SOUND_PLAYER )
  		{
  			fFireTarget = TRUE;
  		}
  		break;
  	case AITRIGGER_HEARCOMBAT:
  		if ( m_afConditions & bits_COND_HEAR_SOUND && m_afSoundTypes & bits_SOUND_COMBAT )
  		{
  			fFireTarget = TRUE;
  		}
  		break;
  	}
  
  	if ( fFireTarget )
  	{
  		// fire the target, then set the trigger conditions to NONE so we don't fire again
  		ALERT ( at_aiconsole, "AI Trigger Fire Target\n" );
  		FireTargets( STRING( m_iszTriggerTarget ), this, this, USE_TOGGLE, 0 );
  		m_iTriggerCondition = AITRIGGER_NONE;
  		return TRUE;
  	}
  
  	return FALSE;
  }
  
  //=========================================================	
  // CanPlaySequence - determines whether or not the monster
  // can play the scripted sequence or AI sequence that is 
  // trying to possess it. If DisregardState is set, the monster
  // will be sucked into the script no matter what state it is
  // in. ONLY Scripted AI ents should allow this.
  //=========================================================	
  int CBaseMonster :: CanPlaySequence( BOOL fDisregardMonsterState, int interruptLevel )
  {
  	if ( m_pCine || !IsAlive() || m_MonsterState == MONSTERSTATE_PRONE )
  	{
  		// monster is already running a scripted sequence or dead!
  		return FALSE;
  	}
  	
  	if ( fDisregardMonsterState )
  	{
  		// ok to go, no matter what the monster state. (scripted AI)
  		return TRUE;
  	}
  
  	if ( m_MonsterState == MONSTERSTATE_NONE || m_MonsterState == MONSTERSTATE_IDLE || m_IdealMonsterState == MONSTERSTATE_IDLE )
  	{
  		// ok to go, but only in these states
  		return TRUE;
  	}
  	
  	if ( m_MonsterState == MONSTERSTATE_ALERT && interruptLevel >= SS_INTERRUPT_BY_NAME )
  		return TRUE;
  
  	// unknown situation
  	return FALSE;
  }
  
  
  //=========================================================
  // FindLateralCover - attempts to locate a spot in the world
  // directly to the left or right of the caller that will
  // conceal them from view of pSightEnt
  //=========================================================
  #define	COVER_CHECKS	5// how many checks are made
  #define COVER_DELTA		48// distance between checks
  
  BOOL CBaseMonster :: FindLateralCover ( const Vector &vecThreat, const Vector &vecViewOffset )
  {
  	TraceResult	tr;
  	Vector	vecBestOnLeft;
  	Vector	vecBestOnRight;
  	Vector	vecLeftTest;
  	Vector	vecRightTest;
  	Vector	vecStepRight;
  	int		i;
  
  	UTIL_MakeVectors ( pev->angles );
  	vecStepRight = gpGlobals->v_right * COVER_DELTA;
  	vecStepRight.z = 0; 
  	
  	vecLeftTest = vecRightTest = pev->origin;
  
  	for ( i = 0 ; i < COVER_CHECKS ; i++ )
  	{
  		vecLeftTest = vecLeftTest - vecStepRight;
  		vecRightTest = vecRightTest + vecStepRight;
  
  		// it's faster to check the SightEnt's visibility to the potential spot than to check the local move, so we do that first.
  		UTIL_TraceLine( vecThreat + vecViewOffset, vecLeftTest + pev->view_ofs, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr);
  		
  		if (tr.flFraction != 1.0)
  		{
  			if ( FValidateCover ( vecLeftTest ) && CheckLocalMove( pev->origin, vecLeftTest, NULL, NULL ) == LOCALMOVE_VALID )
  			{
  				if ( MoveToLocation( ACT_RUN, 0, vecLeftTest ) )
  				{
  					return TRUE;
  				}
  			}
  		}
  		
  		// it's faster to check the SightEnt's visibility to the potential spot than to check the local move, so we do that first.
  		UTIL_TraceLine(vecThreat + vecViewOffset, vecRightTest + pev->view_ofs, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr);
  		
  		if ( tr.flFraction != 1.0 )
  		{
  			if (  FValidateCover ( vecRightTest ) && CheckLocalMove( pev->origin, vecRightTest, NULL, NULL ) == LOCALMOVE_VALID )
  			{
  				if ( MoveToLocation( ACT_RUN, 0, vecRightTest ) )
  				{
  					return TRUE;
  				}
  			}
  		}
  	}
  
  	return FALSE;
  }
  
  
  Vector CBaseMonster :: ShootAtEnemy( const Vector &shootOrigin )
  {
  	CBaseEntity *pEnemy = m_hEnemy;
  
  	if ( pEnemy )
  	{
  		return ( (pEnemy->BodyTarget( shootOrigin ) - pEnemy->pev->origin) + m_vecEnemyLKP - shootOrigin ).Normalize();
  	}
  	else
  		return gpGlobals->v_forward;
  }
  
  
  
  //=========================================================
  // FacingIdeal - tells us if a monster is facing its ideal
  // yaw. Created this function because many spots in the 
  // code were checking the yawdiff against this magic
  // number. Nicer to have it in one place if we're gonna
  // be stuck with it.
  //=========================================================
  BOOL CBaseMonster :: FacingIdeal()
  {
  	if ( fabs( FlYawDiff() ) <= 0.006 )//!!!BUGBUG - no magic numbers!!!
  	{
  		return TRUE;
  	}
  
  	return FALSE;
  }
  
  //=========================================================
  // FCanActiveIdle
  //=========================================================
  BOOL CBaseMonster :: FCanActiveIdle ()
  {
  	/*
  	if ( m_MonsterState == MONSTERSTATE_IDLE && m_IdealMonsterState == MONSTERSTATE_IDLE && !IsMoving() )
  	{
  		return TRUE;
  	}
  	*/
  	return FALSE;
  }
  
  
  void CBaseMonster::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation )
  {
  	if ( pszSentence && IsAlive() )
  	{
  		if ( pszSentence[0] == '!' )
  			EMIT_SOUND_DYN( edict(), CHAN_VOICE, pszSentence, volume, attenuation, 0, PITCH_NORM );
  		else
  			SENTENCEG_PlayRndSz( edict(), pszSentence, volume, attenuation, 0, PITCH_NORM );
  	}
  }
  
  
  void CBaseMonster::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener )
  { 
  	PlaySentence( pszSentence, duration, volume, attenuation );
  }
  
  
  void CBaseMonster::SentenceStop()
  {
  	EMIT_SOUND( edict(), CHAN_VOICE, "common/null.wav", 1.0, ATTN_IDLE );
  }
  
  
  void CBaseMonster::CorpseFallThink()
  {
  	if ( pev->flags & FL_ONGROUND )
  	{
  		SetThink ( NULL );
  
  		SetSequenceBox( );
  		UTIL_SetOrigin( pev, pev->origin );// link into world.
  	}
  	else
  		pev->nextthink = gpGlobals->time + 0.1;
  }
  
  // Call after animation/pose is set up
  void CBaseMonster :: MonsterInitDead()
  {
  	InitBoneControllers();
  
  	pev->solid			= SOLID_BBOX;
  	pev->movetype		= MOVETYPE_TOSS;// so he'll fall to ground
  
  	pev->frame = 0;
  	ResetSequenceInfo( );
  	pev->framerate = 0;
  	
  	// Copy health
  	pev->max_health		= pev->health;
  	pev->deadflag		= DEAD_DEAD;
  	
  	UTIL_SetSize(pev, g_vecZero, g_vecZero );
  	UTIL_SetOrigin( pev, pev->origin );
  
  	// Setup health counters, etc.
  	BecomeDead();
  	SetThink( &CBaseMonster::CorpseFallThink );
  	pev->nextthink = gpGlobals->time + 0.5;
  }
  
  //=========================================================
  // BBoxIsFlat - check to see if the monster's bounding box
  // is lying flat on a surface (traces from all four corners
  // are same length.)
  //=========================================================
  BOOL CBaseMonster :: BBoxFlat ()
  {
  	TraceResult	tr;
  	Vector		vecPoint;
  	float		flXSize, flYSize;
  	float		flLength;
  	float		flLength2;
  
  	flXSize = pev->size.x / 2;
  	flYSize = pev->size.y / 2;
  
  	vecPoint.x = pev->origin.x + flXSize;
  	vecPoint.y = pev->origin.y + flYSize;
  	vecPoint.z = pev->origin.z;
  
  	UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr );
  	flLength = (vecPoint - tr.vecEndPos).Length();
  
  	vecPoint.x = pev->origin.x - flXSize;
  	vecPoint.y = pev->origin.y - flYSize;
  
  	UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr );
  	flLength2 = (vecPoint - tr.vecEndPos).Length();
  	if ( flLength2 > flLength )
  	{
  		return FALSE;
  	}
  	flLength = flLength2;
  
  	vecPoint.x = pev->origin.x - flXSize;
  	vecPoint.y = pev->origin.y + flYSize;
  	UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr );
  	flLength2 = (vecPoint - tr.vecEndPos).Length();
  	if ( flLength2 > flLength )
  	{
  		return FALSE;
  	}
  	flLength = flLength2;
  
  	vecPoint.x = pev->origin.x + flXSize;
  	vecPoint.y = pev->origin.y - flYSize;
  	UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr );
  	flLength2 = (vecPoint - tr.vecEndPos).Length();
  	if ( flLength2 > flLength )
  	{
  		return FALSE;
  	}
  	flLength = flLength2;
  
  	return TRUE;
  }
  
  //=========================================================
  // Get Enemy - tries to find the best suitable enemy for the monster.
  //=========================================================
  BOOL CBaseMonster :: GetEnemy ()
  {
  	CBaseEntity *pNewEnemy;
  
  	if ( HasConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_NEMESIS) )
  	{
  		pNewEnemy = BestVisibleEnemy();
  
  		if ( pNewEnemy != m_hEnemy && pNewEnemy != NULL)
  		{
  			// DO NOT mess with the monster's m_hEnemy pointer unless the schedule the monster is currently running will be interrupted
  			// by COND_NEW_ENEMY. This will eliminate the problem of monsters getting a new enemy while they are in a schedule that doesn't care,
  			// and then not realizing it by the time they get to a schedule that does. I don't feel this is a good permanent fix. 
  
  			if ( m_pSchedule )
  			{
  				if ( m_pSchedule->iInterruptMask & bits_COND_NEW_ENEMY )
  				{
  					PushEnemy( m_hEnemy, m_vecEnemyLKP );
  					SetConditions(bits_COND_NEW_ENEMY);
  					m_hEnemy = pNewEnemy;
  					m_vecEnemyLKP = m_hEnemy->pev->origin;
  				}
  				// if the new enemy has an owner, take that one as well
  				if (pNewEnemy->pev->owner != NULL)
  				{
  					CBaseEntity *pOwner = GetMonsterPointer( pNewEnemy->pev->owner );
  					if ( pOwner && (pOwner->pev->flags & FL_MONSTER) && IRelationship( pOwner ) != R_NO )
  						PushEnemy( pOwner, m_vecEnemyLKP );
  				}
  			}
  		}
  	}
  
  	// remember old enemies
  	if (m_hEnemy == NULL && PopEnemy( ))
  	{
  		if ( m_pSchedule )
  		{
  			if ( m_pSchedule->iInterruptMask & bits_COND_NEW_ENEMY )
  			{
  				SetConditions(bits_COND_NEW_ENEMY);
  			}
  		}
  	}
  
  	if ( m_hEnemy != NULL )
  	{
  		// monster has an enemy.
  		return TRUE;
  	}
  
  	return FALSE;// monster has no enemy
  }
  
  
  //=========================================================
  // DropItem - dead monster drops named item 
  //=========================================================
  CBaseEntity* CBaseMonster :: DropItem ( const char *pszItemName, const Vector &vecPos, const Vector &vecAng )
  {
  	if ( !pszItemName )
  	{
  		ALERT ( at_console, "DropItem() - No item name!\n" );
  		return NULL;
  	}
  
  	CBaseEntity *pItem = CBaseEntity::Create( pszItemName, vecPos, vecAng, edict() );
  
  	if ( pItem )
  	{
  		// do we want this behavior to be default?! (sjb)
  		pItem->pev->velocity = pev->velocity;
  		pItem->pev->avelocity = Vector ( 0, RANDOM_FLOAT( 0, 100 ), 0 );
  		return pItem;
  	}
  	else
  	{
  		ALERT ( at_console, "DropItem() - Didn't create!\n" );
  		return FALSE;
  	}
  
  }
  
  
  BOOL CBaseMonster :: ShouldFadeOnDeath()
  {
  	// if flagged to fade out or I have an owner (I came from a monster spawner)
  	if ( (pev->spawnflags & SF_MONSTER_FADECORPSE) || !FNullEnt( pev->owner ) )
  		return TRUE;
  
  	return FALSE;
  }
  