//-----------------------------------------------------------------------------
//
//  $Logfile:: /Quake 2 Engine/Sin/code/game/behavior.cpp                     $
// $Revision:: 117                                                            $
//   $Author:: Markd                                                          $
//     $Date:: 11/20/98 7:17p                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/behavior.cpp                         $
// 
// 117   11/20/98 7:17p Markd
// Fixed Hide behavior
// 
// 116   11/18/98 8:53p Markd
// Made nearestnode check for one without bounding box for thrall
// 
// 115   11/18/98 7:47p Markd
// made it so FindNearEnemy doesn't use bounding box mainly for THrall
// 
// 114   11/15/98 8:02p Jimdose
// Swimming monsters no longer choose random directions that are in air
// Flying monsters no longer choose random directions that are in water
// Actors using FindEnemy no longer get stuck when the nearest node to them is
// the same as the nearest node to the enemy.
// 
// 113   11/15/98 1:24a Jimdose
// monsters don't play snd_sightenemy now unless DoAction( "sightenemy" )
// succeeds.
// 
// 112   11/13/98 3:29p Markd
// Fixed some ChooseRandomDirection stuff
// 
// 111   11/07/98 8:14p Jimdose
// Made it so Hide and FleeAndRemove ensure the guy either runs away or
// crouches down
// 
// 110   10/27/98 7:48p Jimdose
// Added PlayAnimSeekEnemy
// 
// 109   10/27/98 3:52a Markd
// put in state = 0 if can't resolve FindEnemy
// 
// 108   10/27/98 12:42a Jimdose
// added animprefix to AimAndShoot
// 
// 107   10/26/98 2:18p Markd
// Put in last_jump_time support
// 
// 106   10/25/98 4:56a Jimdose
// Fixed bug with chase where guys would do a spin at the end of a path
// Changed wait time in opendoor
// made FindEnemy check if the enemy has moved far before doing another search
// 
// 105   10/23/98 4:44a Markd
// made jump early exit if no land animation
// 
// 104   10/23/98 12:30a Markd
// Fixed up Flee behavior
// 
// 103   10/22/98 11:36p Jimdose
// Added GetCloseToEnemy
// Changed FindEnemy's seektime
// 
// 102   10/20/98 11:29p Markd
// guys won't get stuck aiming anymore
// 
// 101   10/20/98 4:05a Markd
// made snd_idle more frequent in wander
// 
// 100   10/20/98 3:52a Markd
// Added idle chatter to Wander
// 
// 99    10/20/98 2:37a Markd
// Put in ClearEnemies and JumpTo modifications
// 
// 98    10/19/98 12:07a Jimdose
// made all code use fast checks for inheritance (no text lookups when
// possible)
// isSubclassOf no longer requires ::_classinfo()
// 
// 97    10/18/98 4:11p Markd
// Made FindEnemy not use FindClosestSightNode
// 
// 96    10/18/98 12:34a Markd
// Added randomness to AimAndShoot and AimAndMelee
// 
// 95    10/17/98 11:01p Markd
// Added code to AimAndShoot
// 
// 94    10/17/98 4:43p Markd
// Added curious time and xydist to Investigate
// 
// 93    10/17/98 4:05p Markd
// replaced OpenDOor initial trace with vec_zero's again
// 
// 92    10/17/98 3:30p Markd
// Fixed OpenDoor code not taking into account bounding boxes
// 
// 91    10/17/98 12:34a Markd
// Commented out warning for the time being
// 
// 90    10/16/98 7:20p Markd
// put in numshots for AimAndMelee, also tweaked PlayAnim warning reporting
// 
// 89    10/14/98 11:53p Markd
// If PlayAnim doesn't resolve an anim, make sure and post and EndBehavior
// 
// 88    10/14/98 9:02p Markd
// Made the Jump behavior more robust, and added landing
// 
// 87    10/14/98 5:20p Markd
// Updated Jump behavior
// 
// 86    10/14/98 2:17a Markd
// Took out can_melee code
// 
// 85    10/14/98 1:27a Markd
// Put in can_melee in FindEnemy
// 
// 84    10/13/98 11:13p Markd
// Scaled anim_deltas and fixed closeattacks not waiting long enough to reach
// player
// 
// 83    10/13/98 9:11p Markd
// Fixed RandomChooseDirection bug
// 
// 82    10/13/98 7:38p Markd
// Created behaviors for Wander, Fly, FLyCloseAttack, and WanderCloseAttack
// 
// 81    10/13/98 12:25a Markd
// Fixed swimming creatures AGAIN!
// 
// 80    10/10/98 6:08p Markd
// Added FleeAndRemove, FindFlee, fixed max_ainode bug
// 
// 79    10/10/98 5:01p Markd
// Changed trace masks to edict->clipmasks
// 
// 78    10/10/98 3:27p Markd
// Fixed FindEnemy behavior
// 
// 77    10/09/98 11:57p Markd
// Fixed up melee close range attacks
// 
// 76    10/06/98 5:25p Markd
// Added ForceAction, hopefully fixed some sightEnemy issues
// 
// 75    10/05/98 10:32p Markd
// Made Strafe Attack use a random strafe direction, also tweaked
// SwimCloseAttack
// 
// 74    10/05/98 4:36p Markd
// Clear out seenEnemy
// 
// 73    10/05/98 3:04p Markd
// Added miss detection to AimAndShoot
// 
// 72    10/05/98 12:29a Jimdose
// moved angledist to q_shared
// 
// 71    10/04/98 7:49p Markd
// When done GotoPathNode, play the idle animation if no animation exists at
// the movegoal
// 
// 70    10/04/98 5:32p Markd
// Fixed ChooseRandomDirection problems
// 
// 69    10/04/98 3:47p Markd
// fixed swimming getting stuck
// 
// 68    10/04/98 3:01p Markd
// fixed swimming behavior
// 
// 67    10/03/98 7:19p Markd
// Added SwimCloseAttack and Melee behaviors
// 
// 66    10/01/98 8:00p Markd
// Added AimAndMelee
// 
// 65    10/01/98 4:14p Jimdose
// Improved Aim::Evaluate so that it properly targets partially obscured guys.
// 
// 64    9/22/98 4:16a Jimdose
// Made GotoPathNode check new paths every 4 seconds instead of every 20.
// 
// 63    9/22/98 1:54a Jimdose
// Completed ShowInfo for all behaviors
// Added StrafeTo
// 
// 62    9/18/98 10:56p Jimdose
// Separated steering behaviors into their own file
// Made AimAndShoot exit when enemy is killed while aiming
// 
// 61    9/14/98 5:29p Jimdose
// Added SetPathRate to Chase so that actors can choose how often they search
// for a new path.
// NearestNode now requires that you pass in the entity that is going to use
// the path.
// 
// 60    8/31/98 7:48p Jimdose
// Removed unused variables from FollowPath
// 
// 59    8/31/98 7:46p Jimdose
// Simplified FollowPath
// 
// 58    8/29/98 9:39p Jimdose
// Added call info to G_Trace
// 
// 57    8/26/98 11:13p Jimdose
// Added StrafeAttack
// 
// 56    8/24/98 6:57p Jimdose
// Moved hueristics for path finding to actor
// Modified how closely actors follow paths
// 
// 55    8/19/98 8:47p Jimdose
// Added Jump behavior
// 
// 54    8/19/98 7:58p Jimdose
// took out debug print
// starting jump node support
// 
// 53    8/19/98 4:00p Jimdose
// Made PickupAndThrow exit if it has a NULL pickup_target
// 
// 52    8/19/98 2:31p Jimdose
// Changed Chase so that if the target is an entity, it ends when it touches
// that entity.
// 
// 51    8/18/98 10:00p Jimdose
// Actors follow paths tighter when near doors
// 
// 50    8/15/98 11:18p Jimdose
// Extended reach for opening doors
// 
// 49    8/14/98 8:30p Jimdose
// TurnTo now doesn't return false when turning to ents. (need to change
// eventually).
// FindEnemy now searches based on CanShoot rather than CanSee
// 
// 48    8/14/98 6:26p Jimdose
// Got rid of decelleration for steering
// Added GoalEnt to GotoPathNode
// Added check for arrival to seek.  Reduces running in circles
// 
// 47    8/12/98 2:12p Markd
// fixed initialization of bestnode parameter in FindWanderNode
// 
// 46    8/10/98 6:51p Aldie
// Modified wander behavior a little to make it less intensive
// 
// 45    8/09/98 5:49p Markd
// Rewrote ThrowObjects pickup and throw
// 
// 44    8/08/98 8:39p Markd
// fixed throwing bug
// 
// 43    8/08/98 8:41p Jimdose
// Fixed oscillating problem in Aim
// 
// 42    8/08/98 8:24p Markd
// working on pickupandthrow behavior
// 
// 41    8/07/98 8:47p Markd
// working on pickupandthrow
// 
// 40    8/07/98 6:01p Jimdose
// Rewrote AimAndShoot
// 
// 39    8/06/98 6:57p Jimdose
// Removed FireFromCover (now done in script)
// Added SetArgs to AimAndShoot
// 
// 38    8/05/98 7:18p Jimdose
// Renamed states
// 
// 37    8/03/98 7:58p Aldie
// Added a first attempt at wander behavior
// 
// 36    7/26/98 11:43a Jimdose
// Tweaked hiding behavior
// 
// 35    7/26/98 6:39a Jimdose
// Avoiding occupied nodes now works
// 
// 34    7/26/98 3:48a Jimdose
// Modified aim based on skill
// 
// 33    7/26/98 2:45a Jimdose
// Changed pathway_t structure
// 
// 32    7/25/98 2:09a Jimdose
// FindCoverNode now uses the closest cover node to search from, rather than
// choosing a random node and letting the search algorithm find a closer one.
// 
// 31    7/22/98 10:51p Jimdose
// Added repel behavior
// 
// 30    7/21/98 4:14p Jimdose
// disabled the - operator on Vectors
// 
// 29    7/19/98 9:19p Jimdose
// Fixed bug in FindCoverNode where the actor would try searching for a path
// to every cover node in the level.
// 
// 28    7/08/98 9:00p Jimdose
// Made OpenDoor script callable
// 
// 27    7/06/98 1:06p Jimdose
// working on ai
// 
// 26    6/30/98 6:03p Jimdose
// Chase and GotoPathNode now can use vectors for goals
// Investigate no longer needs currentEnemy to be set
// 
// 25    6/25/98 8:11p Jimdose
// Changed Idle stuff
// 
// 24    6/24/98 4:26p Jimdose
// Fixed bugs in TurnTo
// 
// 23    6/17/98 9:59p Jimdose
// Changed path radius
// 
// 22    6/17/98 3:03p Markd
// Changed NumArgs back to previous behavior
// 
// 21    6/17/98 1:19a Jimdose
// Removed TargetEnemies.  Moved it back into actor
// Working on weapon firing and sighting enemies
// 
// 20    6/15/98 10:47p Jimdose
// Added Hide, and CoverFire
// 
// 19    6/13/98 8:23p Jimdose
// Added FindCover
// 
// 18    6/11/98 12:44a Jimdose
// behaviors now get info from the script at startup
// 
// 17    6/10/98 10:25p Jimdose
// Added priority based state system
// 
// 16    6/09/98 5:34p Jimdose
// made flee better at finding a random node.
// 
// 15    6/09/98 4:18p Jimdose
// worked on ai
// 
// 14    6/04/98 10:48p Jimdose
// Fixed a bunch of things that got broken just in time for E3.  Paths and
// scripting actually work now.
// 
// 13    6/03/98 5:43p Jimdose
// Fixed spelling of behavior. :)
// 
// 12    5/27/98 7:12a Jimdose
// ai ai ai
// 
// 11    5/27/98 6:39a Jimdose
// working on ai
// 
// 10    5/27/98 5:11a Jimdose
// working on ai
// 
// 9     5/25/98 5:31p Jimdose
// Pathnodes are no longer a subclass of Entity.  This was done to save on
// edicts
// 
// 8     5/25/98 1:06a Jimdose
// Added chatter
// 
// 7     5/24/98 1:02a Jimdose
// added Investigate
// 
// 6     5/23/98 6:27p Jimdose
// improved steering code
// 
// 5     5/22/98 9:45p Jimdose
// Disabled debug lines for forces
// 
// 4     5/22/98 9:36p Jimdose
// AI Scripting works again (somewhat)
// 
// 3     5/20/98 6:36p Jimdose
// Working on ai
// 
// 2     5/18/98 8:15p Jimdose
// Created file
// 
// DESCRIPTION:
// Behaviors used by the AI.
//

