text: // Copyright (C) 2004 Id Software, Inc.
  //
  
  #include "../../idlib/precompiled.h"
  #pragma hdrstop
  
  #include "../Game_local.h"
  
  /***********************************************************************
  
  	AI Events
  
  ***********************************************************************/
  
  const idEventDef AI_FindEnemy( "findEnemy", "d", 'e' );
  const idEventDef AI_FindEnemyAI( "findEnemyAI", "d", 'e' );
  const idEventDef AI_FindEnemyInCombatNodes( "findEnemyInCombatNodes", NULL, 'e' );
  const idEventDef AI_ClosestReachableEnemyOfEntity( "closestReachableEnemyOfEntity", "E", 'e' );
  const idEventDef AI_HeardSound( "heardSound", "d", 'e' );
  const idEventDef AI_SetEnemy( "setEnemy", "E" );
  const idEventDef AI_ClearEnemy( "clearEnemy" );
  const idEventDef AI_MuzzleFlash( "muzzleFlash", "s" );
  const idEventDef AI_CreateMissile( "createMissile", "s", 'e' );
  const idEventDef AI_AttackMissile( "attackMissile", "s", 'e' );
  const idEventDef AI_FireMissileAtTarget( "fireMissileAtTarget", "ss", 'e' );
  const idEventDef AI_LaunchMissile( "launchMissile", "vv", 'e' );
  const idEventDef AI_AttackMelee( "attackMelee", "s", 'd' );
  const idEventDef AI_DirectDamage( "directDamage", "es" );
  const idEventDef AI_RadiusDamageFromJoint( "radiusDamageFromJoint", "ss" );
  const idEventDef AI_BeginAttack( "attackBegin", "s" );
  const idEventDef AI_EndAttack( "attackEnd" );
  const idEventDef AI_MeleeAttackToJoint( "meleeAttackToJoint", "ss", 'd' );
  const idEventDef AI_RandomPath( "randomPath", NULL, 'e' );
  const idEventDef AI_CanBecomeSolid( "canBecomeSolid", NULL, 'f' );
  const idEventDef AI_BecomeSolid( "becomeSolid" );
  const idEventDef AI_BecomeRagdoll( "becomeRagdoll", NULL, 'd' );
  const idEventDef AI_StopRagdoll( "stopRagdoll" );
  const idEventDef AI_SetHealth( "setHealth", "f" );
  const idEventDef AI_GetHealth( "getHealth", NULL, 'f' );
  const idEventDef AI_AllowDamage( "allowDamage" );
  const idEventDef AI_IgnoreDamage( "ignoreDamage" );
  const idEventDef AI_GetCurrentYaw( "getCurrentYaw", NULL, 'f' );
  const idEventDef AI_TurnTo( "turnTo", "f" );
  const idEventDef AI_TurnToPos( "turnToPos", "v" );
  const idEventDef AI_TurnToEntity( "turnToEntity", "E" );
  const idEventDef AI_MoveStatus( "moveStatus", NULL, 'd' );
  const idEventDef AI_StopMove( "stopMove" );
  const idEventDef AI_MoveToCover( "moveToCover" );
  const idEventDef AI_MoveToEnemy( "moveToEnemy" );
  const idEventDef AI_MoveToEnemyHeight( "moveToEnemyHeight" );
  const idEventDef AI_MoveOutOfRange( "moveOutOfRange", "ef" );
  const idEventDef AI_MoveToAttackPosition( "moveToAttackPosition", "es" );
  const idEventDef AI_Wander( "wander" );
  const idEventDef AI_MoveToEntity( "moveToEntity", "e" );
  const idEventDef AI_MoveToPosition( "moveToPosition", "v" );
  const idEventDef AI_SlideTo( "slideTo", "vf" );
  const idEventDef AI_FacingIdeal( "facingIdeal", NULL, 'd' );
  const idEventDef AI_FaceEnemy( "faceEnemy" );
  const idEventDef AI_FaceEntity( "faceEntity", "E" );
  const idEventDef AI_GetCombatNode( "getCombatNode", NULL, 'e' );
  const idEventDef AI_EnemyInCombatCone( "enemyInCombatCone", "Ed", 'd' );
  const idEventDef AI_WaitMove( "waitMove" );
  const idEventDef AI_GetJumpVelocity( "getJumpVelocity", "vff", 'v' );
  const idEventDef AI_EntityInAttackCone( "entityInAttackCone", "E", 'd' );
  const idEventDef AI_CanSeeEntity( "canSee", "E", 'd' );
  const idEventDef AI_SetTalkTarget( "setTalkTarget", "E" );
  const idEventDef AI_GetTalkTarget( "getTalkTarget", NULL, 'e' );
  const idEventDef AI_SetTalkState( "setTalkState", "d" );
  const idEventDef AI_EnemyRange( "enemyRange", NULL, 'f' );
  const idEventDef AI_EnemyRange2D( "enemyRange2D", NULL, 'f' );
  const idEventDef AI_GetEnemy( "getEnemy", NULL, 'e' );
  const idEventDef AI_GetEnemyPos( "getEnemyPos", NULL, 'v' );
  const idEventDef AI_GetEnemyEyePos( "getEnemyEyePos", NULL, 'v' );
  const idEventDef AI_PredictEnemyPos( "predictEnemyPos", "f", 'v' );
  const idEventDef AI_CanHitEnemy( "canHitEnemy", NULL, 'd' );
  const idEventDef AI_CanHitEnemyFromAnim( "canHitEnemyFromAnim", "s", 'd' );
  const idEventDef AI_CanHitEnemyFromJoint( "canHitEnemyFromJoint", "s", 'd' );
  const idEventDef AI_EnemyPositionValid( "enemyPositionValid", NULL, 'd' );
  const idEventDef AI_ChargeAttack( "chargeAttack", "s" );
  const idEventDef AI_TestChargeAttack( "testChargeAttack", NULL, 'f' );
  const idEventDef AI_TestMoveToPosition( "testMoveToPosition", "v", 'd' );
  const idEventDef AI_TestAnimMoveTowardEnemy( "testAnimMoveTowardEnemy", "s", 'd' );
  const idEventDef AI_TestAnimMove( "testAnimMove", "s", 'd' );
  const idEventDef AI_TestMeleeAttack( "testMeleeAttack", NULL, 'd' );
  const idEventDef AI_TestAnimAttack( "testAnimAttack", "s", 'd' );
  const idEventDef AI_Shrivel( "shrivel", "f" );
  const idEventDef AI_Burn( "burn" );
  const idEventDef AI_ClearBurn( "clearBurn" );
  const idEventDef AI_PreBurn( "preBurn" );
  const idEventDef AI_SetSmokeVisibility( "setSmokeVisibility", "dd" );
  const idEventDef AI_NumSmokeEmitters( "numSmokeEmitters", NULL, 'd' );
  const idEventDef AI_WaitAction( "waitAction", "s" );
  const idEventDef AI_StopThinking( "stopThinking" );
  const idEventDef AI_GetTurnDelta( "getTurnDelta", NULL, 'f' );
  const idEventDef AI_GetMoveType( "getMoveType", NULL, 'd' );
  const idEventDef AI_SetMoveType( "setMoveType", "d" );
  const idEventDef AI_SaveMove( "saveMove" );
  const idEventDef AI_RestoreMove( "restoreMove" );
  const idEventDef AI_AllowMovement( "allowMovement", "f" );
  const idEventDef AI_JumpFrame( "<jumpframe>" );
  const idEventDef AI_EnableClip( "enableClip" );
  const idEventDef AI_DisableClip( "disableClip" );
  const idEventDef AI_EnableGravity( "enableGravity" );
  const idEventDef AI_DisableGravity( "disableGravity" );
  const idEventDef AI_EnableAFPush( "enableAFPush" );
  const idEventDef AI_DisableAFPush( "disableAFPush" );
  const idEventDef AI_SetFlySpeed( "setFlySpeed", "f" );
  const idEventDef AI_SetFlyOffset( "setFlyOffset", "d" );
  const idEventDef AI_ClearFlyOffset( "clearFlyOffset" );
  const idEventDef AI_GetClosestHiddenTarget( "getClosestHiddenTarget", "s", 'e' );
  const idEventDef AI_GetRandomTarget( "getRandomTarget", "s", 'e' );
  const idEventDef AI_TravelDistanceToPoint( "travelDistanceToPoint", "v", 'f' );
  const idEventDef AI_TravelDistanceToEntity( "travelDistanceToEntity", "e", 'f' );
  const idEventDef AI_TravelDistanceBetweenPoints( "travelDistanceBetweenPoints", "vv", 'f' );
  const idEventDef AI_TravelDistanceBetweenEntities( "travelDistanceBetweenEntities", "ee", 'f' );
  const idEventDef AI_LookAtEntity( "lookAt", "Ef" );
  const idEventDef AI_LookAtEnemy( "lookAtEnemy", "f" );
  const idEventDef AI_SetJointMod( "setBoneMod", "d" );
  const idEventDef AI_ThrowMoveable( "throwMoveable" );
  const idEventDef AI_ThrowAF( "throwAF" );
  const idEventDef AI_RealKill( "<kill>" );
  const idEventDef AI_Kill( "kill" );
  const idEventDef AI_WakeOnFlashlight( "wakeOnFlashlight", "d" );
  const idEventDef AI_LocateEnemy( "locateEnemy" );
  const idEventDef AI_KickObstacles( "kickObstacles", "Ef" );
  const idEventDef AI_GetObstacle( "getObstacle", NULL, 'e' );
  const idEventDef AI_PushPointIntoAAS( "pushPointIntoAAS", "v", 'v' );
  const idEventDef AI_GetTurnRate( "getTurnRate", NULL, 'f' );
  const idEventDef AI_SetTurnRate( "setTurnRate", "f" );
  const idEventDef AI_AnimTurn( "animTurn", "f" );
  const idEventDef AI_AllowHiddenMovement( "allowHiddenMovement", "d" );
  const idEventDef AI_TriggerParticles( "triggerParticles", "s" );
  const idEventDef AI_FindActorsInBounds( "findActorsInBounds", "vv", 'e' );
  const idEventDef AI_CanReachPosition( "canReachPosition", "v", 'd' );
  const idEventDef AI_CanReachEntity( "canReachEntity", "E", 'd' );
  const idEventDef AI_CanReachEnemy( "canReachEnemy", NULL, 'd' );
  const idEventDef AI_GetReachableEntityPosition( "getReachableEntityPosition", "e", 'v' );
  
  CLASS_DECLARATION( idActor, idAI )
  	EVENT( EV_Activate,							idAI::Event_Activate )
  	EVENT( EV_Touch,							idAI::Event_Touch )
  	EVENT( AI_FindEnemy,						idAI::Event_FindEnemy )
  	EVENT( AI_FindEnemyAI,						idAI::Event_FindEnemyAI )
  	EVENT( AI_FindEnemyInCombatNodes,			idAI::Event_FindEnemyInCombatNodes )
  	EVENT( AI_ClosestReachableEnemyOfEntity,	idAI::Event_ClosestReachableEnemyOfEntity )
  	EVENT( AI_HeardSound,						idAI::Event_HeardSound )
  	EVENT( AI_SetEnemy,							idAI::Event_SetEnemy )
  	EVENT( AI_ClearEnemy,						idAI::Event_ClearEnemy )
  	EVENT( AI_MuzzleFlash,						idAI::Event_MuzzleFlash )
  	EVENT( AI_CreateMissile,					idAI::Event_CreateMissile )
  	EVENT( AI_AttackMissile,					idAI::Event_AttackMissile )
  	EVENT( AI_FireMissileAtTarget,				idAI::Event_FireMissileAtTarget )
  	EVENT( AI_LaunchMissile,					idAI::Event_LaunchMissile )
  	EVENT( AI_AttackMelee,						idAI::Event_AttackMelee )
  	EVENT( AI_DirectDamage,						idAI::Event_DirectDamage )
  	EVENT( AI_RadiusDamageFromJoint,			idAI::Event_RadiusDamageFromJoint )
  	EVENT( AI_BeginAttack,						idAI::Event_BeginAttack )
  	EVENT( AI_EndAttack,						idAI::Event_EndAttack )
  	EVENT( AI_MeleeAttackToJoint,				idAI::Event_MeleeAttackToJoint )
  	EVENT( AI_RandomPath,						idAI::Event_RandomPath )
  	EVENT( AI_CanBecomeSolid,					idAI::Event_CanBecomeSolid )
  	EVENT( AI_BecomeSolid,						idAI::Event_BecomeSolid )
  	EVENT( EV_BecomeNonSolid,					idAI::Event_BecomeNonSolid )
  	EVENT( AI_BecomeRagdoll,					idAI::Event_BecomeRagdoll )
  	EVENT( AI_StopRagdoll,						idAI::Event_StopRagdoll )
  	EVENT( AI_SetHealth,						idAI::Event_SetHealth )
  	EVENT( AI_GetHealth,						idAI::Event_GetHealth )
  	EVENT( AI_AllowDamage,						idAI::Event_AllowDamage )
  	EVENT( AI_IgnoreDamage,						idAI::Event_IgnoreDamage )
  	EVENT( AI_GetCurrentYaw,					idAI::Event_GetCurrentYaw )
  	EVENT( AI_TurnTo,							idAI::Event_TurnTo )
  	EVENT( AI_TurnToPos,						idAI::Event_TurnToPos )
  	EVENT( AI_TurnToEntity,						idAI::Event_TurnToEntity )
  	EVENT( AI_MoveStatus,						idAI::Event_MoveStatus )
  	EVENT( AI_StopMove,							idAI::Event_StopMove )
  	EVENT( AI_MoveToCover,						idAI::Event_MoveToCover )
  	EVENT( AI_MoveToEnemy,						idAI::Event_MoveToEnemy )
  	EVENT( AI_MoveToEnemyHeight,				idAI::Event_MoveToEnemyHeight )
  	EVENT( AI_MoveOutOfRange,					idAI::Event_MoveOutOfRange )
  	EVENT( AI_MoveToAttackPosition,				idAI::Event_MoveToAttackPosition )
  	EVENT( AI_Wander,							idAI::Event_Wander )
  	EVENT( AI_MoveToEntity,						idAI::Event_MoveToEntity )
  	EVENT( AI_MoveToPosition,					idAI::Event_MoveToPosition )
  	EVENT( AI_SlideTo,							idAI::Event_SlideTo )
  	EVENT( AI_FacingIdeal,						idAI::Event_FacingIdeal )
  	EVENT( AI_FaceEnemy,						idAI::Event_FaceEnemy )
  	EVENT( AI_FaceEntity,						idAI::Event_FaceEntity )
  	EVENT( AI_WaitAction,						idAI::Event_WaitAction )
  	EVENT( AI_GetCombatNode,					idAI::Event_GetCombatNode )
  	EVENT( AI_EnemyInCombatCone,				idAI::Event_EnemyInCombatCone )
  	EVENT( AI_WaitMove,							idAI::Event_WaitMove )
  	EVENT( AI_GetJumpVelocity,					idAI::Event_GetJumpVelocity )
  	EVENT( AI_EntityInAttackCone,				idAI::Event_EntityInAttackCone )
  	EVENT( AI_CanSeeEntity,						idAI::Event_CanSeeEntity )
  	EVENT( AI_SetTalkTarget,					idAI::Event_SetTalkTarget )
  	EVENT( AI_GetTalkTarget,					idAI::Event_GetTalkTarget )
  	EVENT( AI_SetTalkState,						idAI::Event_SetTalkState )
  	EVENT( AI_EnemyRange,						idAI::Event_EnemyRange )
  	EVENT( AI_EnemyRange2D,						idAI::Event_EnemyRange2D )
  	EVENT( AI_GetEnemy,							idAI::Event_GetEnemy )
  	EVENT( AI_GetEnemyPos,						idAI::Event_GetEnemyPos )
  	EVENT( AI_GetEnemyEyePos,					idAI::Event_GetEnemyEyePos )
  	EVENT( AI_PredictEnemyPos,					idAI::Event_PredictEnemyPos )
  	EVENT( AI_CanHitEnemy,						idAI::Event_CanHitEnemy )
  	EVENT( AI_CanHitEnemyFromAnim,				idAI::Event_CanHitEnemyFromAnim )
  	EVENT( AI_CanHitEnemyFromJoint,				idAI::Event_CanHitEnemyFromJoint )
  	EVENT( AI_EnemyPositionValid,				idAI::Event_EnemyPositionValid )
  	EVENT( AI_ChargeAttack,						idAI::Event_ChargeAttack )
  	EVENT( AI_TestChargeAttack,					idAI::Event_TestChargeAttack )
  	EVENT( AI_TestAnimMoveTowardEnemy,			idAI::Event_TestAnimMoveTowardEnemy )
  	EVENT( AI_TestAnimMove,						idAI::Event_TestAnimMove )
  	EVENT( AI_TestMoveToPosition,				idAI::Event_TestMoveToPosition )
  	EVENT( AI_TestMeleeAttack,					idAI::Event_TestMeleeAttack )
  	EVENT( AI_TestAnimAttack,					idAI::Event_TestAnimAttack )
  	EVENT( AI_Shrivel,							idAI::Event_Shrivel )
  	EVENT( AI_Burn,								idAI::Event_Burn )
  	EVENT( AI_PreBurn,							idAI::Event_PreBurn )
  	EVENT( AI_SetSmokeVisibility,				idAI::Event_SetSmokeVisibility )
  	EVENT( AI_NumSmokeEmitters,					idAI::Event_NumSmokeEmitters )
  	EVENT( AI_ClearBurn,						idAI::Event_ClearBurn )
  	EVENT( AI_StopThinking,						idAI::Event_StopThinking )
  	EVENT( AI_GetTurnDelta,						idAI::Event_GetTurnDelta )
  	EVENT( AI_GetMoveType,						idAI::Event_GetMoveType )
  	EVENT( AI_SetMoveType,						idAI::Event_SetMoveType )
  	EVENT( AI_SaveMove,							idAI::Event_SaveMove )
  	EVENT( AI_RestoreMove,						idAI::Event_RestoreMove )
  	EVENT( AI_AllowMovement,					idAI::Event_AllowMovement )	
  	EVENT( AI_JumpFrame,						idAI::Event_JumpFrame )
  	EVENT( AI_EnableClip,						idAI::Event_EnableClip )
  	EVENT( AI_DisableClip,						idAI::Event_DisableClip )
  	EVENT( AI_EnableGravity,					idAI::Event_EnableGravity )
  	EVENT( AI_DisableGravity,					idAI::Event_DisableGravity )
  	EVENT( AI_EnableAFPush,						idAI::Event_EnableAFPush )
  	EVENT( AI_DisableAFPush,					idAI::Event_DisableAFPush )
  	EVENT( AI_SetFlySpeed,						idAI::Event_SetFlySpeed )
  	EVENT( AI_SetFlyOffset,						idAI::Event_SetFlyOffset )
  	EVENT( AI_ClearFlyOffset,					idAI::Event_ClearFlyOffset )
  	EVENT( AI_GetClosestHiddenTarget,			idAI::Event_GetClosestHiddenTarget )
  	EVENT( AI_GetRandomTarget,					idAI::Event_GetRandomTarget )
  	EVENT( AI_TravelDistanceToPoint,			idAI::Event_TravelDistanceToPoint )
  	EVENT( AI_TravelDistanceToEntity,			idAI::Event_TravelDistanceToEntity )
  	EVENT( AI_TravelDistanceBetweenPoints,		idAI::Event_TravelDistanceBetweenPoints )
  	EVENT( AI_TravelDistanceBetweenEntities,	idAI::Event_TravelDistanceBetweenEntities )
  	EVENT( AI_LookAtEntity,						idAI::Event_LookAtEntity )
  	EVENT( AI_LookAtEnemy,						idAI::Event_LookAtEnemy )
  	EVENT( AI_SetJointMod,						idAI::Event_SetJointMod )
  	EVENT( AI_ThrowMoveable,					idAI::Event_ThrowMoveable )
  	EVENT( AI_ThrowAF,							idAI::Event_ThrowAF )
  	EVENT( EV_GetAngles,						idAI::Event_GetAngles )
  	EVENT( EV_SetAngles,						idAI::Event_SetAngles )
  	EVENT( AI_RealKill,							idAI::Event_RealKill )
  	EVENT( AI_Kill,								idAI::Event_Kill )
  	EVENT( AI_WakeOnFlashlight,					idAI::Event_WakeOnFlashlight )
  	EVENT( AI_LocateEnemy,						idAI::Event_LocateEnemy )
  	EVENT( AI_KickObstacles,					idAI::Event_KickObstacles )
  	EVENT( AI_GetObstacle,						idAI::Event_GetObstacle )
  	EVENT( AI_PushPointIntoAAS,					idAI::Event_PushPointIntoAAS )
  	EVENT( AI_GetTurnRate,						idAI::Event_GetTurnRate )
  	EVENT( AI_SetTurnRate,						idAI::Event_SetTurnRate )
  	EVENT( AI_AnimTurn,							idAI::Event_AnimTurn )
  	EVENT( AI_AllowHiddenMovement,				idAI::Event_AllowHiddenMovement )
  	EVENT( AI_TriggerParticles,					idAI::Event_TriggerParticles )
  	EVENT( AI_FindActorsInBounds,				idAI::Event_FindActorsInBounds )
  	EVENT( AI_CanReachPosition,					idAI::Event_CanReachPosition )
  	EVENT( AI_CanReachEntity,					idAI::Event_CanReachEntity )
  	EVENT( AI_CanReachEnemy,					idAI::Event_CanReachEnemy )
  	EVENT( AI_GetReachableEntityPosition,		idAI::Event_GetReachableEntityPosition )
  END_CLASS
  
  /*
  =====================
  idAI::Event_Activate
  =====================
  */
  void idAI::Event_Activate( idEntity *activator ) {
  	Activate( activator );
  }
  
  /*
  =====================
  idAI::Event_Touch
  =====================
  */
  void idAI::Event_Touch( idEntity *other, trace_t *trace ) {
  	if ( !enemy.GetEntity() && !other->fl.notarget && ( ReactionTo( other ) & ATTACK_ON_ACTIVATE ) ) {
  		Activate( other );
  	}
  	AI_PUSHED = true;
  }
  
  /*
  =====================
  idAI::Event_FindEnemy
  =====================
  */
  void idAI::Event_FindEnemy( int useFOV ) {
  	int			i;
  	idEntity	*ent;
  	idActor		*actor;
  
  	if ( gameLocal.InPlayerPVS( this ) ) {
  		for ( i = 0; i < gameLocal.numClients ; i++ ) {
  			ent = gameLocal.entities[ i ];
  
  			if ( !ent || !ent->IsType( idActor::Type ) ) {
  				continue;
  			}
  
  			actor = static_cast<idActor *>( ent );
  			if ( ( actor->health <= 0 ) || !( ReactionTo( actor ) & ATTACK_ON_SIGHT ) ) {
  				continue;
  			}
  
  			if ( CanSee( actor, useFOV != 0 ) ) {
  				idThread::ReturnEntity( actor );
  				return;
  			}
  		}
  	}
  
  	idThread::ReturnEntity( NULL );
  }
  
  /*
  =====================
  idAI::Event_FindEnemyAI
  =====================
  */
  void idAI::Event_FindEnemyAI( int useFOV ) {
  	idEntity	*ent;
  	idActor		*actor;
  	idActor		*bestEnemy;
  	float		bestDist;
  	float		dist;
  	idVec3		delta;
  	pvsHandle_t pvs;
  
  	pvs = gameLocal.pvs.SetupCurrentPVS( GetPVSAreas(), GetNumPVSAreas() );
  
  	bestDist = idMath::INFINITY;
  	bestEnemy = NULL;
  	for ( ent = gameLocal.activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
  		if ( ent->fl.hidden || ent->fl.isDormant || !ent->IsType( idActor::Type ) ) {
  			continue;
  		}
  
  		actor = static_cast<idActor *>( ent );
  		if ( ( actor->health <= 0 ) || !( ReactionTo( actor ) & ATTACK_ON_SIGHT ) ) {
  			continue;
  		}
  
  		if ( !gameLocal.pvs.InCurrentPVS( pvs, actor->GetPVSAreas(), actor->GetNumPVSAreas() ) ) {
  			continue;
  		}
  
  		delta = physicsObj.GetOrigin() - actor->GetPhysics()->GetOrigin();
  		dist = delta.LengthSqr();
  		if ( ( dist < bestDist ) && CanSee( actor, useFOV != 0 ) ) {
  			bestDist = dist;
  			bestEnemy = actor;
  		}
  	}
  
  	gameLocal.pvs.FreeCurrentPVS( pvs );
  	idThread::ReturnEntity( bestEnemy );
  }
  
  /*
  =====================
  idAI::Event_FindEnemyInCombatNodes
  =====================
  */
  void idAI::Event_FindEnemyInCombatNodes( void ) {
  	int				i, j;
  	idCombatNode	*node;
  	idEntity		*ent;
  	idEntity		*targetEnt;
  	idActor			*actor;
  
  	if ( !gameLocal.InPlayerPVS( this ) ) {
  		// don't locate the player when we're not in his PVS
  		idThread::ReturnEntity( NULL );
  		return;
  	}
  
  	for ( i = 0; i < gameLocal.numClients ; i++ ) {
  		ent = gameLocal.entities[ i ];
  
  		if ( !ent || !ent->IsType( idActor::Type ) ) {
  			continue;
  		}
  
  		actor = static_cast<idActor *>( ent );
  		if ( ( actor->health <= 0 ) || !( ReactionTo( actor ) & ATTACK_ON_SIGHT ) ) {
  			continue;
  		}
  
  		for( j = 0; j < targets.Num(); j++ ) {
  			targetEnt = targets[ j ].GetEntity();
  			if ( !targetEnt || !targetEnt->IsType( idCombatNode::Type ) ) {
  				continue;
  			}
  
  			node = static_cast<idCombatNode *>( targetEnt );
  			if ( !node->IsDisabled() && node->EntityInView( actor, actor->GetPhysics()->GetOrigin() ) ) {
  				idThread::ReturnEntity( actor );
  				return;
  			}
  		}
  	}
  
  	idThread::ReturnEntity( NULL );
  }
  
  /*
  =====================
  idAI::Event_ClosestReachableEnemyOfEntity
  =====================
  */
  void idAI::Event_ClosestReachableEnemyOfEntity( idEntity *team_mate ) {
  	idActor *actor;
  	idActor *ent;
  	idActor	*bestEnt;
  	float	bestDistSquared;
  	float	distSquared;
  	idVec3	delta;
  	int		areaNum;
  	int		enemyAreaNum;
  	aasPath_t path;
  	
  	if ( !team_mate->IsType( idActor::Type ) ) {
  		gameLocal.Error( "Entity '%s' is not an AI character or player", team_mate->GetName() );
  	}
  
  	actor = static_cast<idActor *>( team_mate );
  
  	const idVec3 &origin = physicsObj.GetOrigin();
  	areaNum = PointReachableAreaNum( origin );
  
  	bestDistSquared = idMath::INFINITY;
  	bestEnt = NULL;
  	for( ent = actor->enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
  		if ( ent->fl.hidden ) {
  			continue;
  		}
  		delta = ent->GetPhysics()->GetOrigin() - origin;
  		distSquared = delta.LengthSqr();
  		if ( distSquared < bestDistSquared ) {
  			const idVec3 &enemyPos = ent->GetPhysics()->GetOrigin();
  			enemyAreaNum = PointReachableAreaNum( enemyPos );
  			if ( ( areaNum != 0 ) && PathToGoal( path, areaNum, origin, enemyAreaNum, enemyPos ) ) {
  				bestEnt = ent;
  				bestDistSquared = distSquared;
  			}
  		}
  	}
  
  	idThread::ReturnEntity( bestEnt );
  }
  
  /*
  =====================
  idAI::Event_HeardSound
  =====================
  */
  void idAI::Event_HeardSound( int ignore_team ) {
  	// check if we heard any sounds in the last frame
  	idActor	*actor = gameLocal.GetAlertEntity();
  	if ( actor && ( !ignore_team || ( ReactionTo( actor ) & ATTACK_ON_SIGHT ) ) && gameLocal.InPlayerPVS( this ) ) {
  		idVec3 pos = actor->GetPhysics()->GetOrigin();
  		idVec3 org = physicsObj.GetOrigin();
  		float dist = ( pos - org ).LengthSqr();
  		if ( dist < Square( AI_HEARING_RANGE ) ) {
  			idThread::ReturnEntity( actor );
  			return;
  		}
  	}
  
  	idThread::ReturnEntity( NULL );
  }
  
  /*
  =====================
  idAI::Event_SetEnemy
  =====================
  */
  void idAI::Event_SetEnemy( idEntity *ent ) {
  	if ( !ent ) {
  		ClearEnemy();
  	} else if ( !ent->IsType( idActor::Type ) ) {
  		gameLocal.Error( "'%s' is not an idActor (player or ai controlled character)", ent->name.c_str() );
  	} else {
  		SetEnemy( static_cast<idActor *>( ent ) );
  	}
  }
  
  /*
  =====================
  idAI::Event_ClearEnemy
  =====================
  */
  void idAI::Event_ClearEnemy( void ) {
  	ClearEnemy();
  }
  
  /*
  =====================
  idAI::Event_MuzzleFlash
  =====================
  */
  void idAI::Event_MuzzleFlash( const char *jointname ) {
  	idVec3	muzzle;
  	idMat3	axis;
  
  	GetMuzzle( jointname, muzzle, axis );
  	TriggerWeaponEffects( muzzle, axis );
  }
  
  /*
  =====================
  idAI::Event_CreateMissile
  =====================
  */
  void idAI::Event_CreateMissile( const char *jointname ) {
  	idVec3 muzzle;
  	idMat3 axis;
  
  	if ( !projectileDef ) {
  		gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
  		return idThread::ReturnEntity( NULL );
  	}
  
  	GetMuzzle( jointname, muzzle, axis );
  	CreateProjectile( muzzle, viewAxis[ 0 ] * physicsObj.GetGravityAxis() );
  	if ( projectile.GetEntity() ) {
  		if ( !jointname || !jointname[ 0 ] ) {
  			projectile.GetEntity()->Bind( this, true );
  		} else {
  			projectile.GetEntity()->BindToJoint( this, jointname, true );
  		}
  	}
  	idThread::ReturnEntity( projectile.GetEntity() );
  }
  
  /*
  =====================
  idAI::Event_AttackMissile
  =====================
  */
  void idAI::Event_AttackMissile( const char *jointname, const idDict *projDef, int boneDir ) {
  	idProjectile *proj;
  
  	proj = LaunchProjectile( jointname, enemy.GetEntity(), true, projDef );	//HUMANHEAD mdc - pass projDef on for multiple proj support
  	idThread::ReturnEntity( proj );
  }
  
  /*
  =====================
  idAI::Event_FireMissileAtTarget
  =====================
  */
  void idAI::Event_FireMissileAtTarget( const char *jointname, const char *targetname ) {
  	idEntity		*aent;
  	idProjectile	*proj;
  
  	aent = gameLocal.FindEntity( targetname );
  	if ( !aent ) {
  		gameLocal.Warning( "Entity '%s' not found for 'fireMissileAtTarget'", targetname );
  	}
  
  	proj = LaunchProjectile( jointname, aent, false, NULL );
  	idThread::ReturnEntity( proj );
  }
  /* JRMMERGE - OLD LAUNCH PROJECTILE
  // HUMANHEAD JRM - We want to launch projectiles of ANY type!
  void idAI::Event_AttackMissile( const char *jointname, const idDict *projDef, int boneDir ) {
  	idVec3		muzzle;
  	idVec3		dir;
  	idVec3		start;
  	trace_t		tr;
  	idBounds	projBounds;
  	float		distance;
  	const idClipModel *projClip;
  	float		attack_accuracy;
  	float		attack_cone;
  	float		projectile_spread;
  	float		diff;
  	float		angle;
  	float		spin;
  	idAngles	ang;
  	int			num_projectiles;
  	int			i;
  	idMat3		axis;	
  
  	// HUMANHEAD JRM - START
  	const idDict *oldProjDef = projectileDef;
  	if(projDef)
  		projectileDef = projDef;
  	// HUMANHEAD JRM - END
  
  	if ( !projectileDef ) {
  		gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
  		return;
  	}
  
  	attack_accuracy = spawnArgs.GetFloat( "attack_accuracy", "7" );
  	attack_cone = spawnArgs.GetFloat( "attack_cone", "70" );
  	projectile_spread = spawnArgs.GetFloat( "projectile_spread", "0" );
  	num_projectiles = spawnArgs.GetInt( "num_projectiles", "1" );
  	
  	GetMuzzle( jointname, muzzle, axis );
  
  	if ( !projectile ) {
  		CreateProjectile( muzzle, axis[ 0 ] );
  	}
  
  	// make sure the projectile starts inside the monster bounding box
  	const idBounds &ownerBounds = physicsObj.GetAbsBounds();
  	projClip = projectile->GetPhysics()->GetClipModel();
  	projBounds = projClip->GetBounds().Rotate( projClip->GetAxis() );
  	if ( (ownerBounds - projBounds).RayIntersection( muzzle, GetGravViewAxis()[ 0 ], distance ) ) { // HUMANHEAD JRM - VIEWAXIS_TO_GETGRAVVIEWAXIS
  		start = muzzle + distance * GetGravViewAxis()[ 0 ]; // HUMANHEAD JRM - VIEWAXIS_TO_GETGRAVVIEWAXIS
  	} else {
  		start = ownerBounds.GetCenter();
  	}
  	gameLocal.clip.Translation( tr, start, muzzle, projClip, projClip->GetAxis(), MASK_SHOT_RENDERMODEL, this );
  	muzzle = tr.endpos;
  
  	// set aiming direction
  #ifdef HUMANHEAD
  	// nla - Allow us to aim from the center of the body.  Used in the case of the harvester's twisting rockets
  	if ( projectile->spawnArgs.GetBool( "aim_center" ) ) {
  		idVec3 muzzleNew;
  		
  		muzzleNew = muzzle - GetOrigin();	// Vector to current muzzle from origin		
  		muzzleNew.ProjectOntoPlane( GetAxis()[ 1 ], 1.001f );	// Project this onto the Y center of the monster
  		muzzleNew += GetOrigin();	// Get the new end point of the vector				
  		
  		GetAimDir( muzzleNew, enemy.GetEntity(), projectileDef, this, dir );
  	} 
  	else {
  		GetAimDir( muzzle, enemy.GetEntity(), projectileDef, this, dir );	
  	}
  #else
  		GetAimDir( muzzle, enemy.GetEntity(), projectileDef, this, dir );	
  #endif
  
  	// HUMANHEAD JRM - need to be able to launch down the bone dir if we choose
  	if(boneDir) {		 
  		//HH_ASSERT(joint != INVALID_JOINT);
  		//dir.Normalize();
  		//idAngles oldDirAng = dir.ToAngles();
          dir = axis[0];// * GetGravViewAxis(); // HUMANHEAD JRM - VIEWAXIS_TO_GETGRAVVIEWAXIS
  		//idAngles ang = dir.ToAngles();
  		//ang.pitch = oldDirAng.pitch;
  		//ang.ToVectors(&dir);		
  	}
  	// HUMANHEAD END
  
  	// adjust his aim so it's not perfect.  uses sine based movement so the tracers appear less random in their spread.
  	ang = dir.ToAngles();
  	float t = MS2SEC( gameLocal.time + entityNumber * 497 );
  	ang.pitch += idMath::Sin16( t * 5.1 ) * attack_accuracy;
  	ang.yaw	+= idMath::Sin16( t * 6.7 ) * attack_accuracy;
  
  	// clamp the attack direction to be within monster's attack cone so he doesn't do
  	// things like throw the missile backwards if you're behind him
  	diff = idMath::AngleDelta( ang.yaw, current_yaw );
  	if ( diff > attack_cone ) {
  		ang.yaw = current_yaw + attack_cone;
  	} else if ( diff < -attack_cone ) {
  		ang.yaw = current_yaw - attack_cone;
  	}
  	axis = ang.ToMat3();
  
  	float spreadRad = DEG2RAD( projectile_spread );
  	for( i = 0; i < num_projectiles; i++ ) {
  		if(spreadRad > 0.0f) { // HUMANHEAD JRM - a spread of 0 means don't do anything!
  			// spread the projectiles out		
  			angle = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
  			spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
  			dir = axis[ 0 ] + axis[ 2 ] * ( angle * idMath::Sin( spin ) ) - axis[ 1 ] * ( angle * idMath::Cos( spin ) );
  		}
  		dir.Normalize();
  
  		// launch the projectile
  		if ( !projectile ) {
  			CreateProjectile( muzzle, dir );
  		}
  		projectile->Launch( muzzle, dir, vec3_origin );
  		projectile = NULL;
  	}
  	
  	TriggerWeaponEffects( muzzle );
  
  	// HUMANHEAD JRM - START
  	projectileDef = oldProjDef;	
  	// HUMANHEAD JRM - END
  }
  */
  /*
  =====================
  idAI::Event_LaunchMissile
  =====================
  */
  void idAI::Event_LaunchMissile( const idVec3 &org, const idAngles &ang ) {
  	idVec3		start;
  	trace_t		tr;
  	idBounds	projBounds;
  	const idClipModel *projClip;
  	idMat3		axis;
  	float		distance;
  
  	if ( !projectileDef ) {
  		gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
  		idThread::ReturnEntity( NULL );
  		return;
  	}
  
  	axis = ang.ToMat3();
  	if ( !projectile.GetEntity() ) {
  		CreateProjectile( org, axis[ 0 ] );
  	}
  
  	// make sure the projectile starts inside the monster bounding box
  	const idBounds &ownerBounds = physicsObj.GetAbsBounds();
  	projClip = projectile.GetEntity()->GetPhysics()->GetClipModel();
  	projBounds = projClip->GetBounds().Rotate( projClip->GetAxis() );
  
  	// check if the owner bounds is bigger than the projectile bounds
  	if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
  		( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
  		( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
  		if ( (ownerBounds - projBounds).RayIntersection( org, viewAxis[ 0 ], distance ) ) {
  			start = org + distance * viewAxis[ 0 ];
  		} else {
  			start = ownerBounds.GetCenter();
  		}
  	} else {
  		// projectile bounds bigger than the owner bounds, so just start it from the center
  		start = ownerBounds.GetCenter();
  	}
  
  	gameLocal.clip.Translation( tr, start, org, projClip, projClip->GetAxis(), MASK_SHOT_RENDERMODEL, this );
  
  	// launch the projectile
  	idThread::ReturnEntity( projectile.GetEntity() );
  	projectile.GetEntity()->Launch( tr.endpos, axis[ 0 ], vec3_origin );
  	projectile = NULL;
  
  	TriggerWeaponEffects( tr.endpos, axis );
  
  	lastAttackTime = gameLocal.time;
  }
  
  /*
  =====================
  idAI::Event_AttackMelee
  =====================
  */
  void idAI::Event_AttackMelee( const char *meleeDefName ) {
  	bool hit;
  	
  	hit = AttackMelee( meleeDefName );
  	idThread::ReturnInt( hit );
  }
  
  /*
  =====================
  idAI::Event_DirectDamage
  =====================
  */
  void idAI::Event_DirectDamage( idEntity *damageTarget, const char *damageDefName ) {
  	DirectDamage( damageDefName, damageTarget );
  }
  
  /*
  =====================
  idAI::Event_RadiusDamageFromJoint
  =====================
  */
  void idAI::Event_RadiusDamageFromJoint( const char *jointname, const char *damageDefName ) {
  	jointHandle_t joint;
  	idVec3 org;
  	idMat3 axis;
  
  	if ( !jointname || !jointname[ 0 ] ) {
  		org = physicsObj.GetOrigin();
  	} else {
  		joint = animator.GetJointHandle( jointname );
  		if ( joint == INVALID_JOINT ) {
  			gameLocal.Error( "Unknown joint '%s' on %s", jointname, GetEntityDefName() );
  		}
  		GetJointWorldTransform( joint, gameLocal.time, org, axis );
  	}
  
  	gameLocal.RadiusDamage( org, this, this, this, this, damageDefName );
  }
  
  /*
  =====================
  idAI::Event_RandomPath
  =====================
  */
  void idAI::Event_RandomPath( void ) {
  	idPathCorner *path;
  
  	path = idPathCorner::RandomPath( this, NULL );
  	idThread::ReturnEntity( path );
  }
  
  /*
  =====================
  idAI::Event_BeginAttack
  =====================
  */
  void idAI::Event_BeginAttack( const char *name ) {
  	BeginAttack( name );
  }
  
  /*
  =====================
  idAI::Event_EndAttack
  =====================
  */
  void idAI::Event_EndAttack( void ) {
  	EndAttack();
  }
  
  /*
  =====================
  idAI::Event_MeleeAttackToJoint
  =====================
  */
  void idAI::Event_MeleeAttackToJoint( const char *jointname, const char *meleeDefName ) {
  	jointHandle_t	joint;
  	idVec3			start;
  	idVec3			end;
  	idMat3			axis;
  	trace_t			trace;
  	idEntity		*hitEnt;
  
  	joint = animator.GetJointHandle( jointname );
  	if ( joint == INVALID_JOINT ) {
  		gameLocal.Error( "Unknown joint '%s' on %s", jointname, GetEntityDefName() );
  	}
  	animator.GetJointTransform( joint, gameLocal.time, end, axis );
  	end = physicsObj.GetOrigin() + ( end + modelOffset ) * viewAxis * physicsObj.GetGravityAxis();
  	start = GetEyePosition();
  	
  	if ( ai_debugMove.GetBool() ) {
  		gameRenderWorld->DebugLine( colorYellow, start, end, gameLocal.msec );
  	}
  
  	gameLocal.clip.TranslationEntities( trace, start, end, NULL, mat3_identity, MASK_SHOT_BOUNDINGBOX, this );
  	if ( trace.fraction < 1.0f ) {
  		hitEnt = gameLocal.GetTraceEntity( trace );
  		if ( hitEnt && hitEnt->IsType( idActor::Type ) ) {
  			DirectDamage( meleeDefName, hitEnt );
  			idThread::ReturnInt( true );
  			return;
  		}
  	}
  
  	idThread::ReturnInt( false );
  }
  
  /*
  =====================
  idAI::Event_CanBecomeSolid
  =====================
  */
  void idAI::Event_CanBecomeSolid( void ) {
  	int			i;
  	int			num;
  	idEntity *	hit;
  	idClipModel *cm;
  	idClipModel *clipModels[ MAX_GENTITIES ];
  
  	num = gameLocal.clip.ClipModelsTouchingBounds( physicsObj.GetAbsBounds(), MASK_MONSTERSOLID, clipModels, MAX_GENTITIES );
  	for ( i = 0; i < num; i++ ) {
  		cm = clipModels[ i ];
  
  		// don't check render entities
  		if ( cm->IsRenderModel() ) {
  			continue;
  		}
  
  		hit = cm->GetEntity();
  		if ( ( hit == this ) || !hit->fl.takedamage ) {
  			continue;
  		}
  
  		if ( physicsObj.ClipContents( cm ) ) {
  			idThread::ReturnFloat( false );
  			return;
  		}
  	}
  
  	idThread::ReturnFloat( true );
  }
  
  /*
  =====================
  idAI::Event_BecomeSolid
  =====================
  */
  void idAI::Event_BecomeSolid( void ) {
  	physicsObj.EnableClip();
  	if ( spawnArgs.GetBool( "big_monster" ) ) {
  		physicsObj.SetContents( 0 );
  	} else if ( use_combat_bbox ) {
  		physicsObj.SetContents( CONTENTS_BODY|CONTENTS_SOLID );
  	} else {
  		physicsObj.SetContents( CONTENTS_BODY );
  	}
  	physicsObj.GetClipModel()->Link( gameLocal.clip );
  	fl.takedamage = !spawnArgs.GetBool( "noDamage" );
  }
  
  /*
  =====================
  idAI::Event_BecomeNonSolid
  =====================
  */
  void idAI::Event_BecomeNonSolid( void ) {
  	fl.takedamage = false;
  	physicsObj.SetContents( 0 );
  	physicsObj.GetClipModel()->Unlink();
  }
  
  /*
  =====================
  idAI::Event_BecomeRagdoll
  =====================
  */
  void idAI::Event_BecomeRagdoll( void ) {
  	bool result;
  
  	result = StartRagdoll();
  	idThread::ReturnInt( result );
  }
  
  /*
  =====================
  idAI::Event_StopRagdoll
  =====================
  */
  void idAI::Event_StopRagdoll( void ) {
  	StopRagdoll();
  
  	// set back the monster physics
  	SetPhysics( &physicsObj );
  }
  
  /*
  =====================
  idAI::Event_SetHealth
  =====================
  */
  void idAI::Event_SetHealth( float newHealth ) {
  #ifdef HUMANHEAD
  	SetHealth(newHealth); // JRM
  #else
  	health = newHealth;
  #endif
  	fl.takedamage = true;
  	if ( health > 0 ) {
  		AI_DEAD = false;
  	} else {
  		AI_DEAD = true;
  	}
  }
  
  /*
  =====================
  idAI::Event_GetHealth
  =====================
  */
  void idAI::Event_GetHealth( void ) {
  	idThread::ReturnFloat( health );
  }
  
  /*
  =====================
  idAI::Event_AllowDamage
  =====================
  */
  void idAI::Event_AllowDamage( void ) {
  	fl.takedamage = true;
  }
  
  /*
  =====================
  idAI::Event_IgnoreDamage
  =====================
  */
  void idAI::Event_IgnoreDamage( void ) {
  	fl.takedamage = false;
  }
  
  /*
  =====================
  idAI::Event_GetCurrentYaw
  =====================
  */
  void idAI::Event_GetCurrentYaw( void ) {
  	idThread::ReturnFloat( current_yaw );
  }
  
  /*
  =====================
  idAI::Event_TurnTo
  =====================
  */
  void idAI::Event_TurnTo( float angle ) {
  	TurnToward( angle );
  }
  
  /*
  =====================
  idAI::Event_TurnToPos
  =====================
  */
  void idAI::Event_TurnToPos( const idVec3 &pos ) {
  	TurnToward( pos );
  }
  
  /*
  =====================
  idAI::Event_TurnToEntity
  =====================
  */
  void idAI::Event_TurnToEntity( idEntity *ent ) {
  	if ( ent ) {
  		TurnToward( ent->GetPhysics()->GetOrigin() );
  	}
  }
  
  /*
  =====================
  idAI::Event_MoveStatus
  =====================
  */
  void idAI::Event_MoveStatus( void ) {
  	idThread::ReturnInt( move.moveStatus );
  }
  
  /*
  =====================
  idAI::Event_StopMove
  =====================
  */
  void idAI::Event_StopMove( void ) {
  	StopMove( MOVE_STATUS_DONE );
  }
  
  /*
  =====================
  idAI::Event_MoveToCover
  =====================
  */
  void idAI::Event_MoveToCover( void ) {
  	idActor *enemyEnt = enemy.GetEntity();
  
  	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
  	if ( !enemyEnt || !MoveToCover( enemyEnt, lastVisibleEnemyPos ) ) {
  		return;
  	}
  }
  
  /*
  =====================
  idAI::Event_MoveToEnemy
  =====================
  */
  void idAI::Event_MoveToEnemy( void ) {
  	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
  	if ( !enemy.GetEntity() || !MoveToEnemy() ) {
  		return;
  	}
  }
  
  /*
  =====================
  idAI::Event_MoveToEnemyHeight
  =====================
  */
  void idAI::Event_MoveToEnemyHeight( void ) {
  	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
  	MoveToEnemyHeight();
  }
  
  /*
  =====================
  idAI::Event_MoveOutOfRange
  =====================
  */
  void idAI::Event_MoveOutOfRange( idEntity *entity, float range ) {
  	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
  	MoveOutOfRange( entity, range );
  }
  
  /*
  =====================
  idAI::Event_MoveToAttackPosition
  =====================
  */
  void idAI::Event_MoveToAttackPosition( idEntity *entity, const char *attack_anim ) {
  	int anim;
  
  	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
  
  	anim = GetAnim( ANIMCHANNEL_LEGS, attack_anim );
  	if ( !anim ) {
  		gameLocal.Error( "Unknown anim '%s'", attack_anim );
  	}
  
  	MoveToAttackPosition( entity, anim );
  }
  
  /*
  =====================
  idAI::Event_MoveToEntity
  =====================
  */
  void idAI::Event_MoveToEntity( idEntity *ent ) {
  	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
  	if ( ent ) {
  		MoveToEntity( ent );
  	}
  }
  
  /*
  =====================
  idAI::Event_MoveToPosition
  =====================
  */
  void idAI::Event_MoveToPosition( const idVec3 &pos ) {
  	StopMove( MOVE_STATUS_DONE );
  	MoveToPosition( pos );
  }
  
  /*
  =====================
  idAI::Event_SlideTo
  =====================
  */
  void idAI::Event_SlideTo( const idVec3 &pos, float time ) {
  	SlideToPosition( pos, time );
  }
  /*
  =====================
  idAI::Event_Wander
  =====================
  */
  void idAI::Event_Wander( void ) {
  	WanderAround();
  }
  
  /*
  =====================
  idAI::Event_FacingIdeal
  =====================
  */
  void idAI::Event_FacingIdeal( void ) {
  	bool facing = FacingIdeal();
  	idThread::ReturnInt( facing );
  }
  
  /*
  =====================
  idAI::Event_FaceEnemy
  =====================
  */
  void idAI::Event_FaceEnemy( void ) {
  	FaceEnemy();
  }
  
  /*
  =====================
  idAI::Event_FaceEntity
  =====================
  */
  void idAI::Event_FaceEntity( idEntity *ent ) {
  	FaceEntity( ent );
  }
  
  /*
  =====================
  idAI::Event_WaitAction
  =====================
  */
  void idAI::Event_WaitAction( const char *waitForState ) {
  	if ( idThread::BeginMultiFrameEvent( this, &AI_WaitAction ) ) {
  		SetWaitState( waitForState );
  	}
  
  	if ( !WaitState() ) {
  		idThread::EndMultiFrameEvent( this, &AI_WaitAction );
  	}
  }
  
  /*
  =====================
  idAI::Event_GetCombatNode
  =====================
  */
  void idAI::Event_GetCombatNode( void ) {
  	int				i;
  	float			dist;
  	idEntity		*targetEnt;
  	idCombatNode	*node;
  	float			bestDist;
  	idCombatNode	*bestNode;
  	idActor			*enemyEnt = enemy.GetEntity();
  
  	if ( !targets.Num() ) {
  		// no combat nodes
  		idThread::ReturnEntity( NULL );
  		return;
  	}
  
  	if ( !enemyEnt || !EnemyPositionValid() ) {
  		// don't return a combat node if we don't have an enemy or
  		// if we can see he's not in the last place we saw him
  		idThread::ReturnEntity( NULL );
  		return;
  	}
  
  	// find the closest attack node that can see our enemy and is closer than our enemy
  	bestNode = NULL;
  	const idVec3 &myPos = physicsObj.GetOrigin();
  	bestDist = ( myPos - lastVisibleEnemyPos ).LengthSqr();
  	for( i = 0; i < targets.Num(); i++ ) {
  		targetEnt = targets[ i ].GetEntity();
  		if ( !targetEnt || !targetEnt->IsType( idCombatNode::Type ) ) {
  			continue;
  		}
  
  		node = static_cast<idCombatNode *>( targetEnt );
  		if ( !node->IsDisabled() && node->EntityInView( enemyEnt, lastVisibleEnemyPos ) ) {
  			idVec3 org = node->GetPhysics()->GetOrigin();
  			dist = ( myPos - org ).LengthSqr();
  			if ( dist < bestDist ) {
  				bestNode = node;
  				bestDist = dist;
  			}
  		}
  	}
  
  	idThread::ReturnEntity( bestNode );
  }
  
  /*
  =====================
  idAI::Event_EnemyInCombatCone
  =====================
  */
  void idAI::Event_EnemyInCombatCone( idEntity *ent, int use_current_enemy_location ) {
  	idCombatNode	*node;
  	bool			result;
  	idActor			*enemyEnt = enemy.GetEntity();
  
  	if ( !targets.Num() ) {
  		// no combat nodes
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	if ( !enemyEnt ) {
  		// have to have an enemy
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	if ( !ent || !ent->IsType( idCombatNode::Type ) ) {
  		// not a combat node
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	node = static_cast<idCombatNode *>( ent );
  	if ( use_current_enemy_location ) {
  		const idVec3 &pos = enemyEnt->GetPhysics()->GetOrigin();
  		result = node->EntityInView( enemyEnt, pos );
  	} else {
  		result = node->EntityInView( enemyEnt, lastVisibleEnemyPos );
  	}
  
  	idThread::ReturnInt( result );
  }
  
  /*
  =====================
  idAI::Event_WaitMove
  =====================
  */
  void idAI::Event_WaitMove( void ) {
  	idThread::BeginMultiFrameEvent( this, &AI_WaitMove );
  
  	if ( MoveDone() ) {
  		idThread::EndMultiFrameEvent( this, &AI_WaitMove );
  	}
  }
  
  /*
  =====================
  idAI::Event_GetJumpVelocity
  =====================
  */
  void idAI::Event_GetJumpVelocity( const idVec3 &pos, float speed, float max_height ) {
  	idVec3 start;
  	idVec3 end;
  	idVec3 dir;
  	float dist;
  	bool result;
  	idEntity *enemyEnt = enemy.GetEntity();
  
  	if ( !enemyEnt ) {
  		idThread::ReturnVector( vec3_zero );
  		return;
  	}
  
  	if ( speed <= 0.0f ) {
  		gameLocal.Error( "Invalid speed.  speed must be > 0." );
  	}
  
  	start = physicsObj.GetOrigin();
  	end = pos;
  	dir = end - start;
  	dist = dir.Normalize();
  	if ( dist > 16.0f ) {
  		dist -= 16.0f;
  		end -= dir * 16.0f;
  	}
  
  	result = PredictTrajectory( start, end, speed, physicsObj.GetGravity(), physicsObj.GetClipModel(), MASK_MONSTERSOLID, max_height, this, enemyEnt, ai_debugMove.GetBool() ? 4000 : 0, dir );
  	if ( result ) {
  		idThread::ReturnVector( dir * speed );
  	} else {
  		idThread::ReturnVector( vec3_zero );
  	}
  }
  
  /*
  =====================
  idAI::Event_EntityInAttackCone
  =====================
  */
  void idAI::Event_EntityInAttackCone( idEntity *ent ) {
  	float	attack_cone;
  	idVec3	delta;
  	float	yaw;
  	float	relYaw;
  	
  	if ( !ent ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	delta = ent->GetPhysics()->GetOrigin() - GetEyePosition();
  
  	// get our gravity normal
  	const idVec3 &gravityDir = GetPhysics()->GetGravityNormal();
  
  	// infinite vertical vision, so project it onto our orientation plane
  	delta -= gravityDir * ( gravityDir * delta );
  
  	delta.Normalize();
  	yaw = delta.ToYaw();
  
  	attack_cone = spawnArgs.GetFloat( "attack_cone", "70" );
  	relYaw = idMath::AngleNormalize180( ideal_yaw - yaw );
  	if ( idMath::Fabs( relYaw ) < ( attack_cone * 0.5f ) ) {
  		idThread::ReturnInt( true );
  	} else {
  		idThread::ReturnInt( false );
  	}
  }
  
  /*
  =====================
  idAI::Event_CanSeeEntity
  =====================
  */
  void idAI::Event_CanSeeEntity( idEntity *ent ) {
  	if ( !ent ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	bool cansee = CanSee( ent, false );
  	idThread::ReturnInt( cansee );
  }
  
  /*
  =====================
  idAI::Event_SetTalkTarget
  =====================
  */
  void idAI::Event_SetTalkTarget( idEntity *target ) {
  	if ( target && !target->IsType( idActor::Type ) ) {
  		gameLocal.Error( "Cannot set talk target to '%s'.  Not a character or player.", target->GetName() );
  	}
  	talkTarget = static_cast<idActor *>( target );
  	if ( target ) {
  		AI_TALK = true;
  	} else {
  		AI_TALK = false;
  	}
  }
  
  /*
  =====================
  idAI::Event_GetTalkTarget
  =====================
  */
  void idAI::Event_GetTalkTarget( void ) {
  	idThread::ReturnEntity( talkTarget.GetEntity() );
  }
  
  /*
  ================
  idAI::Event_SetTalkState
  ================
  */
  void idAI::Event_SetTalkState( int state ) {
  	if ( ( state < 0 ) || ( state >= NUM_TALK_STATES ) ) {
  		gameLocal.Error( "Invalid talk state (%d)", state );
  	}
  
  	talk_state = static_cast<talkState_t>( state );
  }
  
  /*
  =====================
  idAI::Event_EnemyRange
  =====================
  */
  void idAI::Event_EnemyRange( void ) {
  	float dist;
  	idActor *enemyEnt = enemy.GetEntity();
  
  	if ( enemyEnt ) {
  		dist = ( enemyEnt->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() ).Length();
  	} else {
  		// Just some really high number
  		dist = idMath::INFINITY;
  	}
  
  	idThread::ReturnFloat( dist );
  }
  
  /*
  =====================
  idAI::Event_EnemyRange2D
  =====================
  */
  void idAI::Event_EnemyRange2D( void ) {
  	float dist;
  	idActor *enemyEnt = enemy.GetEntity();
  
  	if ( enemyEnt ) {
  		dist = ( enemyEnt->GetPhysics()->GetOrigin().ToVec2() - GetPhysics()->GetOrigin().ToVec2() ).Length();
  	} else {
  		// Just some really high number
  		dist = idMath::INFINITY;
  	}
  
  	idThread::ReturnFloat( dist );
  }
  
  /*
  =====================
  idAI::Event_GetEnemy
  =====================
  */
  void idAI::Event_GetEnemy( void ) {
  	idThread::ReturnEntity( enemy.GetEntity() );
  }
  
  /*
  =====================
  idAI::Event_GetEnemyPos
  =====================
  */
  void idAI::Event_GetEnemyPos( void ) {
  	idThread::ReturnVector( lastVisibleEnemyPos );
  }
  
  /*
  =====================
  idAI::Event_GetEnemyEyePos
  =====================
  */
  void idAI::Event_GetEnemyEyePos( void ) {
  	idThread::ReturnVector( lastVisibleEnemyPos + lastVisibleEnemyEyeOffset );
  }
  
  /*
  =====================
  idAI::Event_PredictEnemyPos
  =====================
  */
  void idAI::Event_PredictEnemyPos( float time ) {
  	predictedPath_t path;
  	idActor *enemyEnt = enemy.GetEntity();
  
  	// if no enemy set
  	if ( !enemyEnt ) {
  		idThread::ReturnVector( physicsObj.GetOrigin() );
  		return;
  	}
  
  	// predict the enemy movement
  	idAI::PredictPath( enemyEnt, aas, lastVisibleEnemyPos, enemyEnt->GetPhysics()->GetLinearVelocity(), SEC2MS( time ), SEC2MS( time ), ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
  
  	idThread::ReturnVector( path.endPos );
  }
  
  /*
  =====================
  idAI::Event_CanHitEnemy
  =====================
  */
  void idAI::Event_CanHitEnemy( void ) {
  	trace_t	tr;
  	idEntity *hit;
  
  	idActor *enemyEnt = enemy.GetEntity();
  	if ( !AI_ENEMY_VISIBLE || !enemyEnt ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	// don't check twice per frame
  	if ( gameLocal.time == lastHitCheckTime ) {
  		idThread::ReturnInt( lastHitCheckResult );
  		return;
  	}
  
  	lastHitCheckTime = gameLocal.time;
  
  	idVec3 toPos = enemyEnt->GetEyePosition();
  	idVec3 eye = GetEyePosition();
  	idVec3 dir;
  
  	// expand the ray out as far as possible so we can detect anything behind the enemy
  	dir = toPos - eye;
  	dir.Normalize();
  	toPos = eye + dir * MAX_WORLD_SIZE;
  	gameLocal.clip.TracePoint( tr, eye, toPos, MASK_SHOT_BOUNDINGBOX, this );
  	hit = gameLocal.GetTraceEntity( tr );
  	if ( tr.fraction >= 1.0f || ( hit == enemyEnt ) ) {
  		lastHitCheckResult = true;
  	} else if ( ( tr.fraction < 1.0f ) && ( hit->IsType( idAI::Type ) ) && 
  		( static_cast<idAI *>( hit )->team != team ) ) {
  		lastHitCheckResult = true;
  	} else {
  		lastHitCheckResult = false;
  	}
  
  	idThread::ReturnInt( lastHitCheckResult );
  }
  
  /*
  =====================
  idAI::Event_CanHitEnemyFromAnim
  =====================
  */
  void idAI::Event_CanHitEnemyFromAnim( const char *animname ) {
  	int		anim;
  	idVec3	dir;
  	idVec3	local_dir;
  	idVec3	fromPos;
  	idMat3	axis;
  	idVec3	start;
  	trace_t	tr;
  	float	distance;
  
  	idActor *enemyEnt = enemy.GetEntity();
  	if ( !AI_ENEMY_VISIBLE || !enemyEnt ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	anim = GetAnim( ANIMCHANNEL_LEGS, animname );
  	if ( !anim ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	// just do a ray test if close enough
  	if ( enemyEnt->GetPhysics()->GetAbsBounds().IntersectsBounds( physicsObj.GetAbsBounds().Expand( 16.0f ) ) ) {
  		Event_CanHitEnemy();
  		return;
  	}
  
  	// calculate the world transform of the launch position
  	const idVec3 &org = physicsObj.GetOrigin();
  	dir = lastVisibleEnemyPos - org;
  	physicsObj.GetGravityAxis().ProjectVector( dir, local_dir );
  	local_dir.z = 0.0f;
  	local_dir.ToVec2().Normalize();
  	axis = local_dir.ToMat3();
  	fromPos = physicsObj.GetOrigin() + missileLaunchOffset[ anim ] * axis;
  
  	if ( projectileClipModel == NULL ) {
  		CreateProjectileClipModel();
  	}
  
  	// check if the owner bounds is bigger than the projectile bounds
  	const idBounds &ownerBounds = physicsObj.GetAbsBounds();
  	const idBounds &projBounds = projectileClipModel->GetBounds();
  	if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
  		( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
  		( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
  		if ( (ownerBounds - projBounds).RayIntersection( org, viewAxis[ 0 ], distance ) ) {
  			start = org + distance * viewAxis[ 0 ];
  		} else {
  			start = ownerBounds.GetCenter();
  		}
  	} else {
  		// projectile bounds bigger than the owner bounds, so just start it from the center
  		start = ownerBounds.GetCenter();
  	}
  
  	gameLocal.clip.Translation( tr, start, fromPos, projectileClipModel, mat3_identity, MASK_SHOT_RENDERMODEL, this );
  	fromPos = tr.endpos;
  
  	if ( GetAimDir( fromPos, enemy.GetEntity(), this, dir ) ) {
  		idThread::ReturnInt( true );
  	} else {
  		idThread::ReturnInt( false );
  	}
  }
  
  /*
  =====================
  idAI::Event_CanHitEnemyFromJoint
  =====================
  */
  void idAI::Event_CanHitEnemyFromJoint( const char *jointname ) {
  	trace_t	tr;
  	idVec3	muzzle;
  	idMat3	axis;
  	idVec3	start;
  	float	distance;
  
  	idActor *enemyEnt = enemy.GetEntity();
  	if ( !AI_ENEMY_VISIBLE || !enemyEnt ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	// don't check twice per frame
  	if ( gameLocal.time == lastHitCheckTime ) {
  		idThread::ReturnInt( lastHitCheckResult );
  		return;
  	}
  
  	lastHitCheckTime = gameLocal.time;
  
  	const idVec3 &org = physicsObj.GetOrigin();
  	idVec3 toPos = enemyEnt->GetEyePosition();
  	jointHandle_t joint = animator.GetJointHandle( jointname );
  	if ( joint == INVALID_JOINT ) {
  		gameLocal.Error( "Unknown joint '%s' on %s", jointname, GetEntityDefName() );
  	}
  	animator.GetJointTransform( joint, gameLocal.time, muzzle, axis );
  	// JRMMERGE_GRAVAXIS - Do we still need to X by GetGravViewAxis()
  	muzzle = org + ( muzzle + modelOffset ) * viewAxis * physicsObj.GetGravityAxis();
  
  	if ( projectileClipModel == NULL ) {
  		CreateProjectileClipModel();
  	}
  
  	// check if the owner bounds is bigger than the projectile bounds
  	const idBounds &ownerBounds = physicsObj.GetAbsBounds();
  	const idBounds &projBounds = projectileClipModel->GetBounds();
  	if ( ( ( ownerBounds[1][0] - ownerBounds[0][0] ) > ( projBounds[1][0] - projBounds[0][0] ) ) &&
  		( ( ownerBounds[1][1] - ownerBounds[0][1] ) > ( projBounds[1][1] - projBounds[0][1] ) ) &&
  		( ( ownerBounds[1][2] - ownerBounds[0][2] ) > ( projBounds[1][2] - projBounds[0][2] ) ) ) {
  		if ( (ownerBounds - projBounds).RayIntersection( org, viewAxis[ 0 ], distance ) ) {
  			start = org + distance * viewAxis[ 0 ];
  		} else {
  			start = ownerBounds.GetCenter();
  		}
  	} else {
  		// projectile bounds bigger than the owner bounds, so just start it from the center
  		start = ownerBounds.GetCenter();
  	}
  
  	gameLocal.clip.Translation( tr, start, muzzle, projectileClipModel, mat3_identity, MASK_SHOT_BOUNDINGBOX, this );
  	muzzle = tr.endpos;
  
  	gameLocal.clip.Translation( tr, muzzle, toPos, projectileClipModel, mat3_identity, MASK_SHOT_BOUNDINGBOX, this );
  	if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == enemyEnt ) ) {
  		lastHitCheckResult = true;
  	} else {
  		lastHitCheckResult = false;
  	}
  
  	idThread::ReturnInt( lastHitCheckResult );
  }
  
  /*
  =====================
  idAI::Event_EnemyPositionValid
  =====================
  */
  void idAI::Event_EnemyPositionValid( void ) {
  	bool result;
  
  	result = EnemyPositionValid();
  	idThread::ReturnInt( result );
  }
  
  /*
  =====================
  idAI::Event_ChargeAttack
  =====================
  */
  void idAI::Event_ChargeAttack( const char *damageDef ) {
  	idActor *enemyEnt = enemy.GetEntity();
  
  	StopMove( MOVE_STATUS_DEST_NOT_FOUND );
  	if ( enemyEnt ) {
  		idVec3 enemyOrg;
  
  		if ( move.moveType == MOVETYPE_FLY ) {
  			// position destination so that we're in the enemy's view
  			enemyOrg = enemyEnt->GetEyePosition();
  			enemyOrg -= enemyEnt->GetPhysics()->GetGravityNormal() * fly_offset;
  		} else {
  			enemyOrg = enemyEnt->GetPhysics()->GetOrigin();
  		}
  
  		BeginAttack( damageDef );
  		DirectMoveToPosition( enemyOrg );
  		TurnToward( enemyOrg );
  	}
  }
  
  /*
  =====================
  idAI::Event_TestChargeAttack
  =====================
  */
  void idAI::Event_TestChargeAttack( void ) {
  	trace_t trace;
  	idActor *enemyEnt = enemy.GetEntity();
  	predictedPath_t path;
  	idVec3 end;
  
  	if ( !enemyEnt ) {
  		idThread::ReturnFloat( 0.0f );
  		return;
  	}
  
  	if ( move.moveType == MOVETYPE_FLY ) {
  		// position destination so that we're in the enemy's view
  		end = enemyEnt->GetEyePosition();
  		end -= enemyEnt->GetPhysics()->GetGravityNormal() * fly_offset;
  	} else {
  		end = enemyEnt->GetPhysics()->GetOrigin();
  	}
  
  	idAI::PredictPath( this, aas, physicsObj.GetOrigin(), end - physicsObj.GetOrigin(), 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
  
  	if ( ai_debugMove.GetBool() ) {
  		gameRenderWorld->DebugLine( colorGreen, physicsObj.GetOrigin(), end, gameLocal.msec );
  		gameRenderWorld->DebugBounds( path.endEvent == 0 ? colorYellow : colorRed, physicsObj.GetBounds(), end, gameLocal.msec );
  	}
  
  	if ( ( path.endEvent == 0 ) || ( path.blockingEntity == enemyEnt ) ) {
  		idVec3 delta = end - physicsObj.GetOrigin();
  		float time = delta.LengthFast();
  		idThread::ReturnFloat( time );
  	} else {
  		idThread::ReturnFloat( 0.0f );
  	}
  }
  
  /*
  =====================
  idAI::Event_TestAnimMoveTowardEnemy
  =====================
  */
  void idAI::Event_TestAnimMoveTowardEnemy( const char *animname ) {
  	int				anim;
  	predictedPath_t path;
  	idVec3			moveVec;
  	float			yaw;
  	idVec3			delta;
  	idActor			*enemyEnt;
  	
  	enemyEnt = enemy.GetEntity();
  	if ( !enemyEnt ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	anim = GetAnim( ANIMCHANNEL_LEGS, animname );
  	if ( !anim ) {
  		gameLocal.DWarning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	delta = enemyEnt->GetPhysics()->GetOrigin() - physicsObj.GetOrigin();
      yaw = delta.ToYaw();
  
  	moveVec = animator.TotalMovementDelta( anim ) * idAngles( 0.0f, yaw, 0.0f ).ToMat3() * physicsObj.GetGravityAxis();
  	idAI::PredictPath( this, aas, physicsObj.GetOrigin(), moveVec, 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
  
  	if ( ai_debugMove.GetBool() ) {
  		gameRenderWorld->DebugLine( colorGreen, physicsObj.GetOrigin(), physicsObj.GetOrigin() + moveVec, gameLocal.msec );
  		gameRenderWorld->DebugBounds( path.endEvent == 0 ? colorYellow : colorRed, physicsObj.GetBounds(), physicsObj.GetOrigin() + moveVec, gameLocal.msec );
  	}
  
  	idThread::ReturnInt( path.endEvent == 0 );
  }
  
  /*
  =====================
  idAI::Event_TestAnimMove
  =====================
  */
  void idAI::Event_TestAnimMove( const char *animname ) {
  	int				anim;
  	predictedPath_t path;
  	idVec3			moveVec;
  
  	anim = GetAnim( ANIMCHANNEL_LEGS, animname );
  	if ( !anim ) {
  		gameLocal.DWarning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	// JRMMERGE_GRAVAXIS - GetGravViewAxis() needed here? Here is old
  	//moveVec = GetAnimator()->TotalMovementDelta( anim ) * GetGravViewAxis();// idAngles( 0.0f, ideal_yaw, 0.0f ).ToMat3() * physicsObj.GetGravityAxis(); // HUMANHEAD JRM - removed the ideal_yaw check and gravity /  REMOVED_GRAV_AXIS_MULT
  	moveVec = animator.TotalMovementDelta( anim ) * idAngles( 0.0f, ideal_yaw, 0.0f ).ToMat3() * physicsObj.GetGravityAxis();
  	idAI::PredictPath( this, aas, physicsObj.GetOrigin(), moveVec, 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
  
  	if ( ai_debugMove.GetBool() ) {
  		gameRenderWorld->DebugLine( colorGreen, physicsObj.GetOrigin(), physicsObj.GetOrigin() + moveVec, gameLocal.msec );
  		gameRenderWorld->DebugBounds( path.endEvent == 0 ? colorYellow : colorRed, physicsObj.GetBounds(), physicsObj.GetOrigin() + moveVec, gameLocal.msec );
  	}
  
  	idThread::ReturnInt( path.endEvent == 0 );
  }
  
  /*
  =====================
  idAI::Event_TestMoveToPosition
  =====================
  */
  void idAI::Event_TestMoveToPosition( const idVec3 &position ) {
  	predictedPath_t path;
  
  	idAI::PredictPath( this, aas, physicsObj.GetOrigin(), position - physicsObj.GetOrigin(), 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
  
  	if ( ai_debugMove.GetBool() ) {
  		gameRenderWorld->DebugLine( colorGreen, physicsObj.GetOrigin(), position, gameLocal.msec );
  		gameRenderWorld->DebugBounds( colorYellow, physicsObj.GetBounds(), position, gameLocal.msec );
  		if ( path.endEvent ) {
  			gameRenderWorld->DebugBounds( colorRed, physicsObj.GetBounds(), path.endPos, gameLocal.msec );
  		}
  	}
  
  	idThread::ReturnInt( path.endEvent == 0 );
  }
  
  /*
  =====================
  idAI::Event_TestMeleeAttack
  =====================
  */
  void idAI::Event_TestMeleeAttack( void ) {
  	bool result = TestMelee();
  	idThread::ReturnInt( result );
  }
  
  /*
  =====================
  idAI::Event_TestAnimAttack
  =====================
  */
  void idAI::Event_TestAnimAttack( const char *animname ) {
  	int				anim;
  	predictedPath_t path;
  
  	anim = GetAnim( ANIMCHANNEL_LEGS, animname );
  	if ( !anim ) {
  		gameLocal.DWarning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	idAI::PredictPath( this, aas, physicsObj.GetOrigin(), animator.TotalMovementDelta( anim ), 1000, 1000, ( move.moveType == MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ), path );
  
  	idThread::ReturnInt( path.blockingEntity && ( path.blockingEntity == enemy.GetEntity() ) );
  }
  
  /*
  =====================
  idAI::Event_Shrivel
  =====================
  */
  void idAI::Event_Shrivel( float shrivel_time ) {
  	float t;
  
  	if ( idThread::BeginMultiFrameEvent( this, &AI_Shrivel ) ) {
  		if ( shrivel_time <= 0.0f ) {
  			idThread::EndMultiFrameEvent( this, &AI_Shrivel );
  			return;
  		}
  
  		shrivel_rate = 0.001f / shrivel_time;
  		shrivel_start = gameLocal.time;
  	}
  
  	t = ( gameLocal.time - shrivel_start ) * shrivel_rate;
  	if ( t > 0.25f ) {
  		renderEntity.noShadow = true;
  	}
  	if ( t > 1.0f ) {
  		t = 1.0f;
  		idThread::EndMultiFrameEvent( this, &AI_Shrivel );
  	}
  
  	// HUMANHEAD pdm: changed to go through our call
  	SetShaderParm(SHADERPARM_MD5_SKINSCALE, 1.0f - t * 0.5f);
  }
  
  /*
  =====================
  idAI::Event_PreBurn
  =====================
  */
  void idAI::Event_PreBurn( void ) {
  	// for now this just turns shadows off
  	renderEntity.noShadow = true;
  }
  
  /*
  =====================
  idAI::Event_Burn
  =====================
  */
  void idAI::Event_Burn( void ) {
  	SpawnParticles( "smoke_burnParticleSystem" );
  	// HUMANHEAD pdm: changed to go through our call
  	SetShaderParm(SHADERPARM_TIME_OF_DEATH, MS2SEC(gameLocal.time));
  }
  
  /*
  =====================
  idAI::Event_ClearBurn
  =====================
  */
  void idAI::Event_ClearBurn( void ) {
  	renderEntity.noShadow = spawnArgs.GetBool( "noshadows" );
  	// HUMANHEAD pdm: changed to go through our call
  	SetShaderParm(SHADERPARM_TIME_OF_DEATH, 0.0f);
  }
  
  /*
  =====================
  idAI::Event_SetSmokeVisibility
  =====================
  */
  void idAI::Event_SetSmokeVisibility( int num, int on ) {
  	int i;
  	int time;
  
  	if ( num >= particles.Num() ) {
  		gameLocal.Warning( "Particle #%d out of range (%d particles) on entity '%s'", num, particles.Num(), name.c_str() );
  		return;
  	}
  
  	if ( on != 0 ) {
  		time = gameLocal.time;
  		BecomeActive( TH_UPDATEPARTICLES );
  	} else {
  		time = 0;
  	}
  
  	if ( num >= 0 ) {
  		particles[ num ].time = time;
  	} else {
  		for ( i = 0; i < particles.Num(); i++ ) {
  			particles[ i ].time = time;
  		}
  	}
  
  	UpdateVisuals();
  }
  
  /*
  =====================
  idAI::Event_NumSmokeEmitters
  =====================
  */
  void idAI::Event_NumSmokeEmitters( void ) {
  	idThread::ReturnInt( particles.Num() );
  }
  
  /*
  =====================
  idAI::Event_StopThinking
  =====================
  */
  void idAI::Event_StopThinking( void ) {
  	BecomeInactive( TH_THINK );
  	idThread *thread = idThread::CurrentThread();
  	if ( thread ) {
  		thread->DoneProcessing();
  	}
  }
  
  /*
  =====================
  idAI::Event_GetTurnDelta
  =====================
  */
  void idAI::Event_GetTurnDelta( void ) {
  	float amount;
  
  	if ( turnRate ) {
  		amount = idMath::AngleNormalize180( ideal_yaw - current_yaw );
  		idThread::ReturnFloat( amount );
  	} else {
  		idThread::ReturnFloat( 0.0f );
  	}
  }
  
  /*
  =====================
  idAI::Event_GetMoveType
  =====================
  */
  void idAI::Event_GetMoveType( void ) {
  	idThread::ReturnInt( move.moveType );
  }
  
  /*
  =====================
  idAI::Event_SetMoveTypes
  =====================
  */
  void idAI::Event_SetMoveType( int moveType ) {
  	if ( ( moveType < 0 ) || ( moveType >= NUM_MOVETYPES ) ) {
  		gameLocal.Error( "Invalid movetype %d", moveType );
  	}
  
  	move.moveType = static_cast<moveType_t>( moveType );
  	if ( move.moveType == MOVETYPE_FLY ) {
  		travelFlags = TFL_WALK|TFL_AIR|TFL_FLY;
  	} else {
  		travelFlags = TFL_WALK|TFL_AIR;
  	}
  }
  
  /*
  =====================
  idAI::Event_SaveMove
  =====================
  */
  void idAI::Event_SaveMove( void ) {
  	savedMove = move;
  }
  
  /*
  =====================
  idAI::Event_RestoreMove
  =====================
  */
  void idAI::Event_RestoreMove( void ) {
  	idVec3 goalPos;
  	idVec3 dest;
  
  	switch( savedMove.moveCommand ) {
  	case MOVE_NONE :
  		StopMove( savedMove.moveStatus );
  		break;
  
  	case MOVE_FACE_ENEMY :
  		FaceEnemy();
  		break;
  
  	case MOVE_FACE_ENTITY :
  		FaceEntity( savedMove.goalEntity.GetEntity() );
  		break;
  
  	case MOVE_TO_ENEMY :
  		MoveToEnemy();
  		break;
  
  	case MOVE_TO_ENEMYHEIGHT :
  		MoveToEnemyHeight();
  		break;
  
  	case MOVE_TO_ENTITY :
  		MoveToEntity( savedMove.goalEntity.GetEntity() );
  		break;
  
  	case MOVE_OUT_OF_RANGE :
  		MoveOutOfRange( savedMove.goalEntity.GetEntity(), savedMove.range );
  		break;
  
  	case MOVE_TO_ATTACK_POSITION :
  		MoveToAttackPosition( savedMove.goalEntity.GetEntity(), savedMove.anim );
  		break;
  
  	case MOVE_TO_COVER :
  		MoveToCover( savedMove.goalEntity.GetEntity(), lastVisibleEnemyPos );
  		break;
  
  	case MOVE_TO_POSITION :
  		MoveToPosition( savedMove.moveDest );
  		break;
  
  	case MOVE_TO_POSITION_DIRECT :
  		DirectMoveToPosition( savedMove.moveDest );
  		break;
  
  	case MOVE_SLIDE_TO_POSITION :
  		SlideToPosition( savedMove.moveDest, savedMove.duration );
  		break;
  
  	case MOVE_WANDER :
  		WanderAround();
  		break;
  	}
  
  	if ( GetMovePos( goalPos ) ) {
  		CheckObstacleAvoidance( goalPos, dest );
  	}
  }
  
  /*
  =====================
  idAI::Event_AllowMovement
  =====================
  */
  void idAI::Event_AllowMovement( float flag ) {
  	allowMove = ( flag != 0.0f );
  }
  
  /*
  =====================
  idAI::Event_JumpFrame
  =====================
  */
  void idAI::Event_JumpFrame( void ) {
  	AI_JUMP = true;
  }
  
  /*
  =====================
  idAI::Event_EnableClip
  =====================
  */
  void idAI::Event_EnableClip( void ) {
  	physicsObj.SetClipMask( MASK_MONSTERSOLID );
  	disableGravity = false;
  }
  
  /*
  =====================
  idAI::Event_DisableClip
  =====================
  */
  void idAI::Event_DisableClip( void ) {
  	physicsObj.SetClipMask( 0 );
  	disableGravity = true;
  }
  
  /*
  =====================
  idAI::Event_EnableGravity
  =====================
  */
  void idAI::Event_EnableGravity( void ) {
  	disableGravity = false;
  }
  
  /*
  =====================
  idAI::Event_DisableGravity
  =====================
  */
  void idAI::Event_DisableGravity( void ) {
  	disableGravity = true;
  }
  
  /*
  =====================
  idAI::Event_EnableAFPush
  =====================
  */
  void idAI::Event_EnableAFPush( void ) {
  	af_push_moveables = true;
  }
  
  /*
  =====================
  idAI::Event_DisableAFPush
  =====================
  */
  void idAI::Event_DisableAFPush( void ) {
  	af_push_moveables = false;
  }
  
  /*
  =====================
  idAI::Event_SetFlySpeed
  =====================
  */
  void idAI::Event_SetFlySpeed( float speed ) {
  	if ( move.speed == fly_speed ) {
  		move.speed = speed;
  	}
  	fly_speed = speed;
  }
  
  /*
  ================
  idAI::Event_SetFlyOffset
  ================
  */
  void idAI::Event_SetFlyOffset( int offset ) {
  	fly_offset = offset;
  }
  
  /*
  ================
  idAI::Event_ClearFlyOffset
  ================
  */
  void idAI::Event_ClearFlyOffset( void ) {
  	spawnArgs.GetInt( "fly_offset",	"0", fly_offset );
  }
  
  /*
  =====================
  idAI::Event_GetClosestHiddenTarget
  =====================
  */
  void idAI::Event_GetClosestHiddenTarget( const char *type ) {
  	int	i;
  	idEntity *ent;
  	idEntity *bestEnt;
  	float time;
  	float bestTime;
  	const idVec3 &org = physicsObj.GetOrigin();
  	idActor *enemyEnt = enemy.GetEntity();
  
  	if ( !enemyEnt ) {
  		// no enemy to hide from
  		idThread::ReturnEntity( NULL );
  		return;
  	}
  
  	if ( targets.Num() == 1 ) {
  		ent = targets[ 0 ].GetEntity();
  		if ( ent && idStr::Cmp( ent->GetEntityDefName(), type ) == 0 ) {
  			if ( !EntityCanSeePos( enemyEnt, lastVisibleEnemyPos, ent->GetPhysics()->GetOrigin() ) ) {
  				idThread::ReturnEntity( ent );
  				return;
  			}
  		}
  		idThread::ReturnEntity( NULL );
  		return;
  	}
  
  	bestEnt = NULL;
  	bestTime = idMath::INFINITY;
  	for( i = 0; i < targets.Num(); i++ ) {
  		ent = targets[ i ].GetEntity();
  		if ( ent && idStr::Cmp( ent->GetEntityDefName(), type ) == 0 ) {
  			const idVec3 &destOrg = ent->GetPhysics()->GetOrigin();
  			time = TravelDistance( org, destOrg );
  			if ( ( time >= 0.0f ) && ( time < bestTime ) ) {
  				if ( !EntityCanSeePos( enemyEnt, lastVisibleEnemyPos, destOrg ) ) {
  					bestEnt = ent;
  					bestTime = time;
  				}
  			}
  		}
  	}
  	idThread::ReturnEntity( bestEnt );
  }
  
  /*
  =====================
  idAI::Event_GetRandomTarget
  =====================
  */
  void idAI::Event_GetRandomTarget( const char *type ) {
  	int	i;
  	int	num;
  	int which;
  	idEntity *ent;
  	idEntity *ents[ MAX_GENTITIES ];
  
  	num = 0;
  	for( i = 0; i < targets.Num(); i++ ) {
  		ent = targets[ i ].GetEntity();
  		if ( ent && idStr::Cmp( ent->GetEntityDefName(), type ) == 0 ) {
  			ents[ num++ ] = ent;
  			if ( num >= MAX_GENTITIES ) {
  				break;
  			}
  		}
  	}
  
  	if ( !num ) {
  		idThread::ReturnEntity( NULL );
  		return;
  	}
  
  	which = gameLocal.random.RandomInt( num );
  	idThread::ReturnEntity( ents[ which ] );
  }
  
  /*
  ================
  idAI::Event_TravelDistanceToPoint
  ================
  */
  void idAI::Event_TravelDistanceToPoint( const idVec3 &pos ) {
  	float time;
  
  	time = TravelDistance( physicsObj.GetOrigin(), pos );
  	idThread::ReturnFloat( time );
  }
  
  /*
  ================
  idAI::Event_TravelDistanceToEntity
  ================
  */
  void idAI::Event_TravelDistanceToEntity( idEntity *ent ) {
  	float time;
  
  	time = TravelDistance( physicsObj.GetOrigin(), ent->GetPhysics()->GetOrigin() );
  	idThread::ReturnFloat( time );
  }
  
  /*
  ================
  idAI::Event_TravelDistanceBetweenPoints
  ================
  */
  void idAI::Event_TravelDistanceBetweenPoints( const idVec3 &source, const idVec3 &dest ) {
  	float time;
  
  	time = TravelDistance( source, dest );
  	idThread::ReturnFloat( time );
  }
  
  /*
  ================
  idAI::Event_TravelDistanceBetweenEntities
  ================
  */
  void idAI::Event_TravelDistanceBetweenEntities( idEntity *source, idEntity *dest ) {
  	float time;
  
  	assert( source );
  	assert( dest );
  	time = TravelDistance( source->GetPhysics()->GetOrigin(), dest->GetPhysics()->GetOrigin() );
  	idThread::ReturnFloat( time );
  }
  
  /*
  =====================
  idAI::Event_LookAtEntity
  =====================
  */
  void idAI::Event_LookAtEntity( idEntity *ent, float duration ) {
  	if ( ent == this ) {
  		ent = NULL;
  	}
  
  	if ( ( ent != focusEntity.GetEntity() ) || ( focusTime < gameLocal.time ) ) {
  		focusEntity	= ent;
  		alignHeadTime = gameLocal.time;
  		forceAlignHeadTime = gameLocal.time + SEC2MS( 1 );
  		blink_time = 0;
  	}
  
  	focusTime = gameLocal.time + SEC2MS( duration );
  }
  
  /*
  =====================
  idAI::Event_LookAtEnemy
  =====================
  */
  void idAI::Event_LookAtEnemy( float duration ) {
  	idActor *enemyEnt;
  
  	enemyEnt = enemy.GetEntity();
  	if ( ( enemyEnt != focusEntity.GetEntity() ) || ( focusTime < gameLocal.time ) ) {
  		focusEntity	= enemyEnt;
  		alignHeadTime = gameLocal.time;
  		forceAlignHeadTime = gameLocal.time + SEC2MS( 1 );
  		blink_time = 0;
  	}
  
  	focusTime = gameLocal.time + SEC2MS( duration );
  }
  
  /*
  ===============
  idAI::Event_SetJointMod
  ===============
  */
  void idAI::Event_SetJointMod( int allow ) {
  	allowJointMod = ( allow != 0 );
  }
  
  /*
  ================
  idAI::Event_ThrowMoveable
  ================
  */
  void idAI::Event_ThrowMoveable( void ) {
  	idEntity *ent;
  	idEntity *moveable = NULL;
  
  	for ( ent = GetNextTeamEntity(); ent != NULL; ent = ent->GetNextTeamEntity() ) {
  		if ( ent->GetBindMaster() == this && ent->IsType( idMoveable::Type ) ) {
  			moveable = ent;
  			break;
  		}
  	}
  	if ( moveable ) {
  		moveable->Unbind();
  		moveable->PostEventMS( &EV_SetOwner, 200, NULL );
  	}
  }
  
  /*
  ================
  idAI::Event_ThrowAF
  ================
  */
  void idAI::Event_ThrowAF( void ) {
  	idEntity *ent;
  	idEntity *af = NULL;
  
  	for ( ent = GetNextTeamEntity(); ent != NULL; ent = ent->GetNextTeamEntity() ) {
  		if ( ent->GetBindMaster() == this && ent->IsType( idAFEntity_Base::Type ) ) {
  			af = ent;
  			break;
  		}
  	}
  	if ( af ) {
  		af->Unbind();
  		af->PostEventMS( &EV_SetOwner, 200, NULL );
  	}
  }
  
  /*
  ================
  idAI::Event_SetAngles
  ================
  */
  void idAI::Event_SetAngles( idAngles const &ang ) {
  	current_yaw = ang.yaw;
  	viewAxis = idAngles( 0, current_yaw, 0 ).ToMat3();
  }
  
  /*
  ================
  idAI::Event_GetAngles
  ================
  */
  void idAI::Event_GetAngles( void ) {
  	idThread::ReturnVector( idVec3( 0.0f, current_yaw, 0.0f ) );
  }
  
  /*
  ================
  idAI::Event_RealKill
  ================
  */
  void idAI::Event_RealKill( void ) {
  	health = 0;
  
  	if ( af.IsLoaded() ) {
  		// clear impacts
  		af.Rest();
  
  		// physics is turned off by calling af.Rest()
  		BecomeActive( TH_PHYSICS );
  	}
  
  	Killed( this, this, 0, vec3_zero, INVALID_JOINT );
  }
  
  /*
  ================
  idAI::Event_Kill
  ================
  */
  void idAI::Event_Kill( void ) {
  	PostEventMS( &AI_RealKill, 0 );
  }
  
  /*
  ================
  idAI::Event_WakeOnFlashlight
  ================
  */
  void idAI::Event_WakeOnFlashlight( int enable ) {
  	wakeOnFlashlight = ( enable != 0 );
  }
  
  /*
  ================
  idAI::Event_LocateEnemy
  ================
  */
  void idAI::Event_LocateEnemy( void ) {
  	idActor *enemyEnt;
  	int areaNum;
  	
  	enemyEnt = enemy.GetEntity();
  	if ( !enemyEnt ) {
  		return;
  	}
  
  	enemyEnt->GetAASLocation( aas, lastReachableEnemyPos, areaNum );
  	SetEnemyPosition();
  	UpdateEnemyPosition();
  }
  
  /*
  ================
  idAI::Event_KickObstacles
  ================
  */
  void idAI::Event_KickObstacles( idEntity *kickEnt, float force ) {
  	idVec3 dir;
  	idEntity *obEnt;
  	
  	if ( kickEnt ) {
  		obEnt = kickEnt;
  	} else {
  		obEnt = move.obstacle.GetEntity();
  	}
  
  	if ( obEnt ) {
  		dir = obEnt->GetPhysics()->GetOrigin() - physicsObj.GetOrigin();
  		dir.Normalize();
  	} else {
  		dir = viewAxis[ 0 ];
  	}
  	KickObstacles( dir, force, obEnt );
  }
  
  /*
  ================
  idAI::Event_GetObstacle
  ================
  */
  void idAI::Event_GetObstacle( void ) {
  	idThread::ReturnEntity( move.obstacle.GetEntity() );
  }
  
  /*
  ================
  idAI::Event_PushPointIntoAAS
  ================
  */
  void idAI::Event_PushPointIntoAAS( const idVec3 &pos ) {
  	int		areaNum;
  	idVec3	newPos;
  
  	areaNum = PointReachableAreaNum( pos );
  	if ( areaNum ) {
  		newPos = pos;
  		aas->PushPointIntoAreaNum( areaNum, newPos );
  		idThread::ReturnVector( newPos );
  	} else {
  		idThread::ReturnVector( pos );
  	}
  }
  
  
  /*
  ================
  idAI::Event_GetTurnRate
  ================
  */
  void idAI::Event_GetTurnRate( void ) {
  	idThread::ReturnFloat( turnRate );
  }
  
  /*
  ================
  idAI::Event_SetTurnRate
  ================
  */
  void idAI::Event_SetTurnRate( float rate ) {
  	turnRate = rate;
  }
  
  /*
  ================
  idAI::Event_AnimTurn
  ================
  */
  void idAI::Event_AnimTurn( float angles ) {
  	turnVel = 0.0f;
  	anim_turn_angles = angles;
  	if ( angles ) {
  		anim_turn_yaw = current_yaw;
  		anim_turn_amount = idMath::Fabs( idMath::AngleNormalize180( current_yaw - ideal_yaw ) );
  		if ( anim_turn_amount > anim_turn_angles ) {
  			anim_turn_amount = anim_turn_angles;
  		}
  	} else {
  		anim_turn_amount = 0.0f;
  		animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 0, 1.0f );
  		animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 1, 0.0f );
  		animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 0, 1.0f );
  		animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 1, 0.0f );
  	}
  }
  
  /*
  ================
  idAI::Event_AllowHiddenMovement
  ================
  */
  void idAI::Event_AllowHiddenMovement( int enable ) {
  	allowHiddenMovement = ( enable != 0 );
  }
  
  /*
  ================
  idAI::Event_TriggerParticles
  ================
  */
  void idAI::Event_TriggerParticles( const char *jointName ) {
  	TriggerParticles( jointName );
  }
  
  /*
  =====================
  idAI::Event_FindActorsInBounds
  =====================
  */
  void idAI::Event_FindActorsInBounds( const idVec3 &mins, const idVec3 &maxs ) {
  	idEntity *	ent;
  	idEntity *	entityList[ MAX_GENTITIES ];
  	int			numListedEntities;
  	int			i;
  
  	numListedEntities = gameLocal.clip.EntitiesTouchingBounds( idBounds( mins, maxs ), CONTENTS_BODY, entityList, MAX_GENTITIES );
  	for( i = 0; i < numListedEntities; i++ ) {
  		ent = entityList[ i ];
  		if ( ent != this && !ent->IsHidden() && ( ent->health > 0 ) && ent->IsType( idActor::Type ) ) {
  			idThread::ReturnEntity( ent );
  			return;
  		}
  	}
  
  	idThread::ReturnEntity( NULL );
  }
  
  /*
  ================
  idAI::Event_CanReachPosition
  ================
  */
  void idAI::Event_CanReachPosition( const idVec3 &pos ) {
  	aasPath_t	path;
  	int			toAreaNum;
  	int			areaNum;
  
  	toAreaNum = PointReachableAreaNum( pos );
  	areaNum	= PointReachableAreaNum( physicsObj.GetOrigin() );
  	if ( !toAreaNum || !PathToGoal( path, areaNum, physicsObj.GetOrigin(), toAreaNum, pos ) ) {
  		idThread::ReturnInt( false );
  	} else {
  		idThread::ReturnInt( true );
  	}
  }
  
  /*
  ================
  idAI::Event_CanReachEntity
  ================
  */
  void idAI::Event_CanReachEntity( idEntity *ent ) {
  	aasPath_t	path;
  	int			toAreaNum;
  	int			areaNum;
  	idVec3		pos;
  
  	if ( !ent ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	if ( move.moveType != MOVETYPE_FLY ) {
  		if ( !ent->GetFloorPos( 64.0f, pos ) ) {
  			idThread::ReturnInt( false );
  			return;
  		}
  		if ( ent->IsType( idActor::Type ) && static_cast<idActor *>( ent )->OnLadder() ) {
  			idThread::ReturnInt( false );
  			return;
  		}
  	} else {
  		pos = ent->GetPhysics()->GetOrigin();
  	}
  
  	toAreaNum = PointReachableAreaNum( pos );
  	if ( !toAreaNum ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	const idVec3 &org = physicsObj.GetOrigin();
  	areaNum	= PointReachableAreaNum( org );
  	if ( !toAreaNum || !PathToGoal( path, areaNum, org, toAreaNum, pos ) ) {
  		idThread::ReturnInt( false );
  	} else {
  		idThread::ReturnInt( true );
  	}
  }
  
  /*
  ================
  idAI::Event_CanReachEnemy
  ================
  */
  void idAI::Event_CanReachEnemy( void ) {
  	aasPath_t	path;
  	int			toAreaNum;
  	int			areaNum;
  	idVec3		pos;
  	idActor		*enemyEnt;
  
  	enemyEnt = enemy.GetEntity();
  	if ( !enemyEnt ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	if ( move.moveType != MOVETYPE_FLY ) {
  		if ( enemyEnt->OnLadder() ) {
  			idThread::ReturnInt( false );
  			return;
  		}
  		enemyEnt->GetAASLocation( aas, pos, toAreaNum );
  	}  else {
  		pos = enemyEnt->GetPhysics()->GetOrigin();
  		toAreaNum = PointReachableAreaNum( pos );
  	}
  
  	if ( !toAreaNum ) {
  		idThread::ReturnInt( false );
  		return;
  	}
  
  	const idVec3 &org = physicsObj.GetOrigin();
  	areaNum	= PointReachableAreaNum( org );
  	if ( !PathToGoal( path, areaNum, org, toAreaNum, pos ) ) {
  		idThread::ReturnInt( false );
  	} else {
  		idThread::ReturnInt( true );
  	}
  }
  
  /*
  ================
  idAI::Event_GetReachableEntityPosition
  ================
  */
  void idAI::Event_GetReachableEntityPosition( idEntity *ent ) {
  	int		toAreaNum;
  	idVec3	pos;
  
  	if ( move.moveType != MOVETYPE_FLY ) {
  		if ( !ent->GetFloorPos( 64.0f, pos ) ) {
  			// NOTE: not a good way to return 'false'
  			return idThread::ReturnVector( vec3_zero );
  		}
  		if ( ent->IsType( idActor::Type ) && static_cast<idActor *>( ent )->OnLadder() ) {
  			// NOTE: not a good way to return 'false'
  			return idThread::ReturnVector( vec3_zero );
  		}
  	} else {
  		pos = ent->GetPhysics()->GetOrigin();
  	}
  
  	if ( aas ) {
  		toAreaNum = PointReachableAreaNum( pos );
  		aas->PushPointIntoAreaNum( toAreaNum, pos );
  	}
  
  	idThread::ReturnVector( pos );
  }
  