UnrealShare
Class ScriptedPawn

source: e:\games\UnrealTournament\UnrealShare\Classes\ScriptedPawn.uc
Core.Object
   |
   +--Engine.Actor
      |
      +--Engine.Pawn
         |
         +--UnrealShare.ScriptedPawn
Direct Known Subclasses:Gasbag, Krall, Mercenary, Pupae, Queen, Squid, Titan, Warlord, Brute, Cow, DevilFish, Fly, Manta, Nali, Skaarj, Slith, Tentacle

class ScriptedPawn
extends Engine.Pawn

//============================================================================= // ScriptedPawn. //=============================================================================
Variables
 Pawn Hated
           used in states with multiple, sequenced animations
 name LastPainAnim
           used in states with multiple, sequenced animations
 name NextAnim
           used in states with multiple, sequenced animations
 Pawn OldEnemy
           used in states with multiple, sequenced animations
 int Team
 int TeamID
 bool bCanBePissed
           used in states with multiple, sequenced animations
 bool bCanSpeak
           used in states with multiple, sequenced animations
 bool bTeamSpeaking
           used in states with multiple, sequenced animations

States
Greeting, AlarmPaused, TriggerAlarm, VictoryDance, RangedAttack, MeleeAttack, FallingState, TakeHit, StakeOut, Hunting, TacticalMove, Charging, Retreating, Threatening, Attacking, Acquisition, Ambushing, Guarding, Patroling, Wandering, Roaming, Waiting, StartUp

Function Summary
 rotator AdjustAim(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget)
 rotator AdjustToss(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget)
     
/*
Adjust the aim at target.  
	- add aim error (base on skill - FIXME).
	- adjust up or down if barrier
*/
 void AnnoyedBy(Pawn Other)
     
/* 
Annoyed by another pawn, typically after bumping into it
(only when not already fighting)
*/
 eAttitude AttitudeTo(Pawn Other)
     
/* AttitudeTo()
Returns the creature's attitude towards another Pawn
*/
 eAttitude AttitudeToCreature(Pawn Other)
     
/* AttitudeToCreature
Typically implemented in subclass
*/
 eAttitude AttitudeWithFear()
     
/* AttitudeWithFear()
may fear other, unless near home
*/
 void Bump(Actor Other)
 bool CanFireAtEnemy()
 bool CanStakeOut()
     
// Can Stake Out - check if I can see my current Destination point, and so can enemy
 void ChooseLeaderAttack()
 bool ChooseTeamAttackFor(ScriptedPawn TeamMember)
 void EnemyAcquired()
 void Falling()
 void FearThisSpot(Actor aSpot)
 bool FindBestPathToward(Actor desired)
     
/* FindBestPathToward() assumes the desired destination is not directly reachable, 
given the creature's intelligence, it tries to set Destination to the location of the 
best waypoint, and returns true if successful
*/
 void FireProjectile(vector StartOffset, float Accuracy)
 void FireWeapon()
 bool Gibbed(name damageType)
 void HearNoise(float Loudness, Actor NoiseMaker)
 void JumpOffPawn()
 void JumpOutOfWater(vector jumpDir)
 void Killed(Pawn Killer, Pawn Other, name damageType)
 void LongFall()
 bool MeleeDamageTarget(int hitdamage, vector pushdir)
 bool NearWall(float walldist)
     
/* NearWall() returns true if there is a nearby barrier at eyeheight, and
changes Focus to a suggested value
*/
 bool NeedToTurn(vector targ)
 void PlayAcquisitionSound()
 void PlayChallenge()
 void PlayCombatMove()
 void PlayDeathHit(float Damage, vector HitLocation, name damageType, vector Momentum)
 void PlayFearSound()
 void PlayFiring()
 void PlayGutHit(float tweentime)
     
/* Default ScriptedPawn location specific take hits  - make sure pain frames are named right */
 void PlayHeadHit(float tweentime)
 void PlayHit(float Damage, vector HitLocation, name damageType, vector Momentum)
     
//**********************************************************************
 void PlayHitAnim(vector HitLocation, float Damage)
 void PlayLeftHit(float tweentime)
 void PlayMeleeAttack()
 void PlayRangedAttack()
 void PlayRightHit(float tweentime)
 void PlayRoamingSound()
 void PlayThreateningSound()
 void PreBeginPlay()
     
//Sounds
 float RelativeStrength(Pawn Other)
     
/* RelativeStrength()
returns a value indicating the relative strength of other
0.0 = equal to self
> 0 stronger than self
< 0 weaker than self

Since the result will be compared to the creature's aggressiveness, it should be
on the same order of magnitude (-1 to 1)

Assess based on health and weapon
*/
 void SeePlayer(Actor SeenPlayer)
 void SetAlertness(float NewAlertness)
     
/*
SetAlertness()
Change creature's alertness, and appropriately modify attributes used by engine for determining
seeing and hearing.
SeePlayer() is affected by PeripheralVision, and also by SightRadius and the target's visibility
HearNoise() is affected by HearingThreshold
*/
 bool SetEnemy(Pawn NewEnemy)
 void SetFall()
 void SetMovementPhysics()
     
// re-implement SetMovementPhysics() in subclass for flying and swimming creatures
 Carcass SpawnCarcass()
 void SpawnGibbedCarcass()
 void Speak()
     
//-----------------------------------------------------------------------------
// Sound functions
 void SpeakOrderTo(ScriptedPawn TeamMember)
 void SpeakTo(ScriptedPawn Other)
 void StartRoaming()
     
// check who is roaming/wandering - turn off oldest if too many
 void StopFiring()
 float StrafeAdjust()
 bool StrafeFromDamage(vector momentum, float Damage, name DamageType, bool bFindDest)
 void Trigger(Actor Other, Pawn EventInstigator)
 void TriggerFirstHate()
 bool TryToCrouch()
     
/* TryToCrouch()
See if far enough away, and geometry favorable for crouching
*/
 void TryToDuck(vector DuckDir, bool bReversed)
 void WarnTarget(Pawn shooter, float projSpeed, vector FireDir)
 void WhatToDoNext(name LikelyState, name LikelyLabel)
 void ZoneChange(ZoneInfo newZone)
 void damageAttitudeTo(Pawn Other)


State Greeting Function Summary
 void Landed(vector HitNormal)
 void AnimEnd()
 void EnemyAcquired()
 void Timer()
 void Bump(Actor Other)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State AlarmPaused Function Summary
 void BeginState()
 void PlayWaitAround()
 void SeePlayer(Actor SeenPlayer)
 void EnemyNotVisible()
 void FindShootTarget()
 void Timer()
 void Bump(Actor Other)
 void SetFall()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State TriggerAlarm Function Summary
 void BeginState()
 void AnimEnd()
 void FindAlarm()
 void AlarmDone()
 void Bump(Actor Other)
 void Touch(Actor Other)
 void EnemyNotVisible()
 void SetFall()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State VictoryDance Function Summary
 void BeginState()
 void PickDestination()
 void EnemyAcquired()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State RangedAttack Function Summary
 void EndState()
 void BeginState()
 void AnimEnd()
 void Timer()
 void KeepAttacking()
 void EnemyNotVisible()
 void StopWaiting()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State MeleeAttack Function Summary
 void BeginState()
 void AnimEnd()
 void EnemyNotVisible()
 void KeepAttacking()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
     
/* DamageTarget
check if attack hit target, and if so damage it
*/


State FallingState Function Summary
 void EndState()
 void BeginState()
 void EnemyAcquired()
 void SetFall()
 void EnemyNotVisible()
 void SeePlayer(Actor SeenPlayer)
 void Landed(vector HitNormal)
 void Timer()
 bool SetEnemy(Pawn NewEnemy)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
 void adjustJump()
     
//choose a jump velocity
 void ZoneChange(ZoneInfo newZone)


State TakeHit Function Summary
 void BeginState()
 void PlayHitAnim(vector HitLocation, float Damage)
 void Timer()
 void Landed(vector HitNormal)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State StakeOut Function Summary
 void EndState()
 void BeginState()
 rotator AdjustAim(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget)
 void Timer()
 bool SetEnemy(Pawn NewEnemy)
 void SetFall()
 void HearNoise(float Loudness, Actor NoiseMaker)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State Hunting Function Summary
 void EndState()
 void BeginState()
 bool FindViewSpot()
 void PickDestination()
 void HitWall(vector HitNormal, Actor Wall)
 void Timer()
 void AnimEnd()
 bool SetEnemy(Pawn NewEnemy)
 void SetFall()
 void HearNoise(float Loudness, Actor NoiseMaker)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
 void FearThisSpot(Actor aSpot)
 void MayFall()
     
	/* MayFall() called by engine physics if walking and bCanJump, and
		is about to go off a ledge.  Pawn has opportunity (by setting 
		bCanJump to false) to avoid fall
	*/


State TacticalMove Function Summary
 void EndState()
 void BeginState()
 void PickDestination(bool bNoCharge)
     
/* PickDestination()
Choose a destination for the tactical move, based on aggressiveness and the tactical
situation. Make sure destination is reachable
*/
 void GiveUpTactical(bool bNoCharge)
 bool ValidRecovery()
 void EnemyNotVisible()
 void Timer()
 void AnimEnd()
 void FearThisSpot(Actor aSpot)
 void HitWall(vector HitNormal, Actor Wall)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
 void SetFall()


State Charging Function Summary
 void EndState()
 void BeginState()
 void EnemyNotVisible()
 void Timer()
 void AnimEnd()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
 bool StrafeFromDamage(vector momentum, float Damage, name DamageType, bool bFindDest)
 void FearThisSpot(Actor aSpot)
 void SetFall()
 void HitWall(vector HitNormal, Actor Wall)
 void MayFall()
     
	/* MayFall() called by engine physics if walking and bCanJump, and
		is about to go off a ledge.  Pawn has opportunity (by setting 
		bCanJump to false) to avoid fall
	*/


State Retreating Function Summary
 void BeginState()
 void AnimEnd()
 void PickNextSpot()
 void ReachedHome()
 void Bump(Actor Other)
 void ChangeDestination()
 void PickDestination()
     
	/* if has a base then run toward it if its not visible to player. (FIXME)
	adjusts attitude based on proximity to base
	Else pick a random pathnode not visible to player and run toward it.
	Also - modify weights of paths visible and near to player up.
	*/
 void HitWall(vector HitNormal, Actor Wall)
 void SetFall()
 void Timer()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State Threatening Function Summary
 void EndState()
 void BeginState()
 void PickThreatenDestination()
 void PickGuardDestination()
 void EnemyAcquired()
 void EnemyNotVisible()
 void Trigger(Actor Other, Pawn EventInstigator)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
     
//ignores SeePlayer if enemy is a player //but not hear noise


State Attacking Function Summary
 void BeginState()
 void Timer()
 void EnemyNotVisible()
     
//EnemyNotVisible implemented so engine will update LastSeenPos
 void ChooseAttackMode()


State Acquisition Function Summary
 void BeginState()
 void SeePlayer(Actor SeenPlayer)
 void HearNoise(float Loudness, Actor NoiseMaker)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
 void WarnTarget(Pawn shooter, float projSpeed, vector FireDir)
     
//fixme


State Ambushing Function Summary
 void BeginState()
 void FindAmbush()
 void EnemyAcquired()
 void AnimEnd()
 void Timer()
 void SetFall()
 void Landed(vector HitNormal)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State Guarding Function Summary
 void BeginState()
 void SetFall()
 void PickDestination()
 void HitWall(vector HitNormal, Actor Wall)
 void AnimEnd()
 void EnemyAcquired()
 void Timer()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State Patroling Function Summary
 void BeginState()
 void FindNextPatrol()
 void PickDestination()
 void EnemyAcquired()
 void AnimEnd()
 void Timer()
 void Trigger(Actor Other, Pawn EventInstigator)
 void HitWall(vector HitNormal, Actor Wall)
 void SetFall()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State Wandering Function Summary
 void EndState()
 void BeginState()
 void FearThisSpot(Actor aSpot)
 void AnimEnd()
 void PickDestination()
 bool TestDirection(vector dir, out vector)
 void HitWall(vector HitNormal, Actor Wall)
 void EnemyAcquired()
 void SetFall()
 void Timer()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State Roaming Function Summary
 void BeginState()
 void PickDestination()
 void HitWall(vector HitNormal, Actor Wall)
 void EnemyAcquired()
 void SetFall()
 void Bump(Actor Other)
 void Timer()
 void FearThisSpot(Actor aSpot)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State Waiting Function Summary
 void BeginState()
 void Landed(vector HitNormal)
 void AnimEnd()
 void EnemyAcquired()
 void Timer()
 void Bump(Actor Other)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)


State StartUp Function Summary
 void BeginState()
 void SetAlarm()
 void SetTeam()
 void SetHome()
 void InitPatrolLoc()
 void InitAmbushLoc()



Source Code