#include "g_local.h"
#include "behavior.h"
#include "actor.h"
#include "doors.h"
#include "object.h"

Event EV_Behavior_Args( "args" );
Event EV_Behavior_AnimDone( "animdone" );

/****************************************************************************

  Behavior Class Definition

****************************************************************************/

CLASS_DECLARATION( Listener, Behavior, NULL );

ResponseDef Behavior::Responses[] =
	{
		{ NULL, NULL }
	};

Behavior::Behavior()
	{
	}

void Behavior::ShowInfo
	(
	Actor &self
	)

	{
   if ( movegoal )
      {
      gi.printf( "movegoal: ( %f, %f, %f ) - '%s'\n", 
         movegoal->worldorigin.x, movegoal->worldorigin.y, movegoal->worldorigin.z, movegoal->targetname.c_str() );
      }
   else
      {
      gi.printf( "movegoal: NULL\n" );
      }
	}

void Behavior::Begin
	(
	Actor &self
	)

	{
	}

qboolean	Behavior::Evaluate
	(
	Actor &self
	)

	{
	return false;
	}

void Behavior::End
	(
	Actor &self
	)

	{
	}

/****************************************************************************

  Idle Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Idle, NULL );

ResponseDef Idle::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Idle::SetArgs },
		{ NULL, NULL }
	};

void Idle::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

void Idle::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nnexttwitch : %f\n", nexttwitch );
   gi.printf( "anim : %s\n", anim.c_str() );
	}

void Idle::Begin
	(
	Actor &self
	)

	{
	self.currentEnemy = NULL;
   self.seenEnemy = false;
	nexttwitch = level.time + 10 + G_Random( 30 );

	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
	}

qboolean	Idle::Evaluate
	(
	Actor &self
	)

	{
	if ( self.currentEnemy )
		{
		if ( self.DoAction( "sightenemy" ) )
			{
			self.seenEnemy = true;
    		self.Chatter( "snd_sightenemy", 5 );
			}
		else
			{
			self.currentEnemy = NULL;
			}
		return true;
		}

	if ( nexttwitch < level.time )
		{
		self.chattime += 10;
		self.DoAction( "twitch" );
		return true;
		}
	else
		{
		self.Chatter( "snd_idle", 1 );
		}
	
	return true;
	}

void Idle::End
	(
	Actor &self
	)

	{
	}

/****************************************************************************

  Aim Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Aim, NULL );

ResponseDef Aim::Responses[] =
	{
		{ NULL, NULL }
	};

void Aim::SetTarget
	(
	Entity *ent
	)

	{
	target = ent;
	}

void Aim::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nseek:\n" );
   seek.ShowInfo( self );
   if ( target )
      {
      gi.printf( "\ntarget : #%d '%s'\n", target->entnum, target->targetname.c_str() );
      }
   else
      {
      gi.printf( "\ntarget : NULL\n" );
      }
	}

void Aim::Begin
	(
	Actor &self
	)

	{
	seek.Begin( self );
	}

qboolean	Aim::Evaluate
	(
	Actor &self
	)

	{
   Vector dir;
   Vector ang;
   Vector pos;

	if ( !target )
		{
		return false;
		}

   //
   // get my gun pos
   //
   pos = self.GunPosition();

   ang = self.MyGunAngles( pos, false );

   // 
   // invert PITCH
   //
   ang[ PITCH ] = -ang[ PITCH ];

   ang.AngleVectors( &dir, NULL, NULL );

   seek.SetTargetPosition( target->centroid );
	seek.SetTargetVelocity( target->velocity );
	seek.SetPosition( self.centroid );
   seek.SetDir( dir );
	seek.SetMaxSpeed( 1400 + skill->value * 600 );
	seek.Evaluate( self );
	if ( ( fabs( seek.steeringforce.y ) > 5 ) && ( self.enemyRange > RANGE_MELEE ) ) 
		{
		seek.steeringforce.y *= 2;
		}

	self.Accelerate( seek.steeringforce );
	if ( seek.steeringforce.y < 0.25f )
		{
		// dead on
		return false;
		}

	return true;
	}

void Aim::End
	(
	Actor &self
	)

	{
	seek.End( self );
	}

/****************************************************************************

  FireOnSight Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, FireOnSight, NULL );

ResponseDef FireOnSight::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )FireOnSight::SetArgs },
		{ NULL, NULL }
	};

void FireOnSight::SetArgs
	(
	Event *ev
	)

	{
	if ( ev->NumArgs() > 1 )
		{
		anim = ev->GetString( 1 );
		}
	}

void FireOnSight::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nchase:\n" );
   chase.ShowInfo( self );

   gi.printf( "\naim:\n" );
   aim.ShowInfo( self );
   gi.printf( "\nmode : %d\n", mode );
   gi.printf( "anim : %s\n", anim.c_str() );
	}

void FireOnSight::Begin
	(
	Actor &self
	)

	{
	mode = 0;
	if ( !anim.length() )
		{
		anim = "run";
		}
	}

qboolean	FireOnSight::Evaluate
	(
	Actor &self
	)

	{
	if ( !self.currentEnemy || self.currentEnemy->deadflag || self.currentEnemy->health <= 0 )
		{
		return false;
		}

	switch( mode )
		{
		case 0 :
			// Start chasing
			self.SetAnim( anim );
			chase.Begin( self );
			mode = 1;

		case 1 :
			// Chasing
			if ( self.WeaponReady() && self.CanShoot( self.currentEnemy, false ) )
				{
				chase.End( self );
				self.SetAnim( "readyfire" );
				aim.Begin( self );
				mode = 2;
				break;
				}
			else 
				{
				self.Chatter( "snd_pursuit", 1 );
				}

			chase.SetTarget( self.currentEnemy );
			chase.Evaluate( self );
			break;

		case 2 :
			// Aiming
			aim.SetTarget( self.currentEnemy );
			aim.Evaluate( self );

			if ( self.WeaponReady() && self.CanShoot( self.currentEnemy, true ) )
				{
				self.Chatter( "snd_inmysights", 5 );
				self.SetAnim( "fire" );
				mode = 3;
				}
			else if ( !self.WeaponReady() || !self.CanShoot( self.currentEnemy, false ) )
				{
				aim.End( self );
				mode = 0;
				break;
				}
			break;

		case 3 :
			// Fire
			aim.SetTarget( self.currentEnemy );
			aim.Evaluate( self );
			if ( !self.CanShoot( self.currentEnemy, true ) )
				{
				self.SetAnim( "aim" );
				mode = 2;
				}
			else
				{
				self.Chatter( "snd_attacktaunt", 4 );
				}
			break;
		}

	return true;
	}

void FireOnSight::End
	(
	Actor &self
	)

	{
	chase.End( self );
	aim.End( self );
	}

/****************************************************************************

  TurnTo Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, TurnTo, NULL );

ResponseDef TurnTo::Responses[] =
	{
		{ NULL, NULL }
	};

TurnTo::TurnTo()
	{
	dir = Vector( 1, 0, 0 );
	mode = 0;
	ent = NULL;
   yaw = 0;
	}

void TurnTo::SetDirection
	(
	float yaw
	)

	{
	Vector ang;

	ang = Vector( 0, yaw, 0 );
	this->yaw = anglemod( yaw );
	ang.AngleVectors( &dir, NULL, NULL );
	mode = 1;
	}

void TurnTo::SetTarget
	(
	Entity *ent
	)

	{
	this->ent = ent;
	mode = 2;
	}

void TurnTo::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nseek:\n" );
   seek.ShowInfo( self );

   if ( ent )
      {
      gi.printf( "\nent: #%d '%s'\n", ent->entnum, ent->targetname.c_str() );
      }
   else
      {
      gi.printf( "\nent: NULL\n" );
      }
   
   gi.printf( "dir: ( %f, %f, %f )\n", dir.x, dir.y, dir.z );
   gi.printf( "yaw: %f\n", yaw );
   gi.printf( "mode: %d\n", mode );
	}

void TurnTo::Begin
	(
	Actor &self
	)

	{
	seek.Begin( self );
	}

qboolean	TurnTo::Evaluate
	(
	Actor &self
	)

	{
	Vector delta;
	float ang;

	switch( mode )
		{
		case 1 :
			ang = angledist( yaw - self.angles.yaw() );
			if ( fabs( ang ) < 1 )
				{
				self.Accelerate( Vector( 0, ang, 0 ) );
				return false;
				}

			seek.SetTargetPosition( self.worldorigin + dir );
			seek.SetTargetVelocity( vec_zero );
			break;

		case 2 :
			if ( !ent )
				{
				return false;
				}

			delta = ent->worldorigin - self.worldorigin;
			yaw = delta.toYaw();
			//if ( self.angles.yaw() == yaw )
			//	{
			//	return false;
			//	}

			seek.SetTargetPosition( ent->worldorigin );
			seek.SetTargetVelocity( vec_zero );
			break;

		default :
			return false;
		}

	seek.SetPosition( self.worldorigin );
	seek.SetDir( self.movedir );
	seek.SetMaxSpeed( self.movespeed );
	seek.Evaluate( self );
	//seek.DrawForces();

	self.Accelerate( seek.steeringforce );

	return true;
	}

void TurnTo::End
	(
	Actor &self
	)

	{
	seek.End( self );
	}

/****************************************************************************

  GotoPathNode Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, GotoPathNode, NULL );

ResponseDef GotoPathNode::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )GotoPathNode::SetArgs },
		{ NULL, NULL }
	};

GotoPathNode::GotoPathNode()
	{
	usevec = false;
	movegoal = NULL;
	goal = vec_zero;
	goalent = NULL;
	}

void GotoPathNode::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	
	if ( ev->IsVectorAt( 3 ) )
		{
		goal = ev->GetVector( 3 );
		usevec = true;
		}
	else
		{
      usevec = false;
		movegoal = AI_FindNode( ev->GetString( 3 ) );
		if ( !movegoal )
			{
			goalent = ev->GetEntity( 3 );
			}
		}
	}

void GotoPathNode::SetGoal
	(
	PathNode *node
	)

	{
	usevec = false;
	movegoal = node;
	}

void GotoPathNode::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nturnto:\n" );
   turnto.ShowInfo( self );

   gi.printf( "\nchase:\n" );
   chase.ShowInfo( self );

   gi.printf( "\nstate: %d\n", state );
   gi.printf( "usevec: %d\n", usevec );
   gi.printf( "time: %f\n", time );
   gi.printf( "anim: %s\n", anim.c_str() );

   if ( goalent )
      {
      gi.printf( "\ngoalent: #%d '%s'\n", goalent->entnum, goalent->targetname.c_str() );
      }
   else
      {
      gi.printf( "\ngoalent: NULL\n" );
      }
   
   gi.printf( "goal: ( %f, %f, %f )\n", goal.x, goal.y, goal.z );
	}

void GotoPathNode::Begin
	(
	Actor &self
	)

	{
	state = 0;
	chase.Begin( self );
	turnto.Begin( self );
	if ( goalent )
		{
		chase.SetTarget( goalent );
		}
	else if ( movegoal )
      {
      chase.SetGoal( movegoal );
      }
   else
		{
		chase.SetGoalPos( goal );
		}

   // don't check for new paths as often
   chase.SetPathRate( 4 );

	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
	}

qboolean	GotoPathNode::Evaluate
	(
	Actor &self
	)

	{
	float yaw;

	if ( !usevec && !goalent && !movegoal )
		{
		return false;
		}

	switch( state )
		{
		case 0 :
			if ( chase.Evaluate( self ) )
				{
				break;
				}

			state = 1;
			self.SetAnim( "idle" );

			// cascade down to case 1
		case 1 :
			if ( !movegoal )
				{
				return false;
				}

			if ( movegoal->setangles )
				{
				yaw = movegoal->worldangles.yaw();
				turnto.SetDirection( yaw );
				if ( turnto.Evaluate( self ) )
					{
					break;
					}
				}

			if ( movegoal->animname == "" )
				{
            self.SetAnim( "idle" );
				return false;
				}

			self.SetAnim( movegoal->animname, EV_Actor_FinishedBehavior );
			state = 2;
			break;

		case 2 :
			break;
		}

	return true;
	}

void GotoPathNode::End
	(
	Actor &self
	)

	{
	chase.End( self );
	}

/****************************************************************************

  Investigate Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Investigate, NULL );

ResponseDef Investigate::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Investigate::SetArgs },
		{ NULL, NULL }
	};

void Investigate::SetArgs
	(
	Event *ev
	)

	{
	Entity *ent;
	Actor *self;

	ent = ev->GetEntity( 1 );
	if ( ent && ent->isSubclassOf( Actor ) )
		{
		self = ( Actor * )ent;
		}

	anim = ev->GetString( 2 );
	goal = ev->GetVector( 3 );
	}

void Investigate::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nchase:\n" );
   chase.ShowInfo( self );
   gi.printf( "\nanim: %s\n", anim.c_str() );
   gi.printf( "curioustime: %f\n", curioustime );
   gi.printf( "goal: ( %f, %f, %f )\n", goal.x, goal.y, goal.z );
	}

void Investigate::Begin
	(
	Actor &self
	)

	{
   //
   // we are only interested for about 10 seconds, if we can't get there, lets go back to what we were doing
   //
   curioustime = level.time + 10;
	self.Chatter( "snd_investigate", 10 );
	chase.Begin( self );
	chase.SetGoalPos( goal );

	// Don't allow guys to change their anim if we're already close enough to the goal
	if ( !Done( self ) && anim.length() )
		{
		self.SetAnim( anim );
		}
	}

qboolean Investigate::Done
	(
	Actor &self
	)

	{
	Vector delta;
	float xydist;

   if ( curioustime < level.time )
      {
      return true;
      }

	if ( self.CanSeeEnemyFrom( self.worldorigin ) )
		{
		return true;
		}

   if ( self.lastmove == STEPMOVE_STUCK )
      {
      return true;
      }
	delta = goal - self.worldorigin;
   // 
   // get rid of Z variance
   //
   delta[ 2 ] = 0;
   xydist = delta.length();
	if ( xydist < 100 )
		{
		return true;
		}

	return false;
	}

qboolean	Investigate::Evaluate
	(
	Actor &self
	)

	{
	if ( Done( self ) || !chase.Evaluate( self ) )
		{
		return false;
		}

	return true;
	}

void Investigate::End
	(
	Actor &self
	)

	{
	chase.End( self );
	}

/****************************************************************************

  Flee Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Flee, NULL );

ResponseDef Flee::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Flee::SetArgs },
		{ NULL, NULL }
	};

void Flee::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

void Flee::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nfollow:\n" );
   follow.ShowInfo( self );

   if ( path )
      {
      gi.printf( "\npath : ( %f, %f, %f ) to ( %f, %f, %f )\n", 
         path->Start()->worldorigin.x, path->Start()->worldorigin.y, path->Start()->worldorigin.z, 
         path->End()->worldorigin.x, path->End()->worldorigin.y, path->End()->worldorigin.z );
      }
   else
      {
      gi.printf( "\npath : NULL\n" );
      }

   gi.printf( "\navoid:\n" );
   avoid.ShowInfo( self );

   gi.printf( "\navoidtime: %f\n", avoidtime );
   gi.printf( "anim: %s\n", anim.c_str() );
	}

void Flee::Begin
	(
	Actor &self
	)

	{
	follow.Begin( self );
   avoid.AvoidWalls( false );
	avoid.Begin( self );
	avoidtime = 0;

	path = NULL;

	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
	}

qboolean	Flee::Evaluate
	(
	Actor &self
	)

	{
	PathNode *node;
	int i;

	self.Chatter( "snd_panic", 3 );

	if ( path && follow.DoneWithPath( self ) )
		{
		path = NULL;

		if ( !self.currentEnemy || !self.CanSee( self.currentEnemy ) )
			{
			return false;
			}
		}

	if ( !path )
		{
		for( i = 0; i < 5; i++ )
			{
			node = AI_GetNode( ( int )G_Random( ai_maxnode + 1 ) );
			if ( node )
				{
				break;
				}
			}

		if ( node )
			{
			path = follow.SetPath( self, self.worldorigin, node->worldorigin );
			}
		else
			{
			return false;
			}
		}

	follow.SetPosition( self.worldorigin );
	follow.SetDir( self.movedir );
	follow.SetMaxSpeed( self.movespeed );
	follow.Evaluate( self );

	if ( avoidtime < level.time )
		{
		avoidtime = level.time + 0.4;

		avoid.SetMaxSpeed( self.movespeed );
		avoid.SetPosition( self.worldorigin );
		avoid.SetDir( self.movedir );
		avoid.Evaluate( self );

		follow.steeringforce += avoid.steeringforce;
		}

	self.Accelerate( follow.steeringforce );

	return true;
	}

void Flee::End
	(
	Actor &self
	)

	{
	avoid.End( self );
	follow.End( self );
	path = NULL;
	}

/****************************************************************************

  OpenDoor Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, OpenDoor, NULL );

ResponseDef OpenDoor::Responses[] =
	{
		{ NULL, NULL }
	};

OpenDoor::OpenDoor()
	{
	usedir = false;
	}

void OpenDoor::SetArgs
	(
	Event *ev
	)

	{
	if ( ev->NumArgs() > 1 )
		{
		dir = ev->GetVector( 2 );
		//usedir = true;
		}
	}

void OpenDoor::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\ntime: %f\n", time );
   gi.printf( "endtime: %f\n", endtime );
   gi.printf( "usedir: %d\n", usedir );
   gi.printf( "dir: ( %f, %f, %f )\n", dir.x, dir.y, dir.z );
	}

void OpenDoor::Begin
	(
	Actor &self
	)

	{
	Event		*e;
	trace_t	trace;
	Entity	*ent;
	Vector	pos;
	Vector	end;

	endtime = 0;

	pos = self.worldorigin + self.eyeposition;
	if ( usedir )
		{
		end = pos + dir;
		}
	else
		{
		end = pos + Vector( self.orientation[ 0 ] ) * 64;
		}

	trace = G_Trace( pos, vec_zero, vec_zero, end, &self, self.edict->clipmask, "OpenDoor 1" );

	ent = trace.ent->entity;
	if ( ent && ent->isSubclassOf( Door ) )
		{
		self.SetAnim( "idle" );

		time = level.time + 0.1;
		endtime = time + 1;

		e = new Event( EV_Use );
		e->AddEntity( &self );
		ent->ProcessEvent( e );
		}
	}

qboolean	OpenDoor::Evaluate
	(
	Actor &self
	)

	{
	trace_t	trace;
	Vector	pos;

	if ( level.time > endtime )
		{
		return false;
		}

	if ( time < level.time )
		{
		pos = self.worldorigin + self.eyeposition;
		trace = G_Trace( pos, self.mins, self.maxs, pos + Vector( self.orientation[ 0 ] ) * 32, &self, self.edict->clipmask, "OpenDoor 2" );
		if ( trace.fraction == 1 )
			{
			return false;
			}
		}

	return true;
	}

void OpenDoor::End
	(
	Actor &self
	)

	{
	}

/****************************************************************************

  PlayAnim Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, PlayAnim, NULL );

ResponseDef PlayAnim::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )PlayAnim::SetArgs },
		{ NULL, NULL }
	};

void PlayAnim::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

void PlayAnim::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nanim: %s\n", anim.c_str() );
	}

void PlayAnim::Begin
	(
	Actor &self
	)

	{
	if ( anim.length() )
		{
		if ( !self.SetAnim( anim, EV_Actor_FinishedBehavior ) )
         {
         //warning( "Begin", "%s does not exist for %s.", anim.c_str(), self.targetname.c_str() );
         self.PostEvent( EV_Actor_FinishedBehavior, 0 );
         }
		}
	}

qboolean	PlayAnim::Evaluate
	(
	Actor &self
	)

	{
	return true;
	}

void PlayAnim::End
	(
	Actor &self
	)

	{
	}

/****************************************************************************

  Wander Class Definition

****************************************************************************/
/*
CLASS_DECLARATION( Behavior, Wander, NULL );

ResponseDef Wander::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Wander::SetArgs },
		{ NULL, NULL }
	};

void Wander::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
   maxdistance = ev->GetFloat( 3 );
   maxdistance *= maxdistance;
	}

PathNode *Wander::FindWanderNode
	(
	Actor &self
	)

	{
	int i;
	PathNode	*bestnode;
	PathNode *node;
	FindCoverPath find;
	Path		*path;
	Vector	delta;
	float		dist;
	Vector	pos;
   int      count = 0;

	pos = self.worldorigin;
   bestnode = NULL;

   for ( i = 0; i < 5; i++ )
      {
      node = AI_GetNode( G_Random( ai_maxnode + 1 ) );
      
      if ( !node )
         continue;

      delta = node->worldorigin - pos;
      dist = delta * delta;
      if ( ( dist > 1024 ) && ( dist < maxdistance ) )
         {
         bestnode = node;
         break;
         }
      }
   
	if ( bestnode )
		{
		find.heuristic.self = &self;
		find.heuristic.setSize( self.size );
		find.heuristic.entnum = self.entnum;

		path = find.FindPath( self.worldorigin, bestnode->worldorigin );
		if ( path )
			{
			node = path->End();
			// Mark node as occupied for a short time
			node->occupiedTime = level.time + 1.5;
			node->entnum = self.entnum;
			chase.SetGoal( node );
			chase.SetPath( path );
			return node;
			}
      }
   return NULL;
	}

void Wander::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nchase:\n" );
   chase.ShowInfo( self );

   gi.printf( "\nanim: %s\n", anim.c_str() );
   gi.printf( "state: %d\n", state );
   gi.printf( "maxdistance: %f\n", maxdistance );
	}

void Wander::Begin
	(
	Actor &self
	)

	{
	if ( !anim.length() )
		{
		anim = "walk";
		}

	movegoal = NULL;
	state = 0;
	}

qboolean	Wander::Evaluate
	(
	Actor &self
	)

	{
	if ( !movegoal )
		{
		state = 0;
		}

	switch( state )
		{
		case 0 :
			chase.Begin( self );
			movegoal = FindWanderNode( self );
			if ( !movegoal )
				{
				return false;
				}
			if ( anim.length() && ( anim != self.animname ) )
				{
				self.SetAnim( anim );
				}

			state = 1;
         
		case 1 :
			if ( chase.Evaluate( self ) )
				{
				return true;
				}

         // Look for another wander node
			state = 0;
			chase.End( self );
			return false;
			break;
		}
	return true;
	}

void Wander::End
	(
	Actor &self
	)

	{
	chase.End( self );
	}
*/

