UnrealShare
Class Bots

source: e:\games\UnrealTournament\UnrealShare\Classes\Bots.uc
Core.Object
   |
   +--Engine.Actor
      |
      +--Engine.Pawn
         |
         +--UnrealShare.Bots
Direct Known Subclasses:SkaarjPlayerBot, HumanBot

class Bots
extends Engine.Pawn

//============================================================================= // Bots. //=============================================================================
Variables
 float Accuracy
           for debugging
 AmbushPoint AmbushSpot
           for debugging
 float CampTime
           for debugging
 float CampingRate
           for debugging
 Weapon EnemyDropped
           for debugging
 class FavoriteWeapon
           for debugging
 float LastCampCheck
           for debugging
 float LastInvFind
           for debugging
 name LastPainAnim
           for debugging
 name NextAnim
           used in states with multiple, sequenced animations
 Pawn OldEnemy
           used in states with multiple, sequenced animations
 float PlayerDeaths
           for debugging
 float PlayerKills
           for debugging
 Actor Pointer
           for debugging
 Pawn TeamLeader
 bool bVerbose
           for debugging

States
FindAir, Dying, GameEnded, VictoryDance, RangedAttack, at, while, FallingState, TakeHit, StakeOut, Hunting, TacticalMove, Charging, Fallback, Retreating, Attacking, Acquisition, Wandering, Roaming, StartUp

Function Summary
 rotator AdjustAim(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget)
 float AdjustDesireFor(Inventory Inv)
 bool AdjustHitLocation(out vector, vector TraceDir)
     
/* Adjust hit location - adjusts the hit location in for pawns, and returns
true if it was really a hit, and false if not (for ducking, etc.)
*/
 void AdjustSkill(bool bWinner)
 rotator AdjustToss(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget)
     
/*
Adjust the aim at target.  
	- add aim error
	- adjust up or down if barrier
*/
 float AssessThreat(Pawn NewThreat)
 eAttitude AttitudeTo(Pawn Other)
 void BecomeViewTarget()
 void Bump(Actor Other)
 void CallForHelp()
 bool CanFireAtEnemy()
 bool CanStakeOut()
     
// Can Stake Out - check if I can see my current Destination point, and so can enemy
 void ChangedWeapon()
 bool CheckFutureSight(float deltatime)
     
// check for line of sight to target deltatime from now.
 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 FireWeapon()
 void Gasp()
 bool Gibbed(name damageType)
 void HaltFiring()
 void HearNoise(float Loudness, Actor NoiseMaker)
 void JumpOffPawn()
 void JumpOutOfWater(vector jumpDir)
 string KillMessage(name damageType, Pawn Other)
 void Killed(Pawn Killer, Pawn Other, name damageType)
 void LongFall()
 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 PainTimer()
 void PlayChallenge()
 void PlayCombatMove()
     
//FIXME - here decide when to pause/start firing based on weapon,etc
 void PlayDeathHit(float Damage, vector HitLocation, name damageType, vector Momentum)
 void PlayDodge(bool bDuckLeft)
 void PlayDyingSound()
 void PlayFiring()
 
simulated
PlayFootStep()
 void PlayGutHit(float tweentime)
     
/* Default 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 PlayMovingAttack()
 void PlayOutOfWater()
 void PlayRangedAttack()
 void PlayRightHit(float tweentime)
 void PlayTakeHitSound(int damage, name damageType, int Mult)
 void PreBeginPlay()
 void PreSetMovement()
 void ReSetSkill()
 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()
     
//=============================================================================
 Carcass SpawnCarcass()
 void SpawnGibbedCarcass()
 void SpecialFire()
 float StrafeAdjust()
 bool StrafeFromDamage(vector momentum, float Damage, name DamageType, bool bFindDest)
 bool SwitchToBestWeapon()
 void Trigger(Actor Other, Pawn EventInstigator)
 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)


State FindAir Function Summary
 void SetMultiSkin(Actor SkinActor, string SkinName, string FaceName, byte TeamNum)
 void PickDestination(bool bNoCharge)
     
/* PickDestination()
*/
 void EnemyNotVisible()
 void Timer()
 void AnimEnd()
 void HitWall(vector HitNormal, Actor Wall)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
 void HeadZoneChange(ZoneInfo newHeadZone)


State Dying Function Summary
 void EndState()
 void BeginState()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
 void ReStartPlayer()


State GameEnded Function Summary
 void BeginState()


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


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


State at Function Summary
 void EndState()


State while Function Summary
 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


State FallingState Function Summary
 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 Bump(Actor Other)
 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 PickRegDestination(bool bNoCharge)
 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 PainTimer()
 bool TryToward(Inventory Inv, float Weight)
 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 WarnTarget(Pawn shooter, float projSpeed, vector FireDir)
 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 TryStrafe(vector sideDir)
 bool StrafeFromDamage(vector momentum, float Damage, name DamageType, bool bFindDest)
 void FearThisSpot(Actor aSpot)
 void SetFall()
 void HitWall(vector HitNormal, Actor Wall)
 void TryToDuck(vector duckDir, bool bReversed)
 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 Fallback Function Summary
 void BeginState()
 void AnimEnd()
 void Bump(Actor Other)
 void PickDestination()
 void HitWall(vector HitNormal, Actor Wall)
 void SetFall()
 void Timer()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
 void HearNoise(float Loudness, Actor NoiseMaker)
 void SeePlayer(Actor SeenPlayer)
 void EnemyNotVisible()


State Retreating Function Summary
 void BeginState()
 void AnimEnd()
 void PickNextSpot()
 void ReachedHome()
 void Bump(Actor Other)
 void ChangeDestination()
 void PickDestination()
 void HitWall(vector HitNormal, Actor Wall)
 void SetFall()
 void Timer()
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
 void HearNoise(float Loudness, Actor NoiseMaker)
 void SeePlayer(Actor SeenPlayer)
 void WarnTarget(Pawn shooter, float projSpeed, vector FireDir)


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 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 EndState()
 void BeginState()
 void ShareWithTeam()
 void AnimEnd()
 bool FindAmbushSpot()
 void PickDestination()
 void HitWall(vector HitNormal, Actor Wall)
 void EnemyAcquired()
 void SetFall()
 void Timer()
 void FearThisSpot(Actor aSpot)
 void TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
 void HandleHelpMessageFrom(Pawn Other)
 void Bump(Actor Other)


State StartUp Function Summary
 void BeginState()



Source Code


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

End Source Code