00001	//=============================================================================
00002	// ScriptedPawn.
00003	//=============================================================================
00004	class ScriptedPawn extends Pawn
00005		abstract;
00006	
00007	#exec AUDIO IMPORT FILE="Sounds\Generic\land1.WAV" NAME="Land1" GROUP="Generic"
00008	#exec AUDIO IMPORT FILE="Sounds\Generic\lsplash.WAV" NAME="LSplash" GROUP="Generic"
00009	
00010	var(Pawn) class<carcass> CarcassType;
00011	
00012	// Advanced AI attributes.
00013	var(AI) name		TeamTag;  
00014	var	ScriptedPawn	TeamLeader;
00015	var int Team;
00016	var int TeamID;
00017	var(Orders) name	Orders;			//orders a creature is carrying out 
00018										// will be initial state, plus creature will attenmpt
00019										//to return to this state
00020	var(Orders) name	OrderTag;		// tag of object referred to by orders
00021	var		actor		OrderObject;		// object referred to by orders (if applicable)
00022	var(Combat) float	TimeBetweenAttacks;  // seconds - modified by difficulty
00023	var 	name		NextAnim;		// used in states with multiple, sequenced animations	
00024	var(Combat) float	Aggressiveness; //0.0 to 1.0 (typically) 
00025	var  	Pawn		Hated;
00026	var(Combat) float	ReFireRate;		//chance of shooting multiple times in one attack
00027	var   	Pawn		OldEnemy;
00028	var		float		RoamStartTime;
00029	var		int			numHuntPaths;
00030	var		float		HuntStartTime;
00031	var		vector		HidingSpot;
00032	var		float		WalkingSpeed;
00033	var(AI) name		FirstHatePlayerEvent;	// event when first hate player
00034	
00035	//AI flags
00036	var	 	bool   		bReadyToAttack;		// can attack again 
00037	var(Combat) bool	bHasRangedAttack;	// can attack from beyond melee range
00038	var(Combat) bool	bMovingRangedAttack; //can perform ranged attack while moving
00039	var		bool		bCanFire;			//used by TacticalMove and Charging states
00040	var(AI) bool		bQuiet;
00041	var     bool		bTeamSpeaking;
00042	var(AI) bool		bTeamLeader;
00043	var(AI) bool		bIgnoreFriends;  	//don't react to friend's noises (and make their enemy yours)
00044	var 	bool		bCanSpeak;
00045	var		bool 		bIsSpeaking;
00046	var 	bool		bCanBePissed;
00047	var		bool		bCanDuck;
00048	var(AI) bool		bHateWhenTriggered;
00049	var(Orders) bool	bDelayedPatrol;
00050	var		bool		bStrafeDir;
00051	var(Combat) bool	bIsWuss;			// always takes hit
00052	var(Combat) bool	bLeadTarget;		// lead target with projectile attack
00053	var(Combat) bool	bWarnTarget;		// warn target when projectile attack
00054	var		bool		bFirstShot;
00055	var		bool		bCrouching;
00056	var		bool		bFirstHatePlayer;
00057	var		bool		bClearShot;
00058	var		bool		bSpecialGoal;
00059	var		bool		bChangeDir;			// tactical move boolean
00060	var		bool		bMoraleBoosted;
00061	var		bool		bFiringPaused;
00062	var		bool		bSpecialPausing;
00063	var		bool		bGreenBlood;
00064	var		bool		bFrustrated;
00065	var		bool		bInitialFear;
00066	var(AI)	bool		bIsBoss;
00067	var(Orders) bool	bNoWait;		// Friendly creature no wait going to alarm
00068	
00069	var(Combat) class<actor> RangedProjectile;
00070	var(Combat)	float	ProjectileSpeed;
00071	var     name		LastPainAnim;
00072	var		float		LastPainTime;
00073	
00074	//Sounds
00075	var(Sounds)	sound	Acquire;
00076	var(Sounds)	sound	Fear;
00077	var(Sounds)	sound	Roam;
00078	var(Sounds)	sound	Threaten;
00079	
00080	function PreBeginPlay()
00081	{
00082		Super.PreBeginPlay();
00083	
00084		if ( Level.Game.bVeryLowGore )
00085			bGreenBlood = true;
00086	
00087		if ( Skill > 2 )
00088			bLeadTarget = true;
00089		else if ( (Skill == 0) && (Health < 500) )
00090		{
00091			bLeadTarget = false;		
00092			ReFireRate = 0.75 * ReFireRate;
00093		}	
00094	
00095		if ( bIsBoss )
00096			Health = Health + 0.15 * Skill * Health;
00097	
00098		bInitialFear = (AttitudeToPlayer == ATTITUDE_Fear);
00099	}
00100	
00101	//*********************************************************************
00102	/* Default ScriptedPawn location specific take hits  - make sure pain frames are named right */
00103	function PlayGutHit(float tweentime)
00104	{
00105		if ( (LastPainTime - Level.TimeSeconds < 0.3) && (LastPainAnim == 'GutHit') )
00106		{
00107			if (FRand() < 0.5)
00108				TweenAnim('LeftHit', tweentime);
00109			else
00110				TweenAnim('RightHit', tweentime);
00111		}
00112		else
00113			TweenAnim('GutHit', tweentime);
00114	}
00115	
00116	function PlayHeadHit(float tweentime)
00117	{
00118		if ( (LastPainTime - Level.TimeSeconds < 0.3) && (LastPainAnim == 'HeadHit') )
00119		{
00120			if (FRand() < 0.5)
00121				TweenAnim('LeftHit', tweentime);
00122			else
00123				TweenAnim('RightHit', tweentime);
00124		}
00125		else
00126			TweenAnim('HeadHit', tweentime);
00127	}
00128	
00129	function PlayLeftHit(float tweentime)
00130	{
00131		if ( (LastPainTime - Level.TimeSeconds < 0.3) && (LastPainAnim == 'LeftHit') )
00132			TweenAnim('GutHit', tweentime);
00133		else
00134			TweenAnim('LeftHit', tweentime);
00135	}
00136	
00137	function PlayRightHit(float tweentime)
00138	{
00139		if ( (LastPainTime - Level.TimeSeconds < 0.3) && (LastPainAnim == 'RightHit') )
00140			TweenAnim('GutHit', tweentime);
00141		else
00142			TweenAnim('RightHit', tweentime);
00143	}
00144	
00145	function bool StrafeFromDamage(vector momentum, float Damage,name DamageType, bool bFindDest);
00146	
00147	//**********************************************************************
00148	
00149	function PlayHit(float Damage, vector HitLocation, name damageType, vector Momentum)
00150	{
00151		local float rnd;
00152		local Bubble1 bub;
00153		local bool bOptionalTakeHit;
00154		local vector BloodOffset;
00155		local blood2 b;
00156	
00157		if (Damage > 1) //spawn some blood
00158		{
00159			if (damageType == 'Drowned')
00160			{
00161				bub = spawn(class 'Bubble1',,, Location 
00162					+ 0.7 * CollisionRadius * vector(ViewRotation) + 0.3 * EyeHeight * vect(0,0,1));
00163				if (bub != None)
00164					bub.DrawScale = FRand()*0.06+0.04; 
00165			}
00166			else if ( damageType != 'Corroded' )
00167			{
00168				BloodOffset = 0.2 * CollisionRadius * Normal(HitLocation - Location);
00169				BloodOffset.Z = BloodOffset.Z * 0.5;
00170				b = spawn(class 'BloodBurst',,,hitLocation + BloodOffset, rotator(BloodOffset));
00171				if ( bGreenBlood && (b != None) ) b.GreenBlood();
00172				if ( Level.bHighDetailMode )
00173				{
00174					if ( bGreenBlood )
00175						spawn(class'GreenBloodPuff',,, hitLocation + BloodOffset);
00176					else
00177						spawn(class'BloodPuff',,, hitLocation + BloodOffset);
00178				}
00179			}
00180		}	
00181	
00182		if ( (Weapon != None) && Weapon.bPointing && !bIsPlayer )
00183		{
00184			bFire = 0;
00185			bAltFire = 0;
00186		}
00187		
00188		bOptionalTakeHit = bIsWuss || ( (Level.TimeSeconds - LastPainTime > 0.3 + 0.25 * skill)
00189							&& (Damage * FRand() > 0.08 * Health) && (Skill < 3)
00190							&& (GetAnimGroup(AnimSequence) != 'MovingAttack') 
00191							&& (GetAnimGroup(AnimSequence) != 'Attack') ); 
00192		if ( (!bIsPlayer || (Weapon == None) || !Weapon.bPointing) 
00193			&& (bOptionalTakeHit || (Momentum.Z > 140) || (bFirstShot && (Damage > 0.015 * (skill + 6) * Health)) 
00194				 || (Damage * FRand() > (0.17 + 0.04 * skill) * Health)) ) 
00195		{
00196			PlayTakeHitSound(Damage, damageType, 3);
00197			PlayHitAnim(HitLocation, Damage);
00198		}
00199		else if (NextState == 'TakeHit')
00200		{
00201			PlayTakeHitSound(Damage, damageType, 2);
00202			NextState = '';
00203		}
00204	}
00205	
00206	function PlayHitAnim(vector HitLocation, float Damage)
00207	{
00208		bFirstShot = false;
00209		NextAnim = ''; 
00210		NextState = 'TakeHit';
00211		PlayTakeHit(0.08, hitLocation, Damage); 
00212	} 
00213	
00214	function PlayDeathHit(float Damage, vector HitLocation, name damageType, vector Momentum)
00215	{
00216		local Bubble1 bub;
00217		local BloodBurst b;
00218	
00219		if ( Region.Zone.bDestructive && (Region.Zone.ExitActor != None) )
00220			Spawn(Region.Zone.ExitActor);
00221		if (HeadRegion.Zone.bWaterZone)
00222		{
00223			bub = spawn(class 'Bubble1',,, Location 
00224				+ 0.3 * CollisionRadius * vector(Rotation) + 0.8 * EyeHeight * vect(0,0,1));
00225			if (bub != None)
00226				bub.DrawScale = FRand()*0.08+0.03; 
00227			bub = spawn(class 'Bubble1',,, Location 
00228				+ 0.2 * CollisionRadius * VRand() + 0.7 * EyeHeight * vect(0,0,1));
00229			if (bub != None)
00230				bub.DrawScale = FRand()*0.08+0.03; 
00231			bub = spawn(class 'Bubble1',,, Location 
00232				+ 0.3 * CollisionRadius * VRand() + 0.6 * EyeHeight * vect(0,0,1));
00233			if (bub != None)
00234				bub.DrawScale = FRand()*0.08+0.03; 
00235		}
00236		if ( (damageType != 'Burned') && (damageType != 'Corroded') 
00237			 && (damageType != 'Drowned') && (damageType != 'Fell') )
00238		{
00239			b = spawn(class 'BloodBurst',self,'', hitLocation);
00240			if ( bGreenBlood && (b != None) ) 
00241				b.GreenBlood();		
00242		}
00243	}
00244	
00245	function PlayChallenge()
00246	{
00247		TweenToFighter(0.1);
00248	}
00249	
00250	function ZoneChange(ZoneInfo newZone)
00251	{
00252		local vector jumpDir;
00253	
00254		if ( newZone.bWaterZone )
00255		{
00256			if (!bCanSwim)
00257				MoveTimer = -1.0;
00258			else if (Physics != PHYS_Swimming)
00259			{
00260				if (Physics != PHYS_Falling)
00261					PlayDive(); 
00262				setPhysics(PHYS_Swimming);
00263			}
00264		}
00265		else if (Physics == PHYS_Swimming)
00266		{
00267			if ( bCanFly )
00268				 SetPhysics(PHYS_Flying); 
00269			else
00270			{ 
00271				SetPhysics(PHYS_Falling);
00272				if ( bCanWalk && (Abs(Acceleration.X) + Abs(Acceleration.Y) > 0) && CheckWaterJump(jumpDir) )
00273					JumpOutOfWater(jumpDir);
00274			}
00275		}
00276	}
00277	
00278	function JumpOutOfWater(vector jumpDir)
00279	{
00280		Falling();
00281		Velocity = jumpDir * WaterSpeed;
00282		Acceleration = jumpDir * AccelRate;
00283		velocity.Z = 380; //set here so physics uses this for remainder of tick
00284		PlayOutOfWater();
00285		bUpAndOut = true;
00286	}
00287	
00288	function bool Gibbed(name damageType)
00289	{
00290		if ( (damageType == 'decapitated') || (damageType == 'shot') )
00291			return false; 	
00292		return ( !bIsBoss && (Mass < 500) && ((Health < -0.5 * (Default.Health + Mass)) ||
00293			((Health < -25 - 0.1 * (Default.Health + Mass) * (0.2 + FRand())) && (FRand() < 0.8))) );
00294	}
00295	
00296	function SpawnGibbedCarcass()
00297	{
00298		local carcass carc;
00299	
00300		carc = Spawn(CarcassType);
00301		if ( carc != None )
00302		{
00303			carc.Initfor(self);
00304			carc.ChunkUp(-1 * Health);
00305		}
00306	}
00307	
00308	function Carcass SpawnCarcass()
00309	{
00310		local carcass carc;
00311	
00312		carc = Spawn(CarcassType);
00313		if ( carc != None )
00314			carc.Initfor(self);
00315	
00316		return carc;
00317	}
00318	
00319	function JumpOffPawn()
00320	{
00321		Velocity += (60 + CollisionRadius) * VRand();
00322		Velocity.Z = 180 + CollisionHeight;
00323		SetPhysics(PHYS_Falling);
00324		bJumpOffPawn = true;
00325		SetFall();
00326	}
00327	//-----------------------------------------------------------------------------
00328	// Sound functions
00329		
00330	function Speak()
00331	{
00332		//implemented in speaking subclasses
00333	}
00334	
00335	function SpeakTo(ScriptedPawn Other)
00336	{
00337		//implemented in speaking subclasses
00338	}
00339	
00340	function SpeakOrderTo(ScriptedPawn TeamMember)
00341	{
00342		//implemented in speaking subclasses
00343	}
00344	
00345	function PlayAcquisitionSound()
00346	{
00347		if (Acquire != None) 
00348			PlaySound(Acquire, SLOT_Talk,, true); 
00349	}
00350	
00351	function PlayFearSound()
00352	{
00353		if (Fear != None)
00354			PlaySound(Fear, SLOT_Talk,, true); 
00355	}
00356	
00357	function PlayRoamingSound()
00358	{
00359		if ( (Threaten != None) && (FRand() < 0.4) )
00360			PlaySound(Threaten, SLOT_Talk,, true);
00361		if (Roam != None)
00362			PlaySound(Roam, SLOT_Talk,, true);
00363	}
00364	
00365	function PlayThreateningSound()
00366	{
00367		if (Threaten == None) return;
00368		if (FRand() < 0.6)
00369			PlaySound(Threaten, SLOT_Talk,, true);
00370		else
00371			PlaySound(Acquire, SLOT_Talk,, true);
00372	}
00373	
00374	//=============================================================================
00375		
00376	// re-implement SetMovementPhysics() in subclass for flying and swimming creatures
00377	function SetMovementPhysics()
00378	{
00379		if (Physics == PHYS_Falling)
00380			return;
00381		
00382		SetPhysics(PHYS_Walking); 
00383	}
00384	
00385	function FearThisSpot(Actor aSpot)
00386	{
00387		Acceleration = vect(0,0,0);
00388		MoveTimer = -1.0;
00389	}
00390	
00391	/*
00392	SetAlertness()
00393	Change creature's alertness, and appropriately modify attributes used by engine for determining
00394	seeing and hearing.
00395	SeePlayer() is affected by PeripheralVision, and also by SightRadius and the target's visibility
00396	HearNoise() is affected by HearingThreshold
00397	*/
00398	final function SetAlertness(float NewAlertness)
00399	{
00400		if ( Alertness != NewAlertness )
00401		{
00402			PeripheralVision += 0.707 * (Alertness - NewAlertness); //Used by engine for SeePlayer()
00403			HearingThreshold += 0.5 * (Alertness - NewAlertness); //Used by engine for HearNoise()
00404			Alertness = NewAlertness;
00405		}
00406	}
00407	
00408	function WhatToDoNext(name LikelyState, name LikelyLabel)
00409	{
00410		bQuiet = false;
00411		Enemy = None;
00412		if ( OldEnemy != None )
00413		{
00414			Enemy = OldEnemy;
00415			OldEnemy = None;
00416			GotoState('Attacking');
00417		}
00418		else if (Orders == 'Patroling') 
00419			GotoState('Patroling');
00420		else if (Orders == 'Guarding')
00421			GotoState('Guarding');
00422		else if ( Orders == 'Ambushing' )
00423			GotoState('Ambushing','FindAmbushSpot');
00424		else if ( (LikelyState != '') && (FRand() < 0.35) )
00425			GotoState(LikelyState, LikelyLabel);
00426		else
00427			StartRoaming();
00428	}
00429	
00430	// check who is roaming/wandering - turn off oldest if too many
00431	function StartRoaming()
00432	{
00433		local float oldestRoamTime;
00434		local pawn oldestRoamer, next;
00435		local int numRoamers;
00436	
00437		RoamStartTime = Level.TimeSeconds;
00438		oldestRoamTime = RoamStartTime;
00439		oldestRoamer = None;
00440		numRoamers = 0;
00441		next = Level.PawnList;
00442		while (next != None)
00443		{
00444			if ( (ScriptedPawn(next) != None) && !next.bIsPlayer 
00445				&& ((next.IsInState('Roaming')) || (next.IsInState('Wandering'))) )
00446			{
00447				numRoamers++;
00448				if ( (ScriptedPawn(next).RoamStartTime < oldestRoamTime) || (oldestRoamer == None) )
00449				{
00450					oldestRoamer = next;
00451					oldestRoamTime = ScriptedPawn(next).RoamStartTime;
00452				}
00453			}
00454			next = next.nextPawn;
00455		}
00456		if ( numRoamers > 4 )
00457			oldestRoamer.GotoState('Waiting', 'TurnFromWall');
00458		OrderObject = None;
00459		OrderTag = '';
00460		GotoState('Roaming');
00461	}
00462	
00463	function Bump(actor Other)
00464	{
00465		local vector VelDir, OtherDir;
00466		local float speed;
00467	
00468		if ( Enemy != None )
00469		{
00470			if (Other == Enemy)
00471			{
00472				GotoState('MeleeAttack');
00473				return;
00474			}
00475			else if ( (Pawn(Other) != None) && SetEnemy(Pawn(Other)) )
00476			{
00477				GotoState('MeleeAttack');
00478				return;
00479			} 
00480		}
00481		else
00482		{
00483			if (Pawn(Other) != None)
00484			{
00485				AnnoyedBy(Pawn(Other));
00486				if ( SetEnemy(Pawn(Other)) )
00487				{
00488					bReadyToAttack = True; //can melee right away
00489					PlayAcquisitionSound();
00490					GotoState('Attacking');
00491					return;
00492				}
00493			}
00494			if ( TimerRate <= 0 )
00495				setTimer(1.0, false);
00496			if ( bCanSpeak && (ScriptedPawn(Other) != None) && ((TeamLeader == None) || !TeamLeader.bTeamSpeaking) )
00497				SpeakTo(ScriptedPawn(Other));
00498		}
00499		
00500		speed = VSize(Velocity);
00501		if ( speed > 1 )
00502		{
00503			VelDir = Velocity/speed;
00504			VelDir.Z = 0;
00505			OtherDir = Other.Location - Location;
00506			OtherDir.Z = 0;
00507			OtherDir = Normal(OtherDir);
00508			if ( (VelDir Dot OtherDir) > 0.8 )
00509			{
00510				/*if ( Pawn(Other) == None )
00511				{
00512					MoveTimer = -1.0;	
00513					HitWall(-1 * OtherDir, Other);
00514				} */
00515				Velocity.X = VelDir.Y;
00516				Velocity.Y = -1 * VelDir.X;
00517				Velocity *= FMax(speed, 280);
00518			}
00519		} 
00520		Disable('Bump');
00521	}
00522			
00523	singular function Falling()
00524	{
00525		if (bCanFly)
00526		{
00527			SetPhysics(PHYS_Flying);
00528			return;
00529		}			
00530		//log(class$" Falling");
00531		// SetPhysics(PHYS_Falling); //note - done by default in physics
00532	 	if (health > 0)
00533			SetFall();
00534	}
00535		
00536	function SetFall()
00537	{
00538		if (Enemy != None)
00539		{
00540			NextState = 'Attacking'; //default
00541			NextLabel = 'Begin';
00542			NextAnim = 'Fighter';
00543			GotoState('FallingState');
00544		}
00545	}
00546	
00547	function LongFall()
00548	{
00549		SetFall();
00550		GotoState('FallingState', 'LongFall');
00551	}
00552	
00553	function HearNoise(float Loudness, Actor NoiseMaker)
00554	{
00555		//log(class$" heard noise by "$NoiseMaker.class);
00556		if ( SetEnemy(NoiseMaker.instigator) )
00557			LastSeenPos = 0.5 * (NoiseMaker.Location + VSize(NoiseMaker.Location - Location) * vector(Rotation));
00558	}
00559	
00560	function SeePlayer(Actor SeenPlayer)
00561	{
00562		//log(class$" has line of sight to player");
00563		if (SetEnemy(Pawn(SeenPlayer)))
00564			LastSeenPos = Enemy.Location;
00565	}
00566	
00567	/* FindBestPathToward() assumes the desired destination is not directly reachable, 
00568	given the creature's intelligence, it tries to set Destination to the location of the 
00569	best waypoint, and returns true if successful
00570	*/
00571	function bool FindBestPathToward(actor desired)
00572	{
00573		local Actor path;
00574		local bool success;
00575		
00576		if ( specialGoal != None)
00577			desired = specialGoal;
00578		path = None;
00579		if (Intelligence <= BRAINS_Reptile)
00580			path = FindPathToward(desired, true);
00581		else 
00582			path = FindPathToward(desired); 
00583			
00584		success = (path != None);	
00585		if (success)
00586		{
00587			MoveTarget = path; 
00588			Destination = path.Location;
00589		}	
00590		return success;
00591	}	
00592	
00593	function bool NeedToTurn(vector targ)
00594	{
00595		local int YawErr;
00596	
00597		DesiredRotation = Rotator(targ - location);
00598		DesiredRotation.Yaw = DesiredRotation.Yaw & 65535;
00599		YawErr = (DesiredRotation.Yaw - (Rotation.Yaw & 65535)) & 65535;
00600		if ( (YawErr < 4000) || (YawErr > 61535) )
00601			return false;
00602	
00603		return true;
00604	}
00605	
00606	/* NearWall() returns true if there is a nearby barrier at eyeheight, and
00607	changes Focus to a suggested value
00608	*/
00609	function bool NearWall(float walldist)
00610	{
00611		local actor HitActor;
00612		local vector HitLocation, HitNormal, ViewSpot, ViewDist, LookDir;
00613	
00614		LookDir = vector(Rotation);
00615		ViewSpot = Location + BaseEyeHeight * vect(0,0,1);
00616		ViewDist = LookDir * walldist; 
00617		HitActor = Trace(HitLocation, HitNormal, ViewSpot + ViewDist, ViewSpot, false);
00618		if ( HitActor == None )
00619			return false;
00620	
00621		ViewDist = Normal(HitNormal Cross vect(0,0,1)) * walldist;
00622		if (FRand() < 0.5)
00623			ViewDist *= -1;
00624	
00625		HitActor = Trace(HitLocation, HitNormal, ViewSpot + ViewDist, ViewSpot, false);
00626		if ( HitActor == None )
00627		{
00628			Focus = Location + ViewDist;
00629			return true;
00630		}
00631	
00632		ViewDist *= -1;
00633	
00634		HitActor = Trace(HitLocation, HitNormal, ViewSpot + ViewDist, ViewSpot, false);
00635		if ( HitActor == None )
00636		{
00637			Focus = Location + ViewDist;
00638			return true;
00639		}
00640	
00641		Focus = Location - LookDir * 300;
00642		return true;
00643	}
00644	
00645	final function FireProjectile(vector StartOffset, float Accuracy)
00646	{
00647		local vector X,Y,Z, projStart;
00648	
00649		MakeNoise(1.0);
00650		GetAxes(Rotation,X,Y,Z);
00651		projStart = Location + StartOffset.X * CollisionRadius * X 
00652						+ StartOffset.Y * CollisionRadius * Y 
00653						+ StartOffset.Z * CollisionRadius * Z;
00654		spawn(RangedProjectile ,self,'',projStart,AdjustAim(ProjectileSpeed, projStart, Accuracy, bLeadTarget, bWarnTarget));
00655	}
00656	
00657	function StopFiring()
00658	{
00659		bFire = 0;
00660		bAltFire = 0;
00661		SetTimer((0.75 + 0.5 * FRand()) * TimeBetweenAttacks, false);
00662	}
00663	
00664	function FireWeapon()
00665	{
00666		local float rating;
00667		local int bUseAltMode;
00668	
00669		if( Weapon!=None )
00670		{
00671			Weapon.RateSelf(bUseAltMode);
00672			ViewRotation = Rotation;
00673			if ( bUseAltMode > 0 ) 
00674			{
00675				bFire = 0;
00676				bAltFire = 1;
00677				Weapon.AltFire(1.0);
00678			}
00679			else
00680			{
00681				bFire = 1;
00682				bAltFire = 0;
00683				Weapon.Fire(1.0);
00684			}
00685			PlayFiring();
00686		}
00687	}
00688	
00689	function PlayFiring();
00690	
00691	/*
00692	Adjust the aim at target.  
00693		- add aim error (base on skill - FIXME).
00694		- adjust up or down if barrier
00695	*/
00696	
00697	function rotator AdjustToss(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget)
00698	{
00699		//FIXME- implement
00700		return AdjustAim(projSpeed, projStart, aimerror, leadTarget, warnTarget);
00701	}
00702	
00703	function rotator AdjustAim(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget)
00704	{
00705		local rotator FireRotation;
00706		local vector FireSpot;
00707		local actor HitActor;
00708		local vector HitLocation, HitNormal;
00709	
00710		if ( Target == None )
00711			Target = Enemy;
00712		if ( Target == None )
00713			return Rotation;
00714		if ( !Target.IsA('Pawn') )
00715			return rotator(Target.Location - Location);
00716						
00717		FireSpot = Target.Location;
00718	
00719		aimerror = aimerror * (1 - 10 *  
00720			((Normal(Target.Location - Location) 
00721				Dot Normal((Target.Location + 0.5 * Target.Velocity) - (Location + 0.5 * Velocity))) - 1)); 
00722	
00723		aimerror = aimerror * (2.4 - 0.5 * (skill + FRand()));	
00724	
00725		if (leadTarget && (projSpeed > 0))
00726		{
00727			FireSpot += FMin(1, 0.7 + 0.6 * FRand()) * (Target.Velocity * VSize(Target.Location - ProjStart)/projSpeed);
00728			HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
00729			if (HitActor != None)
00730				FireSpot = 0.5 * (FireSpot + Target.Location);
00731		}
00732	
00733		HitActor = self; //so will fail first check unless shooting at feet  
00734		if ( bIsPlayer && (Location.Z + 19 >= Target.Location.Z) && Target.IsA('Pawn') 
00735			&& (Weapon != None) && Weapon.bSplashDamage && (0.5 * (skill - 1) > FRand()) )
00736		{
00737			// Try to aim at feet
00738	 		HitActor = Trace(HitLocation, HitNormal, FireSpot - vect(0,0,80), FireSpot, false);
00739			if ( HitActor != None )
00740			{
00741				FireSpot = HitLocation + vect(0,0,3);
00742				HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
00743			}
00744			else
00745				HitActor = self;
00746		}
00747		if ( HitActor != None )
00748		{
00749			//try middle
00750			FireSpot.Z = Target.Location.Z;
00751	 		HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
00752		}
00753		if( HitActor != None ) 
00754		{
00755			////try head
00756	 		FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight;
00757	 		HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
00758		}
00759		if ( (HitActor != None) && (Target == Enemy) )
00760		{
00761			FireSpot = LastSeenPos;
00762			if ( Location.Z >= LastSeenPos.Z )
00763				FireSpot.Z -= 0.5 * Enemy.CollisionHeight;
00764			if ( Weapon != None )
00765			{
00766		 		HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
00767				if ( HitActor != None )
00768				{
00769					bFire = 0;
00770					bAltFire = 0;
00771					SetTimer(TimeBetweenAttacks, false);
00772				}
00773			}
00774		}
00775		
00776		FireRotation = Rotator(FireSpot - ProjStart);
00777		     
00778		FireRotation.Yaw = FireRotation.Yaw + 0.5 * (Rand(2 * aimerror) - aimerror);
00779		if (warnTarget && Pawn(Target) != None) 
00780			Pawn(Target).WarnTarget(self, projSpeed, vector(FireRotation)); 
00781	
00782		FireRotation.Yaw = FireRotation.Yaw & 65535;
00783		if ( (Abs(FireRotation.Yaw - (Rotation.Yaw & 65535)) > 8192)
00784			&& (Abs(FireRotation.Yaw - (Rotation.Yaw & 65535)) < 57343) )
00785		{
00786			if ( (FireRotation.Yaw > Rotation.Yaw + 32768) || 
00787				((FireRotation.Yaw < Rotation.Yaw) && (FireRotation.Yaw > Rotation.Yaw - 32768)) )
00788				FireRotation.Yaw = Rotation.Yaw - 8192;
00789			else
00790				FireRotation.Yaw = Rotation.Yaw + 8192;
00791		}
00792		viewRotation = FireRotation;			
00793		return FireRotation;
00794	}
00795	
00796	function WarnTarget(Pawn shooter, float projSpeed, vector FireDir)
00797	{
00798		local float enemyDist;
00799		local eAttitude att;
00800		local vector X,Y,Z, enemyDir;
00801		
00802		if ( health <= 0 )
00803			return;	
00804		att = AttitudeTo(shooter);
00805		if ( (att == ATTITUDE_Ignore) || (att == ATTITUDE_Threaten) )
00806		{
00807			if ( intelligence >= BRAINS_Mammal )
00808				damageAttitudeTo(shooter);
00809			if (att == ATTITUDE_Ignore)
00810				return;	
00811		}
00812		
00813		// AI controlled creatures may duck if not falling
00814		if ( !bCanDuck || (Enemy == None) || (Physics == PHYS_Falling) || (Physics == PHYS_Swimming) )
00815			return;
00816	
00817		if ( bIsPlayer )
00818		{
00819			if ( FRand() > 0.33 * skill )
00820				return;
00821		}
00822		else if ( FRand() > 0.55 + 0.15 * skill )
00823			return;
00824	
00825		// and projectile time is long enough
00826		enemyDist = VSize(shooter.Location - Location);
00827		if (enemyDist/projSpeed < 0.11 + 0.15 * FRand()) 
00828			return;
00829						
00830		// only if tight FOV
00831		GetAxes(Rotation,X,Y,Z);
00832		enemyDir = (shooter.Location - Location)/enemyDist;
00833		if ((enemyDir Dot X) < 0.8)
00834			return;
00835	
00836		if ( (FireDir Dot Y) > 0 )
00837		{
00838			Y *= -1;
00839			TryToDuck(Y, true);
00840		}
00841		else
00842			TryToDuck(Y, false);
00843	}
00844	
00845	function TryToDuck(vector DuckDir, bool bReversed)
00846	{
00847		//implemented in subclasses that can duck
00848	}
00849	
00850	/* TryToCrouch()
00851	See if far enough away, and geometry favorable for crouching
00852	*/
00853	function bool TryToCrouch()
00854	{
00855		local float ViewDist;
00856		local actor HitActor;
00857		local vector HitLocation, HitNormal, ViewSpot, StartSpot, ViewDir, Dir2D;
00858	
00859		bCrouching = false;
00860		if ( Enemy == None )
00861			return false;
00862		ViewDist = VSize(Location - Enemy.Location); 
00863		if ( ViewDist < 400 )
00864			return false;
00865		if ( FRand() < 0.3 )
00866			return true; 
00867	
00868		ViewSpot = Enemy.Location + Enemy.BaseEyeHeight * vect(0,0,1);
00869		StartSpot = Location - CollisionHeight * vect(0,0,0.5);
00870		ViewDir = (ViewSpot - StartSpot)/ViewDist;
00871		Dir2D = ViewDir;
00872		Dir2D.Z = 0;
00873		if ( (Dir2D Dot Vector(Rotation)) < 0.8 )
00874			return false;
00875		HitActor = Trace(HitLocation, HitNormal, StartSpot + 100 * ViewDir ,StartSpot, false);
00876		if ( HitActor == None )
00877			return false;
00878		bCrouching = true;
00879		return true;
00880	}
00881	
00882	// Can Stake Out - check if I can see my current Destination point, and so can enemy
00883	function bool CanStakeOut()
00884	{
00885		local vector HitLocation, HitNormal;
00886		local actor HitActor;
00887	
00888		if ( (Physics == PHYS_Flying) && !bCanStrafe )
00889			return false;
00890		if ( VSize(Enemy.Location - LastSeenPos) > 800 )
00891			return false;		
00892		
00893		HitActor = Trace(HitLocation, HitNormal, LastSeenPos, Location + EyeHeight * vect(0,0,1), false);
00894		if ( HitActor == None )
00895		{
00896			HitActor = Trace(HitLocation, HitNormal, LastSeenPos , Enemy.Location + Enemy.BaseEyeHeight * vect(0,0,1), false);
00897			return (HitActor == None);
00898		}
00899		return false;
00900	}
00901	
00902	function TriggerFirstHate()
00903	{
00904		local actor A;
00905	
00906		bFirstHatePlayer = true;
00907		if ( FirstHatePlayerEvent != '' )
00908			foreach allactors(class'Actor', A, FirstHatePlayerEvent)
00909				A.Trigger(self, enemy);				
00910	}				
00911	
00912	
00913	function bool SetEnemy( Pawn NewEnemy )
00914	{
00915		local bool result;
00916		local eAttitude newAttitude, oldAttitude;
00917		local bool noOldEnemy;
00918		local float newStrength;
00919	
00920		if ( !bCanWalk && !bCanFly && !NewEnemy.FootRegion.Zone.bWaterZone )
00921			return false;
00922		if ( (NewEnemy == Self) || (NewEnemy == None) || (NewEnemy.Health <= 0) )
00923			return false;
00924		if ( (PlayerPawn(NewEnemy) == None) && (ScriptedPawn(NewEnemy) == None) )
00925			return false;
00926	
00927		noOldEnemy = (Enemy == None);
00928		result = false;
00929		newAttitude = AttitudeTo(NewEnemy);
00930		//log ("Attitude to potential enemy is "$newAttitude);
00931		if ( !noOldEnemy )
00932		{
00933			if (Enemy == NewEnemy)
00934				return true;
00935			else if ( NewEnemy.bIsPlayer && (AlarmTag != '') )
00936			{
00937				OldEnemy = Enemy;
00938				Enemy = NewEnemy;
00939				result = true;
00940			} 
00941			else if ( newAttitude == ATTITUDE_Friendly )
00942			{
00943				if ( bIgnoreFriends )
00944					return false;
00945				if ( (NewEnemy.Enemy != None) && (NewEnemy.Enemy.Health > 0) ) 
00946				{
00947					if ( NewEnemy.Enemy.bIsPlayer && (NewEnemy.AttitudeToPlayer < AttitudeToPlayer) )
00948						AttitudeToPlayer = NewEnemy.AttitudeToPlayer;
00949					if ( AttitudeTo(NewEnemy.Enemy) < AttitudeTo(Enemy) )
00950					{
00951						OldEnemy = Enemy;
00952						Enemy = NewEnemy.Enemy;
00953						result = true;
00954					}
00955				}
00956			}
00957			else 
00958			{
00959				oldAttitude = AttitudeTo(Enemy);
00960				if ( (newAttitude < oldAttitude) || 
00961					( (newAttitude == oldAttitude) 
00962						&& ((VSize(NewEnemy.Location - Location) < VSize(Enemy.Location - Location)) 
00963							|| !LineOfSightTo(Enemy)) ) ) 
00964				{
00965					if ( bIsPlayer && Enemy.IsA('PlayerPawn') && !NewEnemy.IsA('PlayerPawn') )
00966					{
00967						newStrength = relativeStrength(NewEnemy);
00968						if ( (newStrength < 0.2) && (relativeStrength(Enemy) < FMin(0, newStrength))  
00969							&& (IsInState('Hunting')) && (Level.TimeSeconds - HuntStartTime < 5) )
00970							result = false;
00971						else
00972						{
00973							result = true;
00974							OldEnemy = Enemy;
00975							Enemy = NewEnemy;
00976						}
00977					} 
00978					else
00979					{
00980						result = true;
00981						OldEnemy = Enemy;
00982						Enemy = NewEnemy;
00983					}
00984				}
00985			}
00986		}
00987		else if ( newAttitude < ATTITUDE_Ignore )
00988		{
00989			result = true;
00990			Enemy = NewEnemy;
00991		}
00992		else if ( newAttitude == ATTITUDE_Friendly ) //your enemy is my enemy
00993		{
00994			//log("noticed a friend");
00995			if ( NewEnemy.bIsPlayer && (AlarmTag != '') )
00996			{
00997				Enemy = NewEnemy;
00998				result = true;
00999			} 
01000			if (bIgnoreFriends)
01001				return false;
01002	
01003			if ( (NewEnemy.Enemy != None) && (NewEnemy.Enemy.Health > 0) ) 
01004			{
01005				result = true;
01006				//log("his enemy is my enemy");
01007				Enemy = NewEnemy.Enemy;
01008				if (Enemy.bIsPlayer)
01009					AttitudeToPlayer = ScriptedPawn(NewEnemy).AttitudeToPlayer;
01010				else if ( (ScriptedPawn(NewEnemy) != None) && (ScriptedPawn(NewEnemy).Hated == Enemy) )
01011					Hated = Enemy;
01012			}
01013		}
01014	
01015		if ( result )
01016		{
01017			//log(class$" has new enemy - "$enemy.class);
01018			LastSeenPos = Enemy.Location;
01019			LastSeeingPos = Location;
01020			EnemyAcquired();
01021			if ( !bFirstHatePlayer && Enemy.bIsPlayer && (FirstHatePlayerEvent != '') )
01022				TriggerFirstHate();
01023		}
01024		else if ( NewEnemy.bIsPlayer && (NewAttitude < ATTITUDE_Threaten) )
01025			OldEnemy = NewEnemy;
01026					
01027		return result;
01028	}
01029	
01030	function Killed(pawn Killer, pawn Other, name damageType)
01031	{
01032		local Pawn aPawn;
01033		local ScriptedPawn ScriptedOther;
01034		local bool bFoundTeam;
01035	
01036		if ( Health <= 0 )
01037			return;
01038		if (Other.bIsPlayer)
01039			bCanBePissed = true;
01040			
01041		ScriptedOther = ScriptedPawn(Other);
01042		if ( (TeamTag != '') && (ScriptedOther != None) 
01043			&& (ScriptedOther.TeamTag == TeamTag) )
01044		{
01045			if ( ScriptedOther.bTeamLeader )
01046				TeamTag = '';
01047			else if ( ScriptedOther.TeamID < TeamID )
01048				TeamID--;
01049			else if ( bTeamLeader )
01050			{
01051				aPawn = Level.PawnList;
01052				while ( aPawn != None )
01053				{
01054					if ( (ScriptedPawn(aPawn) != None) && (ScriptedPawn(aPawn) != self) &&
01055						(ScriptedPawn(aPawn).TeamTag == TeamTag) )
01056					{
01057						bFoundTeam = true;
01058						break;
01059					}
01060					aPawn = aPawn.nextPawn;
01061				}
01062				if ( !bFoundTeam )
01063				{
01064					bTeamLeader = false;
01065					TeamTag = '';
01066				}
01067			}
01068		}			
01069		
01070		if ( OldEnemy == Other )
01071			OldEnemy = None;
01072	
01073		if ( Enemy == Other )
01074		{
01075			Enemy = None;
01076			if ( (Killer == self) && (OldEnemy == None) )
01077			{
01078				aPawn = Level.PawnList;
01079				while ( aPawn != None )
01080				{
01081					if ( (aPawn.IsA('PlayerPawn') || aPawn.IsA('ScriptedPawn'))
01082						&& (VSize(Location - aPawn.Location) < 500)
01083						&& CanSee(aPawn) )
01084					{
01085						if ( SetEnemy(aPawn) )
01086						{
01087							GotoState('Attacking');
01088							return;
01089						}
01090					}
01091					aPawn = aPawn.nextPawn;
01092				}	
01093				Target = Other;
01094				GotoState('VictoryDance'); 
01095			}
01096			else 
01097				GotoState('Attacking');
01098		}
01099	}	
01100	
01101	function damageAttitudeTo(pawn Other)
01102	{
01103		local eAttitude OldAttitude;
01104		
01105		if ( (Other == Self) || (Other == None) || (FlockPawn(Other) != None) )
01106			return;
01107		if( Other.bIsPlayer ) //change attitude to player
01108		{ //FIXME - also frenzy or run away against non-players
01109			if ( (Health < 30) && (Aggressiveness * FRand() > 0.5) )	
01110			{
01111				AttitudeToPlayer = ATTITUDE_Frenzy;
01112				Aggressiveness = 1.0;
01113			}
01114			else if (AttitudeToPlayer == ATTITUDE_Ignore) AttitudeToPlayer = ATTITUDE_Hate;
01115			else if (AttitudeToPlayer == ATTITUDE_Threaten) AttitudeToPlayer = ATTITUDE_Hate;
01116			else if (AttitudeToPlayer == ATTITUDE_Friendly) AttitudeToPlayer = ATTITUDE_Threaten;
01117		}
01118		else 
01119		{
01120			OldAttitude = AttitudeToCreature(Other);
01121			if (OldAttitude > ATTITUDE_Ignore )
01122				return;
01123			else if ( OldAttitude > ATTITUDE_Frenzy )
01124			{
01125				//log(class$" hates "$Other.class);
01126				Hated = Other;
01127			}
01128		}
01129		SetEnemy(Other);				
01130	}
01131	
01132	function EnemyAcquired()
01133	{
01134		//log(Class$" just acquired an enemy - no action");
01135	}
01136	
01137	/* RelativeStrength()
01138	returns a value indicating the relative strength of other
01139	0.0 = equal to self
01140	> 0 stronger than self
01141	< 0 weaker than self
01142	
01143	Since the result will be compared to the creature's aggressiveness, it should be
01144	on the same order of magnitude (-1 to 1)
01145	
01146	Assess based on health and weapon
01147	*/
01148	
01149	function float RelativeStrength(Pawn Other)
01150	{
01151		local float compare;
01152		local int adjustedStrength, adjustedOther;
01153		local int bTemp;
01154	
01155		adjustedStrength = health;
01156		adjustedOther = 0.5 * (Other.health + Other.Default.Health);	
01157		compare = 0.01 * float(adjustedOther - adjustedStrength);
01158		if ( Intelligence == BRAINS_Human )
01159		{
01160			if ( Weapon != None )
01161			{
01162				compare -= (Weapon.RateSelf(bTemp) - 0.3);
01163				if ( bIsPlayer && (Weapon.AIRating < 0.3) )
01164				{
01165					compare += 0.2;
01166					if ( (Other.Weapon != None) && (Other.Weapon.AIRating >= 0.3) )
01167						compare += 0.3;
01168				}
01169			}
01170			if ( Other.Weapon != None )
01171				compare += (Other.Weapon.RateSelf(bTemp) - 0.3);
01172		}
01173		//log(other.class$" relative strength to "$class$" is "$compare);
01174		return compare;
01175	}
01176		
01177	/* AttitudeTo()
01178	Returns the creature's attitude towards another Pawn
01179	*/
01180	function eAttitude AttitudeTo(Pawn Other)
01181	{
01182		if (Other.bIsPlayer)
01183		{
01184			if ( bIsPlayer && Level.Game.IsA('TeamGame') && (Team == Other.PlayerReplicationInfo.Team) )
01185				return ATTITUDE_Friendly;
01186			else if ( (Intelligence > BRAINS_None) && 
01187				((AttitudeToPlayer == ATTITUDE_Hate) || (AttitudeToPlayer == ATTITUDE_Threaten) 
01188					|| (AttitudeToPlayer == ATTITUDE_Fear)) ) //check if afraid 
01189			{
01190				if (RelativeStrength(Other) > Aggressiveness)
01191					AttitudeToPlayer = AttitudeWithFear();
01192				else if (AttitudeToPlayer == ATTITUDE_Fear)
01193					AttitudeToPlayer = ATTITUDE_Hate;
01194			}
01195			return AttitudeToPlayer;
01196		}
01197		else if (Hated == Other)
01198		{
01199			if (RelativeStrength(Other) >= Aggressiveness)
01200				return AttitudeWithFear();
01201			else 
01202				return ATTITUDE_Hate;
01203		}
01204		else if ( (TeamTag != '') && (ScriptedPawn(Other) != None) && (TeamTag == ScriptedPawn(Other).TeamTag) )
01205			return ATTITUDE_Friendly;
01206		else	
01207			return AttitudeToCreature(Other); 
01208	}
01209	
01210	
01211	/* AttitudeWithFear()
01212	may fear other, unless near home
01213	*/
01214	
01215	function eAttitude AttitudeWithFear()
01216	{
01217		local vector HitLocation, HitNormal;
01218		local actor HitActor;
01219		
01220		if ( Homebase(home) == None )
01221		{
01222			CombatStyle -= 1.0;
01223			return ATTITUDE_Hate;
01224		}
01225		else if ( VSize(home.Location - Location) < Homebase(home).extent )
01226		{
01227			HitActor =  Trace( HitLocation, HitNormal, home.Location, Location, false);
01228			if (HitActor == None)
01229				return ATTITUDE_Hate;
01230		}
01231	
01232		return ATTITUDE_Fear;
01233	}
01234	
01235	/* AttitudeToCreature
01236	Typically implemented in subclass
01237	*/
01238	
01239	function eAttitude AttitudeToCreature(Pawn Other)
01240	{
01241		if( Other.Class == Class )
01242			return ATTITUDE_Friendly;
01243		else
01244			return ATTITUDE_Ignore;
01245	}
01246	
01247	/* 
01248	Annoyed by another pawn, typically after bumping into it
01249	(only when not already fighting)
01250	*/
01251	function AnnoyedBy(Pawn Other)
01252	{
01253		if ( !bCanBePissed || Other.bIsPlayer || (Enemy != None) || (Aggressiveness < 0.4)
01254			|| (AttitudeTo(Other) != ATTITUDE_Ignore) || (FRand() > 0.2) )
01255			return;
01256	
01257		Hated = Other;
01258	}		
01259		
01260		
01261	function bool ChooseTeamAttackFor(ScriptedPawn TeamMember)
01262	{
01263		if ( (Enemy == None) && (TeamMember.Enemy != None) && LineOfSightTo(TeamMember) )
01264		{
01265			if (SetEnemy(TeamMember.Enemy))
01266				MakeNoise(1.0);
01267		}
01268	
01269		// speak order
01270		if ( !bTeamSpeaking )
01271			SpeakOrderTo(TeamMember);
01272		
01273		// set CombatStyle and Aggressiveness of TeamMember
01274		if ( TeamMember == Self )
01275		{
01276			ChooseLeaderAttack();
01277			return true;		
01278		}
01279		
01280		if ( TeamMember.bReadyToAttack )
01281		{
01282			////log("Attack!");
01283			TeamMember.Target = TeamMember.Enemy;
01284			If (VSize(Enemy.Location - Location) <= (TeamMember.MeleeRange + TeamMember.Enemy.CollisionRadius + TeamMember.CollisionRadius))
01285			{
01286				TeamMember.GotoState('MeleeAttack');
01287				return true;
01288			}
01289			else if (TeamMember.bMovingRangedAttack || (TeamMember.TeamID == 1) )
01290				TeamMember.SetTimer(TimeBetweenAttacks, False);
01291			else if (TeamMember.bHasRangedAttack && (TeamMember.bIsPlayer || TeamMember.Enemy.bIsPlayer) && TeamMember.CanFireAtEnemy() )
01292			{
01293				if ( !TeamMember.bIsPlayer || (3 * FRand() > Skill) )
01294				{
01295					TeamMember.GotoState('RangedAttack');
01296					return true;
01297				}
01298			}
01299		}
01300	
01301		if ( !TeamMember.bHasRangedAttack || (TeamMember.TeamID == 1) )
01302			TeamMember.GotoState('Charging');
01303		else if ( TeamMember.TeamID == 2 )
01304		{
01305			TeamMember.bStrafeDir = true;
01306			TeamMember.GotoState('TacticalMove', 'NoCharge'); 
01307		}
01308		else if ( TeamMember.TeamID == 3 )
01309		{
01310			TeamMember.bStrafeDir = false;
01311			TeamMember.GotoState('TacticalMove', 'NoCharge'); 
01312		}
01313		else
01314			TeamMember.GotoState('TacticalMove');
01315	
01316		return true;
01317	}
01318	
01319	function ChooseLeaderAttack()
01320	{
01321		if (bReadyToAttack && !bMovingRangedAttack)
01322			GotoState('RangedAttack');
01323		else
01324			GotoState('TacticalMove', 'NoCharge');
01325	}
01326	
01327	function bool MeleeDamageTarget(int hitdamage, vector pushdir)
01328	{
01329		local vector HitLocation, HitNormal, TargetPoint;
01330		local actor HitActor;
01331		
01332		// check if still in melee range
01333		If ( (VSize(Target.Location - Location) <= MeleeRange * 1.4 + Target.CollisionRadius + CollisionRadius)
01334			&& ((Physics == PHYS_Flying) || (Physics == PHYS_Swimming) || (Abs(Location.Z - Enemy.Location.Z) 
01335				<= FMax(CollisionHeight, Enemy.CollisionHeight) + 0.5 * FMin(CollisionHeight, Enemy.CollisionHeight))) )
01336		{	
01337			HitActor = Trace(HitLocation, HitNormal, Enemy.Location, Location, false);
01338			if ( HitActor != None )
01339				return false;
01340			Target.TakeDamage(hitdamage, Self,HitLocation, pushdir, 'hacked');
01341			return true;
01342		}
01343		return false;
01344	}
01345	
01346	function bool CanFireAtEnemy()
01347	{
01348		local vector HitLocation, HitNormal, EnemyDir, EnemyUp;
01349		local actor HitActor;
01350		local float EnemyDist;
01351			
01352		EnemyDir = Enemy.Location - Location;
01353		EnemyDist = VSize(EnemyDir);
01354		EnemyUp = Enemy.CollisionHeight * vect(0,0,0.9);
01355		if ( EnemyDist > 300 )
01356		{
01357			EnemyDir = 300 * EnemyDir/EnemyDist;
01358			EnemyUp = 300 * EnemyUp/EnemyDist;
01359		}
01360		
01361		HitActor = Trace(HitLocation, HitNormal, Location + EnemyDir + EnemyUp, Location, true);
01362	
01363		if ( (HitActor == None) || (HitActor == Enemy) 
01364			|| ((Pawn(HitActor) != None) && (AttitudeTo(Pawn(HitActor)) <= ATTITUDE_Ignore)) )
01365			return true;
01366	
01367		HitActor = Trace(HitLocation, HitNormal, Location + EnemyDir, Location, true);
01368	
01369		return ( (HitActor == None) || (HitActor == Enemy) 
01370				|| ((Pawn(HitActor) != None) && (AttitudeTo(Pawn(HitActor)) <= ATTITUDE_Ignore)) );
01371	}
01372	
01373	function PlayMeleeAttack()
01374	{
01375	log(self$" Error - PlayMeleeAttack should be implemented in subclass");
01376	}
01377	
01378	function PlayRangedAttack()
01379	{
01380	log(self$" Error - PlayRangedAttack should be implemented in subclass");
01381	}
01382	
01383	function PlayCombatMove()
01384	{
01385		if ( bMovingRangedAttack && bReadyToAttack && bCanFire && !NeedToTurn(Enemy.Location) )
01386		{
01387			Target = Enemy;
01388			PlayMovingAttack();
01389			if ( FRand() > 0.5 * (0.5 + skill * 0.25 + ReFireRate) )
01390			{
01391				bReadyToAttack = false;
01392				SetTimer(TimeBetweenAttacks  * (1.0 + FRand()),false); 
01393			}
01394		}		
01395		else 
01396		{
01397			if ( !bReadyToAttack && (TimerRate == 0.0) )
01398				SetTimer(0.7, false);
01399			PlayRunning();
01400		}
01401	}
01402	
01403	function float StrafeAdjust()
01404	{
01405		local vector Focus2D, Loc2D, Dest2D;
01406		local float strafemag; 
01407	
01408		Focus2D = Focus;
01409		Focus2D.Z = 0;
01410		Loc2D = Location;
01411		Loc2D.Z = 0;
01412		Dest2D = Destination;
01413		Dest2D.Z = 0;
01414		strafeMag = Abs( Normal(Focus2D - Loc2D) dot Normal(Dest2D - Loc2D) );
01415	
01416		return ((strafeMag - 2.0)/GroundSpeed);
01417	}
01418	
01419	function Trigger( actor Other, pawn EventInstigator )
01420	{
01421		local Pawn currentEnemy;
01422	
01423		if ( (Other == Self) || (Health <= 0) )
01424			return;
01425		if ( bHateWhenTriggered )
01426		{
01427			if ( EventInstigator.bIsPlayer)
01428				AttitudeToPlayer = ATTITUDE_Hate;
01429			else
01430				Hated = EventInstigator;
01431		}
01432		currentEnemy = Enemy;
01433		SetEnemy(EventInstigator);
01434		if (Enemy != currentEnemy)
01435		{	
01436			PlayAcquisitionSound();
01437			GotoState('Attacking');
01438		}
01439	}
01440	
01441	//**********************************************************************************
01442	//Base Monster AI
01443	
01444	auto state StartUp
01445	{
01446		function InitAmbushLoc()
01447		{
01448			local Ambushpoint newspot;
01449			local float i;
01450			local rotator newRot;
01451		
01452			i = 1.0; 
01453			foreach AllActors( class 'Ambushpoint', newspot, tag )
01454			{
01455				if ( !newspot.taken )
01456				{
01457					i = i + 1;
01458					if (FRand() < 1.0/i)
01459						OrderObject = newspot;
01460				}
01461			}
01462			if (OrderObject != None)
01463				Ambushpoint(OrderObject).Accept(self,None);
01464		}
01465			
01466		function InitPatrolLoc()
01467		{
01468			local Patrolpoint newspot;
01469			/* IMPLEMENT by Walking throught patrol */
01470		}
01471		
01472		function SetHome()
01473		{
01474			local NavigationPoint aNode;
01475	
01476			aNode = Level.NavigationPointList;
01477	
01478			while ( aNode != None )
01479			{
01480				if ( aNode.IsA('HomeBase') && (aNode.tag == tag) )
01481				{
01482					home = HomeBase(aNode);
01483					return;
01484				}
01485				aNode = aNode.nextNavigationPoint;
01486			}
01487		}
01488		
01489		function SetTeam()
01490		{
01491			local Pawn aPawn;
01492			local bool bFoundTeam;
01493			if (bTeamLeader)
01494			{
01495				TeamLeader = self;
01496				return;
01497			}
01498			TeamID = 1;
01499			aPawn = Level.PawnList;
01500			while ( aPawn != None )
01501			{
01502				if ( (ScriptedPawn(aPawn) != None) && (aPawn != self) && (ScriptedPawn(aPawn).TeamTag == TeamTag) )
01503				{
01504					if ( ScriptedPawn(aPawn).bTeamLeader )
01505					{
01506						bFoundTeam = true;
01507						TeamLeader = ScriptedPawn(aPawn);
01508					}
01509					if ( ScriptedPawn(aPawn).TeamID >= TeamID )
01510						TeamID = ScriptedPawn(aPawn).TeamID + 1;
01511				}
01512				aPawn = aPawn.nextPawn;
01513			}
01514			if ( !bFoundTeam )
01515				TeamTag = ''; //didn't find a team leader, so no team
01516		}
01517		
01518		function SetAlarm()
01519		{
01520			local Pawn aPawn, currentWinner;
01521			local float i;
01522		
01523			currentWinner = self;
01524			i = 1.0; 
01525		
01526			aPawn = Level.PawnList;
01527			while ( aPawn != None )
01528			{
01529				if ( aPawn.IsA('ScriptedPawn') && (ScriptedPawn(aPawn).SharedAlarmTag == SharedAlarmTag) )
01530				{
01531					ScriptedPawn(aPawn).SharedAlarmTag = '';
01532					i += 1;
01533					if (FRand() < 1.0/i)
01534						currentWinner = aPawn;
01535				}
01536				aPawn = aPawn.nextPawn;
01537			}
01538			
01539			ScriptedPawn(currentWinner).AlarmTag = SharedAlarmTag;
01540			SharedAlarmTag = '';
01541		}
01542			
01543		function BeginState()
01544		{
01545			SetMovementPhysics(); 
01546			if (Physics == PHYS_Walking)
01547				SetPhysics(PHYS_Falling);
01548		}
01549	
01550	Begin:
01551		SetHome();
01552		if (SharedAlarmTag != '')
01553			SetAlarm();
01554		if (TeamTag != '')
01555			SetTeam();
01556		if (Orders == 'Guarding')
01557		{
01558			OrderObject = Spawn(class 'GuardPoint');
01559			if (OrderTag != '')
01560				Tag = OrderTag; //so will be triggered if guarded object is touched
01561		}
01562		else if (!bFixedStart)
01563		{
01564			if (Orders == 'Patroling')
01565				InitPatrolLoc();
01566			else if (Orders == 'Ambushing')
01567				InitAmbushLoc();
01568		}
01569		
01570		if (Orders != '')
01571		{
01572			if (Orders == 'Attacking')
01573			{
01574				Orders = '';
01575				if (enemy != None)
01576					GotoState('Attacking');
01577				else
01578					StartRoaming();
01579			}
01580			else if ( bDelayedPatrol && (Orders == 'Patroling') )
01581				GotoState('Patroling', 'DelayedPatrol'); 
01582			else
01583				GotoState(Orders);
01584			if ( Physics == PHYS_Falling )
01585				SetFall();
01586			else
01587				SetMovementPhysics();
01588		}
01589		else
01590			GotoState('Waiting');
01591	}
01592	
01593	state Waiting
01594	{
01595		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
01596								Vector momentum, name damageType)
01597		{
01598			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
01599			if ( health <= 0 )
01600				return;
01601			if ( Enemy != None )
01602				LastSeenPos = Enemy.Location;
01603			if (NextState == 'TakeHit')
01604			{
01605				NextState = 'Attacking'; 
01606				NextLabel = 'Begin';
01607				GotoState('TakeHit'); 
01608			}
01609			else if ( Enemy != None )
01610				GotoState('Attacking');
01611		}
01612	
01613		function Bump(actor Other)
01614		{
01615			//log(Other.class$" bumped "$class);
01616			if (Pawn(Other) != None)
01617			{
01618				if (Enemy == Other)
01619					bReadyToAttack = True; //can melee right away
01620				SetEnemy(Pawn(Other));
01621			}
01622			if ( TimerRate <= 0 )
01623				setTimer(1.5, false);
01624			Disable('Bump');
01625		}
01626		
01627		function Timer()
01628		{
01629			Enable('Bump');
01630		}
01631		
01632		function EnemyAcquired()
01633		{
01634			GotoState('Acquisition', 'PlayOut');
01635		}
01636		
01637		function AnimEnd()
01638		{
01639			PlayWaiting();
01640			bStasis = true;
01641		}
01642	 
01643		function Landed(vector HitNormal)
01644		{
01645			SetPhysics(PHYS_None);
01646		}
01647	
01648		function BeginState()
01649		{
01650			Enemy = None;
01651			bStasis = false;
01652			Acceleration = vect(0,0,0);
01653			SetAlertness(0.0);
01654		}
01655	
01656	TurnFromWall:
01657		if ( NearWall(2 * CollisionRadius + 50) )
01658		{
01659			PlayTurning();
01660			TurnTo(Focus);
01661		}
01662	Begin:
01663		TweenToWaiting(0.4);
01664		bReadyToAttack = false;
01665		DesiredRotation = rot(0,0,0);
01666		DesiredRotation.Yaw = Rotation.Yaw;
01667		SetRotation(DesiredRotation);
01668		if (Physics != PHYS_Falling) 
01669			SetPhysics(PHYS_None);
01670	KeepWaiting:
01671		NextAnim = '';
01672	}
01673	
01674	state Roaming
01675	{
01676		ignores EnemyNotVisible;
01677	
01678		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
01679								Vector momentum, name damageType)
01680		{
01681			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
01682			if ( health <= 0 )
01683				return;
01684	
01685			if ( Enemy != None )
01686			{
01687				LastSeenPos = Enemy.Location;
01688				if ( NextState == 'TakeHit' )
01689				{
01690					NextState = 'Attacking'; //default
01691					NextLabel = 'Begin';
01692					GotoState('TakeHit'); 
01693				}
01694				else
01695					GotoState('Attacking');
01696			}
01697		}
01698	
01699		function FearThisSpot(Actor aSpot)
01700		{
01701			Destination = Location + 120 * Normal(Location - aSpot.Location); 
01702			GotoState('Wandering', 'Moving');
01703		}
01704		
01705		function Timer()
01706		{
01707			Enable('Bump');
01708		}
01709	
01710		function Bump(Actor Other)
01711		{
01712			if ( FRand() < 0.03)
01713				GotoState('Wandering');
01714			else
01715				Super.Bump(Other);
01716		}
01717	
01718		function SetFall()
01719		{
01720			NextState = 'Roaming'; 
01721			NextLabel = 'ContinueRoam';
01722			NextAnim = AnimSequence;
01723			GotoState('FallingState'); 
01724		}
01725	
01726		function EnemyAcquired()
01727		{
01728			GotoState('Acquisition');
01729		}
01730	
01731		function HitWall(vector HitNormal, actor Wall)
01732		{
01733			if (Physics == PHYS_Falling)
01734				return;
01735			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
01736			{
01737				bSpecialGoal = true;
01738				if ( SpecialPause > 0 )
01739					Acceleration = vect(0,0,0);
01740				GotoState('Roaming', 'Moving');
01741				return;
01742			}
01743			Focus = Destination;
01744			if (PickWallAdjust())
01745				GotoState('Roaming', 'AdjustFromWall');
01746			else
01747				MoveTimer = -1.0;
01748		}
01749		
01750		function PickDestination()
01751		{
01752			local Actor path;
01753			if ((OrderObject == None) || actorReachable(OrderObject))
01754			{
01755				numHuntPaths = 0;
01756				OrderObject = FindRandomDest();
01757				if ( OrderObject != None )
01758					GotoState('Roaming', 'Pausing');
01759				else
01760					GotoState('Wandering');
01761				return;
01762			}
01763			numHuntPaths++;
01764			if ( numHuntPaths > 80 )
01765				GotoState('Wandering');
01766			if (SpecialGoal != None)
01767				path = FindPathToward(SpecialGoal);
01768			else if (OrderObject != None)
01769				path = FindPathToward(OrderObject);
01770			else
01771				path = None;
01772				
01773			if (path != None)
01774			{
01775				MoveTarget = path;
01776				Destination = path.Location;
01777			}
01778			else 
01779				GotoState('Wandering');
01780		}
01781		
01782		function BeginState()
01783		{
01784			SpecialGoal = None;
01785			bSpecialGoal = false;
01786			SpecialPause = 0.0;
01787			Enemy = None;
01788			SetAlertness(0.2);
01789			bReadyToAttack = false;
01790		}
01791			
01792	Begin:
01793		//log(class$" Roaming");
01794	
01795	Roam:
01796		TweenToWalking(0.15);
01797		NextAnim = '';
01798		WaitForLanding();
01799		PickDestination();
01800		FinishAnim();
01801		PlayWalking();
01802		
01803	Moving:
01804		if (SpecialPause > 0.0)
01805		{
01806			Acceleration = vect(0,0,0);
01807			TweenToPatrolStop(0.3);
01808			Sleep(SpecialPause);
01809			SpecialPause = 0.0;
01810			TweenToWalking(0.1);
01811			FinishAnim();
01812			PlayWalking();
01813		}
01814		MoveToward(MoveTarget, WalkingSpeed);
01815		if ( bSpecialGoal )
01816		{
01817			bSpecialGoal = false;
01818			Goto('Roam');
01819		}
01820		Acceleration = vect(0,0,0);
01821		TweenToPatrolStop(0.3);
01822		FinishAnim();
01823		NextAnim = '';
01824	Pausing:
01825		Acceleration = vect(0,0,0);
01826		PlayPatrolStop();
01827		FinishAnim();
01828		if ( !bQuiet && (FRand() < 0.3) )
01829			PlayRoamingSound();
01830		Goto('Roam');
01831	
01832	ContinueRoam:
01833		FinishAnim();
01834		PlayWalking();
01835		Goto('Roam');
01836	
01837	AdjustFromWall:
01838		StrafeTo(Destination, Focus); 
01839		Destination = Focus; 
01840		Goto('Moving');
01841	}
01842	
01843	state Wandering
01844	{
01845		ignores EnemyNotVisible;
01846	
01847		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
01848							Vector momentum, name damageType)
01849		{
01850			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
01851			if ( health <= 0 )
01852				return;
01853			if ( Enemy != None )
01854				LastSeenPos = Enemy.Location;
01855	
01856			if ( NextState == 'TakeHit' )
01857				{
01858				NextState = 'Attacking'; 
01859				NextLabel = 'Begin';
01860				GotoState('TakeHit'); 
01861				}
01862			else
01863				GotoState('Attacking');
01864		}
01865	
01866		function Timer()
01867		{
01868			Enable('Bump');
01869		}
01870	
01871		function SetFall()
01872		{
01873			NextState = 'Wandering'; 
01874			NextLabel = 'ContinueWander';
01875			NextAnim = AnimSequence;
01876			GotoState('FallingState'); 
01877		}
01878	
01879		function EnemyAcquired()
01880		{
01881			GotoState('Acquisition');
01882		}
01883	
01884		function HitWall(vector HitNormal, actor Wall)
01885		{
01886			if (Physics == PHYS_Falling)
01887				return;
01888			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
01889			{
01890				if ( SpecialPause > 0 )
01891					Acceleration = vect(0,0,0);
01892				GotoState('Wandering', 'Pausing');
01893				return;
01894			}
01895			Focus = Destination;
01896			if (PickWallAdjust())
01897				GotoState('Wandering', 'AdjustFromWall');
01898			else
01899				MoveTimer = -1.0;
01900		}
01901		
01902		function bool TestDirection(vector dir, out vector pick)
01903		{	
01904			local vector HitLocation, HitNormal, dist;
01905			local float minDist;
01906			local actor HitActor;
01907	
01908			minDist = FMin(150.0, 4*CollisionRadius);
01909			pick = dir * (minDist + (450 + 12 * CollisionRadius) * FRand());
01910	
01911			HitActor = Trace(HitLocation, HitNormal, Location + pick + 1.5 * CollisionRadius * dir , Location, false);
01912			if (HitActor != None)
01913			{
01914				pick = HitLocation + (HitNormal - dir) * 2 * CollisionRadius;
01915				HitActor = Trace(HitLocation, HitNormal, pick , Location, false);
01916				if (HitActor != None)
01917					return false;
01918			}
01919			else
01920				pick = Location + pick;
01921			 
01922			dist = pick - Location;
01923			if (Physics == PHYS_Walking)
01924				dist.Z = 0;
01925			
01926			return (VSize(dist) > minDist); 
01927		}
01928				
01929		function PickDestination()
01930		{
01931			local vector pick, pickdir;
01932			local bool success;
01933			local float XY;
01934			//Favor XY alignment
01935			XY = FRand();
01936			if (XY < 0.3)
01937			{
01938				pickdir.X = 1;
01939				pickdir.Y = 0;
01940			}
01941			else if (XY < 0.6)
01942			{
01943				pickdir.X = 0;
01944				pickdir.Y = 1;
01945			}
01946			else
01947			{
01948				pickdir.X = 2 * FRand() - 1;
01949				pickdir.Y = 2 * FRand() - 1;
01950			}
01951			if (Physics != PHYS_Walking)
01952			{
01953				pickdir.Z = 2 * FRand() - 1;
01954				pickdir = Normal(pickdir);
01955			}
01956			else
01957			{
01958				pickdir.Z = 0;
01959				if (XY >= 0.6)
01960					pickdir = Normal(pickdir);
01961			}	
01962	
01963			success = TestDirection(pickdir, pick);
01964			if (!success)
01965				success = TestDirection(-1 * pickdir, pick);
01966			
01967			if (success)	
01968				Destination = pick;
01969			else
01970				GotoState('Wandering', 'Turn');
01971		}
01972	
01973		function AnimEnd()
01974		{
01975			PlayPatrolStop();
01976		}
01977	
01978		function FearThisSpot(Actor aSpot)
01979		{
01980			Destination = Location + 120 * Normal(Location - aSpot.Location); 
01981		}
01982	
01983		function BeginState()
01984		{
01985			Enemy = None;
01986			SetAlertness(0.2);
01987			bReadyToAttack = false;
01988			Disable('AnimEnd');
01989			NextAnim = '';
01990			bCanJump = false;
01991		}
01992		
01993		function EndState()
01994		{
01995			if (JumpZ > 0)
01996				bCanJump = true;
01997		}
01998	
01999	Begin:
02000		//log(class$" Wandering");
02001	
02002	Wander: 
02003		TweenToWalking(0.15);
02004		WaitForLanding();
02005		PickDestination();
02006		FinishAnim();
02007		PlayWalking();
02008		
02009	Moving:
02010		Enable('HitWall');
02011		MoveTo(Destination, WalkingSpeed);
02012	Pausing:
02013		Acceleration = vect(0,0,0);
02014		if ( NearWall(2 * CollisionRadius + 50) )
02015		{
02016			PlayTurning();
02017			TurnTo(Focus);
02018		}
02019		if (FRand() < 0.3)
02020			PlayRoamingSound();
02021		Enable('AnimEnd');
02022		NextAnim = '';
02023		TweenToPatrolStop(0.2);
02024		Sleep(1.0);
02025		Disable('AnimEnd');
02026		FinishAnim();
02027		Goto('Wander');
02028	
02029	ContinueWander:
02030		FinishAnim();
02031		PlayWalking();
02032		if ( !bQuiet && (FRand() < 0.3) )
02033			PlayRoamingSound();
02034		if (FRand() < 0.2)
02035			Goto('Turn');
02036		Goto('Wander');
02037	
02038	Turn:
02039		Acceleration = vect(0,0,0);
02040		PlayTurning();
02041		TurnTo(Location + 20 * VRand());
02042		Goto('Pausing');
02043	
02044	AdjustFromWall:
02045		StrafeTo(Destination, Focus); 
02046		Destination = Focus; 
02047		Goto('Moving');
02048	}
02049	
02050	State Patroling
02051	{
02052		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
02053							Vector momentum, name damageType)
02054		{
02055			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
02056			if ( health <= 0 )
02057				return;
02058			LastSeenPos = Enemy.Location;
02059			if (NextState == 'TakeHit')
02060			{
02061				NextState = 'Attacking'; 
02062				NextLabel = 'Begin';
02063				GotoState('TakeHit'); 
02064			}
02065			else if ( Enemy != None )
02066				GotoState('Attacking');
02067		}
02068	
02069		function SetFall()
02070		{
02071			NextState = 'Patroling'; 
02072			NextLabel = 'ResumePatrol';
02073			NextAnim = AnimSequence;
02074			GotoState('FallingState'); 
02075		}
02076	
02077		function HitWall(vector HitNormal, actor Wall)
02078		{
02079			if (Physics == PHYS_Falling)
02080				return;
02081			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
02082			{
02083				if ( SpecialPause > 0 )
02084					Acceleration = vect(0,0,0);
02085				GotoState('Patroling', 'SpecialNavig');
02086				return;
02087			}
02088			Focus = Destination;
02089			if (PickWallAdjust())
02090				GotoState('Patroling', 'AdjustFromWall');
02091			else
02092				MoveTimer = -1.0;
02093		}
02094	
02095		function Trigger( actor Other, pawn EventInstigator )
02096		{
02097			if ( bDelayedPatrol )
02098			{
02099				if ( bHateWhenTriggered )
02100				{
02101					if ( EventInstigator.bIsPlayer)
02102						AttitudeToPlayer = ATTITUDE_Hate;
02103					else
02104						Hated = EventInstigator;
02105				}
02106				GotoState('Patroling', 'Patrol');
02107			}
02108			else
02109				Global.Trigger(Other, EventInstigator);
02110		}
02111		
02112		function Timer()
02113		{
02114			Enable('Bump');
02115		}
02116		
02117		function AnimEnd()
02118		{
02119			PlayPatrolStop();
02120		}
02121	
02122		function EnemyAcquired()
02123		{
02124			//log(Class$" just acquired an enemy");
02125			GotoState('Acquisition');
02126		}
02127	
02128		function PickDestination()
02129		{
02130			local Actor path;
02131			
02132			path = None;
02133			if (SpecialGoal != None)
02134				path = FindPathToward(SpecialGoal);
02135			else if ( OrderObject != None )
02136				path = FindPathToward(OrderObject);
02137			if (path != None)
02138			{
02139				MoveTarget = path;
02140				Destination = path.Location;
02141			}
02142			else
02143				OrderObject = None;
02144		}
02145	
02146		function FindNextPatrol()
02147		{
02148			local PatrolPoint pat;
02149			if ( (PatrolPoint(OrderObject) != None) && (PatrolPoint(OrderObject).nextPatrol == OrderTag) )
02150				OrderObject = PatrolPoint(OrderObject).NextPatrolPoint;
02151			else
02152			{
02153				foreach AllActors( class 'Patrolpoint', pat, OrderTag )
02154				{
02155					OrderObject = pat;
02156					return;
02157				}
02158			}
02159		}
02160	
02161		function BeginState()
02162		{
02163			SpecialGoal = None;
02164			SpecialPause = 0.0;
02165			Enemy = None;
02166			NextAnim = '';
02167			Disable('AnimEnd');
02168			SetAlertness(0.0);
02169			bReadyToAttack = (FRand() < 0.3 + 0.2 * skill); 
02170		}
02171	
02172	
02173	AdjustFromWall:
02174		StrafeTo(Destination, Focus); 
02175		Destination = Focus; 
02176		MoveTo(Destination);
02177		Goto('MoveToPatrol');
02178	
02179	ResumePatrol:
02180		if (MoveTarget != None)
02181		{
02182			PlayWalking();
02183			MoveToward(MoveTarget, WalkingSpeed);
02184			Goto('ReachedPatrol');
02185		}
02186		else
02187			Goto('Patrol');
02188				
02189	Begin:
02190		sleep(0.1);
02191	
02192	Patrol: //FIXME -add stasis mode? - also set random start point in roam area
02193		WaitForLanding();
02194		FindNextPatrol();
02195		Disable('AnimEnd');
02196		if (PatrolPoint(OrderObject) != None)
02197		{
02198			////log("Move to next patrol point");
02199			if ( !bQuiet && (FRand() < 0.4) )
02200				PlayRoamingSound();
02201			TweenToWalking(0.3);
02202			FinishAnim();
02203			PlayWalking();
02204			numHuntPaths = 0;
02205	
02206	MoveToPatrol:
02207			if (actorReachable(OrderObject))
02208				MoveToward(OrderObject, WalkingSpeed);
02209			else
02210			{
02211				PickDestination();
02212				if (OrderObject != None)
02213				{
02214	SpecialNavig:
02215					if (SpecialPause > 0.0)
02216					{
02217						Acceleration = vect(0,0,0);
02218						TweenToPatrolStop(0.3);
02219						Sleep(SpecialPause);
02220						SpecialPause = 0.0;
02221						TweenToWalking(0.1);
02222						FinishAnim();
02223						PlayWalking();
02224					}
02225					numHuntPaths++;
02226					MoveToward(MoveTarget, WalkingSpeed);
02227					if ( numHuntPaths < 30 )
02228						Goto('MoveToPatrol');
02229					else
02230						Goto('GiveUp');
02231				}
02232				else
02233					Goto('GiveUp');
02234			}
02235	
02236	ReachedPatrol:		
02237			////log("Got to patrol point "$OrderTag);	
02238			OrderTag = Patrolpoint(OrderObject).Nextpatrol;
02239			////log("Next patrol point "$OrderTag);	
02240			if ( Patrolpoint(OrderObject).pausetime > 0.0 )
02241			{
02242				////log("Pause patrol");
02243				Acceleration = vect(0,0,0);
02244				TweenToFighter(0.2);
02245				FinishAnim();
02246				PlayTurning();
02247				TurnTo(Location + (Patrolpoint(OrderObject)).lookdir);
02248				if ( Patrolpoint(OrderObject).PatrolAnim != '')
02249				{
02250					TweenAnim( Patrolpoint(OrderObject).PatrolAnim, 0.3);
02251					FinishAnim();
02252					Patrolpoint(OrderObject).AnimCount = Patrolpoint(OrderObject).numAnims;
02253					While ( Patrolpoint(OrderObject).AnimCount > 0 )
02254					{
02255						Patrolpoint(OrderObject).AnimCount--;
02256						if (Patrolpoint(OrderObject).PatrolSound != None )
02257							PlaySound( Patrolpoint(OrderObject).PatrolSound ); 
02258						PlayAnim(Patrolpoint(OrderObject).PatrolAnim);
02259						FinishAnim();
02260					}
02261				}
02262				else
02263				{
02264					TweenToPatrolStop(0.3);
02265					FinishAnim();
02266					Enable('AnimEnd');
02267					NextAnim = '';
02268					PlayPatrolStop();
02269					////log("stop here for "$(Patrolpoint(OrderObject)).pausetime);
02270					Sleep((Patrolpoint(OrderObject)).pausetime);
02271					Disable('AnimEnd');
02272					FinishAnim();
02273				}
02274			}
02275			Goto('Patrol');
02276		}
02277	
02278	GiveUp:
02279			//log(self$" gave up patrol");
02280			Acceleration = vect(0,0,0);		
02281			TweenToPatrolStop(0.3);
02282			FinishAnim();
02283	DelayedPatrol:
02284			Enable('AnimEnd');
02285			PlayPatrolStop();
02286	}
02287	
02288	state Guarding
02289	{
02290		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
02291								Vector momentum, name damageType)
02292		{
02293			LastSeenPos = Enemy.Location;
02294			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
02295			if ( health <= 0 )
02296				return;
02297			if (NextState == 'TakeHit')
02298			{
02299				NextState = 'Attacking'; 
02300				NextLabel = 'Begin';
02301				GotoState('TakeHit'); 
02302			}
02303			else if ( Enemy != None )
02304				GotoState('Attacking');
02305		}
02306		
02307		function Timer()
02308		{
02309			Enable('Bump');
02310		}
02311	
02312		function EnemyAcquired()
02313		{
02314			GotoState('Acquisition');
02315		}
02316		
02317		function AnimEnd()
02318		{
02319			PlayPatrolStop();
02320		}
02321	
02322		function HitWall(vector HitNormal, actor Wall)
02323		{
02324			if (Physics == PHYS_Falling)
02325				return;
02326			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
02327			{
02328				if ( SpecialPause > 0 )
02329					Acceleration = vect(0,0,0);
02330				GotoState('Guarding', 'SpecialNavig');
02331				return;
02332			}
02333			Focus = Destination;
02334			if (PickWallAdjust())
02335				GotoState('Guarding', 'AdjustFromWall');
02336			else
02337				MoveTimer = -1.0;
02338		}
02339		
02340		function PickDestination()
02341		{
02342			local Actor path;
02343			
02344			path = None;
02345			if (SpecialGoal != None)
02346				path = FindPathToward(SpecialGoal);
02347			else if ( OrderObject != None )
02348				path = FindPathTo(OrderObject.Location);
02349			//log("Next path is "$path);
02350			if (path != None)
02351			{
02352				MoveTarget = path;
02353				Destination = path.Location;
02354			}
02355			else
02356				StartRoaming();
02357		}
02358		
02359		function SetFall()
02360		{
02361			NextState = 'Guarding'; 
02362			NextLabel = 'Begin';
02363			NextAnim = AnimSequence;
02364			GotoState('FallingState'); 
02365		}
02366		
02367		function BeginState()
02368		{
02369			SpecialGoal = None;
02370			SpecialPause = 0.0;
02371			Enemy = None;
02372			NextAnim = '';
02373			SetAlertness(0.0);
02374		}
02375	
02376	AdjustFromWall:
02377		StrafeTo(Destination, Focus); 
02378		Destination = Focus; 
02379		MoveTo(Destination);
02380		Goto('GoToGuard');
02381		
02382	Begin:
02383		//log(class$" guarding "$OrderObject);
02384		Disable('AnimEnd');	
02385	
02386	GoToGuard:
02387		if ( VSize(Location - OrderObject.Location) < 2 * CollisionRadius)
02388			Goto('Turn');
02389		TweenToRunning(0.2);
02390		FinishAnim();
02391		PlayRunning();
02392		WaitForLanding();
02393		if (actorReachable(OrderObject))
02394			MoveToward(OrderObject, FMax(0.75, WalkingSpeed)); 
02395		else
02396		{
02397			PickDestination();
02398	SpecialNavig:
02399			if (SpecialPause > 0.0)
02400			{
02401				Acceleration = vect(0,0,0);
02402				TweenToPatrolStop(0.3);
02403				Sleep(SpecialPause);
02404				SpecialPause = 0.0;
02405				TweenToRunning(0.1);
02406				FinishAnim();
02407				PlayRunning();
02408			}			
02409			MoveToward(MoveTarget);
02410		}
02411		Goto('GoToGuard');
02412		
02413	Turn:
02414		//log(class$" got to guardpoint");
02415		Acceleration = vect(0,0,0);
02416		TweenToFighter(0.3);
02417		FinishAnim();
02418		PlayTurning();
02419		TurnTo( Location + 1000 * vector(OrderObject.Rotation) );
02420	
02421		NextAnim = '';
02422		bReadyToAttack = false;
02423		TweenToPatrolStop(0.2);
02424		FinishAnim();
02425		Enable('AnimEnd');
02426		NextAnim = '';
02427		PlayPatrolStop();
02428		DesiredRotation = rot(0,0,0);
02429		DesiredRotation.Yaw = Rotation.Yaw;
02430		setRotation(DesiredRotation);
02431		if (Physics != PHYS_Falling) 
02432			SetPhysics(PHYS_None);
02433	}
02434	
02435	state Ambushing
02436	{
02437		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
02438								Vector momentum, name damageType)
02439		{
02440			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
02441			if ( health <= 0 )
02442				return;
02443			if ( Enemy != None )
02444				LastSeenPos = Enemy.Location;
02445	
02446			if (NextState == 'TakeHit')
02447			{
02448				NextState = 'Attacking'; 
02449				NextLabel = 'Begin';
02450				GotoState('TakeHit'); 
02451			}
02452			else if ( Enemy != None )
02453				GotoState('Attacking');
02454		}
02455	
02456		function Landed(vector HitNormal)
02457		{
02458			SetPhysics(PHYS_None);
02459		}
02460	
02461		function SetFall()
02462		{
02463			NextState = 'Ambushing'; 
02464			NextLabel = 'Begin';
02465			NextAnim = AnimSequence;
02466			GotoState('FallingState'); 
02467		}
02468		
02469		function Timer()
02470		{
02471			Enable('Bump');
02472		}
02473	
02474		function AnimEnd()
02475		{
02476			PlayWaitingAmbush();
02477		}
02478		
02479		function EnemyAcquired()
02480		{
02481			local Ambushpoint oldspot;
02482		
02483			//log(Class$" just acquired an enemy");
02484			MakeNoise(1.0);
02485			oldspot = Ambushpoint(OrderObject);
02486			if (oldspot != None)
02487				oldspot.taken = false;
02488			SetMovementPhysics();
02489			GotoState('Attacking');
02490		}
02491	
02492		function FindAmbush()
02493		{
02494			local Ambushpoint newspot;
02495			local float count;
02496			count = 0;
02497			//FIXME- instead of looking for pawn's spots, look for any nearby spot
02498			foreach AllActors( class 'Ambushpoint', newspot, tag )
02499			{
02500				if ( !newspot.taken )
02501				{
02502					count += 1;
02503					if (FRand() < 1/count)
02504						OrderObject = newspot;
02505				}
02506			}
02507		}
02508		
02509		function BeginState()
02510		{
02511			Disable('AnimEnd');
02512			SpecialGoal = None;
02513			SpecialPause = 0.0;
02514			OldEnemy = Enemy;
02515			Enemy = None;
02516			SetAlertness(0.3);
02517		}
02518		
02519	FindAmbushSpot:
02520		if ((OldEnemy == None) || (FRand() < 0.7))
02521		{
02522			FindAmbush();
02523			if (Ambushpoint(OrderObject) != None)
02524			{
02525				//log("move to ambush spot");
02526				Disable('Landed');
02527				OldEnemy = None;
02528				Ambushpoint(OrderObject).taken = true;
02529				SetMovementPhysics();
02530				TweenToRunning(0.2);
02531				FinishAnim();
02532				PlayRunning();
02533	MoveToAmbush:
02534				WaitForLanding();
02535				if (actorReachable(OrderObject))
02536					MoveToward(OrderObject);
02537				else
02538				{
02539					if (SpecialGoal != None)
02540						MoveTarget = FindPathToward(SpecialGoal);
02541					else
02542						MoveTarget = FindPathToward(OrderObject);
02543					if (MoveTarget != None)
02544					{
02545	SpecialNavig:
02546						if (SpecialPause > 0.0)
02547						{
02548							Acceleration = vect(0,0,0);
02549							TweenToPatrolStop(0.3);
02550							Sleep(SpecialPause);
02551							SpecialPause = 0.0;
02552							TweenToRunning(0.1);
02553							FinishAnim();
02554							PlayRunning();
02555						}
02556						MoveToward(MoveTarget);
02557						Goto('MoveToAmbush');
02558					}
02559					else
02560						StartRoaming();
02561				}
02562				if (Physics != PHYS_Falling)
02563					Acceleration = vect(0,0,0);
02564				DesiredSpeed = 0.0;
02565				TweenToFighter(0.2);
02566				FinishAnim();
02567				PlayTurning();
02568				TurnTo(Location + (Ambushpoint(OrderObject)).lookdir);
02569			}
02570		}
02571		
02572	Begin:
02573		NextAnim = '';
02574		Enable('Landed');
02575		Disable('AnimEnd');
02576		
02577		if (OldEnemy != None)
02578			{
02579			////log("turn toward probably enemy dir");
02580			OldEnemy = None;
02581			if (Physics != PHYS_Falling)
02582				Acceleration = vect(0,0,0);
02583			SetMovementPhysics();
02584			DesiredSpeed = 0.0;
02585			TweenToFighter(0.2);
02586			FinishAnim();
02587			PlayTurning();
02588			TurnTo(LastSeenPos); //FIXME - turn to a nearby pathnode?
02589			}
02590		
02591		DesiredSpeed = 0.0;
02592		bReadyToAttack = true;
02593		//log(class$" waiting in ambush");
02594		DesiredRotation = rot(0,0,0);
02595		DesiredRotation.Yaw = Rotation.Yaw;
02596		Acceleration = vect(0,0,0);
02597		TweenToPatrolStop(0.3);
02598		FinishAnim();
02599		Enable('AnimEnd');
02600		NextAnim = '';
02601		PlayWaitingAmbush();
02602		setRotation(DesiredRotation);
02603		if (Physics != PHYS_Falling) 
02604			SetPhysics(PHYS_None);
02605	}
02606		
02607	/* Acquisition - 
02608	Creature has just reacted to stimulus, and set an enemy
02609	- depending on strength of stimulus, and ongoing stimulii, vary time to focus on target and start attacking (or whatever.  FIXME - need some acquisition specific animation
02610	HearNoise and SeePlayer used to improve/change stimulus
02611	*/
02612	
02613	state Acquisition
02614	{
02615	ignores falling, landed; //fixme
02616	
02617		function WarnTarget(Pawn shooter, float projSpeed, vector FireDir)
02618		{
02619			local eAttitude att;
02620	
02621			if ( intelligence < BRAINS_Mammal )
02622				return;
02623	
02624			att = AttitudeTo(shooter);
02625			if ( ((att == ATTITUDE_Ignore) || (att == ATTITUDE_Threaten)) )
02626				damageAttitudeTo(shooter);
02627		}
02628	
02629		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
02630								Vector momentum, name damageType)
02631		{
02632			LastSeenPos = Enemy.Location;
02633			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
02634			if ( health <= 0 )
02635				return;
02636			if (NextState == 'TakeHit')
02637			{
02638				NextState = 'Attacking'; 
02639				NextLabel = 'Begin';
02640				GotoState('TakeHit'); 
02641			}
02642			else
02643				GotoState('Attacking');
02644		}
02645		
02646		function HearNoise(float Loudness, Actor NoiseMaker)
02647		{
02648			local vector OldLastSeenPos;
02649			
02650			if ( SetEnemy(NoiseMaker.instigator) )
02651			{
02652				OldLastSeenPos = LastSeenPos;
02653				if ( Enemy ==  NoiseMaker.instigator  )
02654					LastSeenPos = 0.5 * (NoiseMaker.Location + VSize(NoiseMaker.Location - Location) * vector(Rotation));
02655				else if ( (Pawn(NoiseMaker) != None) && (Enemy == Pawn(NoiseMaker).Enemy) )
02656					LastSeenPos = 0.5 * (Pawn(NoiseMaker).Enemy.Location + VSize(Pawn(NoiseMaker).Enemy.Location - Location) * vector(Rotation));
02657				if ( VSize(OldLastSeenPos - Enemy.Location) < VSize(LastSeenPos - Enemy.Location) )
02658					LastSeenPos = OldLastSeenPos;				
02659			}
02660			
02661		}
02662		
02663		function SeePlayer(Actor SeenPlayer)
02664		{
02665			if ( SetEnemy(Pawn(SeenPlayer)) )
02666			{
02667				PlayAcquisitionSound();
02668				//log("Enemy Acquired!");
02669				MakeNoise(1.0);
02670				NextAnim = '';
02671				LastSeenPos = Enemy.Location;
02672				GotoState('Attacking');
02673			}
02674		} 
02675		
02676		function BeginState()
02677		{
02678			Disable('Tick'); //only used for bounding anim time
02679			SetAlertness(-0.5);
02680		}
02681		
02682	PlayOut:
02683		Acceleration = vect(0,0,0);
02684		if ( (AnimFrame < 0.6) && IsAnimating() )
02685		{
02686			Sleep(0.05);
02687			Goto('PlayOut');
02688		}
02689			
02690	Begin:
02691		SetMovementPhysics();
02692		//log("Acquiring enemy");
02693		////log("Enemy position = "$Enemy.Location);
02694		////log("Last seen position = "$LastSeenPos);
02695	AcquTurn:
02696		Acceleration = vect(0,0,0);
02697		if (NeedToTurn(LastSeenPos))
02698		{	
02699			PlayTurning();
02700			TurnTo(LastSeenPos);
02701		}
02702		DesiredRotation = Rotator(LastSeenPos - Location);
02703		TweenToFighter(0.2); 
02704		FinishAnim();	
02705		////log("Stimulus = "$Stimulus);
02706		if ( AttitudeTo(Enemy) == ATTITUDE_Fear )  //will run away from noise
02707		{
02708			////log("Run away from noise");
02709			PlayFearSound();
02710			LastSeenPos = Enemy.Location; 
02711			MakeNoise(1.0);
02712			NextAnim = '';
02713			GotoState('Attacking');
02714		}
02715		else //investigate noise
02716		{
02717			////log("investigate noise");
02718			if ( pointReachable((Location + LastSeenPos) * 0.5) )
02719			{
02720				TweenToWalking(0.3);
02721				FinishAnim();
02722				PlayWalking();
02723				MoveTo((Location + LastSeenPos) * 0.5, WalkingSpeed);
02724				Acceleration = vect(0,0,0);
02725			}
02726			WhatToDoNext('','');
02727		}
02728	}
02729	
02730	/* Attacking
02731	Master attacking state - choose which type of attack to do from here
02732	*/
02733	state Attacking
02734	{
02735	ignores SeePlayer, HearNoise, Bump, HitWall;
02736	
02737		function ChooseAttackMode()
02738		{
02739			local eAttitude AttitudeToEnemy;
02740			local float Aggression;
02741			local pawn changeEn;
02742			
02743			if ((Enemy == None) || (Enemy.Health <= 0))
02744			{
02745				if (Orders == 'Attacking')
02746					Orders = '';
02747				WhatToDoNext('','');
02748				return;
02749			}
02750			
02751			if ( (AlarmTag != '') && Enemy.bIsPlayer )
02752			{
02753				if (AttitudeToPlayer > ATTITUDE_Ignore)
02754				{
02755					GotoState('AlarmPaused', 'WaitForPlayer');
02756					return;
02757				}
02758				else if ( (AttitudeToPlayer != ATTITUDE_Fear) || bInitialFear )
02759				{
02760					GotoState('TriggerAlarm');
02761					return;
02762				}
02763			}
02764				
02765			AttitudeToEnemy = AttitudeTo(Enemy);
02766				
02767			if (AttitudeToEnemy == ATTITUDE_Fear)
02768			{
02769				GotoState('Retreating');
02770				return;
02771			}
02772		
02773			else if (AttitudeToEnemy == ATTITUDE_Threaten)
02774			{
02775				GotoState('Threatening');
02776				return;
02777			}
02778		
02779			else if (AttitudeToEnemy == ATTITUDE_Friendly)
02780			{
02781				if (Enemy.bIsPlayer)
02782					GotoState('Greeting');
02783				else
02784					WhatToDoNext('','');
02785				return;
02786			}
02787			
02788			else if (!LineOfSightTo(Enemy))
02789			{
02790				if ( (OldEnemy != None) 
02791					&& (AttitudeTo(OldEnemy) == ATTITUDE_Hate) && LineOfSightTo(OldEnemy) )
02792				{
02793					changeEn = enemy;
02794					enemy = oldenemy;
02795					oldenemy = changeEn;
02796				}	
02797				else 
02798				{
02799					if ( (Orders == 'Guarding') && !LineOfSightTo(OrderObject) )
02800						GotoState('Guarding');
02801					else if ( !bHasRangedAttack || VSize(Enemy.Location - Location) 
02802								> 600 + (FRand() * RelativeStrength(Enemy) - CombatStyle) * 600 )
02803						GotoState('Hunting');
02804					else if ( bIsBoss || (Intelligence > BRAINS_None) )
02805					{
02806						HuntStartTime = Level.TimeSeconds;
02807						NumHuntPaths = 0; 
02808						GotoState('StakeOut');
02809					}
02810					else
02811						WhatToDoNext('Waiting', 'TurnFromWall');
02812					return;
02813				}
02814			}	
02815			
02816			else if ( (TeamLeader != None) && TeamLeader.ChooseTeamAttackFor(self) )
02817				return;
02818			
02819			if (bReadyToAttack)
02820			{
02821				////log("Attack!");
02822				Target = Enemy;
02823				If (VSize(Enemy.Location - Location) <= (MeleeRange + Enemy.CollisionRadius + CollisionRadius))
02824				{
02825					GotoState('MeleeAttack');
02826					return;
02827				}
02828				else if (bMovingRangedAttack)
02829					SetTimer(TimeBetweenAttacks, False);
02830				else if (bHasRangedAttack && (bIsPlayer || enemy.bIsPlayer) && CanFireAtEnemy() )
02831				{
02832					if (!bIsPlayer || (2.5 * FRand() > Skill) )
02833					{
02834						GotoState('RangedAttack');
02835						return;
02836					}
02837				}
02838			}
02839				
02840			//decide whether to charge or make a tactical move
02841			if ( !bHasRangedAttack ) 
02842				GotoState('Charging');
02843			else
02844				GotoState('TacticalMove');
02845			//log("Next state is "$state);
02846		}
02847		
02848		//EnemyNotVisible implemented so engine will update LastSeenPos
02849		function EnemyNotVisible()
02850		{
02851			////log("enemy not visible");
02852		}
02853	
02854		function Timer()
02855		{
02856			bReadyToAttack = True;
02857		}
02858	
02859		function BeginState()
02860		{
02861			if ( TimerRate <= 0.0 )
02862				SetTimer(TimeBetweenAttacks  * (1.0 + FRand()),false); 
02863			if (Physics == PHYS_None)
02864				SetMovementPhysics(); 
02865		}
02866	
02867	Begin:
02868		//log(class$" choose Attack");
02869		ChooseAttackMode();
02870	}
02871	
02872	state Threatening
02873	{
02874	ignores falling, landed; //fixme
02875	
02876	//ignores SeePlayer if enemy is a player //but not hear noise
02877		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
02878								Vector momentum, name damageType)
02879		{
02880			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
02881			if ( health <= 0 )
02882				return;
02883			if (NextState == 'TakeHit')
02884			{
02885				bReadyToAttack = true;
02886				NextState = 'Attacking'; 
02887				NextLabel = 'Begin';
02888				GotoState('TakeHit'); 
02889			}
02890			else
02891			{
02892				bReadyToAttack = true;
02893				GotoState('Attacking');
02894			}
02895		}
02896	
02897		function Trigger( actor Other, pawn EventInstigator )
02898		{
02899			if (EventInstigator.bIsPlayer)
02900			{
02901				Enemy = EventInstigator;
02902				AttitudeToPlayer = ATTITUDE_Hate;
02903				GotoState('Attacking');
02904			}
02905		}
02906	
02907		function EnemyNotVisible()
02908		{
02909			////log("enemy not visible");
02910			GotoState('Ambushing'); 
02911		}
02912		
02913		function EnemyAcquired()
02914		{
02915			if (AttitudeTo(Enemy) < ATTITUDE_Threaten)
02916				GotoState('Attacking');
02917		}
02918		
02919		function PickGuardDestination()
02920		{
02921			local vector desiredDest;
02922			local Actor path;
02923			
02924			desiredDest = OrderObject.Location + 
02925					(OrderObject.CollisionRadius + 2.5 * CollisionRadius) * Normal(Enemy.Location - OrderObject.Location);
02926	
02927			if ( VSize(desiredDest - Location) < 60 )
02928			{
02929				Destination = Location;
02930				return;
02931			}
02932	
02933			if (pointReachable(desiredDest))
02934				Destination = desiredDest;
02935			else
02936			{
02937				path = FindPathTo(desiredDest, true);
02938				if (path != None)
02939				{
02940					MoveTarget = path;
02941					Destination = path.Location;
02942				}
02943				else
02944					Destination = Location;
02945			}
02946		}
02947		
02948		function PickThreatenDestination()
02949		{
02950			local vector desiredDest;
02951			local Actor path;
02952	
02953			desiredDest = Location + 
02954					0.4 * (VSize(Enemy.Location - Location) - CollisionRadius - Enemy.CollisionRadius - MeleeRange)
02955					* Normal(Enemy.Location - Location);
02956	
02957			if (pointReachable(desiredDest))
02958				Destination = desiredDest;
02959			else
02960			{
02961				path = FindPathTo(desiredDest, true);
02962				if (path != None)
02963				{
02964					MoveTarget = path;
02965					Destination = path.Location;
02966				}
02967				else
02968					Destination = Location;
02969			}
02970		}
02971	
02972		function BeginState()
02973		{
02974			bCanJump = false;
02975		}
02976		
02977		function EndState()
02978		{
02979			if (JumpZ > 0)
02980				bCanJump = true;
02981		}
02982		
02983	Begin:
02984		Acceleration = vect(0,0,0);
02985		bReadyToAttack = true;
02986		if (Enemy.bIsPlayer)
02987			Disable('SeePlayer'); //but not hear noise
02988		TweenToPatrolStop(0.2);
02989		FinishAnim();
02990		NextAnim = '';
02991		
02992	FaceEnemy:
02993		Acceleration = vect(0,0,0);
02994		if (NeedToTurn(enemy.Location))
02995		{	
02996			PlayTurning();
02997			TurnToward(Enemy);
02998			TweenToPatrolStop(0.2);
02999			FinishAnim();
03000			NextAnim = '';
03001		}
03002			
03003	Threaten:
03004		if (AttitudeTo(Enemy) < ATTITUDE_Threaten)
03005			GotoState('Attacking');
03006	
03007		PlayThreatening();
03008		FinishAnim();
03009	
03010		if (AttitudeTo(Enemy) < ATTITUDE_Threaten)
03011			GotoState('Attacking');
03012			
03013		if (Orders == 'Guarding')
03014		{ //stay between enemy and guard object
03015			If (Enemy.bIsPlayer &&
03016				(VSize(Enemy.Location - OrderObject.Location) < OrderObject.CollisionRadius + 2 * CollisionRadius + MeleeRange))
03017			{
03018				AttitudeToPlayer = ATTITUDE_Hate;
03019				GotoState('Attacking');
03020			}
03021		}
03022		else if (FRand() < 0.9 - Aggressiveness) //mostly just turn
03023			Goto('FaceEnemy');
03024		else if (VSize(Enemy.Location - Location) < 2.5 * (CollisionRadius + Enemy.CollisionRadius + MeleeRange))
03025			Goto('FaceEnemy');
03026	
03027		WaitForLanding();
03028		if (Orders == 'Guarding') //stay between enemy and guard object
03029			PickGuardDestination();
03030		else
03031			PickThreatenDestination();
03032			
03033		if (Destination != Location)
03034		{
03035			TweenToWalking(0.2);
03036			FinishAnim();
03037			PlayWalking();
03038			MoveTo(Destination, WalkingSpeed);
03039			Acceleration = vect(0,0,0);
03040			TweenToPatrolStop(0.2);
03041			FinishAnim();
03042			NextAnim = '';
03043		}
03044			
03045		Goto('FaceEnemy');
03046	}
03047	
03048	state Retreating
03049	{
03050	ignores SeePlayer, EnemyNotVisible, HearNoise;
03051	
03052		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
03053								Vector momentum, name damageType)
03054		{
03055			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
03056			if ( health <= 0 )
03057				return;
03058			if (NextState == 'TakeHit')
03059			{
03060				NextState = 'Retreating'; 
03061				NextLabel = 'TakeHit';
03062				GotoState('TakeHit'); 
03063			}
03064		}
03065	
03066		function Timer()
03067		{
03068			bReadyToAttack = True;
03069			Enable('Bump');
03070		}
03071		
03072		function SetFall()
03073		{
03074			NextState = 'Retreating'; 
03075			NextLabel = 'Landed';
03076			NextAnim = AnimSequence;
03077			GotoState('FallingState'); 
03078		}
03079	
03080		function HitWall(vector HitNormal, actor Wall)
03081		{
03082			bSpecialPausing = false;
03083			if (Physics == PHYS_Falling)
03084				return;
03085			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
03086			{
03087				if ( SpecialPause > 0 )
03088					Acceleration = vect(0,0,0);
03089				GotoState('Retreating', 'SpecialNavig');
03090				return;
03091			}
03092			Focus = Destination;
03093			if (PickWallAdjust())
03094				GotoState('Retreating', 'AdjustFromWall');
03095			else
03096			{
03097				Home = None;
03098				MoveTimer = -1.0;
03099			}
03100		}
03101	
03102		/* if has a base then run toward it if its not visible to player. (FIXME)
03103		adjusts attitude based on proximity to base
03104		Else pick a random pathnode not visible to player and run toward it.
03105		Also - modify weights of paths visible and near to player up.
03106		*/
03107		function PickDestination()
03108		{
03109		 	//log("find retreat destination");
03110			if (HomeBase(Home) == None)
03111			{
03112				Home = FindRandomDest(); //find temporary home
03113				if (Home == None)
03114				{
03115					if (bReadyToAttack)
03116					{
03117						setTimer(3.0, false);
03118						Target = Enemy;
03119						GotoState('RangedAttack');
03120					}
03121					else
03122					{
03123						Aggressiveness += 0.3;
03124						GotoState('TacticalMove', 'NoCharge');
03125					}
03126				}
03127			}
03128		}
03129	
03130		function ChangeDestination()
03131		{
03132			local actor oldTarget;
03133			local Actor path;
03134			
03135			oldTarget = Home;
03136			PickDestination();
03137			if (Home == oldTarget)
03138			{
03139				Aggressiveness += 0.3;
03140				//log("same old target");
03141				GotoState('TacticalMove', 'TacticalTick');
03142			}
03143			else
03144			{
03145				path = FindPathToward(Home);
03146				if (path == None)
03147				{
03148					//log("no new target");
03149					Aggressiveness += 0.3;
03150					GotoState('TacticalMove', 'TacticalTick');
03151				}
03152				else 
03153				{
03154					MoveTarget = path;
03155					Destination = path.Location;
03156				}
03157			}
03158		}
03159	
03160		function Bump(actor Other)
03161		{
03162			local vector VelDir, OtherDir;
03163			local float speed;
03164	
03165			//log(Other.class$" bumped "$class);
03166			if (Pawn(Other) != None)
03167			{
03168				if ( (Other == Enemy) || SetEnemy(Pawn(Other)) )
03169					GotoState('MeleeAttack');
03170				else if ( (HomeBase(Home) != None) 
03171					&& (VSize(Location - Home.Location) < HomeBase(Home).Extent) )
03172					ReachedHome();
03173				return;
03174			}
03175			if ( TimerRate <= 0 )
03176				setTimer(1.0, false);
03177			
03178			speed = VSize(Velocity);
03179			if ( speed > 1 )
03180			{
03181				VelDir = Velocity/speed;
03182				VelDir.Z = 0;
03183				OtherDir = Other.Location - Location;
03184				OtherDir.Z = 0;
03185				OtherDir = Normal(OtherDir);
03186				if ( (VelDir Dot OtherDir) > 0.9 )
03187				{
03188					Velocity.X = VelDir.Y;
03189					Velocity.Y = -1 * VelDir.X;
03190					Velocity *= FMax(speed, 200);
03191				}
03192			} 
03193			Disable('Bump');
03194		}
03195		
03196		function ReachedHome()
03197		{
03198			if (LineOfSightTo(Enemy))
03199			{
03200				if (Homebase(home) != None)
03201				{
03202					//log(class$" reached home base - turn and fight");
03203					Aggressiveness += 0.2;
03204					if ( !bMoraleBoosted )
03205						health = Min(default.health, health+20);
03206					MakeNoise(1.0);
03207					GotoState('Attacking');
03208				}
03209				else
03210					ChangeDestination();
03211			}
03212			else
03213			{
03214				if (Homebase(home) != None)
03215					MakeNoise(1.0);
03216				aggressiveness += 0.2;
03217				if ( !bMoraleBoosted )
03218					health = Min(default.health, health+5);
03219				GotoState('Retreating', 'TurnAtHome');
03220			}
03221			bMoraleBoosted = true;	
03222		}
03223	
03224		function PickNextSpot()
03225		{
03226			local Actor path;
03227			local vector dist2d;
03228			local float zdiff;
03229	
03230			if ( Home == None )
03231			{
03232				PickDestination();
03233				if ( Home == None )
03234					return;
03235			}
03236			//log("find retreat spot");
03237			dist2d = Home.Location - Location;
03238			zdiff = dist2d.Z;
03239			dist2d.Z = 0.0;	
03240			if ((VSize(dist2d) < 2 * CollisionRadius) && (Abs(zdiff) < CollisionHeight))
03241				ReachedHome();
03242			else
03243			{
03244				if (ActorReachable(Home))
03245				{
03246					//log("almost there");
03247					path = Home;
03248					if (HomeBase(Home) == None)
03249						Home = None;
03250				}
03251				else
03252				{
03253					if (SpecialGoal != None)
03254						path = FindPathToward(SpecialGoal);
03255					else
03256						path = FindPathToward(Home);
03257				}
03258					
03259				if (path == None)
03260					ChangeDestination();
03261				else
03262				{
03263					MoveTarget = path;
03264					Destination = path.Location;
03265				}
03266			}
03267		}
03268	
03269		function AnimEnd() 
03270		{
03271			if ( bSpecialPausing )
03272				PlayPatrolStop();
03273			else if ( bCanFire && LineOfSightTo(Enemy) )
03274				PlayCombatMove();
03275			else
03276				PlayRunning();
03277		}
03278	
03279		function BeginState()
03280		{
03281			bCanFire = false;
03282			bSpecialPausing = false;
03283			SpecialGoal = None;
03284			SpecialPause = 0.0;
03285		}
03286	
03287	Begin:
03288		//log(class$" retreating");
03289		if ( bReadyToAttack && (FRand() < 0.6) )
03290		{
03291			SetTimer(TimeBetweenAttacks, false);
03292			bReadyToAttack = false;
03293		}
03294		TweenToRunning(0.1);
03295		WaitForLanding();
03296		PickDestination();
03297	
03298	Landed:
03299		TweenToRunning(0.1);
03300		
03301	RunAway:
03302		PickNextSpot();
03303	SpecialNavig:
03304		if (SpecialPause > 0.0)
03305		{
03306			if ( LineOfSightTo(Enemy) )
03307			{
03308				bFiringPaused = true;
03309				NextState = 'Retreating';
03310				NextLabel = 'Moving';
03311				GotoState('RangedAttack');
03312			}
03313			bSpecialPausing = true;
03314			Acceleration = vect(0,0,0);
03315			TweenToPatrolStop(0.25);
03316			Sleep(SpecialPause);
03317			SpecialPause = 0.0;
03318			bSpecialPausing = false;
03319			TweenToRunning(0.1);
03320		}
03321	Moving:
03322		if ( MoveTarget == None )
03323		{
03324			Sleep(0.0);
03325			Goto('RunAway');
03326		}
03327		if ( !bCanStrafe || !LineOfSightTo(Enemy) ||
03328			(Skill - 2 * FRand() + (Normal(Enemy.Location - Location - vect(0,0,1) * (Enemy.Location.Z - Location.Z)) 
03329				Dot Normal(MoveTarget.Location - Location - vect(0,0,1) * (MoveTarget.Location.Z - Location.Z))) < 0) )
03330		{
03331			bCanFire = false;
03332			MoveToward(MoveTarget);
03333		}
03334		else
03335		{
03336			bCanFire = true;
03337			StrafeFacing(MoveTarget.Location, Enemy);
03338		}
03339		Goto('RunAway');
03340	
03341	TakeHit:
03342		TweenToRunning(0.12);
03343		Goto('Moving');
03344	
03345	AdjustFromWall:
03346		StrafeTo(Destination, Focus); 
03347		Destination = Focus; 
03348		MoveTo(Destination);
03349		Goto('Moving');
03350	
03351	TurnAtHome:
03352		Acceleration = vect(0,0,0);
03353		TurnTo(Homebase(Home).lookdir);
03354		GotoState('Ambushing', 'FindAmbushSpot');
03355	}
03356	
03357	state Charging
03358	{
03359	ignores SeePlayer, HearNoise;
03360	
03361		/* MayFall() called by engine physics if walking and bCanJump, and
03362			is about to go off a ledge.  Pawn has opportunity (by setting 
03363			bCanJump to false) to avoid fall
03364		*/
03365		function MayFall()
03366		{
03367			if ( MoveTarget != Enemy )
03368				return;
03369	
03370			if ( intelligence == BRAINS_None )
03371				return;
03372	
03373			bCanJump = ActorReachable(Enemy);
03374			if ( !bCanJump )
03375					GotoState('TacticalMove', 'NoCharge');
03376		}
03377	
03378		function HitWall(vector HitNormal, actor Wall)
03379		{
03380			if (Physics == PHYS_Falling)
03381				return;
03382			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
03383			{
03384				if ( SpecialPause > 0 )
03385					Acceleration = vect(0,0,0);
03386				GotoState('Charging', 'SpecialNavig');
03387				return;
03388			}
03389			Focus = Destination;
03390			if (PickWallAdjust())
03391				GotoState('Charging', 'AdjustFromWall');
03392			else
03393				MoveTimer = -1.0;
03394		}
03395		
03396		function SetFall()
03397		{
03398			NextState = 'Charging'; 
03399			NextLabel = 'ResumeCharge';
03400			NextAnim = AnimSequence;
03401			GotoState('FallingState'); 
03402		}
03403	
03404		function FearThisSpot(Actor aSpot)
03405		{
03406			Destination = Location + 120 * Normal(Location - aSpot.Location); 
03407			GotoState('TacticalMove', 'DoStrafeMove');
03408		}
03409	
03410		function bool StrafeFromDamage(vector momentum, float Damage, name DamageType, bool bFindDest)
03411		{
03412			local vector sideDir, extent, HitLocation, HitNormal;
03413			local actor HitActor;
03414			local float healthpct;
03415	
03416			if ( (damageType == 'shot') || (damageType == 'jolted') )
03417				healthpct = 0.17;
03418			else
03419				healthpct = 0.25;
03420	
03421			healthpct *= CombatStyle;
03422			if ( FRand() * Damage < healthpct * Health ) 
03423				return false;
03424	
03425			if ( !bFindDest )
03426				return true;
03427	
03428			sideDir = Normal( Normal(Enemy.Location - Location) Cross vect(0,0,1) );
03429			if ( (momentum Dot sidedir) > 0 )
03430				sidedir *= -1;
03431			Extent.X = CollisionRadius;
03432			Extent.Y = CollisionRadius;
03433			Extent.Z = CollisionHeight;
03434			HitActor = Trace(HitLocation, HitNormal, Location + 100 * sideDir, Location, false, Extent);
03435			if (HitActor != None)
03436			{
03437				sideDir *= -1;
03438				HitActor = Trace(HitLocation, HitNormal, Location + 100 * sideDir, Location, false, Extent);
03439			}
03440			if (HitActor != None)
03441				return false;
03442			
03443			if ( Physics == PHYS_Walking )
03444			{
03445				HitActor = Trace(HitLocation, HitNormal, Location + 100 * sideDir - MaxStepHeight * vect(0,0,1), Location + 100 * sideDir, false, Extent);
03446				if ( HitActor == None )
03447					return false;
03448			}
03449			Destination = Location + 250 * sideDir;
03450			GotoState('TacticalMove', 'DoStrafeMove');
03451			return true;
03452		}
03453				
03454		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
03455								Vector momentum, name damageType)
03456		{
03457			local float pick;
03458			local vector sideDir, extent;
03459			local bool bWasOnGround;
03460	
03461			bWasOnGround = (Physics == PHYS_Walking);
03462			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
03463			if ( health <= 0 )
03464				return;
03465			if (NextState == 'TakeHit')
03466			{
03467				if (AttitudeTo(Enemy) == ATTITUDE_Fear)
03468				{
03469					NextState = 'Retreating';
03470					NextLabel = 'Begin';
03471				}
03472				else if ( (Intelligence > BRAINS_Mammal) && bHasRangedAttack && bCanStrafe 
03473					&& StrafeFromDamage(momentum, Damage, damageType, false) )
03474				{
03475					NextState = 'TacticalMove';
03476					NextLabel = 'NoCharge';
03477				}
03478				else
03479				{
03480					NextState = 'Charging';
03481					NextLabel = 'TakeHit';
03482				}
03483				GotoState('TakeHit'); 
03484			}
03485			else if ( (Intelligence > BRAINS_Mammal) && bHasRangedAttack && bCanStrafe 
03486				&& StrafeFromDamage(momentum, Damage, damageType, true) )
03487				return;
03488			else if ( bWasOnGround && (MoveTarget == Enemy) && 
03489						(Physics == PHYS_Falling) && (Intelligence == BRAINS_Human) ) //weave
03490			{
03491				pick = 1.0;
03492				if ( bStrafeDir )
03493					pick = -1.0;
03494				sideDir = Normal( Normal(Enemy.Location - Location) Cross vect(0,0,1) );
03495				sideDir.Z = 0;
03496				Velocity += pick * GroundSpeed * 0.7 * sideDir;   
03497				if ( FRand() < 0.2 )
03498					bStrafeDir = !bStrafeDir;
03499			}
03500		}
03501								
03502		function AnimEnd() 
03503		{
03504			PlayCombatMove();
03505		}
03506		
03507		function Timer()
03508		{
03509			bReadyToAttack = True;
03510			Target = Enemy;	
03511			if (VSize(Enemy.Location - Location) 
03512					<= (MeleeRange + Enemy.CollisionRadius + CollisionRadius))
03513				GotoState('MeleeAttack');
03514			else if ( bHasRangedAttack && (FRand() > 0.7 + 0.1 * skill) ) 
03515				GotoState('RangedAttack');
03516			else if ( bHasRangedAttack && !bMovingRangedAttack)
03517			{ 
03518				if ( FRand() < CombatStyle * 0.8 ) //then keep charging
03519					SetTimer(1.0,false); 
03520				else
03521					GotoState('Attacking');
03522			}
03523		}
03524		
03525		function EnemyNotVisible()
03526		{
03527			GotoState('Hunting'); 
03528		}
03529	
03530		function BeginState()
03531		{
03532			bCanFire = false;
03533			SpecialGoal = None;
03534			SpecialPause = 0.0;
03535		}
03536	
03537		function EndState()
03538		{
03539			if ( JumpZ > 0 )
03540				bCanJump = true;
03541		}
03542	
03543	AdjustFromWall:
03544		StrafeTo(Destination, Focus); 
03545		Goto('CloseIn');
03546	
03547	ResumeCharge:
03548		PlayRunning();
03549		Goto('Charge');
03550	
03551	Begin:
03552		TweenToRunning(0.15);
03553	
03554	Charge:
03555		bFromWall = false;
03556		
03557	CloseIn:
03558		if ( (Enemy == None) || (Enemy.Health <=0) )
03559			GotoState('Attacking');
03560	
03561		if ( Enemy.Region.Zone.bWaterZone )
03562		{
03563			if (!bCanSwim)
03564				GotoState('TacticalMove', 'NoCharge');
03565		}
03566		else if (!bCanFly && !bCanWalk)
03567			GotoState('TacticalMove', 'NoCharge');
03568	
03569		if (Physics == PHYS_Falling)
03570		{
03571			DesiredRotation = Rotator(Enemy.Location - Location);
03572			Focus = Enemy.Location;
03573			Destination = Enemy.Location;
03574			WaitForLanding();
03575		}
03576		if( (Intelligence <= BRAINS_Reptile) || actorReachable(Enemy) )
03577		{
03578			bCanFire = true;
03579			if ( FRand() < 0.3 )
03580				PlayThreateningSound();
03581			MoveToward(Enemy);
03582			if (bFromWall)
03583			{
03584				bFromWall = false;
03585				if (PickWallAdjust())
03586					StrafeFacing(Destination, Enemy);
03587				else
03588					GotoState('TacticalMove', 'NoCharge');
03589			}
03590		}
03591		else
03592		{
03593	NoReach:
03594			bCanFire = false;
03595			bFromWall = false;
03596			//log("route to enemy "$Enemy);
03597			if (!FindBestPathToward(Enemy))
03598			{
03599				Sleep(0.0);
03600				GotoState('TacticalMove', 'NoCharge');
03601			}
03602	SpecialNavig:
03603			if ( SpecialPause > 0.0 )
03604			{
03605				bFiringPaused = true;
03606				NextState = 'Charging';
03607				NextLabel = 'Moving';
03608				GotoState('RangedAttack');
03609			}
03610	Moving:
03611			if (VSize(MoveTarget.Location - Location) < 2.5 * CollisionRadius)
03612			{
03613				bCanFire = true;
03614				StrafeFacing(MoveTarget.Location, Enemy);
03615			}
03616			else
03617			{
03618				if ( !bCanStrafe || !LineOfSightTo(Enemy) ||
03619					(Skill - 2 * FRand() + (Normal(Enemy.Location - Location - vect(0,0,1) * (Enemy.Location.Z - Location.Z)) 
03620						Dot Normal(MoveTarget.Location - Location - vect(0,0,1) * (MoveTarget.Location.Z - Location.Z))) < 0) )
03621				{
03622					if ( GetAnimGroup(AnimSequence) == 'MovingAttack' )
03623					{
03624						AnimSequence = '';
03625						TweenToRunning(0.12);
03626					}
03627					MoveToward(MoveTarget);
03628				}
03629				else
03630				{
03631					bCanFire = true;
03632					StrafeFacing(MoveTarget.Location, Enemy);	
03633				}
03634				if ( !bFromWall && (FRand() < 0.5) )
03635					PlayThreateningSound();
03636			}
03637		}
03638		//log("finished move");
03639		if (VSize(Location - Enemy.Location) < CollisionRadius + Enemy.CollisionRadius + MeleeRange)
03640			Goto('GotThere');
03641		if ( bIsPlayer || (!bFromWall && bHasRangedAttack && (FRand() > CombatStyle + 0.1)) )
03642			GotoState('Attacking');
03643		MoveTimer = 0.0;
03644		bFromWall = false;
03645		Goto('CloseIn');
03646	
03647	GotThere:
03648		////log("Got to enemy");
03649		Target = Enemy;
03650		GotoState('MeleeAttack');
03651	
03652	TakeHit:
03653		TweenToRunning(0.12);
03654		if (MoveTarget == Enemy)
03655		{
03656			bCanFire = true;
03657			MoveToward(MoveTarget);
03658		}
03659		
03660		Goto('Charge');
03661	}
03662	
03663	state TacticalMove
03664	{
03665	ignores SeePlayer, HearNoise;
03666	
03667		function SetFall()
03668		{
03669			Acceleration = vect(0,0,0);
03670			Destination = Location;
03671			NextState = 'Attacking'; 
03672			NextLabel = 'Begin';
03673			NextAnim = 'Fighter';
03674			GotoState('FallingState');
03675		}
03676	
03677		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
03678							Vector momentum, name damageType)
03679		{
03680			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
03681			if ( health <= 0 )
03682				return;
03683			if ( NextState == 'TakeHit' )
03684			{
03685				NextState = 'TacticalMove'; 
03686				NextLabel = 'TakeHit';
03687				GotoState('TakeHit'); 
03688			}
03689		}
03690	
03691		function HitWall(vector HitNormal, actor Wall)
03692		{
03693			if (Physics == PHYS_Falling)
03694				return;
03695			Focus = Destination;
03696			//if (PickWallAdjust())
03697			//	GotoState('TacticalMove', 'AdjustFromWall');
03698			if ( bChangeDir || (FRand() < 0.5) 
03699				|| (((Enemy.Location - Location) Dot HitNormal) < 0) )
03700			{
03701				DesiredRotation = Rotator(Enemy.Location - location);
03702				GiveUpTactical(false);
03703			}
03704			else
03705			{
03706				bChangeDir = true;
03707				Destination = Location - HitNormal * FRand() * 500;
03708			}
03709		}
03710	
03711		function FearThisSpot(Actor aSpot)
03712		{
03713			Destination = Location + 120 * Normal(Location - aSpot.Location); 
03714		}
03715	
03716		function AnimEnd() 
03717		{
03718			PlayCombatMove();
03719		}
03720	
03721		function Timer()
03722		{
03723			bReadyToAttack = True;
03724			Enable('Bump');
03725			Target = Enemy;
03726			if (VSize(Enemy.Location - Location) 
03727					<= (MeleeRange + Enemy.CollisionRadius + CollisionRadius))
03728				GotoState('MeleeAttack');		 
03729			else if ( bHasRangedAttack && ((!bMovingRangedAttack && (FRand() < 0.8)) || (FRand() > 0.5 + 0.17 * skill)) ) 
03730				GotoState('RangedAttack');
03731		}
03732	
03733		function EnemyNotVisible()
03734		{
03735			if ( aggressiveness > relativestrength(enemy) )
03736			{
03737				if (ValidRecovery())
03738					GotoState('TacticalMove','RecoverEnemy');
03739				else
03740					GotoState('Attacking');
03741			}
03742			Disable('EnemyNotVisible');
03743		}
03744	
03745		function bool ValidRecovery()
03746		{
03747			local actor HitActor;
03748			local vector HitLocation, HitNormal;
03749			
03750			HitActor = Trace(HitLocation, HitNormal, Enemy.Location, LastSeeingPos, false);
03751			return (HitActor == None);
03752		}
03753			
03754		function GiveUpTactical(bool bNoCharge)
03755		{	
03756			if ( !bNoCharge && (2 * CombatStyle > (3 - Skill) * FRand()) )
03757				GotoState('Charging');
03758			else if ( bReadyToAttack && (skill > 3 * FRand() - 1) )
03759				GotoState('RangedAttack');
03760			else
03761				GotoState('RangedAttack', 'Challenge'); 
03762		}		
03763	
03764	/* PickDestination()
03765	Choose a destination for the tactical move, based on aggressiveness and the tactical
03766	situation. Make sure destination is reachable
03767	*/
03768		function PickDestination(bool bNoCharge)
03769		{
03770			local vector pickdir, enemydir, enemyPart, Y, minDest;
03771			local actor HitActor;
03772			local vector HitLocation, HitNormal, collSpec;
03773			local float Aggression, enemydist, minDist, strafeSize, optDist;
03774			local bool success, bNoReach;
03775		
03776			bChangeDir = false;
03777			if (Region.Zone.bWaterZone && !bCanSwim && bCanFly)
03778			{
03779				Destination = Location + 75 * (VRand() + vect(0,0,1));
03780				Destination.Z += 100;
03781				return;
03782			}
03783			if ( Enemy.Region.Zone.bWaterZone )
03784				bNoCharge = bNoCharge || !bCanSwim;
03785			else 
03786				bNoCharge = bNoCharge || (!bCanFly && !bCanWalk);
03787			
03788			success = false;
03789			enemyDist = VSize(Location - Enemy.Location);
03790			Aggression = 2 * (CombatStyle + FRand()) - 1.1;
03791			if ( intelligence == BRAINS_Human )
03792			{
03793				if ( Enemy.bIsPlayer && (AttitudeToPlayer == ATTITUDE_Fear) && (CombatStyle > 0) )
03794					Aggression = Aggression - 2 - 2 * CombatStyle;
03795				if ( Weapon != None )
03796					Aggression += 2 * Weapon.SuggestAttackStyle();
03797				if ( Enemy.Weapon != None )
03798					Aggression += 2 * Enemy.Weapon.SuggestDefenseStyle();
03799			}
03800	
03801			if ( enemyDist > 1000 )
03802				Aggression += 1;
03803			if ( bIsPlayer && !bNoCharge )
03804				bNoCharge = ( Aggression < FRand() );
03805	
03806			if ( (Physics == PHYS_Walking) || (Physics == PHYS_Falling) )
03807			{
03808				if (Location.Z > Enemy.Location.Z + 140) //tactical height advantage
03809					Aggression = FMax(0.0, Aggression - 1.0 + CombatStyle);
03810				else if (Location.Z < Enemy.Location.Z - CollisionHeight) // below enemy
03811				{
03812					if ( !bNoCharge && (Intelligence > BRAINS_Reptile) 
03813						&& (Aggression > 0) && (FRand() < 0.6) )
03814					{
03815						GotoState('Charging');
03816						return;
03817					}
03818					else if ( (enemyDist < 1.1 * (Enemy.Location.Z - Location.Z)) 
03819							&& !actorReachable(Enemy) ) 
03820					{
03821						bNoReach = (Intelligence > BRAINS_None);
03822						aggression = -1.5 * FRand();
03823					}
03824				}
03825			}
03826		
03827			if (!bNoCharge && (Aggression > 2 * FRand()))
03828			{
03829				if ( bNoReach && (Physics != PHYS_Falling) )
03830				{
03831					TweenToRunning(0.15);
03832					GotoState('Charging', 'NoReach');
03833				}
03834				else
03835					GotoState('Charging');
03836				return;
03837			}
03838	
03839			if (enemyDist > FMax(VSize(OldLocation - Enemy.OldLocation), 240))
03840				Aggression += 0.4 * FRand();
03841				 
03842			enemydir = (Enemy.Location - Location)/enemyDist;
03843			minDist = FMin(160.0, 3*CollisionRadius);
03844			if ( bIsPlayer )
03845				optDist = 80 + FMin(EnemyDist, 250 * (FRand() + FRand()));  
03846			else 
03847				optDist = 50 + FMin(EnemyDist, 500 * FRand());
03848			Y = (enemydir Cross vect(0,0,1));
03849			if ( Physics == PHYS_Walking )
03850			{
03851				Y.Z = 0;
03852				enemydir.Z = 0;
03853			}
03854			else 
03855				enemydir.Z = FMax(0,enemydir.Z);
03856				
03857			strafeSize = FMax(-0.7, FMin(0.85, (2 * Aggression * FRand() - 0.3)));
03858			enemyPart = enemydir * strafeSize;
03859			strafeSize = FMax(0.0, 1 - Abs(strafeSize));
03860			pickdir = strafeSize * Y;
03861			if ( bStrafeDir )
03862				pickdir *= -1;
03863			bStrafeDir = !bStrafeDir;
03864			collSpec.X = CollisionRadius;
03865			collSpec.Y = CollisionRadius;
03866			collSpec.Z = FMax(6, CollisionHeight - 18);
03867			
03868			minDest = Location + minDist * (pickdir + enemyPart);
03869			HitActor = Trace(HitLocation, HitNormal, minDest, Location, false, collSpec);
03870			if (HitActor == None)
03871			{
03872				success = (Physics != PHYS_Walking);
03873				if ( !success )
03874				{
03875					collSpec.X = FMin(14, 0.5 * CollisionRadius);
03876					collSpec.Y = collSpec.X;
03877					HitActor = Trace(HitLocation, HitNormal, minDest - (18 + MaxStepHeight) * vect(0,0,1), minDest, false, collSpec);
03878					success = (HitActor != None);
03879				}
03880				if (success)
03881					Destination = minDest + (pickdir + enemyPart) * optDist;
03882			}
03883		
03884			if ( !success )
03885			{					
03886				collSpec.X = CollisionRadius;
03887				collSpec.Y = CollisionRadius;
03888				minDest = Location + minDist * (enemyPart - pickdir); 
03889				HitActor = Trace(HitLocation, HitNormal, minDest, Location, false, collSpec);
03890				if (HitActor == None)
03891				{
03892					success = (Physics != PHYS_Walking);
03893					if ( !success )
03894					{
03895						collSpec.X = FMin(14, 0.5 * CollisionRadius);
03896						collSpec.Y = collSpec.X;
03897						HitActor = Trace(HitLocation, HitNormal, minDest - (18 + MaxStepHeight) * vect(0,0,1), minDest, false, collSpec);
03898						success = (HitActor != None);
03899					}
03900					if (success)
03901						Destination = minDest + (enemyPart - pickdir) * optDist;
03902				}
03903				else 
03904				{
03905					if ( (CombatStyle <= 0) || (Enemy.bIsPlayer && (AttitudeToPlayer == ATTITUDE_Fear)) )
03906						enemypart = vect(0,0,0);
03907					else if ( (enemydir Dot enemyPart) < 0 )
03908						enemyPart = -1 * enemyPart;
03909					pickDir = Normal(enemyPart - pickdir + HitNormal);
03910					minDest = Location + minDist * pickDir;
03911					collSpec.X = CollisionRadius;
03912					collSpec.Y = CollisionRadius;
03913					HitActor = Trace(HitLocation, HitNormal, minDest, Location, false, collSpec);
03914					if (HitActor == None)
03915					{
03916						success = (Physics != PHYS_Walking);
03917						if ( !success )
03918						{
03919							collSpec.X = FMin(14, 0.5 * CollisionRadius);
03920							collSpec.Y = collSpec.X;
03921							HitActor = Trace(HitLocation, HitNormal, minDest - (18 + MaxStepHeight) * vect(0,0,1), minDest, false, collSpec);
03922							success = (HitActor != None);
03923						}
03924						if (success)
03925							Destination = minDest + pickDir * optDist;
03926					}
03927				}	
03928			}
03929						
03930			if ( !success )
03931				GiveUpTactical(bNoCharge);
03932			else 
03933			{
03934				pickDir = (Destination - Location);
03935				enemyDist = VSize(pickDir);
03936				if ( enemyDist > minDist + 2 * CollisionRadius )
03937				{
03938					pickDir = pickDir/enemyDist;
03939					HitActor = Trace(HitLocation, HitNormal, Destination + 2 * CollisionRadius * pickdir, Location, false);
03940					if ( (HitActor != None) && ((HitNormal Dot pickDir) < -0.6) )
03941						Destination = HitLocation - 2 * CollisionRadius * pickdir;
03942				}
03943			}
03944		}
03945	
03946		function BeginState()
03947		{
03948			MinHitWall += 0.15;
03949			bAvoidLedges = ( !bCanJump && (CollisionRadius > 40) );
03950			bCanJump = false;
03951			bCanFire = false;
03952		}
03953		
03954		function EndState()
03955		{
03956			bAvoidLedges = false;
03957			MinHitWall -= 0.15;
03958			if (JumpZ > 0)
03959				bCanJump = true;
03960		}
03961	
03962	//FIXME - what if bReadyToAttack at start
03963	TacticalTick:
03964		Sleep(0.02);	
03965	Begin:
03966		TweenToRunning(0.15);
03967		Enable('AnimEnd');
03968		if (Physics == PHYS_Falling)
03969		{
03970			DesiredRotation = Rotator(Enemy.Location - Location);
03971			Focus = Enemy.Location;
03972			Destination = Enemy.Location;
03973			WaitForLanding();
03974		}
03975		PickDestination(false);
03976	
03977	DoMove:
03978		if ( !bCanStrafe )
03979		{ 
03980	DoDirectMove:
03981			Enable('AnimEnd');
03982			if ( GetAnimGroup(AnimSequence) == 'MovingAttack' )
03983			{
03984				AnimSequence = '';
03985				TweenToRunning(0.12);
03986			}
03987			bCanFire = false;
03988			MoveTo(Destination);
03989		}
03990		else
03991		{
03992	DoStrafeMove:
03993			Enable('AnimEnd');
03994			bCanFire = true;
03995			StrafeFacing(Destination, Enemy);	
03996		}
03997		if (FRand() < 0.5)
03998			PlayThreateningSound();
03999	
04000		if ( (Enemy != None) && !LineOfSightTo(Enemy) && ValidRecovery() )
04001			Goto('RecoverEnemy');
04002		else
04003		{
04004			bReadyToAttack = true;
04005			GotoState('Attacking');
04006		}
04007		
04008	NoCharge:
04009		TweenToRunning(0.15);
04010		Enable('AnimEnd');
04011		if (Physics == PHYS_Falling)
04012		{
04013			DesiredRotation = Rotator(Enemy.Location - Location);
04014			Focus = Enemy.Location;
04015			Destination = Enemy.Location;
04016			WaitForLanding();
04017		}
04018		PickDestination(true);
04019		Goto('DoMove');
04020		
04021	AdjustFromWall:
04022		Enable('AnimEnd');
04023		StrafeTo(Destination, Focus); 
04024		Destination = Focus; 
04025		Goto('DoMove');
04026	
04027	TakeHit:
04028		TweenToRunning(0.12);
04029		Goto('DoMove');
04030	
04031	RecoverEnemy:
04032		Enable('AnimEnd');
04033		bReadyToAttack = true;
04034		HidingSpot = Location;
04035		bCanFire = false;
04036		Destination = LastSeeingPos + 3 * CollisionRadius * Normal(LastSeeingPos - Location);
04037		if ( bCanStrafe || (VSize(LastSeeingPos - Location) < 3 * CollisionRadius) )
04038			StrafeFacing(Destination, Enemy);
04039		else
04040			MoveTo(Destination);
04041		if ( Weapon == None ) 
04042			Acceleration = vect(0,0,0);
04043		if ( NeedToTurn(Enemy.Location) )
04044		{
04045			PlayTurning();
04046			TurnToward(Enemy);
04047		}
04048		if ( bHasRangedAttack && CanFireAtEnemy() )
04049		{
04050			Disable('AnimEnd');
04051			DesiredRotation = Rotator(Enemy.Location - Location);
04052			if ( Weapon == None ) 
04053			{
04054				PlayRangedAttack();
04055				FinishAnim();
04056				TweenToRunning(0.1);
04057				bReadyToAttack = false;
04058				SetTimer(TimeBetweenAttacks, false);
04059			}
04060			else
04061			{
04062				FireWeapon();
04063				if ( Weapon.bSplashDamage )
04064				{
04065					bFire = 0;
04066					bAltFire = 0;
04067				}
04068			}
04069	
04070			if ( bCanStrafe && (FRand() + 0.1 > CombatStyle) )
04071			{
04072				Enable('EnemyNotVisible');
04073				Enable('AnimEnd');
04074				Destination = HidingSpot + 4 * CollisionRadius * Normal(HidingSpot - Location);
04075				Goto('DoMove');
04076			}
04077		}
04078		if ( !bMovingRangedAttack )
04079			bReadyToAttack = false;
04080	
04081		GotoState('Attacking');
04082	}
04083	
04084	state Hunting
04085	{
04086	ignores EnemyNotVisible; 
04087	
04088		/* MayFall() called by engine physics if walking and bCanJump, and
04089			is about to go off a ledge.  Pawn has opportunity (by setting 
04090			bCanJump to false) to avoid fall
04091		*/
04092		function MayFall()
04093		{
04094			bCanJump = ( (intelligence == BRAINS_None) || (MoveTarget != None) || PointReachable(Destination) );
04095		}
04096	
04097		function FearThisSpot(Actor aSpot)
04098		{
04099			Destination = Location + 120 * Normal(Location - aSpot.Location); 
04100			GotoState('Wandering', 'Moving');
04101		}
04102	
04103		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
04104								Vector momentum, name damageType)
04105		{
04106			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
04107			if ( health <= 0 )
04108				return;
04109			bFrustrated = true;
04110			if (NextState == 'TakeHit')
04111			{
04112				if (AttitudeTo(Enemy) == ATTITUDE_Fear)
04113				{
04114					NextState = 'Retreating';
04115					NextLabel = 'Begin';
04116				}
04117				else
04118				{
04119					NextState = 'Hunting';
04120					NextLabel = 'AfterFall';
04121				}
04122				GotoState('TakeHit'); 
04123			}
04124		}
04125	
04126		function HearNoise(float Loudness, Actor NoiseMaker)
04127		{
04128			if ( SetEnemy(NoiseMaker.instigator) )
04129				LastSeenPos = Enemy.Location; 
04130		}
04131	
04132		function SetFall()
04133		{
04134			NextState = 'Hunting'; 
04135			NextLabel = 'AfterFall';
04136			NextAnim = AnimSequence;
04137			GotoState('FallingState'); 
04138		}
04139	
04140		function bool SetEnemy(Pawn NewEnemy)
04141		{
04142			local float rnd;
04143	
04144			if (Global.SetEnemy(NewEnemy))
04145			{
04146				rnd = FRand();
04147				if ( bReadyToAttack )
04148				{
04149					if (rnd < 0.3)
04150						PlayAcquisitionSound();
04151					else if (rnd < 0.6)
04152						PlayThreateningSound();
04153				}
04154				bReadyToAttack = true;
04155				DesiredRotation = Rotator(Enemy.Location - Location);
04156				if ( !bHasRangedAttack || (CombatStyle > FRand()) )
04157					GotoState('Charging'); 
04158				else
04159					GotoState('Attacking');
04160				return true;
04161			}
04162			return false;
04163		} 
04164	
04165		function AnimEnd()
04166		{
04167			PlayRunning();	
04168			Disable('AnimEnd');
04169		}
04170		
04171		function Timer()
04172		{
04173			bReadyToAttack = true;
04174			Enable('Bump');
04175			SetTimer(1.0, false);
04176		}
04177	
04178		function HitWall(vector HitNormal, actor Wall)
04179		{
04180			if (Physics == PHYS_Falling)
04181				return;
04182			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
04183			{
04184				if ( SpecialPause > 0 )
04185					Acceleration = vect(0,0,0);
04186				GotoState('Hunting', 'SpecialNavig');
04187				return;
04188			}
04189			Focus = Destination;
04190			if (PickWallAdjust())
04191				GotoState('Hunting', 'AdjustFromWall');
04192			else
04193				MoveTimer = -1.0;
04194		}
04195	
04196		function PickDestination()
04197		{
04198			local NavigationPoint path;
04199			local actor HitActor;
04200			local vector HitNormal, HitLocation, nextSpot, ViewSpot;
04201			local float posZ, elapsed;
04202			local bool bCanSeeLastSeen;
04203	
04204			// If no enemy, or I should see him but don't, then give up		
04205			if ( (Enemy == None) || (Enemy.Health <= 0) )
04206			{
04207				WhatToDoNext('','');
04208				return;
04209			}
04210	
04211			bAvoidLedges = false;
04212			elapsed = Level.TimeSeconds - HuntStartTime;
04213			if ( (elapsed > 30) && ((intelligence < BRAINS_Human) || (elapsed > 90)) )
04214			{
04215					WhatToDoNext('','');
04216					return;
04217			}
04218	
04219			if ( JumpZ > 0 )
04220				bCanJump = true;
04221			
04222			if ( ActorReachable(Enemy) )
04223			{
04224				if ( bIsBoss || (numHuntPaths < 8 + Skill) || (elapsed < 15)
04225					|| ((Normal(Enemy.Location - Location) Dot vector(Rotation)) > -0.5) )
04226				{
04227					Destination = Enemy.Location;
04228					MoveTarget = None;
04229					numHuntPaths++;
04230				}
04231				else
04232					WhatToDoNext('','');
04233				return;
04234			}
04235			numHuntPaths++;
04236	
04237			ViewSpot = Location + EyeHeight * vect(0,0,1);
04238			bCanSeeLastSeen = false;
04239			if ( intelligence > BRAINS_Reptile )
04240			{
04241				HitActor = Trace(HitLocation, HitNormal, LastSeenPos, ViewSpot, false);
04242				bCanSeeLastSeen = (HitActor == None);
04243				if ( bCanSeeLastSeen )
04244				{
04245					HitActor = Trace(HitLocation, HitNormal, LastSeenPos, Enemy.Location, false);
04246					bHunting = (HitActor != None);
04247				}
04248				else
04249					bHunting = true;
04250				if ( FindBestPathToward(Enemy) )
04251					return;
04252			}
04253				
04254			MoveTarget = None;
04255			if ( bFromWall )
04256			{
04257				bFromWall = false;
04258				if ( !PickWallAdjust() )
04259				{
04260					if ( CanStakeOut() )
04261						GotoState('StakeOut');
04262					else
04263						WhatToDoNext('', '');
04264				}
04265				return;
04266			}
04267			
04268			if ( !bIsBoss && (NumHuntPaths > 20) && ((Intelligence < BRAINS_Human) || (NumHuntPaths > 60)) )
04269			{
04270				WhatToDoNext('', '');
04271				return;
04272			}
04273	
04274			if ( LastSeeingPos != vect(1000000,0,0) )
04275			{
04276				Destination = LastSeeingPos;
04277				LastSeeingPos = vect(1000000,0,0);		
04278				HitActor = Trace(HitLocation, HitNormal, Enemy.Location, ViewSpot, false);
04279				if ( HitActor == None )
04280				{
04281					If (VSize(Location - Destination) < 20)
04282					{
04283						HitActor = Trace(HitLocation, HitNormal, Enemy.Location, ViewSpot, false);
04284						if (HitActor == None)
04285						{
04286							SetEnemy(Enemy);
04287							return;
04288						}
04289					}
04290					return;
04291				}
04292			}
04293	
04294			bAvoidLedges = ( (CollisionRadius > 42) && (Intelligence < BRAINS_Human) );
04295			posZ = LastSeenPos.Z + CollisionHeight - Enemy.CollisionHeight;
04296			nextSpot = LastSeenPos - Normal(Enemy.Location - Enemy.OldLocation) * CollisionRadius;
04297			nextSpot.Z = posZ;
04298			HitActor = Trace(HitLocation, HitNormal, nextSpot , ViewSpot, false);
04299			if ( HitActor == None )
04300				Destination = nextSpot;
04301			else if ( bCanSeeLastSeen )
04302				Destination = LastSeenPos;
04303			else
04304			{
04305				Destination = LastSeenPos;
04306				HitActor = Trace(HitLocation, HitNormal, LastSeenPos , ViewSpot, false);
04307				if ( HitActor != None )
04308				{
04309					// check if could adjust and see it
04310					if ( PickWallAdjust() || FindViewSpot() )
04311						GotoState('Hunting', 'AdjustFromWall');
04312					else if ( bIsBoss || VSize(Enemy.Location - Location) < 1200 )
04313						GotoState('StakeOut');
04314					else
04315					{
04316						WhatToDoNext('Waiting', 'TurnFromWall');
04317						return;
04318					}
04319				}
04320			}
04321			LastSeenPos = Enemy.Location;				
04322		}	
04323	
04324		function bool FindViewSpot()
04325		{
04326			local vector X,Y,Z, HitLocation, HitNormal;
04327			local actor HitActor;
04328			local bool bAlwaysTry;
04329			GetAxes(Rotation,X,Y,Z);
04330	
04331			// try left and right
04332			// if frustrated, always move if possible
04333			bAlwaysTry = bFrustrated;
04334			bFrustrated = false;
04335			
04336			HitActor = Trace(HitLocation, HitNormal, Enemy.Location, Location + 2 * Y * CollisionRadius, false);
04337			if ( HitActor == None )
04338			{
04339				Destination = Location + 2.5 * Y * CollisionRadius;
04340				return true;
04341			}
04342	
04343			HitActor = Trace(HitLocation, HitNormal, Enemy.Location, Location - 2 * Y * CollisionRadius, false);
04344			if ( HitActor == None )
04345			{
04346				Destination = Location - 2.5 * Y * CollisionRadius;
04347				return true;
04348			}
04349			if ( bAlwaysTry )
04350			{
04351				if ( FRand() < 0.5 )
04352					Destination = Location - 2.5 * Y * CollisionRadius;
04353				else
04354					Destination = Location - 2.5 * Y * CollisionRadius;
04355				return true;
04356			}
04357	
04358			return false;
04359		}
04360	
04361		function BeginState()
04362		{
04363			SpecialGoal = None;
04364			SpecialPause = 0.0;
04365			bFromWall = false;
04366			SetAlertness(0.5);
04367		}
04368	
04369		function EndState()
04370		{
04371			bAvoidLedges = false;
04372			bHunting = false;
04373			if ( JumpZ > 0 )
04374				bCanJump = true;
04375		}
04376	
04377	AdjustFromWall:
04378		StrafeTo(Destination, Focus); 
04379		Destination = Focus; 
04380		if ( MoveTarget != None )
04381			Goto('SpecialNavig');
04382		else
04383			Goto('Follow');
04384	
04385	Begin:
04386		numHuntPaths = 0;
04387		HuntStartTime = Level.TimeSeconds;
04388	AfterFall:
04389		TweenToRunning(0.15);
04390		bFromWall = false;
04391	
04392	Follow:
04393		WaitForLanding();
04394		if ( CanSee(Enemy) )
04395			SetEnemy(Enemy);
04396		PickDestination();
04397	SpecialNavig:
04398		if ( SpecialPause > 0.0 )
04399		{
04400			Disable('AnimEnd');
04401			Acceleration = vect(0,0,0);
04402			PlayChallenge();
04403			Sleep(SpecialPause);
04404			SpecialPause = 0.0;
04405			Enable('AnimEnd');
04406		}
04407		if (MoveTarget == None)
04408			MoveTo(Destination);
04409		else
04410			MoveToward(MoveTarget); 
04411		if ( Intelligence < BRAINS_Human )
04412		{
04413			if ( FRand() > 0.3 )
04414				PlayRoamingSound();
04415			else if ( FRand() > 0.3 )
04416				PlayThreateningSound();
04417		}
04418		if ( (Orders == 'Guarding') && !LineOfSightTo(OrderObject) )
04419			GotoState('Guarding'); 
04420		Goto('Follow');
04421	}
04422	
04423	state StakeOut
04424	{
04425	ignores EnemyNotVisible; 
04426	
04427		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
04428								Vector momentum, name damageType)
04429		{
04430			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
04431			if ( health <= 0 )
04432				return;
04433			bFrustrated = true;
04434			LastSeenPos = Enemy.Location;
04435			if (NextState == 'TakeHit')
04436			{
04437				if (AttitudeTo(Enemy) == ATTITUDE_Fear)
04438				{
04439					NextState = 'Retreating';
04440					NextLabel = 'Begin';
04441				}
04442				else
04443				{
04444					NextState = 'Attacking';
04445					NextLabel = 'Begin';
04446				}
04447				GotoState('TakeHit'); 
04448			}
04449			else
04450				GotoState('Attacking');
04451		}
04452	
04453		function HearNoise(float Loudness, Actor NoiseMaker)
04454		{
04455			if ( SetEnemy(NoiseMaker.instigator) )
04456				LastSeenPos = Enemy.Location; 
04457		}
04458	
04459		function SetFall()
04460		{
04461			NextState = 'StakeOut'; 
04462			NextLabel = 'Begin';
04463			NextAnim = AnimSequence;
04464			GotoState('FallingState'); 
04465		}
04466	
04467		function bool SetEnemy(Pawn NewEnemy)
04468		{
04469			local float rnd;
04470	
04471			if (Global.SetEnemy(NewEnemy))
04472			{
04473				rnd = FRand();
04474				if (rnd < 0.3)
04475					PlayAcquisitionSound();
04476				else if (rnd < 0.6)
04477					PlayThreateningSound();
04478				bReadyToAttack = true;
04479				DesiredRotation = Rotator(Enemy.Location - Location);
04480				GotoState('Attacking');
04481				return true;
04482			}
04483			return false;
04484		} 
04485		
04486		function Timer()
04487		{
04488			bReadyToAttack = true;
04489			Enable('Bump');
04490			SetTimer(1.0, false);
04491		}
04492	
04493		function rotator AdjustAim(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget)
04494		{
04495			local rotator FireRotation;
04496			local vector FireSpot;
04497			local actor HitActor;
04498			local vector HitLocation, HitNormal;
04499					
04500			FireSpot = LastSeenPos;
04501			aimerror = aimerror * (0.5 * (4 - skill - FRand()));	
04502				 
04503			HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
04504			if( HitActor != None ) 
04505			{
04506				////log("adjust aim up");
04507	 			FireSpot.Z += 0.9 * Target.CollisionHeight;
04508	 			HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
04509				bClearShot = (HitActor == None);
04510			}
04511			
04512			FireRotation = Rotator(FireSpot - ProjStart);
04513				 
04514			FireRotation.Yaw = FireRotation.Yaw + 0.5 * (Rand(2 * aimerror) - aimerror);
04515			viewRotation = FireRotation;			
04516			return FireRotation;
04517		}
04518			
04519		function BeginState()
04520		{
04521			Acceleration = vect(0,0,0);
04522			bCanJump = false;
04523			bClearShot = true;
04524			bReadyToAttack = true;
04525			SetAlertness(0.5);
04526		}
04527	
04528		function EndState()
04529		{
04530			if ( JumpZ > 0 )
04531				bCanJump = true;
04532		}
04533	
04534	Begin:
04535		Acceleration = vect(0,0,0);
04536		PlayChallenge();
04537		TurnTo(LastSeenPos);
04538		if ( bHasRangedAttack && bClearShot && (FRand() < 0.5) && (VSize(Enemy.Location - LastSeenPos) < 150) && CanStakeOut() )
04539			PlayRangedAttack();
04540		FinishAnim();
04541		PlayChallenge();
04542		if ( bCrouching && !Region.Zone.bWaterZone )
04543			Sleep(1);
04544		bCrouching = false;
04545		Sleep(1 + FRand());
04546		if ( !bHasRangedAttack || !bClearShot || (VSize(Enemy.Location - Location) 
04547					> 350 + (FRand() * RelativeStrength(Enemy) - CombatStyle) * 350) )
04548			GotoState('Hunting', 'AfterFall');
04549		else if ( CanStakeOut() )
04550			Goto('Begin');
04551		else
04552			GotoState('Hunting', 'AfterFall');
04553	}
04554	
04555	state TakeHit 
04556	{
04557	ignores seeplayer, hearnoise, bump, hitwall;
04558	
04559		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
04560							Vector momentum, name damageType)
04561		{
04562			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
04563		}
04564	
04565		function Landed(vector HitNormal)
04566		{
04567			if (Velocity.Z < -1.4 * JumpZ)
04568				MakeNoise(-0.5 * Velocity.Z/(FMax(JumpZ, 150.0)));
04569			bJustLanded = true;
04570		}
04571	
04572		function Timer()
04573		{
04574			bReadyToAttack = true;
04575			if ( SpeechTime > 0 )
04576			{
04577				SpeechTime = -1.0;
04578				bIsSpeaking = false;
04579				if ( TeamLeader != None )
04580					TeamLeader.bTeamSpeaking = false;
04581			}
04582		}
04583	
04584		function PlayHitAnim(vector HitLocation, float Damage)
04585		{
04586			if ( LastPainTime - Level.TimeSeconds > 0.1 )
04587			{
04588				PlayTakeHit(0.1, hitLocation, Damage);
04589				BeginState();
04590				GotoState('TakeHit', 'Begin');
04591			} 
04592		}	
04593	
04594		function BeginState()
04595		{
04596			LastPainTime = Level.TimeSeconds;
04597			LastPainAnim = AnimSequence;
04598		}
04599			
04600	Begin:
04601		// Acceleration = Normal(Acceleration);
04602		FinishAnim();
04603		if ( skill < 2 )
04604			Sleep(0.05);
04605		if ( (Physics == PHYS_Falling) && !Region.Zone.bWaterZone )
04606		{
04607			Acceleration = vect(0,0,0);
04608			NextAnim = '';
04609			GotoState('FallingState', 'Ducking');
04610		}
04611		else if (NextState != '')
04612			GotoState(NextState, NextLabel);
04613		else
04614			GotoState('Attacking');
04615	}
04616	
04617	state FallingState 
04618	{
04619	ignores Bump, Hitwall, WarnTarget;
04620	
04621		function ZoneChange(ZoneInfo newZone)
04622		{
04623			Global.ZoneChange(newZone);
04624			if (newZone.bWaterZone)
04625			{
04626				TweenToWaiting(0.15);
04627				//FIXME - play splash sound and effect
04628				GotoState('FallingState', 'Splash');
04629			}
04630		}
04631		
04632		//choose a jump velocity
04633		function adjustJump()
04634		{
04635			local float velZ;
04636			local vector FullVel;
04637	
04638			velZ = Velocity.Z;
04639			FullVel = Normal(Velocity) * GroundSpeed;
04640	
04641			If (Location.Z > Destination.Z + CollisionHeight + 2 * MaxStepHeight)
04642			{
04643				Velocity = FullVel;
04644				Velocity.Z = velZ;
04645				Velocity = EAdjustJump();
04646				Velocity.Z = 0;
04647				if ( VSize(Velocity) < 0.9 * GroundSpeed )
04648				{
04649					Velocity.Z = velZ;
04650					return;
04651				}
04652			}
04653	
04654			Velocity = FullVel;
04655			Velocity.Z = JumpZ + velZ;
04656			Velocity = EAdjustJump();
04657		}
04658	
04659		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
04660								Vector momentum, name damageType)
04661		{
04662			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
04663			if ( health <= 0 )
04664				return;
04665			if (Enemy == None)
04666			{
04667				Enemy = instigatedBy;
04668				NextState = 'Attacking'; 
04669				NextLabel = 'Begin';
04670			}
04671			if (Enemy != None)
04672				LastSeenPos = Enemy.Location;
04673			if (NextState == 'TakeHit')
04674			{
04675				NextState = 'Attacking'; 
04676				NextLabel = 'Begin';
04677				GotoState('TakeHit'); 
04678			}
04679		}
04680	
04681		function bool SetEnemy(Pawn NewEnemy)
04682		{
04683			local bool result;
04684			result = false;
04685			if ( Global.SetEnemy(NewEnemy))
04686			{
04687				result = true;
04688				NextState = 'Attacking'; 
04689				NextLabel = 'Begin';
04690			}
04691			return result;
04692		} 
04693	
04694		function Timer()
04695		{
04696			if (Enemy != None)
04697				bReadyToAttack = true;
04698		}
04699	
04700		function Landed(vector HitNormal)
04701		{
04702			local float landVol, minJumpZ;
04703	
04704			minJumpZ = FMax(JumpZ, 150.0);
04705			bJustLanded = true;
04706			if ( (Velocity.Z < -0.8 * minJumpZ) || bUpAndOut)
04707			{
04708				MakeNoise(-0.5 * Velocity.Z/minJumpZ);
04709				PlayLanded(Velocity.Z);
04710				if ( Velocity.Z < FMin(-600, -3.5 * JumpZ) )
04711					TakeDamage(-0.04 * (Velocity.Z + FMax(400, 3.5 * JumpZ)), Self, Location, vect(0,0,0), 'Fell');
04712				landVol = Velocity.Z/JumpZ;
04713				landVol = 0.005 * Mass * FMin(5, landVol * landVol);
04714				if ( !FootRegion.Zone.bWaterZone )
04715					PlaySound(Land, SLOT_Interact, FMin(20, landVol));
04716				if ( health > 0 )
04717					GotoState('FallingState', 'Landed');
04718			}
04719			else if ( Velocity.Z < -0.8 * JumpZ )
04720			{
04721				PlayLanded(Velocity.Z);
04722				GotoState('FallingState', 'FastLanded');
04723			}
04724			else 
04725				GotoState('FallingState', 'Done');
04726		}
04727		
04728		function SeePlayer(Actor SeenPlayer)
04729		{
04730			Global.SeePlayer(SeenPlayer);
04731			disable('SeePlayer');
04732			disable('HearNoise');
04733		}
04734	
04735		function EnemyNotVisible()
04736		{
04737			enable('SeePlayer');
04738			enable('HearNoise');
04739		}
04740	
04741		function SetFall()
04742		{
04743			if (!bUpAndOut)
04744				GotoState('FallingState');
04745		}
04746		
04747		function EnemyAcquired()
04748		{
04749			NextState = 'Acquisition';
04750			NextLabel = 'Begin';
04751		}
04752	
04753		function BeginState()
04754		{
04755			if (Enemy == None)
04756				Disable('EnemyNotVisible');
04757			else
04758			{
04759				Disable('HearNoise');
04760				Disable('SeePlayer');
04761			}
04762		}
04763	
04764		function EndState()
04765		{
04766			bUpAndOut = false;
04767		}
04768	
04769	LongFall:
04770		if ( bCanFly )
04771		{
04772			SetPhysics(PHYS_Flying);
04773			Goto('Done');
04774		}
04775		Sleep(0.7);
04776		TweenToFighter(0.2);
04777		if ( bHasRangedAttack && (Enemy != None) )
04778		{
04779			TurnToward(Enemy);
04780			FinishAnim();
04781			if ( CanFireAtEnemy() )
04782			{
04783				PlayRangedAttack();
04784				FinishAnim();
04785			}
04786			PlayChallenge();
04787			FinishAnim();
04788		}
04789		TweenToFalling();
04790		if ( Velocity.Z > -150 ) //stuck
04791		{
04792			SetPhysics(PHYS_Falling);
04793			if ( Enemy != None )
04794				Velocity = groundspeed * normal(Enemy.Location - Location);
04795			else
04796				Velocity = groundspeed * VRand();
04797	
04798			Velocity.Z = FMax(JumpZ, 250);
04799		}
04800		Goto('LongFall');	
04801	FastLanded:
04802		FinishAnim();
04803		Goto('Done');
04804	Landed:
04805		if ( !bIsPlayer ) //bots act like players
04806			Acceleration = vect(0,0,0);
04807		FinishAnim();
04808		if ( !bIsPlayer && (skill < 3) )
04809			Sleep(0.08);
04810	Done:
04811		if ( NextAnim == '' )
04812		{
04813			bUpAndOut = false;
04814			if ( NextState != '' )
04815				GotoState(NextState, NextLabel);
04816			else 
04817				GotoState('Attacking');
04818		}
04819		if ( !bUpAndOut )
04820		{
04821			if ( NextAnim == 'Fighter' )
04822				TweenToFighter(0.2);
04823			else
04824				TweenAnim(NextAnim, 0.2);
04825		} 
04826	Splash:
04827		bUpAndOut = false;
04828		FinishAnim();
04829		if ( NextState != '' )
04830			GotoState(NextState, NextLabel);
04831		else 
04832			GotoState('Attacking');
04833				
04834	Begin:
04835		if (Enemy == None)
04836			Disable('EnemyNotVisible');
04837		else
04838		{
04839			Disable('HearNoise');
04840			Disable('SeePlayer');
04841		}
04842		if (bUpAndOut) //water jump
04843		{
04844			if ( !bIsPlayer ) 
04845			{
04846				DesiredRotation = Rotation;
04847				DesiredRotation.Pitch = 0;
04848				Velocity.Z = 440; 
04849			}
04850		}
04851		else
04852		{	
04853			if (Region.Zone.bWaterZone)
04854			{
04855				SetPhysics(PHYS_Swimming);
04856				GotoState(NextState, NextLabel);
04857			}	
04858			if ( !bJumpOffPawn )
04859				AdjustJump();
04860			else
04861				bJumpOffPawn = false;
04862	PlayFall:
04863			TweenToFalling();
04864			FinishAnim();
04865			PlayInAir();
04866		}
04867		
04868		if (Physics != PHYS_Falling)
04869			Goto('Done');
04870		Sleep(2.0);
04871		Goto('LongFall');
04872	
04873	Ducking:
04874			
04875	}
04876	
04877	state MeleeAttack
04878	{
04879	ignores SeePlayer, HearNoise, Bump;
04880	/* DamageTarget
04881	check if attack hit target, and if so damage it
04882	*/
04883		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
04884								Vector momentum, name damageType)
04885		{
04886			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
04887			if ( health <= 0 )
04888				return;
04889			if (NextState == 'TakeHit')
04890			{
04891				NextState = 'MeleeAttack';
04892				NextLabel = 'Begin';
04893			}
04894		}
04895	
04896		function KeepAttacking()
04897		{
04898			if ( (Enemy == None) || (Enemy.Health <= 0) 
04899				|| (VSize(Enemy.Location - Location) > (MeleeRange + Enemy.CollisionRadius + CollisionRadius)) )
04900				GotoState('Attacking');
04901		}
04902	
04903		function EnemyNotVisible()
04904		{
04905			//log("enemy not visible");
04906			GotoState('Attacking');
04907		}
04908	
04909		function AnimEnd()
04910		{
04911			GotoState('MeleeAttack', 'DoneAttacking');
04912		}
04913		
04914		function BeginState()
04915		{
04916			Target = Enemy;
04917			Disable('AnimEnd');
04918			bReadyToAttack = false;
04919		}
04920	
04921	Begin:
04922		if ( Enemy == None )
04923			GotoState('Attacking');
04924		DesiredRotation = Rotator(Enemy.Location - Location);
04925		if ( skill < 3 )
04926			TweenToFighter(0.15); 
04927		else
04928			TweenToFighter(0.11);
04929		
04930	FaceTarget:
04931		Acceleration = vect(0,0,0); //stop
04932		if (NeedToTurn(Enemy.Location))
04933		{
04934			PlayTurning();
04935			TurnToward(Enemy);
04936			TweenToFighter(0.1);
04937		}
04938		FinishAnim();
04939		OldAnimRate = 0;	// force no tween 
04940		
04941		if ( (Physics == PHYS_Swimming) || (Physics == PHYS_Flying) )
04942		{
04943			 if ( VSize(Location - Enemy.Location) > MeleeRange + CollisionRadius + Enemy.CollisionRadius )
04944				GotoState('RangedAttack', 'ReadyToAttack'); 
04945		}
04946		else if ( (Abs(Location.Z - Enemy.Location.Z) 
04947				> FMax(CollisionHeight, Enemy.CollisionHeight) + 0.5 * FMin(CollisionHeight, Enemy.CollisionHeight)) ||
04948			(VSize(Location - Enemy.Location) > MeleeRange + CollisionRadius + Enemy.CollisionRadius) )
04949			GotoState('RangedAttack', 'ReadyToAttack'); 
04950	
04951	ReadyToAttack:
04952		DesiredRotation = Rotator(Enemy.Location - Location);
04953		PlayMeleeAttack();
04954		Enable('AnimEnd');
04955	Attacking:
04956		TurnToward(Enemy);
04957		Goto('Attacking');
04958	DoneAttacking:
04959		Disable('AnimEnd');
04960		KeepAttacking();
04961		if ( FRand() < 0.3 - 0.1 * skill )
04962		{
04963			Acceleration = vect(0,0,0); //stop
04964			DesiredRotation = Rotator(Enemy.Location - Location);
04965			PlayChallenge();
04966			FinishAnim();
04967			TweenToFighter(0.1);
04968		}
04969		Goto('FaceTarget');
04970	}
04971	
04972	state RangedAttack
04973	{
04974	ignores SeePlayer, HearNoise, Bump;
04975	
04976		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
04977							Vector momentum, name damageType)
04978		{
04979			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
04980			if ( health <= 0 )
04981				return;
04982			if (NextState == 'TakeHit')
04983			{
04984				NextState = 'RangedAttack';
04985				NextLabel = 'Begin';
04986			}
04987		}
04988	
04989		function StopWaiting()
04990		{
04991			Timer();
04992		}
04993	
04994		function EnemyNotVisible()
04995		{
04996			////log("enemy not visible");
04997			//let attack animation completes
04998		}
04999	
05000		function KeepAttacking()
05001		{
05002			if ( !bFiringPaused && ((FRand() > ReFireRate) || (Enemy == None) || (Enemy.Health <= 0) || !CanFireAtEnemy()) ) 
05003				GotoState('Attacking');
05004		}
05005	
05006		function Timer()
05007		{
05008			if ( bFiringPaused )
05009			{
05010				TweenToRunning(0.12);
05011				GotoState(NextState, NextLabel);
05012			}
05013		}
05014	
05015		function AnimEnd()
05016		{
05017			GotoState('RangedAttack', 'DoneFiring');
05018		}
05019		
05020		function BeginState()
05021		{
05022			Target = Enemy;
05023			Disable('AnimEnd');
05024			bReadyToAttack = false;
05025			if ( bFiringPaused )
05026			{
05027				SetTimer(SpecialPause, false);
05028				SpecialPause = 0;
05029			}
05030		}
05031		
05032		function EndState()
05033		{
05034			bFiringPaused = false;
05035		}
05036	
05037	Challenge:
05038		Disable('AnimEnd');
05039		Acceleration = vect(0,0,0); //stop
05040		DesiredRotation = Rotator(Enemy.Location - Location);
05041		PlayChallenge();
05042		FinishAnim();
05043		if ( bCrouching && !Region.Zone.bWaterZone )
05044			Sleep(0.8 + FRand());
05045		bCrouching = false;
05046		TweenToFighter(0.1);
05047		Goto('FaceTarget');
05048	
05049	Begin:
05050		if ( Enemy == None )
05051			GotoState('Attacking');
05052	
05053		Acceleration = vect(0,0,0); //stop
05054		DesiredRotation = Rotator(Enemy.Location - Location);
05055		TweenToFighter(0.15);
05056		
05057	FaceTarget:
05058		Disable('AnimEnd');
05059		if (NeedToTurn(Enemy.Location))
05060		{
05061			PlayTurning();
05062			TurnToward(Enemy);
05063			TweenToFighter(0.1);
05064		}
05065		FinishAnim();
05066	
05067		if (VSize(Location - Enemy.Location) < 0.9 * MeleeRange + CollisionRadius + Enemy.CollisionRadius)
05068			GotoState('MeleeAttack', 'ReadyToAttack'); 
05069	
05070	ReadyToAttack:
05071		if (!bHasRangedAttack)
05072			GotoState('Attacking');
05073		DesiredRotation = Rotator(Enemy.Location - Location);
05074		PlayRangedAttack();
05075		Enable('AnimEnd');
05076	Firing:
05077		if (Enemy == None )
05078			GotoState('Attacking');
05079		TurnToward(Enemy);
05080		Goto('Firing');
05081	DoneFiring:
05082		Disable('AnimEnd');
05083		KeepAttacking();  
05084		Goto('FaceTarget');
05085	}
05086	
05087	state VictoryDance
05088	{
05089	ignores EnemyNotVisible; 
05090	
05091		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
05092								Vector momentum, name damageType)
05093		{
05094			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
05095			if ( health <= 0 )
05096				return;
05097			Enemy = instigatedBy;
05098			if ( NextState == 'TakeHit' )
05099			{
05100				NextState = 'Attacking'; //default
05101				NextLabel = 'Begin';
05102				GotoState('TakeHit'); 
05103			}
05104			else if (health > 0)
05105				GotoState('Attacking');
05106		}
05107	
05108		function EnemyAcquired()
05109		{
05110			//log(Class$" just acquired an enemy");
05111			GotoState('Acquisition');
05112		}
05113		
05114		function PickDestination()
05115		{
05116			local Actor path;
05117			local vector destpoint;
05118			
05119			if (Target == None)
05120			{
05121				WhatToDoNext('Waiting', 'TurnFromWall'); 
05122				return;
05123			}		
05124			destpoint = Target.Location;
05125			destpoint.Z += CollisionHeight - Target.CollisionHeight;
05126			if (pointReachable(destpoint))
05127			{
05128				MoveTarget = Target;
05129				Destination = destpoint;
05130			}
05131			else
05132			{
05133				if (SpecialGoal != None)
05134					path = FindPathToward(SpecialGoal);
05135				else
05136					path = FindPathToward(Target);
05137				if (path != None)
05138				{
05139					MoveTarget = path;
05140					Destination = path.Location;
05141				}
05142				else
05143					WhatToDoNext('Waiting', 'TurnFromWall'); 
05144			}
05145		}
05146		
05147		function BeginState()
05148		{
05149			SpecialGoal = None;
05150			SpecialPause = 0.0;
05151			SetAlertness(-0.3);
05152		}
05153	
05154	Begin:
05155		if ( (Target == None) || 
05156			(VSize(Location - Target.Location) < 
05157			(1.3 * CollisionRadius + Target.CollisionRadius + CollisionHeight - Target.CollisionHeight)) )
05158				Goto('Taunt');
05159		Destination = Target.Location;
05160		TweenToWalking(0.3);
05161		FinishAnim();
05162		PlayWalking();
05163		Enable('Bump');
05164			
05165	MoveToEnemy:
05166	
05167		WaitForLanding();
05168		PickDestination();
05169		if (SpecialPause > 0.0)
05170		{
05171			Acceleration = vect(0,0,0);
05172			TweenToPatrolStop(0.3);
05173			Sleep(SpecialPause);
05174			SpecialPause = 0.0;
05175			TweenToWalking(0.1);
05176			FinishAnim();
05177			PlayWalking();
05178		}
05179		MoveToward(MoveTarget, WalkingSpeed);
05180		Enable('Bump');
05181		If (VSize(Location - Target.Location) < 
05182			(1.3 * CollisionRadius + Target.CollisionRadius + Abs(CollisionHeight - Target.CollisionHeight)))
05183			Goto('Taunt');
05184		Goto('MoveToEnemy');
05185	
05186	Taunt:
05187		Acceleration = vect(0,0,0);
05188		TweenToFighter(0.2);
05189		FinishAnim();
05190		PlayTurning();
05191		TurnToward(Target);
05192		DesiredRotation = rot(0,0,0);
05193		DesiredRotation.Yaw = Rotation.Yaw;
05194		setRotation(DesiredRotation);
05195		TweenToFighter(0.2);
05196		FinishAnim();
05197		PlayVictoryDance();
05198		FinishAnim(); 
05199		WhatToDoNext('Waiting','TurnFromWall');
05200	}
05201	
05202	/*
05203	TriggerAlarm is used by unfriendly creatures to run to an actor and trigger something, or by friendly creatures
05204	to lead the player to something */
05205	
05206	State TriggerAlarm
05207	{
05208		ignores HearNoise, SeePlayer;
05209	
05210		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
05211								Vector momentum, name damageType)
05212		{
05213			local bool bWasFriendly;
05214			
05215			bWasFriendly = ( !bNoWait && Enemy.bIsPlayer && (AttitudeToPlayer == ATTITUDE_Friendly) );
05216			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
05217			if ( health <= 0 )
05218				return;
05219			if (NextState == 'TakeHit')
05220			{
05221				if ( bWasFriendly && (AttitudeToPlayer < ATTITUDE_Friendly) )
05222				{
05223					AlarmTag = '';
05224					NextState = 'Attacking';
05225					NextLabel = 'Begin';
05226				}
05227				else
05228				{
05229					NextState = 'TriggerAlarm';
05230					NextLabel = 'Recover';
05231				}
05232				GotoState('TakeHit'); 
05233			}
05234		}
05235	
05236		function SetFall()
05237		{
05238			NextState = 'TriggerAlarm'; 
05239			NextLabel = 'Recover';
05240			NextAnim = AnimSequence;
05241			GotoState('FallingState'); 
05242		}
05243		
05244		function EnemyNotVisible()
05245		{
05246			//friendly creatures will stop and wait
05247			if (AttitudeTo(Enemy) >= ATTITUDE_Ignore)
05248				GotoState('AlarmPaused', 'WaitForPlayer');
05249		}
05250		
05251		function Touch(actor Other)
05252		{
05253			if (Other == OrderObject)
05254				AlarmDone();
05255		}
05256	
05257		function Bump(actor Other)
05258		{
05259			local vector VelDir, OtherDir;
05260			local float speed;
05261	
05262			if (Other == OrderObject)
05263			{
05264				AlarmDone();
05265				if ( (Pawn(Other) != None) && SetEnemy(Pawn(Other)) )
05266					GotoState('MeleeAttack');
05267				return;
05268			}
05269			if ( (Other == Enemy) || SetEnemy(Pawn(Other)) )
05270			{
05271				GotoState('MeleeAttack');
05272				return;
05273			}
05274			if ( TimerRate <= 0 )
05275				setTimer(1.0, false);
05276			 
05277			speed = VSize(Velocity);
05278			if ( speed > 1 )
05279			{
05280				VelDir = Velocity/speed;
05281				VelDir.Z = 0;
05282				OtherDir = Other.Location - Location;
05283				OtherDir.Z = 0;
05284				OtherDir = Normal(OtherDir);
05285				if ( (VelDir Dot OtherDir) > 0.9 )
05286				{
05287					Velocity.X = VelDir.Y;
05288					Velocity.Y = -1 * VelDir.X;
05289					Velocity *= FMax(speed, 200);
05290				}
05291			}  
05292			Disable('Bump');
05293		}
05294		
05295		function AlarmDone()
05296		{
05297			local pawn OtherPawn;
05298			local Actor A;
05299			local AlarmPoint AlarmSpot;
05300	
05301			AlarmSpot = AlarmPoint(OrderObject);
05302			if ( AlarmSpot != None )
05303			{
05304				if( AlarmSpot.Event != '' )
05305					foreach AllActors( class 'Actor', A, AlarmSpot.Event )
05306					{
05307						if ( (ScriptedPawn(A) != None) && AlarmSpot.bKillMe )
05308							ScriptedPawn(A).Hated = self;
05309						A.Trigger( self, instigator );
05310					}
05311				if ( AlarmSpot.bDestroyAlarmTriggerer )
05312				{
05313					OtherPawn = Level.PawnList;
05314					while ( OtherPawn != None )
05315					{
05316						OtherPawn.Killed(self, self, '');
05317						OtherPawn = OtherPawn.nextPawn;
05318					}
05319					level.game.Killed(self, self, '');
05320					//log(class$" dying");
05321					if( Event != '' )
05322						foreach AllActors( class 'Actor', A, Event )
05323							A.Trigger( Self, Instigator );
05324					Weapon = None;
05325					Level.Game.DiscardInventory(self);
05326					Destroy();		
05327					return;
05328				}
05329				AlarmTag = AlarmSpot.NextAlarm;
05330				if ( AlarmSpot.pausetime > 0.0 )
05331				{
05332					Acceleration = vect(0,0,0);
05333					if (AttitudeTo(Enemy) > ATTITUDE_Ignore)
05334						GotoState('AlarmPaused', 'WaitAround');
05335					else
05336						GotoState('AlarmPaused');
05337				}
05338				else if ( AlarmTag != '' )
05339				{
05340					FindAlarm();
05341					GotoState('TriggerAlarm', 'Begin');
05342				}
05343				else if (AttitudeTo(Enemy) > ATTITUDE_Ignore)
05344				{
05345					Acceleration = vect(0,0,0);
05346					GotoState('Roaming');
05347				}
05348				else
05349				{
05350					bReadyToAttack = true;
05351					GotoState('Attacking');
05352				}
05353				return;
05354			} 
05355			
05356			AlarmTag = '';
05357					
05358			if (AttitudeToPlayer > ATTITUDE_Ignore)
05359				GotoState('AlarmPaused', 'WaitAround');
05360			else
05361			{
05362				bReadyToAttack = true;
05363				GotoState('Attacking');
05364			}
05365		}
05366		
05367		function FindAlarm()
05368		{		
05369			if ( (OrderObject == None) || (OrderObject.Tag != AlarmTag) ) //find alarm object
05370			{
05371				if ( (AlarmPoint(OrderObject) != None) && (AlarmPoint(OrderObject).NextAlarm == AlarmTag) )
05372					OrderObject = AlarmPoint(OrderObject).NextAlarmObject;
05373				else if ( AlarmTag != '' )
05374					foreach AllActors(class 'Actor', OrderObject, AlarmTag)
05375						break; 
05376			}
05377			if ( (OrderObject == None) || (OrderObject.Tag != AlarmTag) )
05378			{
05379				AlarmTag = '';
05380				GotoState('Attacking');
05381			}
05382		}
05383		
05384		function AnimEnd()
05385		{
05386			if ( bSpecialPausing )
05387				PlayPatrolStop();
05388			else if (!bCanFire)
05389				PlayRunning();
05390			else
05391				PlayCombatMove();
05392			bReadyToAttack = bMovingRangedAttack;
05393		}
05394		
05395		function BeginState()
05396		{
05397			bCanFire = false;
05398			SpecialGoal = None;
05399			SpecialPause = 0.0;
05400			bSpecialPausing = false;
05401			if ( !Enemy.bIsPlayer 
05402				|| ((AttitudeToPlayer == ATTITUDE_Fear) 
05403					&& !bInitialFear && (Default.AttitudeToPlayer == ATTITUDE_Friendly)) )
05404			{
05405				GotoState('Attacking');
05406				return;
05407			}
05408		
05409			FindAlarm();
05410			
05411			if ( (TeamLeader != None) && !TeamLeader.bTeamSpeaking )
05412				TeamLeader.SpeakOrderTo(self);
05413		}
05414	
05415	Recover:
05416		if ( (AlarmPoint(OrderObject) != None) && !AlarmPoint(OrderObject).bNoFail 
05417			&& !actorReachable(OrderObject) )
05418		{
05419			AlarmTag='';
05420			GotoState('Attacking');
05421		}
05422						
05423	Begin:
05424		bReadyToAttack = false;
05425		Target = Enemy;
05426		TweenToRunning(0.15);
05427		bFromWall = false;
05428	
05429	CloseIn:
05430		WaitForLanding();
05431		Enable('AnimEnd');
05432		if ( OrderObject == None )
05433		{
05434			Alarmtag = '';
05435			GotoState('Attacking');
05436		}
05437		If ( actorReachable(OrderObject) )
05438		{
05439			if ( bCanStrafe && (AlarmPoint(OrderObject) != None) && AlarmPoint(OrderObject).bStrafeTo )
05440			{
05441				bCanFire = true;
05442				StrafeFacing(OrderObject.Location, Enemy);
05443			}
05444			else
05445			{
05446				bCanFire = false;
05447				MoveToward(OrderObject);
05448			}
05449		}
05450		else
05451		{
05452			if (SpecialGoal != None)
05453				MoveTarget = FindPathToward(SpecialGoal);
05454			else
05455				MoveTarget = FindPathToward(OrderObject);
05456			if (MoveTarget == None)
05457			{
05458				AlarmTag = '';
05459				log("no path to alarm");
05460				GotoState('Attacking');
05461			}
05462			if ( SpecialPause > 0.0 )
05463			{
05464				if ( (AlarmPoint(OrderObject) != None) && AlarmPoint(OrderObject).bStrafeTo )
05465				{
05466					bFiringPaused = true;
05467					NextState = 'Charging';
05468					NextLabel = 'Moving';
05469					GotoState('RangedAttack');
05470				}
05471				bSpecialPausing = true;
05472				Acceleration = vect(0,0,0);
05473				TweenToPatrolStop(0.3);
05474				if ( (SpecialPause == 2.5) && IsA('Nali') )
05475					SpecialPause = 8;
05476				Sleep(SpecialPause);
05477				SpecialPause = 0.0;
05478				TweenToRunning(0.1);
05479				bSpecialPausing = False;
05480			}
05481			if ( bCanStrafe && (AlarmPoint(OrderObject) != None) && AlarmPoint(OrderObject).bStrafeTo )
05482			{
05483				bCanFire = true;
05484				StrafeFacing(MoveTarget.Location, Enemy);
05485			}
05486			else
05487			{
05488				bCanFire = false;
05489				MoveToward(MoveTarget); 
05490			}
05491				
05492			if ( !bNoWait && (AttitudeToPlayer > ATTITUDE_Ignore) 
05493				&& ((VSize(Location - Enemy.Location) > CollisionRadius + Enemy.CollisionRadius + 320)
05494					|| !LineOfSightTo(Enemy)) )
05495			{
05496				Acceleration = vect(0,0,0);
05497				GotoState('AlarmPaused', 'WaitForPlayer');
05498			}			
05499		}
05500	
05501		if ( ( NavigationPoint(OrderObject) != None)
05502			&& (VSize(Location - OrderObject.Location) < CollisionRadius + Abs(CollisionHeight - OrderObject.CollisionHeight)) )
05503			Touch(OrderObject); 
05504		else if ( VSize(Location - OrderObject.Location) < 
05505				CollisionRadius + CollisionHeight + OrderObject.CollisionRadius + 10 )
05506			Touch(OrderObject);
05507	
05508		Goto('CloseIn');
05509	}
05510	
05511	state AlarmPaused
05512	{
05513		ignores HearNoise;
05514	
05515		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
05516								Vector momentum, name damageType)
05517		{
05518			local bool bWasFriendly;
05519			
05520			bWasFriendly = ( Enemy.bIsPlayer && (AttitudeToPlayer == ATTITUDE_Friendly) );
05521			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
05522			if ( health <= 0 )
05523				return;
05524			if (NextState == 'TakeHit')
05525			{
05526				if ( bWasFriendly && (instigatedBy == Enemy) )
05527					AlarmTag = '';
05528				if ( AlarmTag == '' )
05529				{
05530					NextState = 'Attacking';
05531					NextLabel = 'Begin';
05532				}
05533				else
05534				{
05535					NextState = 'TriggerAlarm';
05536					NextLabel = 'Recover';
05537				}
05538				GotoState('TakeHit'); 
05539			}
05540		}
05541	
05542		function SetFall()
05543		{
05544			NextState = 'TriggerAlarm'; 
05545			NextLabel = 'Recover';
05546			NextAnim = AnimSequence;
05547			GotoState('FallingState'); 
05548		}
05549		
05550		function Bump(actor Other)
05551		{
05552			if (Other == Enemy)
05553				GotoState('MeleeAttack');
05554			else if ( (Pawn(Other) != None) && SetEnemy(Pawn(Other)) )
05555				GotoState('MeleeAttack');
05556			Disable('Bump');
05557		}
05558		
05559		function Timer()
05560		{
05561			if ( AlarmTag != '' )
05562				GotoState('TriggerAlarm');
05563			else
05564				GotoState('Attacking');
05565		}
05566		
05567		function FindShootTarget()
05568		{
05569			local actor A;
05570		
05571			A = None;	
05572			if ( AlarmPoint(OrderObject).shoottarget != '' )
05573				ForEach AllActors(class 'Actor', A, AlarmPoint(OrderObject).shoottarget )
05574					break;
05575			
05576			if ( A == None)
05577				target = enemy;
05578			else
05579			{
05580				target = A;
05581				if ( Pawn(target) != None)
05582					SetEnemy(pawn(Target));
05583			}
05584		}
05585	
05586		function EnemyNotVisible()
05587		{
05588			if ( AlarmPoint(OrderObject).bStopIfNoEnemy )
05589			{
05590				Enable('SeePlayer');
05591				Disable('EnemyNotVisible');
05592				Disable('Timer');
05593				GotoState('AlarmPaused', 'WaitForEnemy');
05594			}
05595		}
05596		
05597		function SeePlayer(Actor SeenPlayer)
05598		{
05599			Disable('SeePlayer');
05600			Enable('Timer');
05601			GotoState('AlarmPaused', 'Begin');
05602		}
05603	
05604		function PlayWaitAround()
05605		{
05606			PlayPatrolStop();
05607		}
05608			
05609		function BeginState()
05610		{
05611			Disable('EnemyNotVisible');
05612			Disable('SeePlayer');
05613			Disable('Timer');
05614		}
05615	
05616	WaitForEnemy:
05617		Acceleration = vect(0,0,0);
05618		FinishAnim();
05619		TweenToPatrolStop(0.3);
05620		FinishAnim();
05621	Waiting:
05622		PlayPatrolStop();
05623		FinishAnim();
05624		Goto('Waiting');
05625						
05626	Begin:
05627		Acceleration = vect(0,0,0);
05628		Enable('Timer');
05629		SetTimer( AlarmPoint(OrderObject).pausetime, false );
05630		if ( bHasRangedAttack && AlarmPoint(OrderObject).bAttackWhilePaused )
05631		{
05632			Enable('EnemyNotVisible');
05633			if ( AlarmPoint(OrderObject).ShootTarget != '' )
05634				FindShootTarget();
05635			else
05636			{
05637				if (Enemy.bIsPlayer && ( AttitudeToPlayer > ATTITUDE_Hate) )
05638					AttitudeToPlayer = ATTITUDE_Hate;
05639				Target = Enemy;
05640			}
05641			if ( AlarmPoint(OrderObject).AlarmAnim != '')
05642			{
05643				TweenAnim(AlarmPoint(OrderObject).AlarmAnim, 0.2);
05644				if (NeedToTurn(Target.Location))
05645					TurnToward(Target);
05646				FinishAnim();
05647				if ( AlarmPoint(OrderObject).AlarmSound != None)
05648					PlaySound( AlarmPoint(OrderObject).AlarmSound);
05649				PlayAnim(AlarmPoint(OrderObject).AlarmAnim);
05650				if ( AlarmPoint(OrderObject).ducktime > 0 )
05651				{
05652					if ( Target != Enemy )
05653						Sleep(AlarmPoint(OrderObject).ducktime);
05654					else
05655					{
05656						if ( TimerRate <= 0 )
05657							SetTimer( AlarmPoint(OrderObject).ducktime + 1, false);
05658						MoveTimer = TimerCounter;
05659						While ( TimerCounter < MoveTimer + AlarmPoint(OrderObject).ducktime )
05660						{
05661							TurnToward(Enemy);
05662							sleep(0.0);
05663						}
05664					}
05665				}
05666			}
05667	Attack:
05668			if (NeedToTurn(Target.Location))
05669			{
05670				PlayTurning();
05671				TurnToward(Target);
05672			}
05673			TweenToFighter(0.15);
05674			FinishAnim();
05675			DesiredRotation = Rotator(Target.Location - Location);
05676			PlayRangedAttack();
05677			FinishAnim();
05678			Goto('Attack');
05679		}
05680	
05681		if ( AlarmPoint(OrderObject).bStopIfNoEnemy)
05682			Enable('EnemyNotVisible');
05683					
05684		if ( NeedToTurn(Location + AlarmPoint(OrderObject).lookdir) )
05685		{
05686			PlayTurning();
05687			TurnTo(Location + AlarmPoint(OrderObject).lookdir);
05688		}
05689		if ( AlarmPoint(OrderObject).AlarmAnim != '')
05690		{
05691			TweenAnim(AlarmPoint(OrderObject).AlarmAnim, 0.2);
05692			FinishAnim();
05693			PlayAnim(AlarmPoint(OrderObject).AlarmAnim);
05694		}
05695		else
05696		{
05697			TweenToPatrolStop(0.3);
05698			FinishAnim();
05699			PlayPatrolStop();
05700		}
05701		sleep( AlarmPoint(OrderObject).pausetime );
05702		Timer();
05703			
05704	WaitForPlayer:
05705		Disable('AnimEnd');
05706		NextAnim = '';
05707		Acceleration = vect(0,0,0);
05708	Wait:
05709		if (NeedToTurn(Enemy.Location))
05710		{
05711			PlayTurning();
05712			TurnToward(Enemy);
05713		}
05714		TweenToWaiting(0.2);
05715		FinishAnim();
05716		PlayWaiting();
05717		FinishAnim();
05718		if ( (VSize(Location - Enemy.Location) > CollisionRadius + Enemy.CollisionRadius + 220) 
05719			|| (!Enemy.LineOfSightTo(Self)) )
05720			Goto('Wait');
05721		TweenToRunning(0.15);
05722		GotoState('TriggerAlarm');
05723				
05724	WaitAround:
05725		Disable('AnimEnd');
05726		Acceleration = vect(0,0,0);
05727		if ( (AlarmPoint(OrderObject) != None) && NeedToTurn(Location + AlarmPoint(OrderObject).lookdir) )
05728		{
05729			PlayTurning();
05730			TurnTo(Location + AlarmPoint(OrderObject).lookdir);
05731		}
05732		if ( (AlarmPoint(OrderObject) != None) && AlarmPoint(OrderObject).AlarmAnim != '')
05733		{
05734			TweenAnim(AlarmPoint(OrderObject).AlarmAnim, 0.2);
05735			FinishAnim();
05736			PlayAnim(AlarmPoint(OrderObject).AlarmAnim);
05737			FinishAnim();
05738		}
05739		else
05740		{
05741			TweenToPatrolStop(0.2);
05742			FinishAnim();
05743			if (NeedToTurn(Enemy.Location))
05744			{
05745				PlayTurning();
05746				TurnToward(Enemy);
05747				TweenToPatrolStop(0.2);
05748			}
05749			PlayWaitAround();
05750			FinishAnim();
05751			PlayWaitAround();
05752			FinishAnim();
05753			PlayWaitAround();
05754			FinishAnim();
05755			TweenToPatrolStop(0.1);
05756			FinishAnim();
05757		}
05758		if (AlarmTag == '')
05759			WhatToDoNext('','');
05760		else
05761			GotoState('TriggerAlarm');
05762	}
05763	
05764	state Greeting
05765	{
05766		ignores SeePlayer, EnemyNotVisible;
05767	
05768		function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
05769								Vector momentum, name damageType)
05770		{
05771			local eAttitude AttEn;
05772			Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
05773			if ( health <= 0 )
05774				return;
05775			if ( Enemy != None )
05776			{
05777				AttEn = AttitudeTo(Enemy);
05778				if (NextState == 'TakeHit')
05779				{
05780					if (AttEn == ATTITUDE_Fear)
05781					{
05782						NextState = 'Retreating';
05783						NextLabel = 'Begin';
05784					}
05785					else
05786					{
05787						NextState = 'Attacking';
05788						NextLabel = 'Begin';
05789					}
05790					GotoState('TakeHit');
05791				}
05792				else
05793					GotoState('Attacking');
05794			}
05795		}
05796	
05797		function Bump(actor Other)
05798		{
05799			//log(Other.class$" bumped "$class);
05800			if ( (Pawn(Other) != None) && (Enemy != Other) )
05801				SetEnemy(Pawn(Other));
05802			if ( TimerRate <= 0 )
05803				setTimer(1.0, false);
05804			Disable('Bump');
05805		}
05806		
05807		function Timer()
05808		{
05809			Enable('Bump');
05810		}
05811		
05812		function EnemyAcquired()
05813		{
05814			if (AttitudeTo(Enemy) < ATTITUDE_Ignore)
05815				GotoState('Acquisition', 'PlayOut');
05816		}
05817		
05818		function AnimEnd()
05819		{
05820			PlayWaiting();
05821		}
05822	 
05823		function Landed(vector HitNormal)
05824		{
05825			SetPhysics(PHYS_None);
05826		}
05827	
05828		
05829	Begin:
05830		MakeNoise(1.0);
05831		//log(class$ " greeting");
05832		Acceleration = vect(0,0,0);
05833		TweenToWaiting(0.2);
05834	}
05835	
05836	defaultproperties
05837	{
05838	     CarcassType=Class'UnrealShare.CreatureCarcass'
05839	     TimeBetweenAttacks=1.000000
05840	     WalkingSpeed=0.400000
05841	     bLeadTarget=True
05842	     bWarnTarget=True
05843	     bFirstShot=True
05844	     ProjectileSpeed=800.000000
05845	     bFixedStart=True
05846	     AirSpeed=320.000000
05847	     AccelRate=200.000000
05848	     HearingThreshold=0.300000
05849	     Land=Sound'UnrealShare.Generic.Land1'
05850	     WaterStep=Sound'UnrealShare.Generic.LSplash'
05851	}

End Source Code