/****************************************************************************

  FindCover Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, FindCover, NULL );

ResponseDef FindCover::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )FindCover::SetArgs },
		{ NULL, NULL }
	};

void FindCover::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

PathNode *FindCover::FindCoverNode
	(
	Actor &self
	)

	{
	int i;
	PathNode	*bestnode;
	float		bestdist;
	PathNode	*desperatebestnode;
	float		desperatebestdist;
	PathNode *node;
	FindCoverPath find;
	Path		*path;
	Vector	delta;
	float		dist;
	Vector	pos;

	pos = self.worldorigin;

	bestnode = NULL;
	bestdist = 999999999; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance
	desperatebestnode = NULL;
	desperatebestdist = 999999999; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance
	for( i = 0; i <= ai_maxnode; i++ )
		{
		node = AI_GetNode( i );
		if ( node && ( node->nodeflags & ( AI_DUCK | AI_COVER ) ) &&
			( ( node->occupiedTime <= level.time ) || ( node->entnum == self.entnum ) ) )
			{
			// get the distance squared (faster than getting real distance)
			delta = node->worldorigin - pos;
			dist = delta * delta;
			if ( ( dist < bestdist ) && ( !self.CanSeeEnemyFrom( node->worldorigin ) ||//) )//||
				( ( node->nodeflags & AI_DUCK ) && !self.CanSeeEnemyFrom( node->worldorigin - Vector( 0, 0, 32 ) ) ) ) )
				{
				bestnode = node;
				bestdist = dist;
				}
			else if ( ( dist < desperatebestdist ) && ( desperatebestdist > ( 64 * 64 ) ) )
				{
				desperatebestnode = node;
				desperatebestdist = dist;
				}
			}
		}

	if ( !bestnode )
		{
		bestnode = desperatebestnode;
		}

	if ( bestnode )
		{
		find.heuristic.self = &self;
		find.heuristic.setSize( self.size );
		find.heuristic.entnum = self.entnum;

		path = find.FindPath( self.worldorigin, bestnode->worldorigin );
		if ( path )
			{
			node = path->End();

			// Mark node as occupied for a short time
			node->occupiedTime = level.time + 1.5;
			node->entnum = self.entnum;

			chase.SetGoal( node );
			chase.SetPath( path );

			return node;
			}
		}

	return NULL;
	}

void FindCover::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nchase:\n" );
   chase.ShowInfo( self );

   gi.printf( "\nstate: %d\n", state );
   gi.printf( "anim: %s\n", anim.c_str() );
   gi.printf( "nextsearch: %f\n", nextsearch );
	}

void FindCover::Begin
	(
	Actor &self
	)

	{
	if ( !anim.length() )
		{
		anim = "run";
		}

	movegoal = NULL;
	state = 0;
	}

qboolean	FindCover::Evaluate
	(
	Actor &self
	)

	{
	if ( !movegoal )
		{
		state = 0;
		}

	switch( state )
		{
		case 0 :
			// Checking for cover
			chase.Begin( self );
			movegoal = FindCoverNode( self );
			if ( !movegoal )
				{
            // Couldn't find any!
				return false;
				}

			// Found cover, going to it
			if ( anim.length() && ( anim != self.animname ) )
				{
				self.SetAnim( anim );
				}

			state = 1;
			nextsearch = level.time + 1;

		case 1 :
			if ( chase.Evaluate( self ) )
				{
				if ( nextsearch < level.time )
					{
					state = 0;
					}
				return true;
				}

			// Reached cover
			if ( self.CanSeeEnemyFrom( self.worldorigin ) )
				{
				state = 0;
				}

			if ( movegoal->nodeflags & AI_DUCK )
				{
            // ducking
				self.SetAnim( "crouch_down" );
				}
			else
				{
            // standing
				self.SetAnim( "idle" );
				}

			chase.End( self );
			return false;
			break;
		}

	return true;
	}

void FindCover::End
	(
	Actor &self
	)

	{
	chase.End( self );
	}

/****************************************************************************

  FindFlee Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, FindFlee, NULL );

ResponseDef FindFlee::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )FindFlee::SetArgs },
		{ NULL, NULL }
	};

void FindFlee::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

PathNode *FindFlee::FindFleeNode
	(
	Actor &self
	)

	{
	int i;
	PathNode	*bestnode;
	float		bestdist;
	PathNode	*desperatebestnode;
	float		desperatebestdist;
	PathNode *node;
	FindFleePath find;
	Path		*path;
	Vector	delta;
	float		dist;
	Vector	pos;

	pos = self.worldorigin;

	bestnode = NULL;
	bestdist = 999999999; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance
	desperatebestnode = NULL;
	desperatebestdist = 999999999; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance
	for( i = 0; i <= ai_maxnode; i++ )
		{
		node = AI_GetNode( i );
		if ( node && ( node->nodeflags & AI_FLEE ) &&
			( ( node->occupiedTime <= level.time ) || ( node->entnum == self.entnum ) ) )
			{
			// get the distance squared (faster than getting real distance)
			delta = node->worldorigin - pos;
			dist = delta * delta;
			if ( ( dist < bestdist ) && !self.CanSeeEnemyFrom( node->worldorigin ) )
				{
				bestnode = node;
				bestdist = dist;
				}
			else if ( ( dist < desperatebestdist ) && ( desperatebestdist > ( 64 * 64 ) ) )
				{
				desperatebestnode = node;
				desperatebestdist = dist;
				}
			}
		}

	if ( !bestnode )
		{
		bestnode = desperatebestnode;
		}

	if ( bestnode )
		{
		find.heuristic.self = &self;
		find.heuristic.setSize( self.size );
		find.heuristic.entnum = self.entnum;

		path = find.FindPath( self.worldorigin, bestnode->worldorigin );
		if ( path )
			{
			node = path->End();

			// Mark node as occupied for a short time
			node->occupiedTime = level.time + 1.5;
			node->entnum = self.entnum;

			chase.SetGoal( node );
			chase.SetPath( path );

			return node;
			}
		}

	return NULL;
	}

void FindFlee::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nchase:\n" );
   chase.ShowInfo( self );

   gi.printf( "\nstate: %d\n", state );
   gi.printf( "anim: %s\n", anim.c_str() );
   gi.printf( "nextsearch: %f\n", nextsearch );
	}

void FindFlee::Begin
	(
	Actor &self
	)

	{
	if ( !anim.length() )
		{
		anim = "run";
		}

	movegoal = NULL;
	state = 0;
	}

qboolean	FindFlee::Evaluate
	(
	Actor &self
	)

	{
	if ( !movegoal )
		{
		state = 0;
		}

	switch( state )
		{
		case 0 :
         // Checking for flee node
			chase.Begin( self );
			movegoal = FindFleeNode( self );
			if ( !movegoal )
				{
            // Couldn't find any!
				return false;
				}

         // Found flee node, going to it
			if ( anim.length() && ( anim != self.animname ) )
				{
				self.SetAnim( anim );
				}

			state = 1;
			nextsearch = level.time + 1;

		case 1 :
			if ( chase.Evaluate( self ) )
				{
				if ( nextsearch < level.time )
					{
					state = 0;
					}
				return true;
				}

         // Reached cover
			if ( self.CanSeeEnemyFrom( self.worldorigin ) )
				{
				state = 0;
				}
         else
            {
            // standing
	   		self.SetAnim( "idle" );
		   	chase.End( self );
			   return false;
            }
			break;
		}

	return true;
	}

void FindFlee::End
	(
	Actor &self
	)

	{
	chase.End( self );
	}

/****************************************************************************

  FindEnemy Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, FindEnemy, NULL );

ResponseDef FindEnemy::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )FindEnemy::SetArgs },
		{ NULL, NULL }
	};

void FindEnemy::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

void FindEnemy::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nchase:\n" );
   chase.ShowInfo( self );

   gi.printf( "\nstate: %d\n", state );
   gi.printf( "nextsearch: %f\n", nextsearch );
   gi.printf( "anim: %s\n", anim.c_str() );
	}

void FindEnemy::Begin
	(
	Actor &self
	)

	{
	if ( !anim.length() )
		{
		anim = "run";
		}

	movegoal = NULL;
   lastSearchNode = NULL;
	state = 0;
	}

PathNode *FindEnemy::FindClosestSightNode
	(
	Actor &self
	)

	{
	int i;
	PathNode	*bestnode;
	float		bestdist;
	PathNode *node;
	Vector	delta;
	float		dist;
	Vector	pos;

   if ( self.currentEnemy )
      {
	   pos = self.currentEnemy->worldorigin;
      }
   else
      {
	   pos = self.worldorigin;
      }

	bestnode = NULL;
	bestdist = 999999999; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance
	for( i = 0; i <= ai_maxnode; i++ )
		{
		node = AI_GetNode( i );
		if ( node && ( ( node->occupiedTime <= level.time ) || ( node->entnum != self.entnum ) ) )
			{
			// get the distance squared (faster than getting real distance)
			delta = node->worldorigin - pos;
			dist = delta * delta;
			if ( ( dist < bestdist ) && self.CanSeeFrom( node->worldorigin, self.currentEnemy ) )
				{
				bestnode = node;
				bestdist = dist;
				}
			}
		}

	return bestnode;
	}

qboolean	FindEnemy::Evaluate
	(
	Actor &self
	)

	{
	if ( !self.currentEnemy )
		{
		return false;
		}

	if ( nextsearch < level.time )
		{
      // check if we should search for the first time
      if ( !lastSearchNode )
         {
         //gi.dprintf( "%d: %s(%d) first time\n", level.framenum, self.targetname.c_str(), self.entnum );
         state = 0;
         }
      else
         {
         // search less often if we're far away
			nextsearch = self.DistanceTo( self.currentEnemy ) * ( 1.0 / 512.0 );
         if ( nextsearch < 1 )
            {
            nextsearch = 1;
            }
         nextsearch += level.time;

         // don't search again if our enemy hasn't moved very far
         if ( !self.currentEnemy->WithinDistance( lastSearchPos, 256 ) )
            {
            //gi.dprintf( "%d: %s(%d) searching again\n", level.framenum, self.targetname.c_str(), self.entnum );
            state = 0;
            }
         }
		}

	switch( state )
		{
		case 0 :
			// Searching for enemy
			chase.Begin( self );
         lastSearchPos = self.currentEnemy->worldorigin;
         movegoal = PathManager.NearestNode( lastSearchPos, &self );
         if ( !movegoal )
            {
            movegoal = PathManager.NearestNode( lastSearchPos, &self, false );
            }
         lastSearchNode = movegoal;
			if ( movegoal )
				{
				Path *path;
				FindEnemyPath find;
            PathNode *from;

				find.heuristic.self = &self;
				find.heuristic.setSize( self.size );
				find.heuristic.entnum = self.entnum;

            from = PathManager.NearestNode( self.worldorigin, &self );
            if ( ( from == movegoal ) && ( self.DistanceTo( from->worldorigin ) < 8 ) )
               {
               movegoal = NULL;
               from = NULL;
               }
            
            if ( from )
               {
               path = find.FindPath( from, movegoal );
				   if ( path )
					   {
					   chase.SetGoal( movegoal );
					   chase.SetPath( path );
					   }
               else
                  {
                  movegoal = NULL;
                  }
               }
				}

			if ( !movegoal )
				{
            if ( self.CanSee( self.currentEnemy ) || ( !self.currentEnemy->groundentity && !self.waterlevel ) )
               {
					chase.SetGoalPos( self.currentEnemy->worldorigin );
               }
            else
               {
				   // Couldn't find enemy
               // since we can't reach em
               // clear out enemy state
               //
	            self.ClearEnemies();
				   return false;
               }
				}

         // Found enemy, going to it
			if ( anim.length() && ( anim != self.animname ) )
				{
				self.SetAnim( anim );
				}

			state = 1;

		case 1 :
			if ( self.CanShoot( self.currentEnemy, false ) )
				{
				// Reached enemy
				chase.End( self );
				return false;
				}

			if ( !chase.Evaluate( self ) )
				{
            state = 0;
				nextsearch = 0;
				}
			break;
		}

	return true;
	}

void FindEnemy::End
	(
	Actor &self
	)

	{
	chase.End( self );
	}

/****************************************************************************

  Hide Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Hide, NULL );

ResponseDef Hide::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Hide::SetArgs },
		{ NULL, NULL }
	};

void Hide::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

void Hide::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nhide:\n" );
   hide.ShowInfo( self );

   gi.printf( "\nanim: %s\n", anim.c_str() );
   gi.printf( "state: %d\n", state );
   gi.printf( "checktime: %f\n", checktime );
	}

void Hide::Begin
	(
	Actor &self
	)

	{
	if ( !anim.length() )
		{
		anim = "run";
		}

	state = 0;
	}

qboolean	Hide::Evaluate
	(
	Actor &self
	)

	{
	switch( state )
		{
		// init run for cover
		case 0 :
			state = 1;
			hide.Begin( self );
			if ( hide.Evaluate( self ) && anim.length() )
		      {
		      self.SetAnim( anim );
		      }
         else
            {
				hide.End( self );
            self.SetAnim( "crouch_down" );
				state = 2;
				checktime = level.time + 1;
            }
         break;


		// running for cover
		case 1 :
			if ( !hide.Evaluate( self ) )
				{
				hide.End( self );
            self.SetAnim( "crouch_down" );
				state = 2;
				checktime = level.time + 1;
				}
			break;

		// wait for a bit
		case 2 :
			if ( checktime < level.time )
				{
				checktime = level.time + 0.5f;
				if ( self.CanSeeEnemyFrom( self.worldorigin ) )
					{
			      hide.Begin( self );
			      if ( hide.Evaluate( self ) && anim.length() )
		            {
		            self.SetAnim( anim );
   			      state = 1;
		            }
               else
                  {
				      hide.End( self );
      				checktime = level.time + 2;
                  }
					}
				}
			break;
		}

	return true;
	}

void Hide::End
	(
	Actor &self
	)

	{
	hide.End( self );
   self.SetAnim( "crouch_down" );
	}

/****************************************************************************

  FleeAndRemove Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, FleeAndRemove, NULL );

ResponseDef FleeAndRemove::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )FleeAndRemove::SetArgs },
		{ NULL, NULL }
	};

void FleeAndRemove::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

void FleeAndRemove::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nfindflee:\n" );
   flee.ShowInfo( self );

   gi.printf( "\nanim: %s\n", anim.c_str() );
   gi.printf( "state: %d\n", state );
   gi.printf( "checktime: %f\n", checktime );
	}

void FleeAndRemove::Begin
	(
	Actor &self
	)

	{
	if ( !anim.length() )
		{
		anim = "run";
		}

	state = 0;
	}

qboolean	FleeAndRemove::Evaluate
	(
	Actor &self
	)

	{
   if ( !self.currentEnemy )
      {
      return false;
      }

	switch( state )
		{
		// init run for cover
		case 0 :
			state = 1;

	      if ( anim.length() )
		      {
		      self.SetAnim( anim );
		      }

			flee.Begin( self );

         // if we fail on the first try, probably can't flee.
			if ( !flee.Evaluate( self ) )
            {
            flee.End( self );
            return false;
            }
         return true;
         
		// running for cover
		case 1 :
			if ( !flee.Evaluate( self ) )
				{
				flee.End( self );
				state = 2;
				checktime = 0;
            self.SetAnim( "crouch_down" );
				}
			break;

		// wait for a bit
		case 2 :
			if ( checktime < level.time )
				{
				checktime = level.time + 1;
				if ( self.CanSeeEnemyFrom( self.worldorigin ) )
					{
					state = 0;
					}
            else
               {
         		self.PostEvent( EV_Remove, 0 );
               }
				}
			break;
		}

	return true;
	}

void FleeAndRemove::End
	(
	Actor &self
	)

	{
	flee.End( self );
   self.SetAnim( "crouch_down" );
	}

/****************************************************************************

  AimAndShoot Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, AimAndShoot, NULL );

ResponseDef AimAndShoot::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )AimAndShoot::SetArgs },
		{ &EV_Behavior_AnimDone,	( Response )AimAndShoot::AnimDone },
		{ NULL, NULL }
	};

AimAndShoot::AimAndShoot()
	{
	maxshots = 1;
	numshots = 0;
	}

void AimAndShoot::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\naim:\n" );
   aim.ShowInfo( self );

   gi.printf( "\nmode: %d\n", mode );
   gi.printf( "maxshots: %d\n", maxshots );
   gi.printf( "numshots: %d\n", numshots );
   gi.printf( "animdone: %d\n", animdone );
	}

void AimAndShoot::Begin
	(
	Actor &self
	)

	{
   enemy_health = 0;
	mode = 0;
	animdone = false;

   readyfireanim = animprefix + "readyfire";
   if ( !self.HasAnim( readyfireanim.c_str() ) )
		{
      readyfireanim = "";
      }

   aimanim = animprefix + "aim";
   if ( !self.HasAnim( aimanim.c_str() ) )
		{
      aimanim = "";
      }

   fireanim = animprefix + "fire";
   if ( !self.HasAnim( fireanim.c_str() ) )
		{
      fireanim = "";
      }
   }

void AimAndShoot::SetMaxShots
	(
	int num
	)

	{
	maxshots = (num>>1) + G_Random( num );
	}

void AimAndShoot::SetArgs
	(
	Event *ev
	)

	{
	SetMaxShots( ev->GetInteger( 2 ) );
   if ( ev->NumArgs() > 2 )
      {
      animprefix = ev->GetString( 3 );
      }
	}

void AimAndShoot::AnimDone
	(
	Event *ev
	)

	{
	animdone = true;
	}

qboolean	AimAndShoot::Evaluate
	(
	Actor &self
	)

	{
	switch( mode )
		{
		case 0 :
         if ( !self.currentEnemy )
            {
            return false;
            }

			if ( !self.CanShoot( self.currentEnemy, false ) )
            {
            return false;
            }

         if ( readyfireanim.length() )
				{
				animdone = false;
				self.SetAnim( readyfireanim.c_str(), EV_Actor_NotifyBehavior );
				aim.Begin( self );
				mode = 1;
				}
			else
				{
				// skip the ready fire animation
				animdone = true;
				}

		case 1 :
			// readying gun
			if ( !animdone )
				{
				aim.SetTarget( self.currentEnemy );
				aim.Evaluate( self );
				break;
				}
			// start Aiming
			animdone = false;
         if ( aimanim.length() )
            {
			   self.SetAnim( aimanim.c_str() );
            }
         // 
         // save off time, in case we aim for too long
         //
         aim_time = level.time + 1;
			mode = 2;

		case 2 :
			// Aiming
         if ( !self.currentEnemy )
            {
            return false;
            }
         //
         // see if we aimed for too long
         //
         if ( aim_time < level.time )
            {
            return false;
            }

			aim.SetTarget( self.currentEnemy );
			aim.Evaluate( self );

			// don't go into our firing animation until our weapon is ready, and we are on target
			if ( self.WeaponReady() && self.currentEnemy && self.CanShoot( self.currentEnemy, true ) )
				{
				animdone = false;
				self.Chatter( "snd_inmysights", 5 );
				self.SetAnim( fireanim.c_str(), EV_Actor_NotifyBehavior );
            enemy_health = self.currentEnemy->health;
				mode = 3;
				}
			else if ( !self.currentEnemy || self.currentEnemy->deadflag ||
				( self.currentEnemy->health <= 0 ) || !self.CanShoot( self.currentEnemy, false ) )
				{
				// either our enemy is dead, or we can't shoot the enemy from here
				if ( self.CurrentWeapon() )
					{
					self.CurrentWeapon()->ForceReload();
					}
				return false;
				}
			break;

		case 3 :
			// Fire
			aim.SetTarget( self.currentEnemy );
			aim.Evaluate( self );
			if ( animdone )
				{
            if ( !self.currentEnemy || ( self.currentEnemy->health < enemy_health ) )
					self.Chatter( "snd_attacktaunt", 4 );
            else
					self.Chatter( "snd_missed", 7 );

				animdone = false;
				numshots++;

				if ( ( numshots >= maxshots ) || !self.currentEnemy || self.currentEnemy->deadflag ||
					( self.currentEnemy->health <= 0 ) || !self.CanShoot( self.currentEnemy, false ) )
					{
					// either we're out of shots, our enemy is dead, or we can't shoot the enemy from here
					if ( self.CurrentWeapon() )
						{
						self.CurrentWeapon()->ForceReload();
						}
					return false;
					}
				else if ( !self.WeaponReady() || !self.CanShoot( self.currentEnemy, false ) )
					{
					// weapon not ready or not aimed at enemy, so just keep trying to get enemy in our sights
               if ( aimanim.length() )
                  {
		   			self.SetAnim( aimanim.c_str() );
                  }
               // 
               // save off time, in case we aim for too long
               //
               aim_time = level.time + 1;
					mode = 2;
					}
				else
					{
					// keep firing
					self.SetAnim( fireanim.c_str(), EV_Actor_NotifyBehavior );
               enemy_health = self.currentEnemy->health;
					}
				}
			break;
		}

	return true;
	}

void AimAndShoot::End
	(
	Actor &self
	)

	{
	aim.End( self );
	self.SetAnim( "idle" );
	}

/****************************************************************************

  AimAndMelee Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, AimAndMelee, NULL );

ResponseDef AimAndMelee::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )AimAndMelee::SetArgs },
		{ &EV_Behavior_AnimDone,	( Response )AimAndMelee::AnimDone },
		{ NULL, NULL }
	};

AimAndMelee::AimAndMelee()
	{
	}

void AimAndMelee::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\naim:\n" );
   aim.ShowInfo( self );

   gi.printf( "\nmode: %d\n", mode );
   gi.printf( "maxshots: %d\n", maxshots );
   gi.printf( "numshots: %d\n", numshots );
   gi.printf( "animdone: %d\n", animdone );
	}

void AimAndMelee::Begin
	(
	Actor &self
	)

	{
	mode = 0;
   numshots = 0;
	maxshots = 2 + G_Random( 4 );
	animdone = false;
	}

void AimAndMelee::SetArgs
	(
	Event *ev
	)

	{
   int num;

	num = ev->GetInteger( 2 );
	maxshots = (num>>1) + G_Random( num );
	}

void AimAndMelee::AnimDone
	(
	Event *ev
	)

	{
	animdone = true;
	}

qboolean	AimAndMelee::Evaluate
	(
	Actor &self
	)

	{
   float r;
   Vector delta;

	switch( mode )
		{
		case 0 :
         if ( !self.has_melee || !self.currentEnemy )
            {
            return false;
            }

         delta = self.centroid - self.currentEnemy->centroid;
	      r = delta.length();
         if ( r > self.melee_range )
            {
            return false;
            }

			aim.SetTarget( self.currentEnemy );
         if ( aim.Evaluate( self ) )
            {
            break;
            }
         numshots++;
         animdone = false;
			// melee
			self.SetAnim( "melee", EV_Actor_NotifyBehavior );
			self.Chatter( "snd_attacktaunt", 4 );

         mode = 1;

		case 1 :
			// finish up the attack
			if ( animdone )
				{
            if ( numshots < maxshots )
               {
               mode = 0;
               }
            else
               {
				   return false;
               }
				}
			break;
		}

	return true;
	}

void AimAndMelee::End
	(
	Actor &self
	)

	{
	aim.End( self );
	self.SetAnim( "idle" );
	}

/****************************************************************************

  Melee Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Melee, NULL );

ResponseDef Melee::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Melee::SetArgs },
		{ &EV_Behavior_AnimDone,	( Response )Melee::AnimDone },
		{ NULL, NULL }
	};

Melee::Melee()
	{
	}

void Melee::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nmode: %d\n", mode );
   gi.printf( "animdone: %d\n", animdone );
	}

void Melee::Begin
	(
	Actor &self
	)

	{
	mode = 0;
	animdone = false;
	}

void Melee::SetArgs
	(
	Event *ev
	)

	{
	}

void Melee::AnimDone
	(
	Event *ev
	)

	{
	animdone = true;
	}

qboolean	Melee::Evaluate
	(
	Actor &self
	)

	{
   float r;
   Vector delta;
   Vector ang;

	switch( mode )
		{
		case 0 :
         if ( !self.has_melee || !self.currentEnemy )
            {
            return false;
            }

         delta = self.currentEnemy->worldorigin - self.worldorigin;
	      r = delta.length();
         if ( r > self.melee_range )
            {
            return false;
            }
         animdone = false;
			// melee
         ang = delta.toAngles();
         ang[ PITCH ] = -ang[ PITCH ];
         self.setAngles( ang );
			self.SetAnim( "melee", EV_Actor_NotifyBehavior );
			self.Chatter( "snd_attacktaunt", 4 );

         mode = 1;

		case 1 :
			// finsh up the attack
			if ( animdone )
				{
				return false;
				}
			break;
		}

	return true;
	}

void Melee::End
	(
	Actor &self
	)

	{
	self.SetAnim( "idle" );
	}

/****************************************************************************

  Repel Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Repel, NULL );

ResponseDef Repel::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Repel::SetArgs },
		{ NULL, NULL }
	};

void Repel::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	movegoal = AI_FindNode( ev->GetString( 3 ) );
	if ( movegoal )
		{
		goal = movegoal->worldorigin;
		}
	speed = ev->GetFloat( 4 );
	}

void Repel::ShowInfo
	(
	Actor &self
	)
		
	{
   Behavior::ShowInfo( self );

   gi.printf( "\nanim: %s\n", anim.c_str() );

   gi.printf( "dist: %f\n", dist );
   gi.printf( "len: %f\n", len );
   gi.printf( "speed: %f\n", speed );
   gi.printf( "goal: ( %f, %f, %f )\n", goal.x, goal.y, goal.z );
   gi.printf( "start: ( %f, %f, %f )\n", start.x, start.y, start.z );
   gi.printf( "dir: ( %f, %f, %f )\n", dir.x, dir.y, dir.z );
	}

void Repel::Begin
	(
	Actor &self
	)

	{
	start.x = goal.x;
	start.y = goal.y;
	start.z = self.worldorigin.z;
	dir = goal - start;
	len = dir.length();
	dir *= 1 / len;

	if ( speed <= 0 )
		{
		speed = 32;
		}

	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
	dist = -1;
	}

qboolean	Repel::Evaluate
	(
	Actor &self
	)

	{
	Vector pos;
	qboolean done;
	float sp;
	trace_t trace;

	done = false;

	// this is silly, but it works
	if ( dist < 0 )
		{
		sp = 0;
		}
	else if ( dist < 32 )
		{
		sp = dist * speed / 32;
		}
	else if ( ( len - dist ) < 32 )
		{
		sp = ( len - dist ) * speed / 32;
		}
	else
		{
		sp = speed;
		}

	pos = start + dir * ( dist + sp );
	dist = ( pos - start ) * dir;
	if ( dist >= (len-1) )
		{
		pos = goal;
		done = true;
		}
	else if ( dist < 1 )
		{
		dist = 1;
		}

	trace = G_Trace( self.worldorigin, self.mins, self.maxs, pos, &self, self.edict->clipmask, "Repel" );
	self.setOrigin( trace.endpos );

	return !done;
	}

void Repel::End
	(
	Actor &self
	)

	{
	self.SetAnim( "idle" );
	}

/****************************************************************************

  Pickup Behavior Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, PickupAndThrow, NULL );

Event EV_PickupAndThrow_Pickup( "pickup" );
Event EV_PickupAndThrow_Throw( "throw" );

ResponseDef PickupAndThrow::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )PickupAndThrow::SetArgs },
		{ &EV_Behavior_AnimDone,	( Response )PickupAndThrow::AnimDone },
		{ &EV_PickupAndThrow_Pickup,( Response )PickupAndThrow::Pickup },
		{ &EV_PickupAndThrow_Throw,( Response )PickupAndThrow::Throw },
		{ NULL, NULL }
	};

PickupAndThrow::PickupAndThrow()
	{
	}

void PickupAndThrow::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\naim:\n" );
   aim.ShowInfo( self );

   gi.printf( "\nmode: %d\n", mode );
   gi.printf( "animdone: %d\n", animdone );

   if ( pickup_target )
      {
      gi.printf( "\npickup_target: #%d '%s'\n", pickup_target->entnum, pickup_target->targetname.c_str() );
      }
   else
      {
      gi.printf( "\npickup_target: NULL\n" );
      }
	}

void PickupAndThrow::Begin
	(
	Actor &self
	)

	{
	mode = 0;
	animdone = false;
	}

void PickupAndThrow::SetArgs
	(
	Event *ev
	)

	{
   pickup_target = ev->GetEntity( 2 );
	}

void PickupAndThrow::AnimDone
	(
	Event *ev
	)

	{
	animdone = true;
	}

void PickupAndThrow::Pickup
	(
	Event *ev
	)

	{
   Entity * ent;
   Event * e;

   ent = ev->GetEntity( 1 );
   if ( pickup_target )
      {
      e = new Event( EV_ThrowObject_Pickup );
      e->AddEntity( ent );
      e->AddString( ev->GetString( 2 ) );
      pickup_target->ProcessEvent( e );
      }
	}

void PickupAndThrow::Throw
	(
	Event *ev
	)

	{
   Actor * act;
   Event * e;

   act = (Actor *)ev->GetEntity( 1 );
   if ( pickup_target )
      {
      if ( !act->currentEnemy )
         return;
      e = new Event( EV_ThrowObject_Throw );
      e->AddEntity( act );
      e->AddFloat( 500 );
      e->AddEntity( act->currentEnemy );
      e->AddFloat( 1 );
      pickup_target->ProcessEvent( e );
      }
	}

qboolean	PickupAndThrow::Evaluate
	(
	Actor &self
	)

	{
   Event * ev;

   if ( !self.currentEnemy || !pickup_target )
      return false;

	switch( mode )
		{
		case 0 :
			if ( self.HasAnim( "pickup" ) )
				{
				animdone = false;
				self.SetAnim( "pickup", EV_Actor_NotifyBehavior );
				mode = 1;
				}
			else
				{
				// skip the pickup animation
         	ev = new Event( EV_PickupAndThrow_Pickup );
            ev->AddEntity( &self );
            ev->AddString( "gun" );
            ProcessEvent( ev );
				animdone = true;
				}

		case 1 :
         if ( !animdone )
            break;
			aim.Begin( self );
         mode = 2;
			if ( self.HasAnim( "throw_aim" ) )
				{
				self.SetAnim( "throw_aim", EV_Actor_NotifyBehavior );
				}
         else
            {
				self.SetAnim( "idle" );
            }

		case 2 :
         // aim towards our target
			aim.SetTarget( self.currentEnemy );
         if ( aim.Evaluate( self ) )
            {
            break;
            }
         mode = 3;

		case 3 :
			// Throwing
         mode = 4;
			if ( self.HasAnim( "throw" ) )
				{
				animdone = false;
				self.SetAnim( "throw", EV_Actor_NotifyBehavior );
				mode = 4;
				}
			else
				{
				// skip the pickup animation
         	ev = new Event( EV_PickupAndThrow_Throw );
            ev->AddEntity( &self );
            ProcessEvent( ev );
				animdone = true;
				}
			break;

		case 4 :
         if ( !animdone )
            break;
         return false;
         break;
		}

	return true;
	}

void PickupAndThrow::End
	(
	Actor &self
	)

	{
	aim.End( self );
	self.SetAnim( "idle" );
	}

/****************************************************************************

  Jump Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Jump, NULL );

ResponseDef Jump::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Jump::SetArgs },
		{ &EV_Behavior_AnimDone,	( Response )Jump::AnimDone },
		{ NULL, NULL }
	};

Jump::Jump()
	{
	endtime = 0;
	speed = 200;
   state = 0;
	}

void Jump::AnimDone
	(
	Event *ev
	)

	{
	animdone = true;
	}

void Jump::SetArgs
	(
	Event *ev
	)

	{
	anim		= ev->GetString( 2 );

   //
   // see if it is an entity first 
   //
  	movegoal = AI_FindNode( ev->GetString( 3 ) );
   if ( movegoal )
      {
      goal = movegoal->worldorigin;
      }
   else
      {
   	Entity *ent;

   	ent = ev->GetEntity( 3 );
      if ( ent )
         {
         goal = ent->worldorigin;
         }
      else
         {
         gi.dprintf("Jump::SetArgs invalid target %s", ev->GetString( 3 ) );
         }
      }

   if ( ev->NumArgs() >= 4 )
	   speed	= ev->GetFloat( 4 );
   else
      speed = 0;
	}

void Jump::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nendtime: %f\n", endtime );
   gi.printf( "speed: %f\n", speed );
   gi.printf( "state: %d\n", state );
   gi.printf( "animdone: %d\n", animdone );
   gi.printf( "anim: %s\n", anim.c_str() );
	}

void Jump::Begin
	(
	Actor &self
	)

	{
   float traveltime;

	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
   traveltime = self.JumpTo( goal, speed );
	endtime = traveltime + level.time;

   self.last_jump_time = endtime;

   state = 0;
	}

qboolean	Jump::Evaluate
	(
	Actor &self
	)

	{
   switch( state )
      {
      case 0:
         state = 1;
         // this is here so that we at least hit this function at least once
         // this gaves the character the chance to leave the ground, nulling out
         // self.groundentity
         break;
      case 1:
         //
         // wait for the character to hit the ground
         //
	      if ( self.groundentity )
            {
            state = 2;
            //
            // if we have an anim, we go to state 3
            //
            if ( self.HasAnim( "land" ) )
               {
               animdone = false;
         		self.SetAnim( "land", EV_Actor_NotifyBehavior );
               state = 3;
               }
            else
               {
               return false;
               }
            }
         break;
      case 2:
         // 
         // we are on the ground and waiting to timeout
         //
	      if ( level.time > endtime )
	         return false;
         break;
      case 3:
         //
         // we are on the ground and waiting for our landing animation to finish
         //
         if ( animdone )
            {
            return false;
            }
         break;
      }

	return true;
	}

void Jump::End
	(
	Actor &self
	)

	{
	//turn.End( self );
	self.SetAnim( "idle" );
	}

/****************************************************************************

  StrafeAttack Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, StrafeAttack, NULL );

ResponseDef StrafeAttack::Responses[] =
	{
		{ NULL, NULL }
	};

void StrafeAttack::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nturn:\n" );
   turn.ShowInfo( self );

   gi.printf( "\nstate: %d\n", state );
	}

void StrafeAttack::Begin
	(
	Actor &self
	)

	{
	state = 0;
	}

qboolean	StrafeAttack::Evaluate
	(
	Actor &self
	)

	{
	int num;
	Vector delta;
	Vector left;
	Vector pos;

	if ( !self.currentEnemy )
		{
		return false;
		}

	switch( state )
		{
		case 0 :
			delta = self.currentEnemy->worldorigin - self.worldorigin;
			turn.SetDirection( delta.toYaw() );
			turn.Begin( self );
			state = 1;
			break;

		case 1 :
			if ( turn.Evaluate( self ) )
				{
				return true;
				}

			turn.End( self );
			state = 2;

		case 2 :
			delta = self.currentEnemy->worldorigin - self.worldorigin;
			left.x = -delta.y;
			left.y = delta.x;
			left.normalize();

         if ( G_Random( 10 ) < 5 )
            {
			   num = gi.Anim_Random( self.edict->s.modelindex, "step_left" );
			   if ( num != -1 )
				   {
				   gi.Anim_Delta( self.edict->s.modelindex, num, delta.vec3() );
               delta *= self.edict->s.scale;
				   pos = self.worldorigin + left * delta.length();
				   if ( self.CanMoveTo( pos ) && self.CanShootFrom( pos, self.currentEnemy, false ) )
					   {
					   self.SetAnim( "step_left", EV_Actor_FinishedBehavior );
					   state = 3;
					   return true;
					   }
				   }
			   
			   num = gi.Anim_Random( self.edict->s.modelindex, "step_right" );
			   if ( num != -1 )
				   {
				   gi.Anim_Delta( self.edict->s.modelindex, num, delta.vec3() );
               delta *= self.edict->s.scale;
				   pos = self.worldorigin - left * delta.length();
				   if ( self.CanMoveTo( pos ) && self.CanShootFrom( pos, self.currentEnemy, false ) )
					   {
					   self.SetAnim( "step_right", EV_Actor_FinishedBehavior );
					   state = 3;
					   return true;
					   }
				   }
            }
         else
            {
			   num = gi.Anim_Random( self.edict->s.modelindex, "step_right" );
			   if ( num != -1 )
				   {
				   gi.Anim_Delta( self.edict->s.modelindex, num, delta.vec3() );
               delta *= self.edict->s.scale;
				   pos = self.worldorigin - left * delta.length();
				   if ( self.CanMoveTo( pos ) && self.CanShootFrom( pos, self.currentEnemy, false ) )
					   {
					   self.SetAnim( "step_right", EV_Actor_FinishedBehavior );
					   state = 3;
					   return true;
					   }
				   }

			   num = gi.Anim_Random( self.edict->s.modelindex, "step_left" );
			   if ( num != -1 )
				   {
				   gi.Anim_Delta( self.edict->s.modelindex, num, delta.vec3() );
               delta *= self.edict->s.scale;
				   pos = self.worldorigin + left * delta.length();
				   if ( self.CanMoveTo( pos ) && self.CanShootFrom( pos, self.currentEnemy, false ) )
					   {
					   self.SetAnim( "step_left", EV_Actor_FinishedBehavior );
					   state = 3;
					   return true;
					   }
				   }
			   
            }

			return false;
			break;
		}

	return true;
	}

void StrafeAttack::End
	(
	Actor &self
	)

	{
	turn.End( self );
	self.SetAnim( "idle" );
	}

/****************************************************************************

  StrafeTo Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, StrafeTo, NULL );

ResponseDef StrafeTo::Responses[] =
	{
      { &EV_Behavior_Args,			( Response )StrafeTo::SetArgs },
		{ NULL, NULL }
	};

void StrafeTo::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "goal: ( %f, %f, %f )\n", goal.x, goal.y, goal.z );
   gi.printf( "fail: %d\n", fail );
   
   gi.printf( "\nseek:\n" );
   seek.ShowInfo( self );
	}

void StrafeTo::SetArgs
	(
	Event *ev
	)

	{
   Entity *ent;

	if ( ev->IsVectorAt( 2 ) )
		{
		goal = ev->GetVector( 2 );
		}
	else
		{
		movegoal = AI_FindNode( ev->GetString( 2 ) );
		if ( movegoal )
			{
			goal = movegoal->worldorigin;
			}
		else
			{
			ent = ev->GetEntity( 2 );
         if ( ent )
            {
            goal = ent->worldorigin;
            }
			}
		}
	}

void StrafeTo::Begin
	(
	Actor &self
	)

	{
   Vector delta;
   float dot;

   seek.Begin( self );

	delta = goal - self.worldorigin;
   dot = delta * self.orientation[ 1 ];

   if ( dot < 0 )
      {
      self.SetAnim( "step_right" );
      }
   else
      {
      self.SetAnim( "step_left" );
      }

   fail = 0;
	}

qboolean	StrafeTo::Evaluate
	(
	Actor &self
	)

	{
   seek.SetMaxSpeed( self.movespeed );
   seek.SetPosition( self.worldorigin );
	seek.SetDir( self.movedir );
	seek.SetTargetPosition( goal );

	if ( !seek.Evaluate( self ) )
      {
      return false;
      }

   self.Accelerate( seek.steeringforce );

   // prevent him from trying to strafing forever if he's stuck
   if ( self.lastmove != STEPMOVE_OK )
      {
      if ( fail )
         {
         return false;
         }

      fail++;
      }
   else
      {
      fail = 0;
      }

   return true;
	}

void StrafeTo::End
	(
	Actor &self
	)

	{
	seek.End( self );
	self.SetAnim( "idle" );
	}

/****************************************************************************

  Random Direction Utility function

****************************************************************************/

Vector ChooseRandomDirection
   (
   Actor &self,
   Vector previousdir,
   float *time,
   int allowcontentsmask,
   int disallowcontentsmask,
   qboolean usepitch
   )
   {
   static float x[ 9 ] = { 0, 22, -22, 45, -45, 0, 22, -22, 45 };
   Vector dir;
   Vector ang;
   Vector bestdir;
   Vector newdir;
   Vector s;
   float bestfraction;
   trace_t trace;
   int i;
   int j;
   int k;
   int t;
   int u;
   int contents;
   qboolean checkmask;
   Vector centroid;

   centroid = self.centroid - self.worldorigin;

   checkmask = allowcontentsmask || disallowcontentsmask;

	s = Vector( 0, 0, STEPSIZE );
   bestfraction = -1;
   bestdir = self.worldorigin;
   for( i = 0; i <= 4; i++ )
      {
      t = i * 45;
      if ( rand() < 0.5 )
         {
         // sometimes we choose left first, other times right.
         t = -t;
         }
      for( j = -1; j < 2; j += 2 )
         {
         ang.y = self.worldangles.y + ( t * j );

         if ( usepitch )
            {
            u = ( int )G_Random( 5 );
            for( k = 0; k < 5; k++ )
               {
               ang.x = x[ k + u ];
               ang.AngleVectors( &dir, NULL, NULL );

               dir *= self.movespeed * (*time);
               dir += self.worldorigin;
               trace = G_Trace( self.worldorigin, self.mins, self.maxs, dir, &self, 
                  self.edict->clipmask, "ChooseRandomDirection 1" );

               if ( !trace.startsolid && !trace.allsolid )
                  {
                  newdir = Vector( trace.endpos );
                  if ( checkmask )
                     {
                     contents = gi.pointcontents( ( newdir + centroid ).vec3() );
                     if ( 
                           ( !allowcontentsmask || ( contents & allowcontentsmask ) ) &&
                           ( !disallowcontentsmask || !( contents & disallowcontentsmask ) ) &&
                           ( trace.fraction > bestfraction ) && 
                           ( newdir != bestdir ) &&
                           ( newdir != previousdir ) 
                        )

                        {
                        bestdir = newdir;
                        bestfraction = trace.fraction;
                        }
                     }
                  else 
                     {
                     if ( 
                           ( trace.fraction > bestfraction ) && 
                           ( newdir != bestdir ) &&
                           ( newdir != previousdir ) 
                        )
                        {
                        bestdir = newdir;
                        bestfraction = trace.fraction;
                        }
                     }
                  }
               }
            }
         else
            {
            ang.x = 0;
            ang.AngleVectors( &dir, NULL, NULL );

            dir *= self.movespeed * (*time);
            dir += self.worldorigin;
            dir += s;
            trace = G_Trace( self.worldorigin + s, self.mins, self.maxs, dir, &self, 
               self.edict->clipmask, "ChooseRandomDirection 2" );

            if ( !trace.startsolid && !trace.allsolid )
               {
               newdir = Vector( trace.endpos );
               if ( checkmask )
                  {
                  contents = gi.pointcontents( ( newdir + centroid ).vec3() );
                  if ( 
                        ( !allowcontentsmask || ( contents & allowcontentsmask ) ) &&
                        ( !disallowcontentsmask || !( contents & disallowcontentsmask ) ) &&
                        ( trace.fraction > bestfraction ) && 
                        ( newdir != bestdir ) &&
                        ( newdir != previousdir ) 
                     )
                     {
                     bestdir = newdir;
                     bestfraction = trace.fraction;
                     }
                  }
               else 
                  {
                  if ( 
                        ( trace.fraction > bestfraction ) && 
                        ( newdir != bestdir ) &&
                        ( newdir != previousdir ) 
                     )
                     {
                     bestdir = newdir;
                     bestfraction = trace.fraction;
                     }
                  }
               }
            }


         if ( ( i == 4 ) || ( i == 0 ) )
            {
            break;
            }
         }
      }
   if ( bestfraction > 0 )
      {
      *time *= bestfraction;
      }
   else
      {
      *time = 0;
      }

   return bestdir;
   }

/****************************************************************************

  Swim Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Swim, NULL );

ResponseDef Swim::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Swim::SetArgs },
		{ NULL, NULL }
	};

void Swim::SetArgs
	(
	Event *ev
	)

	{
   if ( ev->NumArgs() > 1 )
	   anim = ev->GetString( 2 );
	}

void Swim::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nseek:\n" );
   seek.ShowInfo( self );

   gi.printf( "\navoid:\n" );
   avoid.ShowInfo( self );
	}

void Swim::Begin
	(
	Actor &self
	)

	{
   avoidtime = 0;
   avoidvec = vec_zero;

	avoid.Begin( self );
   seek.Begin( self );
   seek.SetTargetVelocity( vec_zero );
	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
	}

qboolean	Swim::Evaluate
	(
	Actor &self
	)

	{
   if ( ( self.lastmove != STEPMOVE_OK ) || ( avoidtime <= level.time ) )
      {
      Vector dir;
      Vector ang;
      float time;

      time = 3;
      avoidvec = ChooseRandomDirection( self, avoidvec, &time, MASK_WATER, 0, true );
      avoidtime = level.time + time;
      dir = avoidvec - self.worldorigin;
      ang = dir.toAngles();
      ang[ PITCH ] = -ang[ PITCH ];
      self.setAngles( ang );
      }

   seek.SetMaxSpeed( self.movespeed );
	seek.SetPosition( self.worldorigin );
	seek.SetDir( self.movedir );
   seek.SetTargetPosition( avoidvec );
   // if we reached the goal, re-evaluate
   if ( !seek.Evaluate( self ) )
      {
      avoidtime = 0;
      }

   avoid.SetMaxSpeed( self.movespeed * 2 );
	avoid.SetPosition( self.worldorigin );
	avoid.SetDir( self.movedir );
	avoid.Evaluate( self );

   self.Accelerate( seek.steeringforce + avoid.steeringforce );

	return true;
	}

void Swim::End
	(
	Actor &self
	)

	{
	avoid.End( self );
	seek.End( self );
	}

/****************************************************************************

  SwimCloseAttack Class Definition

****************************************************************************/


CLASS_DECLARATION( Behavior, SwimCloseAttack, NULL );

ResponseDef SwimCloseAttack::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )SwimCloseAttack::SetArgs },
		{ NULL, NULL }
	};

void SwimCloseAttack::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

void SwimCloseAttack::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nseek:\n" );
   seek.ShowInfo( self );

   gi.printf( "\navoid:\n" );
   avoid.ShowInfo( self );

   gi.printf( "\navoidtime: %.2f\n", avoidtime ); 
	}

void SwimCloseAttack::Begin
	(
	Actor &self
	)

	{
   avoidtime = 0;
   avoiding = false;
   avoidvec = vec_zero;

	avoid.Begin( self );

   seek.Begin( self );
   seek.SetTargetVelocity( vec_zero );
	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
	}

qboolean	SwimCloseAttack::Evaluate
	(
	Actor &self
	)

	{
   trace_t trace;
   Vector dir;
   Vector ang;
   Vector goal;
   Vector end;

   if ( !self.currentEnemy )
      {
      return false;
      }

   //
   // close enough
   //
   if ( self.CanShoot( self.currentEnemy, false ) )
      {
      if ( self.WeaponReady() )
         return false;
      else
         return true;
      }

   if ( ( self.lastmove == STEPMOVE_STUCK ) || ( avoidtime <= level.time ) )
      {
      //
      // see if we can get to the player
      //
      dir = self.currentEnemy->worldorigin - self.worldorigin;
      dir.normalize();
      goal = self.currentEnemy->worldorigin;
      trace = G_Trace( self.worldorigin, self.mins, self.maxs, goal, &self, self.edict->clipmask, "SwimCloseAttack::Evaluate 2" );
      if ( trace.fraction < 0.5f )
         {
         float time;
         time = 2.5f;
         goal = ChooseRandomDirection( self, goal, &time, MASK_WATER, 0, true );
         avoidtime = level.time + time;
         }
      else
         {
         goal = trace.endpos;
         avoidtime = level.time + 2.5f;
         }
      dir = goal - self.worldorigin;
      dir.normalize();
      //self.movedir = dir;
      ang = dir.toAngles();
      ang[ PITCH ] = -ang[ PITCH ];
      self.setAngles( ang );
      seek.SetTargetPosition( goal );
      }

   seek.SetMaxSpeed( self.movespeed );
	seek.SetPosition( self.worldorigin );
	seek.SetDir( self.movedir );
   // if we reached the goal, re-evaluate
   if ( !seek.Evaluate( self ) )
      {
      avoidtime = 0;
      }

   avoid.SetMaxSpeed( self.movespeed * 2 );
	avoid.SetPosition( self.worldorigin );
	avoid.SetDir( self.movedir );
	avoid.Evaluate( self );

   self.Accelerate( seek.steeringforce + avoid.steeringforce );

	return true;
	}

void SwimCloseAttack::End
	(
	Actor &self
	)

	{
	seek.End( self );
	}


/****************************************************************************

  Fly Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Fly, NULL );

ResponseDef Fly::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Fly::SetArgs },
		{ NULL, NULL }
	};

void Fly::SetArgs
	(
	Event *ev
	)

	{
   if ( ev->NumArgs() > 1 )
	   anim = ev->GetString( 2 );
	}

void Fly::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nseek:\n" );
   seek.ShowInfo( self );

   gi.printf( "\navoid:\n" );
   avoid.ShowInfo( self );
	}

void Fly::Begin
	(
	Actor &self
	)

	{
   avoidtime = 0;
   avoidvec = vec_zero;

	avoid.Begin( self );
   seek.Begin( self );
   seek.SetTargetVelocity( vec_zero );
	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
	}


qboolean	Fly::Evaluate
	(
	Actor &self
	)

	{
   if ( ( self.lastmove != STEPMOVE_OK ) || ( avoidtime <= level.time ) )
      {
      Vector dir;
      Vector ang;
      float time;

      time = 3;
      avoidvec = ChooseRandomDirection( self, avoidvec, &time, 0, MASK_WATER, true );
      avoidtime = level.time + time;
      dir = avoidvec - self.worldorigin;
      ang = dir.toAngles();
      ang[ PITCH ] = -ang[ PITCH ];
      self.setAngles( ang );
      }

   seek.SetMaxSpeed( self.movespeed );
	seek.SetPosition( self.worldorigin );
	seek.SetDir( self.movedir );
   seek.SetTargetPosition( avoidvec );
   // if we reached the goal, re-evaluate
   if ( !seek.Evaluate( self ) )
      {
      avoidtime = 0;
      }

   avoid.SetMaxSpeed( self.movespeed * 2 );
	avoid.SetPosition( self.worldorigin );
	avoid.SetDir( self.movedir );
	avoid.Evaluate( self );

   self.Accelerate( seek.steeringforce + avoid.steeringforce );

	return true;
	}

void Fly::End
	(
	Actor &self
	)

	{
	avoid.End( self );
	seek.End( self );
	}

/****************************************************************************

  FlyCloseAttack Class Definition

****************************************************************************/


CLASS_DECLARATION( Behavior, FlyCloseAttack, NULL );

ResponseDef FlyCloseAttack::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )FlyCloseAttack::SetArgs },
		{ NULL, NULL }
	};

void FlyCloseAttack::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

void FlyCloseAttack::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nseek:\n" );
   seek.ShowInfo( self );

   gi.printf( "\navoid:\n" );
   avoid.ShowInfo( self );

   gi.printf( "\navoidtime: %.2f\n", avoidtime ); 
	}

void FlyCloseAttack::Begin
	(
	Actor &self
	)

	{
   avoidtime = 0;
   avoiding = false;
   avoidvec = vec_zero;

	avoid.Begin( self );

   seek.Begin( self );
   seek.SetTargetVelocity( vec_zero );
	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
	}

qboolean	FlyCloseAttack::Evaluate
	(
	Actor &self
	)

	{
   trace_t trace;
   Vector dir;
   Vector ang;
   float minrange;
   Vector goal;
   Vector end;
   Sentient *sent;

   if ( !self.currentEnemy )
      {
      return false;
      }

   if ( !self.CurrentWeapon() && self.currentEnemy->isSubclassOf( Sentient ) )
      {
      sent = ( Sentient * )( Entity * )self.currentEnemy;
      if ( sent->waterlevel > 1 )
         {
         // can no longer get to enemy
         self.ClearEnemies();
         return false;
         }
      }

   minrange = self.MinimumAttackRange();
   //
   // close enough
   //
   if ( self.CanShoot( self.currentEnemy, false ) )
      {
      if ( self.WeaponReady() )
         return false;
      else
         return true;
      }

   if ( ( self.lastmove == STEPMOVE_STUCK ) || ( avoidtime <= level.time ) )
      {
      //
      // see if we can get to the player
      //
      goal = self.currentEnemy->centroid;
      trace = G_Trace( self.worldorigin, self.mins, self.maxs, goal, &self, self.edict->clipmask, "FlyCloseAttack::Evaluate 2" );
      if ( trace.fraction < 0.5f )
         {
         float time;
         time = 2.5f;
         goal = ChooseRandomDirection( self, goal, &time, 0, MASK_WATER, true );
         avoidtime = level.time + time;
         }
      else
         {
         goal = trace.endpos;
         avoidtime = level.time + 2.5f;
         }
      dir = goal - self.worldorigin;
      dir.normalize();
      //self.movedir = dir;
      ang = dir.toAngles();
      ang[ PITCH ] = -ang[ PITCH ];
      self.setAngles( ang );
      seek.SetTargetPosition( goal );
      }

   seek.SetMaxSpeed( self.movespeed );
	seek.SetPosition( self.worldorigin );
	seek.SetDir( self.movedir );
   // if we reached the goal, re-evaluate
   if ( !seek.Evaluate( self ) )
      {
      avoidtime = 0;
      }

   avoid.SetMaxSpeed( self.movespeed * 2 );
	avoid.SetPosition( self.worldorigin );
	avoid.SetDir( self.movedir );
	avoid.Evaluate( self );

   self.Accelerate( seek.steeringforce + avoid.steeringforce );

	return true;
	}

void FlyCloseAttack::End
	(
	Actor &self
	)

	{
	seek.End( self );
	}



/****************************************************************************

  Wander Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, Wander, NULL );

ResponseDef Wander::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )Wander::SetArgs },
		{ NULL, NULL }
	};

void Wander::SetArgs
	(
	Event *ev
	)

	{
   if ( ev->NumArgs() > 1 )
	   anim = ev->GetString( 2 );
	}

void Wander::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nseek:\n" );
   seek.ShowInfo( self );

   gi.printf( "\navoid:\n" );
   avoid.ShowInfo( self );
	}

void Wander::Begin
	(
	Actor &self
	)

	{
   avoidtime = 0;
   avoidvec = vec_zero;

	avoid.Begin( self );
   seek.Begin( self );
   seek.SetTargetVelocity( vec_zero );
	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
	}


qboolean	Wander::Evaluate
	(
	Actor &self
	)

	{
   if ( ( self.lastmove != STEPMOVE_OK ) || ( avoidtime <= level.time ) )
      {
      Vector dir;
      Vector ang;
      float time;

      time = 5;
		self.Chatter( "snd_idle", 4 );
      avoidvec = ChooseRandomDirection( self, avoidvec, &time, 0, 0, false );
      avoidtime = level.time + time;
      dir = avoidvec - self.worldorigin;
      ang = dir.toAngles();
      self.angles[ YAW ] = ang[ YAW ];
      self.setAngles( self.angles );
      }

   seek.SetMaxSpeed( self.movespeed );
	seek.SetPosition( self.worldorigin );
	seek.SetDir( self.movedir );
   seek.SetTargetPosition( avoidvec );
   // if we reached the goal, re-evaluate
   if ( !seek.Evaluate( self ) )
      {
      avoidtime = 0;
      }

   avoid.SetMaxSpeed( self.movespeed * 2 );
	avoid.SetPosition( self.worldorigin );
	avoid.SetDir( self.movedir );
	avoid.Evaluate( self );

   self.Accelerate( seek.steeringforce + avoid.steeringforce );

	return true;
	}

void Wander::End
	(
	Actor &self
	)

	{
	avoid.End( self );
	seek.End( self );
	}


/****************************************************************************

  WanderCloseAttack Class Definition

****************************************************************************/


CLASS_DECLARATION( Behavior, WanderCloseAttack, NULL );

ResponseDef WanderCloseAttack::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )WanderCloseAttack::SetArgs },
		{ NULL, NULL }
	};

void WanderCloseAttack::SetArgs
	(
	Event *ev
	)

	{
	anim = ev->GetString( 2 );
	}

void WanderCloseAttack::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nseek:\n" );
   seek.ShowInfo( self );

   gi.printf( "\navoid:\n" );
   avoid.ShowInfo( self );

   gi.printf( "\navoidtime: %.2f\n", avoidtime ); 
	}

void WanderCloseAttack::Begin
	(
	Actor &self
	)

	{
   avoidtime = 0;
   avoiding = false;
   avoidvec = vec_zero;

	avoid.Begin( self );

   seek.Begin( self );
   seek.SetTargetVelocity( vec_zero );
	if ( anim.length() )
		{
		self.SetAnim( anim );
		}
	}

qboolean	WanderCloseAttack::Evaluate
	(
	Actor &self
	)

	{
   trace_t trace;
   Vector dir;
   Vector ang;
   float minrange;
   Vector goal;
   Vector end;

   if ( !self.currentEnemy )
      {
      return false;
      }

   minrange = self.MinimumAttackRange();
   //
   // close enough
   //
   if ( self.CanShoot( self.currentEnemy, false ) )
      {
      if ( self.WeaponReady() )
         return false;
      else
         return true;
      }

   if ( ( self.lastmove == STEPMOVE_STUCK ) || ( avoidtime <= level.time ) )
      {
      //
      // see if we can get to the player
      //
      dir = self.currentEnemy->worldorigin - self.worldorigin;
      dir.normalize();
      goal = self.currentEnemy->worldorigin;
      trace = G_Trace( self.worldorigin, self.mins, self.maxs, goal, &self, self.edict->clipmask, "WanderCloseAttack::Evaluate 2" );
      if ( trace.fraction < 0.5f )
         {
         float time;
         time = 2.5f;
         goal = ChooseRandomDirection( self, goal, &time, 0, 0, false );
         avoidtime = level.time + time;
         }
      else
         {
         goal = trace.endpos;
         avoidtime = level.time + 2.5f;
         }
      dir = goal - self.worldorigin;
      dir.normalize();
      //self.movedir = dir;
      ang = dir.toAngles();
      ang[ PITCH ] = -ang[ PITCH ];
      self.setAngles( ang );
      seek.SetTargetPosition( goal );
      }

   seek.SetMaxSpeed( self.movespeed );
	seek.SetPosition( self.worldorigin );
	seek.SetDir( self.movedir );
   // if we reached the goal, re-evaluate
   if ( !seek.Evaluate( self ) )
      {
      avoidtime = 0;
      }

   avoid.SetMaxSpeed( self.movespeed * 2 );
	avoid.SetPosition( self.worldorigin );
	avoid.SetDir( self.movedir );
	avoid.Evaluate( self );

   self.Accelerate( seek.steeringforce + avoid.steeringforce );

	return true;
	}

void WanderCloseAttack::End
	(
	Actor &self
	)

	{
	seek.End( self );
	}

/****************************************************************************

  GetCloseToEnemy Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, GetCloseToEnemy, NULL );

ResponseDef GetCloseToEnemy::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )GetCloseToEnemy::SetArgs },
		{ NULL, NULL }
	};

GetCloseToEnemy::GetCloseToEnemy()
   {
   howclose = 32;
   }

void GetCloseToEnemy::SetArgs
	(
	Event *ev
	)

	{
   int num;

   num = 2;
   if ( !ev->IsNumericAt( num ) )
      {
	   anim = ev->GetString( num );
      num++;
      }

   howclose = 32;
   if ( ev->IsNumericAt( num ) )
      {
      howclose = ev->GetFloat( num );
      num++;
      }
	}

void GetCloseToEnemy::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\nchase:\n" );
   chase.ShowInfo( self );

   gi.printf( "\nstate: %d\n", state );
   gi.printf( "howclose: %f\n", howclose );
   gi.printf( "nextsearch: %f\n", nextsearch );
   gi.printf( "anim: %s\n", anim.c_str() );
	}

void GetCloseToEnemy::Begin
	(
	Actor &self
	)

	{
	if ( !anim.length() )
		{
		anim = "run";
		}

	movegoal = NULL;
	state = 0;
	}

qboolean	GetCloseToEnemy::Evaluate
	(
	Actor &self
	)

	{
	if ( !self.currentEnemy )
		{
		return false;
		}

	if ( nextsearch < level.time )
		{
		state = 0;
		}

	switch( state )
		{
		case 0 :
			chase.Begin( self );
			movegoal = PathManager.NearestNode( self.currentEnemy->worldorigin, &self, false );
			if ( movegoal )
				{
				Path *path;
				FindEnemyPath find;

				find.heuristic.self = &self;
				find.heuristic.setSize( self.size );
				find.heuristic.entnum = self.entnum;

				path = find.FindPath( self.worldorigin, movegoal->worldorigin );
				movegoal = NULL;
				if ( path )
					{
               float dist;
               float movedist;

               //
               // check range 
               // if distance is less then total_delta, than lets go straight for him
               //
               dist = path->DistanceAlongPath( self.worldorigin );
               movedist = self.total_delta.length();
               if ( dist >= movedist )
                  {
					   movegoal = path->End();

					   chase.SetGoal( movegoal );
					   chase.SetPath( path );
                  }
					}
				}

			if ( !movegoal )
				{
            if ( self.CanSee( self.currentEnemy ) )
               {
					chase.SetGoalPos( self.currentEnemy->worldorigin );
               }
            else
               {
               //
               // since we can't reach em
               // clear out enemy state
               //
	            self.ClearEnemies();
				   return false;
               }
				}

			if ( anim.length() && ( anim != self.animname ) )
				{
				self.SetAnim( anim );
				}

			state = 1;
			nextsearch = level.time + 3;

		case 1 :
			if ( self.WithinDistance( self.currentEnemy, howclose ) )
				{
				chase.End( self );
				return false;
				}

			if ( !chase.Evaluate( self ) || ( nextsearch < level.time ) )
				{
				state = 0;
				}
			break;
		}

	return true;
	}

void GetCloseToEnemy::End
	(
	Actor &self
	)

	{
	chase.End( self );
	}

/****************************************************************************

  PlayAnimSeekEnemy Class Definition

****************************************************************************/

CLASS_DECLARATION( Behavior, PlayAnimSeekEnemy, NULL );

ResponseDef PlayAnimSeekEnemy::Responses[] =
	{
		{ &EV_Behavior_Args,			( Response )PlayAnimSeekEnemy::SetArgs },
		{ &EV_Behavior_AnimDone,	( Response )PlayAnimSeekEnemy::AnimDone },
		{ NULL, NULL }
	};

void PlayAnimSeekEnemy::ShowInfo
	(
	Actor &self
	)

	{
   Behavior::ShowInfo( self );

   gi.printf( "\naim:\n" );
   aim.ShowInfo( self );

   gi.printf( "\nmode: %d\n", mode );
   gi.printf( "anim: %s\n", anim.c_str() );
   gi.printf( "animdone: %d\n", animdone );
	}

void PlayAnimSeekEnemy::Begin
	(
	Actor &self
	)

	{
   oldanim = self.animname;
	mode = 0;
	animdone = false;
	}

void PlayAnimSeekEnemy::SetArgs
	(
	Event *ev
	)

	{
   anim = ev->GetString( 2 );
	}

void PlayAnimSeekEnemy::AnimDone
	(
	Event *ev
	)

	{
	animdone = true;
	}

qboolean	PlayAnimSeekEnemy::Evaluate
	(
	Actor &self
	)

	{
	switch( mode )
		{
		case 0 :
         if ( !anim.length() )
            {
            }

         if ( !self.currentEnemy )
            {
            return false;
            }

         animdone = false;
		   self.SetAnim( anim.c_str(), EV_Actor_NotifyBehavior );

         mode = 1;

		case 1 :
			aim.SetTarget( self.currentEnemy );
         aim.Evaluate( self );

			// finish up the attack
			if ( animdone )
				{
            return false;
				}
			break;
		}

	return true;
	}

void PlayAnimSeekEnemy::End
	(
	Actor &self
	)

	{
	aim.End( self );
   if ( oldanim.length() )
      {
	   self.SetAnim( oldanim.c_str() );
      }
	}
