RuneI
Class ScriptPawn

source: c:\runehov\RuneI\Classes\ScriptPawn.uc
Core.Object
   |
   +--Engine.Actor
      |
      +--Engine.Pawn
         |
         +--RuneI.ScriptPawn
Direct Known Subclasses:Baracuda, Bird, Cuttle, Dangler, Dwarf, DwarfMech, Eel, GiantCrab, Goblin, Jellyfish, Loki, LokiBust, LokiHead, Manowar, Odin, Sark, SnowBeast, SpiderBot, StoneGuard, TubeStriker, Viking, Zombie, SarkBallBot

class ScriptPawn
extends Engine.Pawn

//============================================================================= // ScriptPawn. //=============================================================================
Variables
 A_PullUp, A_StepUp
           Actor to ignore from acquisition
 Pawn Ally
           This creature is followed
 float AllyMaxTime
           Amount of time to stay allied
 float AllyTime
           Internal timer
 AttackAction_e AttackAction
           Normalized vector
 name AttackOrders
           Tactical orders for attack
 Actor CurrentPoint
           Last navigation point visited
 int DispatchAction
           Index of current action of ScriptDispatcher
 float EnemyDist
 Facing_e EnemyFacing
           Normalized vector
 Incidence_e EnemyIncidence
           Normalized vector
 Movement_e EnemyMovement
           Normalized vector
 Vertical_e EnemyVertical
           Normalized vector
 Pawn Hated
           This creature is hated
 vector HomeBase
           Startup location, returns to when GoingHome
 rotator HomeRot
           Startup rotation, returns to when GoingHome
 float HuntStartTime
           Amount of time spent hunting
 Actor IgnoreEnemy
           Actor to ignore from acquisition
 AttackAction_e LastAction
           Normalized vector
 NavigationPoint LastNodeVisited
           Last Nav point visited (to avoid looking back)
 float LastPathTime
           Timer to reduce path searching overhead
 Actor LastPointVisited
           Last navigation point visited
 name NextAnim
           Used for queueing anims along with NextState,NextLabel
 Actor NextPoint
           For queueing OrderObject (NextState, NextLabel)
 int NumAmbientFightSounds
           Don't play sounds (TODO: need to support this again)
 int NumAmbientWaitSounds
           Don't play sounds (TODO: need to support this again)
 Actor OrderObject
           Object referred to by orders (if applicable).
 float ProtectionTimer
           Timer for optimizing protection
 float ShadowScale
           RUNE: Scale of the blob shadow
 int SpeechPos
           Position within controls for speech
 vector VecFromEnemy
           Normalized vector
 vector VecToEnemy
           Normalized vector
 float WanderDistance
           Distance used to determine open space when wandering
 bool bAlerted
           Whether pawn has been alerted already
 bool bCanDefend
           Can defend (has left arm)
 bool bCanSwing
           Can swing attack (has right arm)
 bool bDisableCheckForEnemies
           Speedup: don't check for enemies
 bool bFallAtStartup
           Whether to fall to ground at startup (used on crusified guys)
 bool bFrustrated
           Desperate to find player
 bool bMoveWhenUnreachable
           For trialpit beast
 bool bQuiet
           Don't play sounds (TODO: need to support this again)
 bool bTaskLocked
           Don't apply stimulus while bTaskLocked
 bool bTeamLeader
           Whether I am the leader of a group
 bool debugstates
           Actor to ignore from acquisition
 int i
           used for debugging
 int lookIndex
           Index of reachspec looking down during movement
 int numHuntPaths
           number of paths taken hunting
 float testfloat
           used for debugging
 string teststring
           used for debugging
 string teststring2
           used for debugging

States
FallingState, Dying, Drowning, Fighting, Charging, Hunting, StakeOut, Threatening, TacticalDecision, Acquisition, GoingHome, Cower, Fleeing, Wandering, Waiting, Startup, with

Function Summary
 void AddLocalVelocity(float vx, float vy, float vz)
     
//------------------------------------------------
//
// AddLocalVelocity
//
// Add local coordinate system velocities
//------------------------------------------------
 void AfterSpawningInventory()
     
//===================================================================
//					Stimulus Functions
//===================================================================
 bool AllowWeaponToHitActor(Weapon W, Actor A)
     
//------------------------------------------------
//
// AllowWeaponToHitActor
//
// Disallow hitting others of the same class
//------------------------------------------------
 void AmbientSoundTimer()
 eAttitude AttitudeTo(Pawn Other)
     
//------------------------------------------------
//
// AttitudeTo
//
//------------------------------------------------
 eAttitude AttitudeToCreature(Pawn Other)
     
//------------------------------------------------
//
// AttitudeToCreature
//
//------------------------------------------------
 void Breath()
     
//------------------------------------------------
//
// Breath (notify)
//
// Notification called when pawn takes a breath
//------------------------------------------------
 bool CanDirectlyThreaten()
     
//------------------------------------------------
//
// CanDirectlyThreaten
//
//------------------------------------------------
 void CheckForEnemies()
     
// CheckForEnemies
//
// Allows scriptpawns to target other scriptpawns, as well as
// Could be changed to a radius actors check
 void DamageAttitudeTo(Pawn Other)
     
//------------------------------------------------
//
// DamageAttitudeTo
//
//------------------------------------------------
 void DoThrow()
     
//------------------------------------------------
//
// DoThrow (notify)
//
// Throws actor attached to weapon joint at
// Enemy or OrderObject
//------------------------------------------------
 void EnemyAcquired()
     
//------------------------------------------------
//
// EnemyAcquired
//
// Called when Enemy has been set
//------------------------------------------------
 void Falling()
     
//------------------------------------------------
//
// Falling
//
// Called by physics
//------------------------------------------------
 void FearThisSpot(Actor aSpot)
     
//------------------------------------------------
//
// FearThisSpot
//
//------------------------------------------------
 bool FollowOrders(name order, name tag)
     
//------------------------------------------------
//
// FollowOrders
//
//------------------------------------------------
 void GetEnemyProximity()
     
//------------------------------------------------
//
// GetEnemyProximity
//
// Determine attack proximity information
//------------------------------------------------
 bool GrabEdge(float grabDistance, vector grabNormal)
     
//------------------------------------------------
//
// GrabEdge
//
//------------------------------------------------
 void HeadZoneChange(ZoneInfo newHeadZone)
     
//------------------------------------------------------------
//
// HeadZoneChange
//
//------------------------------------------------------------
 void HearNoise(float Loudness, Actor NoiseMaker)
     
//------------------------------------------------
//
// HearNoise
//
//------------------------------------------------
 void HitWall(vector HitNormal, Actor Wall)
     
//------------------------------------------------
//
// HitWall
//
//------------------------------------------------
 bool InAttackRange(Actor Other)
     
//------------------------------------------------
//
// InAttackRange
//
// When within attack range, state changes from
// charging to fighting
//------------------------------------------------
 bool InLungeRange(Actor Other)
     
//------------------------------------------------
//
// InLungeRange
//
//------------------------------------------------
 bool InMeleeRange(Actor Other)
     
//------------------------------------------------
//
// InMeleeRange
//
//------------------------------------------------
 bool InPaceRange(Actor Other)
     
//------------------------------------------------
//
// InPaceRange
//
//------------------------------------------------
 bool InRange(Actor Other, float range)
     
//================================================
//
// InRange
//
//================================================
 bool JointDamaged(int Damage, Pawn EventInstigator, vector HitLoc, vector Momentum, name DamageType, int joint)
     
//------------------------------------------------
//
// JointDamaged
//
//------------------------------------------------
 void Landed(vector HitNormal, Actor HitActor)
     
//------------------------------------------------
//
// Landed
//
//------------------------------------------------
 void LimbSevered(int BodyPart, vector Momentum)
     
//------------------------------------------------
//
// LimbSevered
//
//------------------------------------------------
 void LongFall()
     
// 
 void MayFall()
     
//------------------------------------------------
// MayFall
//
// called by engine physics if walking and bCanJump, and
// is about to go off a ledge.  Pawn has opportunity
// to avoid fall (by setting bCanJump to false)
//------------------------------------------------
 void OrderFinished()
     
//------------------------------------------------
//
// OrderFinished
//
//------------------------------------------------
 void PainTimer()
 void PlayAmbientFightSound()
 void PlayAmbientWaitSound()
 void PlayBackup(optional float)
 void PlayBlockHigh(optional float)
 void PlayBlockLow(optional float)
 void PlayCower(optional float)
     
// Played while turning
 void PlayDeath(name DamageType)
     
// Deaths (from Pawn)
 void PlayDropWeapon(optional float)
 void PlayFrontHit(optional float)
     
// Damage
 void PlayHuntStop(optional float)
     
// Fleeing -> HitWall -> Cower
 void PlayInAir(optional float)
     
// Played while jumping
 void PlayJumping(optional float)
     
// Played during all locomotion (swim,walk,run,fly)
 void PlayLanding(optional float)
 void PlayMeleeHigh(optional float)
     
// These can go ?
 void PlayMeleeLow(optional float)
     
// Obsolete
 void PlayMeleeVertical(optional float)
 void PlayMoving(optional float)
     
// Played when not doing anything else
 void PlayMovingAttack(optional float)
 void PlayPickupShield(optional float)
 void PlayPickupWeapon(optional float)
 void PlayPullUp(optional float)
     
// PullUp
 void PlayStepUp(optional float)
 void PlayStrafeLeft(optional float)
     
// Played while falling
 void PlayStrafeRight(optional float)
 void PlayTaunting(optional float)
     
// Played when choosing next Hunting dest
 void PlayThreatening(optional float)
     
// 
 void PlayThrowing(optional float)
 void PlayTurning(optional float)
     
// Optional
 void PlayWaiting(optional float)
     
// Required
 void PowerupBlaze(Pawn EventInstigator)
 void PowerupElectricity(Pawn EventInstigator)
 void PowerupFire(Pawn EventInstigator)
     
//===================================================================
//					Powerup Support
//===================================================================
 void PowerupFriend(Pawn EventInstigator)
 void PowerupIce(Pawn EventInstigator)
 void PowerupStone(Pawn EventInstigator)
 void PreBeginPlay()
     
//------------------------------------------------
//
// PreBeginPlay
//
//------------------------------------------------
 void ProtectAlly()
 void SeePlayer(Actor seen)
     
//------------------------------------------------
//
// SeePlayer
//
//------------------------------------------------
 bool SetEnemy(Actor NewEnemy)
     
//------------------------------------------------
//
// SetEnemy
//
// Decide whether to set this pawn as new enemy
//------------------------------------------------
 void SetFall()
     
//------------------------------------------------
//
// SetFall
//
// default SetFall handler
//------------------------------------------------
 void SetMovementPhysics()
     
//------------------------------------------------
//
// SetMovementPhysics
//
// Decide what locomotion method to use based on
// the zone we are in
//------------------------------------------------
 void SetOnFire(Pawn EventInstigator, int joint)
 bool SoundChance(sound Sound, float chance, optional ESoundSlot)
     
//------------------------------------------------
//
// Sound Functions
//
//------------------------------------------------
 void Tick(float DeltaTime)
 void Trigger(Actor Other, Pawn EventInstigator)
     
//------------------------------------------------
//
// Trigger
//
//------------------------------------------------
 void TweenToHuntStop(float time)
 void TweenToJumping(float time)
 void TweenToMeleeHigh(float time)
 void TweenToMeleeLow(float time)
     
// These can go
 void TweenToMoving(float time)
 void TweenToThrowing(float time)
 void TweenToTurning(float time)
 void TweenToWaiting(float time)
     
// Tweens
 void UnPowerupFriend()
 bool ValidEnemy()
     
// Used to stop attacking when conditions are met
 bool WantsToPickUp(Inventory item)
     
//------------------------------------------------
//
// WantsToPickup
//
// Returns whether the item is desired
//------------------------------------------------
 void WeaponActivate()
     
//------------------------------------------------
//
// WeaponActivate (notify)
//
//------------------------------------------------
 void WeaponDeactivate()
     
//------------------------------------------------
//
// WeaponDeactivate (notify)
//
//------------------------------------------------
 void ZoneChange(ZoneInfo newZone)
     
//------------------------------------------------
//
// ZoneChange
//
//------------------------------------------------


State FallingState Function Summary
 void EndState()
 void BeginState()
     
//================================================
// Frozen
// Used to make a quiet creature in scripting, trigger to get out
//================================================
 void Landed(vector HitNormal, Actor HitActor)
 void EndState()
 void BeginState()
 void Timer()
 void InventoryNormal()
 void InventoryStatue()
 void CreatureNormal()
 void CreatureStatue()
 void Trigger(Actor Other, Pawn EventInstigator)
 bool JointDamaged(int Damage, Pawn EventInstigator, vector HitLoc, vector Momentum, name DamageType, int joint)
 void SpawnDebris(vector Momentum)
 bool CanBeStatued()
 EMatterType MatterForJoint(int joint)
 void AmbientSoundTimer()
     
//================================================
//
// IceStatue
//
// Used only for Ice Powerup
//================================================
 void Landed(vector HitNormal, Actor HitActor)
 void EndState()
 void BeginState()
 void SeePlayer(Actor Seen)
 void Timer()
 void Tick(float DeltaTime)
 void InventoryNormal()
 void InventoryStatue()
 void CreatureNormal()
 void DebrisCloud()
 void CreatureStatue()
 void Trigger(Actor Other, Pawn EventInstigator)
 bool JointDamaged(int Damage, Pawn EventInstigator, vector HitLoc, vector Momentum, name DamageType, int joint)
 void SpawnDebris(vector Momentum)
 bool CanBeStatued()
 EMatterType MatterForJoint(int joint)
 void AmbientSoundTimer()
     
//================================================
//
// Statue
//
//================================================
 void EnemyAcquired()
 void DoLanded(vector HitNormal)
 void adjustJump()
     
//choose a jump velocity
 bool GrabEdge(float grabDistance, vector grabNormal)
 void AnimEnd()
 bool CanGotoPainState()
 void BeginState()
     
//	ignores Landed, SeePlayer, EnemyNotVisible, HearNoise, KilledBy, Trigger, Bump, HitWall, HeadZoneChange, FootZoneChange, ZoneChange, Falling, WarnTarget, PainTimer, SetFall;


State Dying Function Summary
 void Timer()
 void AmbientSoundTimer()
 void SpawnBloodSplot()


State Drowning Function Summary
 void HeadZoneChange(ZoneInfo newHeadZone)
 void PainTimer()
 void EndState()
 void BeginState()


State Fighting Function Summary
 void AmbientSoundTimer()
 void EndState()
 void BeginState()


State Charging Function Summary
 vector CheckDestination(vector loc)
 void HitWall(vector HitNormal, Actor Wall)
 void EnemyNotVisible()
 void Timer()
 void EndState()
 void BeginState()


State Hunting Function Summary
 void LookAtEndPoint(int index)
 bool FindViewSpot()
 void PickDestination()
 void Landed(vector HitNormal, Actor HitActor)
 void MayFall()
 void HitWall(vector HitNormal, Actor Wall)
 void TryChargeEnemy()
 void HearNoise(float Loudness, Actor NoiseMaker)
 void EndState()
 void BeginState()


State StakeOut Function Summary
 void CheckReachable()
 void Timer()
 void EndState()
 void BeginState()


State Threatening Function Summary
 bool PickDestination()
 void EnemyNotVisible()
 void Timer()
 void EndState()
 void BeginState()


State TacticalDecision Function Summary
 void GiveFleeOrders(int TeamNumbers)
 void GiveAttackOrders(int TeamNumbers)
 void GiveNullOrders()
 void DecideOrders()
 float Evaluate(Pawn A)
 bool IAmLeader()
 void CheckElectLeader()
 void AssimilateVagrants()
 void BeginState()


State Acquisition Function Summary
 void InformTeammates()
 void BeginState()


State GoingHome Function Summary
 void BeginState()


State Cower Function Summary


State Fleeing Function Summary
 void PickDestination()
 void Landed(vector HitNormal, Actor HitActor)
 void HitWall(vector HitNormal, Actor Wall)
 void BeginState()


State Wandering Function Summary
 void ExecuteScriptDispatcherAction(int i)
 void FinishScriptDispatcher()
 void Timer()
 void SpeechTimer()
 void ExecuteScriptAction()
     
//============================================
 void ExecuteScriptPoint()
     
//============================================
 void AmbientSoundTimer()
 bool CanGotoPainState()
 void EndState()
 void BeginState()
 void PlayInAir(optional float)
 void PlayJumping(optional float)
 void PlayLanding(optional float)
     
// Override some animations so these events don't takeover animations between scriptpoints
 void Breath()
     
//, SetOnFire;
 void PickDestination()
 bool TestDirection(vector dir, out vector)
 void HitWall(vector HitNormal, Actor Wall)
 void SetFall()
 void BeginState()


State Waiting Function Summary
 void PickWaypoint()
 void PickDestination()
 void HitWall(vector HitNormal, Actor Wall)
 void BeginState()
     
//================================================
//
// Roaming
//
// Following random paths
//================================================
 void Timer()
 void Landed(vector HitNormal, Actor HitActor)
 void Bump(Actor Other)
 void SeePlayer(Actor seen)
 void EndState()
 void BeginState()


State Startup Function Summary
 void SetHome()
 void Timer()
 void TouchSurroundingObjects()
 void SpawnStartInventory()
 void EndState()
 void BeginState()


State with Function Summary



Source Code


00001	//=============================================================================
00002	// ScriptPawn.
00003	//=============================================================================
00004	class ScriptPawn expands Pawn;
00005	
00006	
00007	/* Description:
00008		Base class for intelligent creatures.  Contains functionality for their major systems.
00009		
00010		Goals:
00011			Supports common pathfinding behaviors between creatures
00012			Locomotion manerisms can be overridden (for crab, fish, etc.)
00013	
00014		Startup:
00015		Set initial parameters, and decide what to do based on orders
00016		EXIT STATES: Waiting, 
00017		
00018		Waiting:
00019		Doing ambient behavior.  Either timed or until some stimulus
00020	
00021		Roaming:
00022		Roaming around level using the patrol points.
00023		EXIT STATES: Wandering, 
00024	
00025		Wandering:
00026		Wandering around without the use of navigation points.
00027	
00028		Acquisition:
00029		Just acquired an enemy, notice it (look/turn to it)
00030		EXIT STATES: TacticalDecision
00031		
00032		TacticalDecision:
00033		Deciding an attack strategy, choosing a leader.
00034		EXIT STATES: Charging, Hunting, Fleeing,
00035	
00036		Charging:
00037		Trying to get near the enemy for an Attack.
00038		EXIT STATES: Tactical, Fighting
00039		
00040		Hunting:
00041		Lost sight of enemy, trying to find him again
00042		EXIT STATES: Charging, GoingHome
00043	
00044		Fleeing:
00045		Outmatched or panicing, trying to get away and survive.
00046		EXIT STATES: Waiting, Cower
00047	
00048		Cower:
00049		Cowering because can't get away when fleeing.
00050		EXIT STATES: None
00051	
00052		GoingHome:
00053		EXIT STATES: Startup, Acquisition
00054	
00055		Commonly overridden states
00056		Fighting:
00057		Doing attack behavior.
00058		EXIT STATES: Charging
00059		
00060		Falling:
00061		EXIT STATES: NextState
00062		
00063		Scripting:
00064		Scripted behavior defined by series of ScriptPoint
00065	
00066	   Valid Orders:
00067	   	Scripting, Roaming, None, Creature defined
00068	
00069	   SOUND slot usage:
00070		SLOT_None				
00071		SLOT_Misc				Water Splashes, Acquire
00072		SLOT_Pain				Hit sounds
00073		SLOT_Interact			Footsteps, Land
00074		SLOT_Ambient			AmbientSound
00075		SLOT_Talk				LandGrunt, Death, AmbientWait, AmbientFight
00076		SLOT_Interface			Breath
00077		
00078	 	
00079	   TODO:
00080		Phase out meleeRange,lungeRange,paceRange for AttackRange()
00081		HitWall handling in all states
00082		   	add climbing ability (set phys_falling) in all hitwalls
00083			do a global hitwall() function that goes to current
00084			state with label of hitwall: and have a hitwall
00085			handler in each
00086		Handle jumping out of water in ZoneChange()
00087	*/
00088	
00089	enum Facing_e
00090	{
00091		FACE_FRONT,
00092		FACE_BACK,
00093		FACE_SIDE
00094	};
00095	
00096	enum Incidence_e
00097	{
00098		INC_FRONT,
00099		INC_BACK,
00100		INC_LEFT,
00101		INC_RIGHT
00102	};
00103	
00104	enum Vertical_e
00105	{
00106		VERT_ABOVE,
00107		VERT_BELOW,
00108		VERT_LEVEL
00109	};
00110	
00111	enum Movement_e
00112	{
00113		MOVE_STANDING,
00114		MOVE_CLOSER,
00115		MOVE_FARTHER,
00116		MOVE_STRAFE_LEFT,
00117		MOVE_STRAFE_RIGHT
00118	};
00119	
00120	enum AttackAction_e
00121	{
00122		AA_WAIT,
00123		AA_LUNGE,
00124		AA_STRAFE_RIGHT,
00125		AA_STRAFE_LEFT,
00126		AA_JUMP,
00127		AA_CHARGE,
00128		AA_BACKUP,
00129		AA_ATTACKMELEE1,
00130		AA_ATTACKMELEE2,
00131		AA_ATTACKMELEE3,
00132		AA_ATTACKMISSILE1,
00133		AA_ATTACKMISSILE2,
00134		AA_THROW,
00135		AA_BLOCK,
00136		AA_RETRIEVE_WEAPON
00137	};
00138	
00139	// Enemy Proximity information
00140	var float EnemyDist;
00141	var vector VecToEnemy; // Normalized vector
00142	var vector VecFromEnemy; // Normalized vector
00143	var Facing_e EnemyFacing;
00144	var Incidence_e EnemyIncidence;
00145	var Vertical_e EnemyVertical;
00146	var Movement_e EnemyMovement;
00147	var AttackAction_e AttackAction;
00148	var AttackAction_e LastAction;
00149	
00150	// Orders related
00151	var(Orders) name		Orders;				// Ambient Orders, will also try to return to this state
00152	var(Orders) name		OrdersTag;			// Object of Ambient Orders
00153	var(Orders) name 		AlertOrders;		// Orders upon alert
00154	var(Orders) name 		AlertOrdersTag;		// Associated object
00155	var(Orders) name		TriggerOrders;		// Orders upon trigger
00156	var(Orders) name		TriggerOrdersTag;	// Associated object
00157	var name				AttackOrders;		// Tactical orders for attack
00158	var actor				OrderObject;		// Object referred to by orders (if applicable).
00159	var bool				bTeamLeader;		// Whether I am the leader of a group
00160	var(AI) name			TeamTag;			// Tag of team if I belong to one
00161	var bool				bAlerted;			// Whether pawn has been alerted already
00162	var bool				bTaskLocked;		// Don't apply stimulus while bTaskLocked
00163	
00164	// Movement related
00165	var NavigationPoint		LastNodeVisited;	// Last Nav point visited (to avoid looking back)
00166	var vector				HomeBase;			// Startup location, returns to when GoingHome
00167	var rotator				HomeRot;			// Startup rotation, returns to when GoingHome
00168	var() bool				bFallAtStartup;		// Whether to fall to ground at startup (used on crusified guys)
00169	var() bool				bMoveWhenUnreachable;	// For trialpit beast
00170	
00171	// Attack related
00172	var bool				bCanSwing;			// Can swing attack (has right arm)
00173	var bool 				bCanDefend;			// Can defend (has left arm)
00174	var(Combat) bool		bLungeAttack;		// Lunges at enemy during attack
00175	var(Combat) bool		bPaceAttack;		// Paces during attack
00176	var(Combat) float		FightOrFlight;		// Bravery Disposition for fleeing
00177	var(Combat) float		FightOrDefend;		// Aggressiveness towards fighting
00178	var(Combat) float		HighOrLow;			// Vertical Attack Disposition
00179	var(Combat) float		LatOrVertDodge;		// Chances of dodge laterally or vertically
00180	var(Combat) float		HighOrLowBlock;		// Vertical blocking disposition
00181	var(Combat) float		BlockChance;		// Blocking chances (of success)
00182	var(Combat) float		LungeRange;			// Distance fighting takes place within
00183	var(Combat) float		PaceRange;			// Distance to stay from enemy while pacing
00184	var(Combat) bool		bDodgeAfterAttack;	// Dodge back after attacking
00185	var(Combat) float		TimeBetweenAttacks;	// Time to wait before attacking again
00186	var(Combat) int			ThrowTrajectory;	// Trajectory creatures uses to throw
00187	
00188	// Hunting related
00189	var float				HuntStartTime;		// Amount of time spent hunting
00190	var int					numHuntPaths;		// number of paths taken hunting
00191	var bool				bFrustrated;		// Desperate to find player
00192	var(AI) float			HuntTime;			// Time to hunt before becoming frustrated
00193	var(AI) float			HuntDistance;		// Distance from home allowed when hunting
00194	var float				LastPathTime;		// Timer to reduce path searching overhead
00195	
00196	// Animation related
00197	var name				NextAnim;			// Used for queueing anims along with NextState,NextLabel
00198	
00199	// Statue related
00200	var(Statue) name		StatueAnimSeq;		// Anim sequence used for statue
00201	var(Statue) float		StatueAnimFrame;	// Anim frame used for statue
00202	var(Statue) bool		bStatueCanWake;		// Can come out of statue mode
00203	var(Statue) bool		bStatueDestructible;// Can't be smashed
00204	
00205	// Scripting related
00206	var actor				NextPoint;			// For queueing OrderObject (NextState, NextLabel)
00207	var int					SpeechPos;			// Position within controls for speech
00208	var int					DispatchAction;		// Index of current action of ScriptDispatcher
00209	
00210	// Sound related
00211	var bool				bQuiet;				// Don't play sounds (TODO: need to support this again)
00212	var(Sounds) sound		BreathSound;
00213	var(Sounds) sound		AcquireSound;
00214	var(Sounds) sound		AmbientWaitSounds[3];
00215	var(Sounds) sound		AmbientFightSounds[3];
00216	var(Sounds) sound		HuntSound;
00217	
00218	var(Sounds) float		AmbientWaitSoundDelay;	// [0..x] amount of time between ambient wait sounds
00219	var(Sounds) float		AmbientFightSoundDelay;	// [0..x] amount of time between ambient fight sounds
00220	var int					NumAmbientWaitSounds;
00221	var int					NumAmbientFightSounds;
00222	
00223	var(Sounds) sound		RoamSound;		//OBS (ambient replaces)
00224	var(Sounds) sound		FearSound;		//OBS
00225	var(Sounds) sound		ThreatenSound;	//OBS
00226	
00227	// Personality
00228	var(AI) bool			bIsBoss;			// Whether creature is boss
00229	var(AI) class<Weapon>	StartWeapon;		// Startup weapon
00230	var(AI) class<Shield>	StartShield;		// Startup shield
00231	var(AI) bool			bWillJoin;			// Will join a team if asked
00232	var(AI) bool			bRoamHome;			// Creature roams instead of goinghome
00233	var(AI) float			MinStopWait;		// Minimum time to wait between roams
00234	var(AI) float			MaxStopWait;		// Maximum time to wait between roams
00235	var(AI) bool			bGlider;			// For fish/birds that always move forward
00236	var(AI) bool			bWaitLook;			// Whether looks around while waiting
00237	
00238	// Internal variables
00239	var int					lookIndex;			// Index of reachspec looking down during movement
00240	var float				testfloat;			// used for debugging
00241	var string				teststring;
00242	var string				teststring2;
00243	var int					i;
00244	var Pawn				Hated;				// This creature is hated
00245	var Pawn				Ally;				// This creature is followed
00246	var float				ProtectionTimer;	// Timer for optimizing protection
00247	var(Advanced) bool		bBurnable;			// RUNE: Can be set on fire
00248	var() float				ShadowScale;		// RUNE:  Scale of the blob shadow
00249	
00250	// Powerup related
00251	var float				AllyTime;			// Internal timer
00252	var float				AllyMaxTime;		// Amount of time to stay allied
00253	
00254	// Fleeing
00255	var actor				LastPointVisited;	// Last navigation point visited
00256	var actor				CurrentPoint;
00257	var float				WanderDistance;		// Distance used to determine open space when wandering
00258	
00259	// Charging
00260	var actor				IgnoreEnemy;		// Actor to ignore from acquisition
00261	
00262	// PullUp Anims
00263	var name A_PullUp, A_StepUp;
00264	
00265	var bool debugstates;
00266	var bool bDisableCheckForEnemies;			// Speedup: don't check for enemies
00267	
00268	
00269	//===================================================================
00270	//					Stimulus Functions
00271	//===================================================================
00272	function AfterSpawningInventory()	{}		// Used to spawn additional chained weapons
00273	
00274	//------------------------------------------------
00275	//
00276	// PreBeginPlay
00277	//
00278	//------------------------------------------------
00279	function PreBeginPlay()
00280	{
00281		Super.PreBeginPlay();
00282		if ( bDeleteMe )
00283			return;
00284		if (DrawScale != Default.DrawScale)
00285		{
00286			WalkingSpeed = WalkingSpeed * DrawScale/Default.DrawScale;
00287		}
00288	
00289		// Grab statue animation from animseq if it's been set
00290		if (StatueAnimSeq == '' && AnimSequence != Default.AnimSequence)
00291		{
00292			StatueAnimSeq = AnimSequence;
00293			StatueAnimFrame = AnimFrame;
00294		}
00295	
00296		// Randomize phase of protection timer
00297		ProtectionTimer = -2.5*FRand();
00298	
00299		NumAmbientWaitSounds = 0;
00300		NumAmbientFightSounds = 0;
00301		for(i = 0; i < 3; i++)
00302		{
00303			if(AmbientWaitSounds[i] != None)
00304				NumAmbientWaitSounds++;
00305			if(AmbientFightSounds[i] != None)
00306				NumAmbientFightSounds++;
00307		}
00308	
00309		// Adjust health based upon level difficulty
00310		switch(Level.Game.Difficulty)
00311		{
00312		case 0: // Easy
00313			Health = Default.Health * 0.75;
00314			break;
00315		case 2: // Hard
00316			Health = Default.Health * 1.25;
00317			break;
00318		}
00319	}
00320	
00321	// Pass frame notifies to weapon
00322	simulated event FrameNotify(int framepassed)
00323	{
00324		if (Weapon != None)
00325		{
00326			Weapon.FrameNotify(framepassed);
00327			if (InvisibleWeapon(Weapon)!=None && InvisibleWeapon(Weapon).ChainedWeapon!=None)
00328				InvisibleWeapon(Weapon).ChainedWeapon.FrameNotify(framepassed);
00329		}
00330	}
00331	
00332	//------------------------------------------------------------
00333	//
00334	// ShadowUpdate
00335	//
00336	// RUNE:  Updates the shadow on the Pawn
00337	// Set ShadowScale to zero to not draw a shadow on this pawn
00338	//------------------------------------------------------------
00339	
00340	event ShadowUpdate(int ShadowType)
00341	{
00342		if(ShadowType == 1 && ShadowScale > 0)
00343		{ // Blob
00344			if(shadow == None)
00345				shadow = Spawn(class'PlayerShadow', self,, Location, Rotation);
00346	
00347			shadow.DrawScale = ShadowScale * DrawScale;
00348			if(shadow != None)
00349				shadow.Update(None);
00350		}
00351	/*
00352		else if(ShadowType == 2)
00353		{ // Projected shadow
00354	//		if(ShadowTexture == None)
00355	//			ShadowTexture = Spawn(class'ShadowTex', self,,,);
00356		
00357		}
00358	*/
00359	}
00360	
00361	//------------------------------------------------
00362	//
00363	// Trigger
00364	//
00365	//------------------------------------------------
00366	function Trigger( Actor Other, Pawn EventInstigator )
00367	{
00368		//slog("triggered - doing orders:"@TriggerOrders@TriggerOrdersTag);
00369		FollowOrders(TriggerOrders, TriggerOrdersTag);
00370	}
00371	
00372	
00373	//------------------------------------------------
00374	//
00375	// WantsToPickup
00376	//
00377	// Returns whether the item is desired
00378	//------------------------------------------------
00379	function bool WantsToPickUp(Inventory item)
00380	{
00381		if (item.IsA('Weapon'))
00382			return (Weapon == None || Weapon(item).Rating > Weapon.Rating);
00383		else if (item.IsA('Shield'))
00384			return (Shield == None || Shield(item).Rating > Shield.Rating);
00385	}
00386	
00387	
00388	//------------------------------------------------
00389	//
00390	// EnemyAcquired
00391	//
00392	// Called when Enemy has been set
00393	//------------------------------------------------
00394	function EnemyAcquired()
00395	{
00396		if ( !bAlerted )
00397		{
00398			bAlerted = true;
00399			if ( FollowOrders(AlertOrders, AlertOrdersTag) )
00400				return;
00401		}
00402		GotoState('Acquisition');
00403	}
00404	
00405	//------------------------------------------------
00406	//
00407	// SeePlayer
00408	//
00409	//------------------------------------------------
00410	function SeePlayer(actor seen)
00411	{
00412		if (SetEnemy(seen))
00413		{
00414		}
00415	}
00416	
00417	
00418	//------------------------------------------------
00419	//
00420	// HearNoise
00421	//
00422	//------------------------------------------------
00423	function HearNoise(float Loudness, Actor NoiseMaker)
00424	{
00425		if (Pawn(NoiseMaker)==None && NoiseMaker.Instigator!=None)
00426		{
00427			if (SetEnemy(NoiseMaker.Instigator))
00428			{
00429			}
00430		}
00431		else
00432		{
00433			if (SetEnemy(NoiseMaker))
00434			{
00435			}
00436		}
00437	}
00438	
00439	
00440	//------------------------------------------------
00441	//
00442	// HitWall
00443	//
00444	//------------------------------------------------
00445	function HitWall(vector HitNormal, actor Wall)
00446	{
00447	//	slog("hit wall in"@GetStateName()@"phys="$Physics);
00448	}
00449	
00450	
00451	//------------------------------------------------
00452	//
00453	// WeaponActivate (notify)
00454	//
00455	//------------------------------------------------
00456	function WeaponActivate()
00457	{
00458		if (Weapon != None)
00459			Weapon.StartAttack();
00460	}
00461	
00462	
00463	//------------------------------------------------
00464	//
00465	// WeaponDeactivate (notify)
00466	//
00467	//------------------------------------------------
00468	function WeaponDeactivate()
00469	{
00470		if (Weapon != None)
00471			Weapon.FinishAttack();
00472	}
00473	
00474	
00475	//------------------------------------------------
00476	//
00477	// DoThrow (notify)
00478	//
00479	// Throws actor attached to weapon joint at
00480	// Enemy or OrderObject
00481	//------------------------------------------------
00482	function DoThrow()
00483	{
00484		local actor throwitem;
00485		local int traj;
00486		local vector throwloc;
00487	
00488		if(Enemy != None)
00489		{ // If this Pawn has an enemy, throw the weapon at it
00490			OrderObject = Enemy;
00491		}
00492		else
00493		{ // ...Otherwise, just toss the weapon
00494			ThrowWeapon();
00495			return;
00496		}
00497		
00498		throwloc = GetJointPos(JointNamed(WeaponJoint));
00499		throwitem = DetachActorFromJoint(JointNamed(WeaponJoint));
00500		if (throwitem != None && OrderObject != None)
00501		{
00502			//TODO: Calculate Trajectory
00503			traj = ThrowTrajectory;
00504	
00505			// Throw the item
00506			throwitem.SetPhysics(PHYS_Falling);
00507	//			throwitem.SetLocation(throwloc);	// More accurate this way for some reason
00508			throwitem.Acceleration = vect(0,0,0);
00509			throwitem.Velocity = CalcArcVelocity(traj, throwloc, OrderObject.Location);
00510			throwitem.GotoState('Throw');
00511	
00512			if(throwItem.IsA('inventory'))
00513				DeleteInventory(Inventory(throwItem));
00514	
00515			if (Weapon==throwitem)
00516				Weapon=None;
00517		}
00518	}
00519	
00520	
00521	//------------------------------------------------
00522	//
00523	// Breath (notify)
00524	//
00525	// Notification called when pawn takes a breath
00526	//------------------------------------------------
00527	function Breath()
00528	{
00529		if (!HeadRegion.Zone.bWaterZone)
00530		{
00531			PlaySound(BreathSound, SLOT_Interface,,,, 1.0 + FRand()*0.2-0.1);
00532		}
00533	}
00534	
00535	
00536	//------------------------------------------------
00537	//
00538	// ZoneChange
00539	//
00540	//------------------------------------------------
00541	function ZoneChange(ZoneInfo newZone)
00542	{
00543		local vector jumpDir;
00544	
00545		if ( newZone.bWaterZone )
00546		{
00547			if (!bCanSwim)
00548				MoveTimer = -1.0;
00549			else if (Physics != PHYS_Swimming)
00550				setPhysics(PHYS_Swimming);
00551		}
00552		else if (Physics == PHYS_Swimming)
00553		{
00554			if ( bCanFly )
00555				 SetPhysics(PHYS_Flying); 
00556			else
00557			{ 
00558				SetPhysics(PHYS_Falling);
00559				if ( bCanWalk && CheckWaterJump(jumpDir) )
00560				{
00561	//				JumpOutOfWater(jumpDir);
00562				}
00563			}
00564		}
00565		UpdateMovementSpeed();
00566	}
00567	
00568	
00569	//------------------------------------------------------------
00570	//
00571	// HeadZoneChange
00572	//
00573	//------------------------------------------------------------
00574	function HeadZoneChange(ZoneInfo newHeadZone)
00575	{
00576		Super.HeadZoneChange(newHeadZone);
00577		if (newHeadZone.bWaterZone)
00578		{
00579			if (!bCanSwim)
00580				GotoState('Drowning');
00581		}
00582	}
00583	
00584	
00585	function PainTimer()
00586	{
00587		Super.PainTimer();
00588		if ( HeadRegion.Zone.bWaterZone )
00589		{
00590			if (!bCanSwim)
00591				GotoState('Drowning');
00592		}
00593	}
00594	
00595	//------------------------------------------------
00596	//
00597	// FearThisSpot
00598	//
00599	//------------------------------------------------
00600	function FearThisSpot(Actor aSpot)
00601	{
00602		Acceleration = vect(0,0,0);
00603		MoveTimer=-1.0;
00604	}
00605	
00606	//------------------------------------------------
00607	//
00608	// JointDamaged
00609	//
00610	//------------------------------------------------
00611	function bool JointDamaged(int Damage, Pawn EventInstigator, vector HitLoc, vector Momentum, name DamageType, int joint)
00612	{
00613		if (EventInstigator != None)
00614			DamageAttitudeTo(EventInstigator);
00615		return Super.JointDamaged(Damage, EventInstigator, HitLoc, Momentum, DamageType, joint);
00616	}
00617	
00618	//------------------------------------------------
00619	//
00620	// LimbSevered
00621	//
00622	//------------------------------------------------
00623	function LimbSevered(int BodyPart, vector Momentum)
00624	{
00625		Super.LimbSevered(BodyPart, Momentum);
00626		SoundChance(FearSound, 0.5);
00627		switch(BodyPart)
00628		{
00629			case BODYPART_RARM1:
00630			case BODYPART_RARM2:
00631				bCanSwing = false;
00632				DropWeapon();
00633				if (Health > 0)
00634					GotoState('Fleeing');
00635				break;
00636			case BODYPART_LARM1:
00637			case BODYPART_LARM2:
00638				bCanDefend = false;
00639				DropShield();
00640				break;
00641		}
00642	}
00643	
00644	
00645	//------------------------------------------------
00646	// MayFall
00647	//
00648	// called by engine physics if walking and bCanJump, and
00649	// is about to go off a ledge.  Pawn has opportunity
00650	// to avoid fall (by setting bCanJump to false)
00651	//------------------------------------------------
00652	function MayFall()
00653	{	// Only jump if reachable
00654		if (intelligence==BRAINS_None)
00655		{
00656			bCanJump = true;
00657		}
00658		else if (MoveTarget!=None)
00659		{
00660			bCanJump = actorReachable(MoveTarget);
00661		}
00662		else if (Enemy!=None)
00663		{
00664			bCanJump = actorReachable(Enemy);
00665		}
00666	}
00667	
00668	
00669	//------------------------------------------------
00670	//
00671	// GrabEdge
00672	//
00673	//------------------------------------------------
00674	function bool GrabEdge(float grabDistance, vector grabNormal)
00675	{
00676	//	slog("!!grab edge event while in state:"@GetStateName());
00677		SetPhysics(PHYS_Walking);
00678	
00679		return(false);
00680	}
00681	
00682	
00683	//------------------------------------------------
00684	//
00685	// Falling
00686	//
00687	// Called by physics
00688	//------------------------------------------------
00689	singular function Falling()
00690	{
00691	 	if (health <= 0)
00692			return;
00693	
00694		Super.Falling();
00695		if (bCanFly)
00696		{
00697			SetPhysics(PHYS_Flying);
00698			return;
00699		}
00700		SetFall();
00701	}
00702	
00703	
00704	//------------------------------------------------
00705	//
00706	// Landed
00707	//
00708	//------------------------------------------------
00709	function Landed(vector HitNormal, actor HitActor)
00710	{
00711		Super.Landed(HitNormal, HitActor);
00712		Acceleration = vect(0,0,0);
00713	}
00714	
00715	
00716	//------------------------------------------------
00717	//
00718	// SetFall
00719	//
00720	// default SetFall handler
00721	//------------------------------------------------
00722	function SetFall()
00723	{
00724		if (Enemy != None)
00725		{
00726			if (GetStateName() != 'FallingState')
00727			{
00728				NextState = GetStateName();
00729				NextLabel = 'Begin';
00730				GotoState('FallingState');
00731			}
00732		}
00733	}
00734	
00735	function SetOnFire(Pawn EventInstigator, int joint)
00736	{
00737		local PawnFire F;
00738	
00739		if (bBurnable)
00740		{
00741			if (ActorAttachedTo(joint) == None)
00742			{
00743				F = Spawn(class'PawnFire',EventInstigator);
00744				AttachActorToJoint(F, joint);
00745			}
00746		}
00747	}
00748	
00749	//===================================================================
00750	//					Powerup Support
00751	//===================================================================
00752	function PowerupFire(Pawn EventInstigator)
00753	{
00754		local int i;
00755	
00756		// Set all collision joints on fire
00757		for (i=0; i<NumJoints(); i++)
00758		{
00759			if ((JointFlags[i] & JOINT_FLAG_COLLISION)!=0)
00760				SetOnFire(EventInstigator, i);
00761		}
00762	}
00763	function PowerupBlaze(Pawn EventInstigator)
00764	{
00765		local int i;
00766	
00767		// Set all collision joints on fire
00768		for (i=0; i<NumJoints(); i++)
00769		{
00770			if ((JointFlags[i] & JOINT_FLAG_COLLISION)!=0)
00771				SetOnFire(EventInstigator, i);
00772		}
00773	}
00774	function PowerupStone(Pawn EventInstigator)
00775	{
00776		if (!bIsBoss)
00777		{
00778			FireEvent(Event);
00779			PlaySound(Sound'WeaponsSnd.Powerups.atfreezestone01', SLOT_Interface);
00780			GotoState('Statue');
00781		}
00782	}
00783	function PowerupIce(Pawn EventInstigator)
00784	{
00785		if (!bIsBoss)
00786		{
00787			PlaySound(Sound'WeaponsSnd.Powerups.atfreezeice01', SLOT_Interface);
00788			GotoState('IceStatue');
00789			SetTimer(5, false);
00790		}
00791	}
00792	function PowerupFriend(Pawn EventInstigator)
00793	{
00794		if (!bIsBoss)
00795		{
00796			PlaySound(Sound'WeaponsSnd.Powerups.aalli01', SLOT_Interface);
00797			AttitudeToPlayer = ATTITUDE_Follow;
00798			Ally = EventInstigator;
00799			AllyTime = 0;
00800			DesiredColorAdjust = vect(0,0,102);
00801			Enemy = None;
00802			FireEvent(Event);
00803			Event='';
00804		}
00805	}
00806	function UnPowerupFriend()
00807	{
00808		AttitudeToPlayer = Default.AttitudeToPlayer;
00809		Ally = None;
00810		DesiredColorAdjust = vect(0,0,0);
00811		Enemy = None;
00812		GotoState('GoingHome');
00813	}
00814	function PowerupElectricity(Pawn EventInstigator)
00815	{
00816	}
00817	
00818	
00819	//===================================================================
00820	//					Internal Work Functions
00821	//===================================================================
00822	
00823	
00824	//------------------------------------------------
00825	//
00826	// FollowOrders
00827	//
00828	//------------------------------------------------
00829	function bool FollowOrders(name order, name tag)
00830	{
00831		if (Order != '' && Health > 0)
00832		{
00833			bTaskLocked = true;
00834			OrderObject = ActorTagged(Tag);
00835			GotoState(Order);
00836			return true;
00837		}
00838		
00839		return false;
00840	}
00841	
00842	
00843	//------------------------------------------------
00844	//
00845	// OrderFinished
00846	//
00847	//------------------------------------------------
00848	function OrderFinished()
00849	{
00850		bTaskLocked = false;
00851		MoveTimer = -1.0;	// Stop any latent movement
00852	}
00853	
00854	
00855	//------------------------------------------------
00856	//
00857	// SetMovementPhysics
00858	//
00859	// Decide what locomotion method to use based on
00860	// the zone we are in
00861	//------------------------------------------------
00862	function SetMovementPhysics()
00863	{
00864		if (Region.Zone.bWaterZone)
00865			SetPhysics(PHYS_Swimming);
00866		else if (bCanFly)
00867			SetPhysics(PHYS_Flying);
00868		else if (bCanWalk)
00869			SetPhysics(PHYS_Walking);
00870		UpdateMovementSpeed();
00871	}
00872	
00873	
00874	//------------------------------------------------
00875	//
00876	// SetEnemy
00877	//
00878	// Decide whether to set this pawn as new enemy
00879	//------------------------------------------------
00880	function bool SetEnemy( Actor NewEnemy )
00881	{
00882		local EAttitude attitude;
00883	
00884		if (bTaskLocked)
00885			return false;
00886		if (NewEnemy == IgnoreEnemy)
00887			return false;
00888		if (Pawn(NewEnemy)!=None && Pawn(NewEnemy).Health<=0)
00889			return false;
00890	
00891		// Attitude logic for choosing enemy
00892		if (Pawn(NewEnemy) != None)
00893			attitude = AttitudeTo(Pawn(NewEnemy));
00894		else
00895			attitude = ATTITUDE_IGNORE;	// ATTITUDE_CURIOUS
00896		if (attitude >= ATTITUDE_IGNORE)
00897			return false;
00898	
00899		if (Enemy==None || attitude < AttitudeTo(Enemy))
00900		{
00901			Enemy = Pawn(NewEnemy);
00902			EnemyAcquired();
00903		}
00904		return true;
00905	}
00906	
00907	//------------------------------------------------
00908	//
00909	// AttitudeTo
00910	//
00911	//------------------------------------------------
00912	function eAttitude AttitudeTo(Pawn Other)
00913	{
00914		local EAttitude att;
00915	
00916		if (Other.bInvisible)
00917			att = ATTITUDE_Ignore;
00918		else if (Other.GetStateName()=='Statue' || Other.GetStateName()=='IceStatue' || Other.GetStateName()=='Crucified')
00919			att = ATTITUDE_Ignore;
00920		else if (Other.bIsPlayer)
00921			att = AttitudeToPlayer;
00922		else
00923			att = AttitudeToCreature(Other);
00924	
00925		return att;
00926	}
00927	
00928	//------------------------------------------------
00929	//
00930	// AttitudeToCreature
00931	//
00932	//------------------------------------------------
00933	function eAttitude AttitudeToCreature(Pawn Other)
00934	{
00935		if( Other != None && Other==Hated)
00936			return ATTITUDE_Hate;
00937		else if ( (TeamTag != '') && (ScriptPawn(Other) != None) && (TeamTag == ScriptPawn(Other).TeamTag) )
00938			return ATTITUDE_Friendly;
00939		else if( Other.Class == Class || Other.ClassID == ClassID)
00940			return ATTITUDE_Friendly;
00941		else
00942			return ATTITUDE_Ignore;
00943	}
00944	
00945	
00946	//------------------------------------------------
00947	//
00948	// DamageAttitudeTo
00949	//
00950	//------------------------------------------------
00951	function DamageAttitudeTo(Pawn Other)
00952	{
00953		if ( (Other == Self) || (Other == None) || (FlockPawn(Other) != None) )
00954			return;
00955		if (Other.bIsPlayer)
00956		{
00957			switch(AttitudeToPlayer)
00958			{
00959				case ATTITUDE_Fear:
00960				case ATTITUDE_Hate:
00961				case ATTITUDE_Frenzy:
00962				case ATTITUDE_Follow:
00963					break;
00964				case ATTITUDE_Threaten:
00965				case ATTITUDE_Ignore:
00966				case ATTITUDE_Friendly:
00967					AttitudeToPlayer = ATTITUDE_Hate;
00968					break;
00969			}
00970		}
00971		else
00972		{
00973			if (Other.ClassID != ClassID ||
00974				(ScriptPawn(Other)!=None && ScriptPawn(Other).Hated==self) ||
00975				(Other.Enemy==Ally)	)
00976			{
00977				Enemy = None;
00978				Hated = Other;
00979			}
00980		}
00981		SetEnemy(Other);
00982	}
00983	
00984	
00985	// CheckForEnemies
00986	//
00987	// Allows scriptpawns to target other scriptpawns, as well as
00988	// Could be changed to a radius actors check
00989	function CheckForEnemies()
00990	{
00991		local Pawn P, ClosestEnemy;
00992		local eAttitude att;
00993		local float Dist,LeastDist;
00994		local float radius;
00995	
00996		ClosestEnemy=None;
00997		LeastDist=9999999.0;
00998	
00999		if (HuntDistance>0)
01000			radius = Min(HuntDistance, SightRadius);
01001		else
01002			radius = SightRadius;
01003	
01004		foreach RadiusActors(class'Pawn', P, radius)
01005		{
01006			if (P==self || P.Class == Class || P.ClassID == ClassID)
01007				continue;
01008			if (P.IsA('FlockPawn'))
01009				continue;
01010	
01011			att = AttitudeTo(P);
01012			if (att != ATTITUDE_Hate && att != ATTITUDE_Fear)
01013				continue;
01014	
01015			dist = VSize(P.Location-Location);
01016	
01017			if (HuntDistance>0 && (dist > 2*HuntDistance || VSize(HomeBase-P.Location)>2*HuntDistance))
01018				continue;
01019	
01020			if (P.bIsPlayer)
01021				dist *= 0.5;
01022	
01023			if (dist<LeastDist)
01024			{
01025				if (actorReachable(P) || FindBestPathToward(P))
01026				{
01027					LeastDist=Dist;
01028					ClosestEnemy=P;
01029				}
01030			}
01031		}
01032	
01033		if (ClosestEnemy != None)
01034		{
01035			SetEnemy(ClosestEnemy);
01036			att = AttitudeTo(ClosestEnemy);
01037			if (att == ATTITUDE_Fear)
01038				GotoState('Fleeing');
01039		}
01040	
01041	/*
01042		foreach RadiusActors(class'Pawn', P, radius)
01043		{
01044			if (P==self)
01045				continue;
01046			if (P.IsA('FlockPawn'))
01047				continue;
01048	
01049			dist = VSize(P.Location-Location);
01050	
01051			if (HuntDistance>0 && (dist > 2*HuntDistance || VSize(HomeBase-P.Location)>2*HuntDistance))
01052				continue;
01053	
01054			if (dist.Z > 300)
01055				continue;
01056	
01057			// fixme: make sure pawn is reachable
01058	
01059			if (P.bIsPlayer)
01060				dist *= 0.5;
01061	
01062			if (dist<LeastDist)
01063			{
01064				LeastDist=Dist;
01065				ClosestEnemy=P;
01066			}
01067		}
01068	
01069		if (ClosestEnemy != None)
01070		{
01071			att = AttitudeTo(ClosestEnemy);
01072			if (att == ATTITUDE_Hate)
01073			{
01074				SetEnemy(ClosestEnemy);
01075			}
01076			else if (att == ATTITUDE_Fear)
01077			{
01078				SetEnemy(ClosestEnemy);
01079				GotoState('Fleeing');
01080			}
01081		}
01082	*/
01083	}
01084	
01085	
01086	function Tick(float DeltaTime)
01087	{
01088		ProtectionTimer += DeltaTime;
01089		if (ProtectionTimer > 2.5)
01090		{
01091			ProtectionTimer = 0;
01092	
01093			if (AttitudeToPlayer==ATTITUDE_Follow)
01094			{
01095				ProtectAlly();
01096			}
01097			else if (!ValidEnemy() && !bDisableCheckForEnemies)
01098			{	// otherwise, attack enemies
01099				CheckForEnemies();
01100			}
01101		}
01102	
01103		Super.Tick(DeltaTime);
01104	}
01105	
01106	
01107	function ProtectAlly()
01108	{	// Follow and protect ally
01109		local Pawn P;
01110	
01111		AllyTime += 1;
01112		if (AllyTime > AllyMaxTime)
01113		{
01114			UnPowerupFriend();
01115			return;
01116		}
01117	
01118		if (AttitudeToPlayer!=ATTITUDE_Follow || Ally == None)
01119			return;
01120	
01121		if (Enemy != None)
01122		{	// Already has an enemy
01123			if (ScriptPawn(Enemy)!=None && ScriptPawn(Enemy).Ally==Ally)
01124			{
01125				Enemy = None;
01126			}
01127			else if (Enemy.Health > 0)	// living enemy, keep attacking
01128				return;
01129		}
01130	
01131		// otherwise, attack enemies
01132		foreach VisibleActors(class'Pawn', P)
01133		{
01134			if (P==Ally || P==self)
01135				continue;
01136	
01137			if (ScriptPawn(P)!=None && ScriptPawn(P).Ally==Ally)
01138				continue;
01139	
01140			// Look around for enemies of the player and make them my enemy
01141			if (P.Enemy == Ally)
01142			{
01143				DamageAttitudeTo(P);
01144				return;
01145			}
01146			else if (ScriptPawn(P.Enemy)!=None && ScriptPawn(P.Enemy).Ally == Ally)
01147			{
01148				DamageAttitudeTo(P);
01149				return;
01150			}
01151		}
01152	}
01153	
01154	
01155	// Used to stop attacking when conditions are met
01156	function bool ValidEnemy()
01157	{
01158		if (AttitudeToPlayer==ATTITUDE_Follow && ScriptPawn(Enemy)!=None && ScriptPawn(Enemy).Ally==Ally)
01159			return false;
01160	
01161		return (Enemy!=None && Enemy.Health>0 && !Enemy.bInvisible);
01162	}
01163	
01164	//------------------------------------------------
01165	//
01166	// AllowWeaponToHitActor
01167	//
01168	// Disallow hitting others of the same class
01169	//------------------------------------------------
01170	function bool AllowWeaponToHitActor(Weapon W, Actor A)
01171	{
01172		if (A!=Enemy)
01173		{
01174			if (A.Class == Class)
01175				return false;
01176	
01177			if (A.Owner != None && A.Owner.Class == Class)
01178				return false;
01179		}
01180	
01181		return true;
01182	}
01183	
01184	
01185	//------------------------------------------------
01186	//
01187	// CanDirectlyThreaten
01188	//
01189	//------------------------------------------------
01190	function bool CanDirectlyThreaten()
01191	{
01192		if ( Enemy==None || (Enemy.Region.Zone.bWaterZone && !bCanSwim && !bCanFly))
01193			return false;
01194		if ( !Enemy.Region.Zone.bWaterZone && !bCanWalk && !bCanFly)
01195			return false;
01196		return ( ActorReachable(Enemy) );
01197	}
01198	
01199	
01200	//================================================
01201	//
01202	// InRange
01203	//
01204	//================================================
01205	function bool InRange(actor Other, float range)
01206	{
01207		if (Other == None)
01208			return false;
01209		return (VSize(Location-Other.Location) < CollisionRadius + Other.CollisionRadius + range);
01210	}
01211	
01212	
01213	//------------------------------------------------
01214	//
01215	// InMeleeRange
01216	//
01217	//------------------------------------------------
01218	function bool InMeleeRange(Actor Other)
01219	{
01220		if (Other == None)
01221			return false;
01222		return (VSize(Location - Other.Location) < CollisionRadius + Other.CollisionRadius + MeleeRange);
01223	}
01224	
01225	//------------------------------------------------
01226	//
01227	// InLungeRange
01228	//
01229	//------------------------------------------------
01230	function bool InLungeRange(Actor Other)
01231	{
01232		if (Other == None)
01233			return false;
01234		return (VSize(Location - Other.Location) < CollisionRadius + Other.CollisionRadius + LungeRange);
01235	}
01236	
01237	//------------------------------------------------
01238	//
01239	// InPaceRange
01240	//
01241	//------------------------------------------------
01242	function bool InPaceRange(Actor Other)
01243	{
01244		if (Other == None)
01245			return false;
01246		return (VSize(Location - Other.Location) < CollisionRadius + Other.CollisionRadius + PaceRange);
01247	}
01248	
01249	
01250	//------------------------------------------------
01251	//
01252	// InAttackRange
01253	//
01254	// When within attack range, state changes from
01255	// charging to fighting
01256	//------------------------------------------------
01257	function bool InAttackRange(Actor Other)
01258	{
01259		local float range;
01260	
01261		range = VSize(Location-Other.Location);
01262	
01263		// Handle Melee Range
01264		if(range <= CollisionRadius + Other.CollisionRadius + MeleeRange)
01265			return(true);
01266	
01267		// Handle Combat Range
01268		if(bStopMoveIfCombatRange && (range < CollisionRadius + Other.CollisionRadius + CombatRange))
01269			return(true);
01270		
01271		return(false);
01272	}
01273	
01274	
01275	//------------------------------------------------
01276	//
01277	// AddLocalVelocity
01278	//
01279	// Add local coordinate system velocities
01280	//------------------------------------------------
01281	function AddLocalVelocity(float vx, float vy, float vz)
01282	{
01283		local vector X, Y, Z;
01284		GetAxes(Rotation, X, Y, Z);
01285		
01286		AddVelocity(X*vx + Y*vy + Z*vz);
01287	}
01288	
01289	
01290	
01291	//------------------------------------------------
01292	//
01293	// GetEnemyProximity
01294	//
01295	// Determine attack proximity information
01296	//------------------------------------------------
01297	function GetEnemyProximity()
01298	{
01299		local vector X, Y, Z;
01300		local vector EX, EY, EZ;
01301		local float dp;
01302	
01303		if(Enemy == None)
01304			return;
01305	
01306		GetAxes(Rotation,  X, Y, Z);
01307		GetAxes(Enemy.Rotation,  EX, EY, EZ);
01308		
01309		VecToEnemy = Enemy.Location - Location;
01310		EnemyDist = VSize(VecToEnemy);
01311	
01312		VecToEnemy = Normal(VecToEnemy);
01313		VecFromEnemy = VecToEnemy * -1;
01314		
01315		// Calculate enemy facing
01316		dp = VecFromEnemy dot vector(Enemy.Rotation);
01317		if(dp >= 0.7)
01318		{
01319			EnemyFacing = FACE_FRONT;
01320		}
01321		else if(dp <= -0.7)
01322		{
01323			EnemyFacing = FACE_BACK;
01324		}
01325		else
01326		{
01327			EnemyFacing = FACE_SIDE;
01328		}
01329		
01330		// Calculate enemy incidence
01331		dp = VecToEnemy dot X;
01332		if(dp >= 0.8)
01333		{
01334			EnemyIncidence = INC_FRONT;
01335		}
01336		else if(dp <= -0.7)
01337		{
01338			EnemyIncidence = INC_BACK;
01339		}
01340		else
01341		{ // Compute left or right
01342			if(VecToEnemy dot Y >= 0)
01343			{
01344				EnemyIncidence = INC_RIGHT;
01345			}
01346			else
01347			{
01348				EnemyIncidence = INC_LEFT;
01349			}
01350		}
01351	
01352		// Calculate Enemy Verticality (above/below)
01353		if(Enemy.Location.Z > Location.Z + CollisionHeight * 0.75)
01354		{
01355			EnemyVertical = VERT_ABOVE;
01356		}
01357		else if(Enemy.Location.Z < Location.Z - CollisionHeight * 0.75)
01358		{
01359			EnemyVertical = VERT_BELOW;
01360		}
01361		else
01362		{
01363			EnemyVertical = VERT_LEVEL;
01364		}
01365	
01366		// Calculate Enemy Movement
01367		if(VSize(Enemy.Velocity) >= Enemy.GroundSpeed * 0.5)
01368		{
01369			dp = VecFromEnemy dot Normal(Enemy.Velocity);
01370			if(dp >= 0.7)
01371			{
01372				EnemyMovement = MOVE_CLOSER;
01373			}
01374			else if(dp <= -0.7)
01375			{
01376				EnemyMovement = MOVE_FARTHER;
01377			}
01378			else
01379			{ // Determine strafe left of right
01380				if(EY dot Normal(Enemy.Velocity) >= 0)
01381				{
01382					EnemyMovement = MOVE_STRAFE_RIGHT;
01383				}
01384				else
01385				{
01386					EnemyMovement = MOVE_STRAFE_LEFT;
01387				}				
01388			}
01389		}
01390		else
01391		{
01392			EnemyMovement = MOVE_STANDING;
01393		}
01394	}
01395	
01396	
01397	
01398	
01399	//------------------------------------------------
01400	//
01401	// Animation Functions
01402	//
01403	// Override in children
01404	//------------------------------------------------
01405	
01406	// Required
01407	function PlayWaiting(optional float tween)			{}	// Played when not doing anything else
01408	function PlayMoving(optional float tween)			{}	// Played during all locomotion (swim,walk,run,fly)
01409	function PlayJumping(optional float tween)			{}	// Played while jumping
01410	function PlayInAir(optional float tween)			{}	// Played while falling
01411	function PlayStrafeLeft(optional float tween)		{ PlayMoving(tween); }
01412	function PlayStrafeRight(optional float tween)		{ PlayMoving(tween); }
01413	function PlayBackup(optional float tween)			{ PlayMoving(tween); }
01414	
01415	// Optional
01416	function PlayTurning(optional float tween)			{ PlayWaiting(tween);	}	// Played while turning
01417	function PlayCower(optional float tween)			{ PlayWaiting(tween);	}	// Fleeing -> HitWall -> Cower
01418	function PlayHuntStop(optional float tween)			{ PlayWaiting(tween);	}	// Played when choosing next Hunting dest
01419	function PlayTaunting(optional float tween)			{ PlayWaiting(tween);	}	// 
01420	function PlayThreatening(optional float tween)		{ PlayWaiting(tween);	}	// 
01421	function LongFall()									{ PlayInAir(0.1);		}
01422	function PlayPickupWeapon(optional float tween)		{}
01423	function PlayDropWeapon(optional float tween)		{}
01424	function PlayPickupShield(optional float tween)		{}
01425	function PlayLanding(optional float tween)			{}
01426	
01427	// PullUp
01428	function PlayPullUp(optional float tween)			{ PlayAnim(A_PullUp, 1.0, tween); }
01429	function PlayStepUp(optional float tween)			{ PlayAnim(A_StepUp, 1.0, tween); }
01430	
01431	// Damage
01432	function PlayFrontHit(optional float tween)			{}
01433	
01434	// Deaths (from Pawn)
01435	function PlayDeath(name DamageType)					{}
01436	
01437	// Obsolete
01438	function PlayMeleeLow(optional float tween)			{}	// These can go ?
01439	function PlayMeleeHigh(optional float tween)		{}
01440	function PlayMeleeVertical(optional float tween)	{}
01441	function PlayBlockLow(optional float tween)			{}
01442	function PlayBlockHigh(optional float tween)		{}
01443	function PlayThrowing(optional float tween)			{}
01444	function PlayMovingAttack(optional float tween)		{}
01445	
01446	// Tweens
01447	function TweenToWaiting(float time)					{}
01448	function TweenToMoving(float time)					{}
01449	function TweenToJumping(float time)					{}
01450	function TweenToTurning(float time)					{}
01451	function TweenToHuntStop(float time)				{}
01452	
01453	function TweenToMeleeHigh(float time)				{}	// These can go
01454	function TweenToMeleeLow(float time)				{}
01455	function TweenToThrowing(float time)				{}
01456	
01457	
01458	
01459	//------------------------------------------------
01460	//
01461	// Sound Functions
01462	//
01463	//------------------------------------------------
01464	function bool SoundChance(sound Sound, float chance, optional ESoundSlot slot)
01465	{
01466		if ((Sound != None) && (FRand() < chance) && !bQuiet)
01467		{
01468			PlaySound(Sound, slot,, true,, 1.0 + FRand()*0.2-0.1);
01469			return true;
01470		}
01471		return false;
01472	}
01473	
01474	function PlayAmbientWaitSound()
01475	{
01476		if (!Region.Zone.bWaterZone)
01477		{
01478			i = Rand(NumAmbientWaitSounds);
01479			PlaySound(AmbientWaitSounds[i], SLOT_Talk,,,, 1.0 + FRand()*0.2-0.1);
01480			AmbientSoundTime = (0.5 + FRand()*0.5) * AmbientWaitSoundDelay;
01481		}
01482	}
01483	
01484	function PlayAmbientFightSound()
01485	{
01486		if (!Region.Zone.bWaterZone)
01487		{
01488			i = Rand(NumAmbientFightSounds);
01489			PlaySound(AmbientFightSounds[i], SLOT_Talk,,,, 1.0 + FRand()*0.2-0.1);
01490			AmbientSoundTime = (0.5 + FRand()*0.5) * AmbientFightSoundDelay;
01491		}
01492	}
01493	
01494	function AmbientSoundTimer()
01495	{
01496		PlayAmbientWaitSound();
01497	}
01498	
01499	
01500	//===================================================================
01501	//					States
01502	//===================================================================
01503				
01504	
01505	//================================================
01506	//
01507	// Startup
01508	//
01509	//================================================
01510	auto State Startup
01511	{
01512	ignores EnemyAcquired, SeePlayer, HearNoise;
01513		
01514		function BeginState()
01515		{
01516			Enemy = None;
01517			AmbientSoundTime=RandRange(2.0, 5.0);	// Start ambient sounds
01518		}
01519		
01520		function EndState()
01521		{
01522			SetMovementPhysics();
01523			bHurrying = false;
01524			UpdateMovementSpeed();
01525		}
01526		
01527		function SpawnStartInventory()
01528		{	// Spawn a start weapon if applicable
01529			local Weapon W;
01530			local Shield S;
01531	
01532			if (StartWeapon != None)
01533			{
01534				W = Spawn(StartWeapon);
01535				W.RespawnTime=0;
01536			}
01537			
01538			if (StartShield != None)
01539			{
01540				S = Spawn(StartShield);
01541				S.RespawnTime=0;
01542			}
01543		}
01544	
01545		function TouchSurroundingObjects()
01546		{	// Since things spawned touching don't get touch messages
01547			local Inventory Inv;
01548			foreach RadiusActors(class'Inventory', Inv, 10)
01549			{
01550				Inv.Touch(self);
01551			}
01552		}
01553	
01554		function Timer()
01555		{
01556			if (PlayerCanSeeMe())
01557			{
01558				SetTimer(0, false);
01559				GotoState('Startup', 'Wake');
01560			}
01561		}
01562		
01563		function SetHome()
01564		{
01565			local NavigationPoint aNode;
01566	
01567			aNode = Level.NavigationPointList;
01568	
01569			while ( aNode != None )
01570			{
01571				if ( aNode.IsA('HomeBase') && (aNode.tag == tag) )
01572				{
01573					HomeBase = aNode.Location;
01574					HomeRot = aNode.Rotation;
01575					return;
01576				}
01577				aNode = aNode.nextNavigationPoint;
01578			}
01579	
01580			// Didn't find a homebase, use spawnpoint
01581			HomeBase = Location;
01582			HomeRot = Rotation;
01583		}
01584	
01585	Wake:
01586		FollowOrders(Orders, OrdersTag);
01587		GotoState('Waiting');
01588		
01589	Begin:
01590		if(debugstates) slog(name@"Starting");
01591		if (!bCanFly && bFallAtStartup)
01592			SetPhysics(PHYS_Falling);
01593		SetHome();
01594		SpawnStartInventory();
01595		TouchSurroundingObjects();
01596		AfterSpawningInventory();
01597	
01598	Restart:
01599		if (bMovable && bFallAtStartup && !Region.Zone.bWaterZone)
01600			WaitForLanding();
01601		SetPhysics(PHYS_None);
01602	//	SetTimer(0.2, true);
01603		Goto('Wake');
01604	}
01605	
01606	
01607	//================================================
01608	//
01609	// Waiting
01610	//
01611	//================================================
01612	State Waiting
01613	{
01614		function BeginState()
01615		{
01616			Enemy = None;
01617			SetPhysics(PHYS_None);
01618		}
01619		
01620		function EndState()
01621		{
01622			// Make sure we start up physics before leaving this state
01623			SetMovementPhysics();
01624			SetTimer(0, false);
01625		}
01626	
01627		function SeePlayer(actor seen)
01628		{
01629			if (HuntDistance > 0)
01630			{
01631				// Disallow if seen is outside hunt radius
01632				if (VSize(HomeBase-seen.Location)>2*HuntDistance)
01633					return;
01634	
01635				// Disallow if path to seen is outside hunt radius
01636			}
01637	
01638			global.SeePlayer(seen);
01639		}
01640	
01641		function Bump(actor Other)
01642		{
01643			SetEnemy(Other);
01644		}
01645	
01646		function Landed(vector HitNormal, actor HitActor)
01647		{
01648			SetPhysics(PHYS_None);
01649		}
01650	
01651		function Timer()
01652		{
01653			if (bCanLook && bWaitLook)
01654			{	// Change look spot
01655				LookToward(Location + VRand() * 100);
01656				SetTimer(RandRange(1.5, 3.5), false);
01657			}
01658		}
01659	
01660	Begin:
01661		if(debugstates) slog(name@"Waiting");
01662	Wait:
01663		PlayWaiting();
01664		SetTimer(1, false);
01665	}
01666	
01667	
01668	//================================================
01669	//
01670	// Roaming
01671	//
01672	// Following random paths
01673	//================================================
01674	State() Roaming
01675	{
01676		function BeginState()
01677		{
01678			bTaskLocked = false;	// Never task locked when roaming so will fight
01679			Enemy = None;
01680			LookAt(None);
01681			bHurrying = false;
01682			UpdateMovementSpeed();
01683		}
01684		
01685		function HitWall(vector HitNormal, actor Wall)
01686		{
01687			global.HitWall(HitNormal, Wall);
01688			if (Physics == PHYS_Falling)
01689				return;
01690			Focus = Destination;
01691			if (PickWallAdjust())
01692				GotoState('Roaming', 'AdjustFromWall');
01693			else
01694				MoveTimer = -1.0;
01695		}
01696	
01697		function PickDestination()
01698		{
01699			OrderObject = FindRandomDest();
01700			if (OrderObject == None)
01701				GotoState('Wandering');
01702		}
01703	
01704		function PickWaypoint()
01705		{
01706			MoveTarget = FindPathToward(OrderObject);
01707			if (MoveTarget != None)
01708			{
01709				Destination = MoveTarget.Location;
01710				if (actorReachable(MoveTarget))
01711				{
01712					return;
01713				}
01714			}
01715			GotoState('Roaming', 'Roam');
01716		}
01717	
01718	Begin:
01719		if(debugstates) slog(name@"Roaming");
01720	Roam:
01721		PickDestination();
01722	
01723	Path:
01724		PickWaypoint();
01725		
01726		LookToward(MoveTarget.Location);
01727	
01728		// Turn to Destination
01729		if (AngleTo(MoveTarget.Location) > ANGLE_45)
01730		{
01731			DesiredRotation.Yaw = Rotator(MoveTarget.Location - Location).Yaw;
01732			PlayTurning();
01733			FinishAnim();
01734		}
01735		
01736		TweenToMoving(0.15);
01737		WaitForLanding();
01738		FinishAnim();
01739		PlayMoving();
01740		StopLookingToward();
01741		
01742	Moving:
01743		MoveToward(MoveTarget, MovementSpeed);
01744	
01745		// Look down some paths if not a straight shot
01746		if ((!bGlider) &&
01747			MaxStopWait > 0 &&
01748			NavigationPoint(MoveTarget) != None &&
01749			NavigationPoint(MoveTarget).NumPaths() > 2 &&
01750			NavigationPoint(MoveTarget) != LastNodeVisited)
01751		{
01752			Acceleration = vect(0,0,0);
01753			TweenToWaiting(0.3);
01754			FinishAnim();
01755		
01756			lookindex = 0;
01757			while (lookIndex < NavigationPoint(MoveTarget).NumPaths())
01758			{
01759				PlayWaiting();
01760				LookToward(NavigationPoint(MoveTarget).PathEndPoint(lookIndex).Location);
01761				lookIndex++;
01762				Sleep(RandRange(MinStopWait, MaxStopWait));
01763				FinishAnim();
01764			}
01765		}
01766		
01767		LastNodeVisited	= NavigationPoint(MoveTarget);
01768		
01769		if (MoveTarget != OrderObject)
01770			Goto('Path');
01771		else
01772		{
01773			SoundChance(RoamSound, 1.0);
01774			Goto('Roam');
01775		}
01776	
01777	AdjustFromWall:
01778		StrafeTo(Destination, Focus); 
01779		Destination = Focus; 
01780		Goto('Moving');
01781	}
01782	
01783	
01784	//================================================
01785	//
01786	// Wandering
01787	//
01788	//================================================
01789	State Wandering
01790	{
01791		function BeginState()
01792		{
01793			Enemy = None;
01794			LookAt(None);
01795			bHurrying = false;
01796			UpdateMovementSpeed();
01797		}
01798		
01799		function SetFall()
01800		{
01801			NextState = 'Wandering'; 
01802			NextLabel = 'ContinueWander';
01803			NextAnim  = AnimSequence;
01804	//		GotoState('FallingState'); 
01805		}
01806		
01807		function HitWall(vector HitNormal, actor Wall)
01808		{
01809			global.HitWall(HitNormal, Wall);
01810			if (Physics == PHYS_Falling)
01811				return;
01812			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
01813			{
01814				if ( SpecialPause > 0 )
01815					Acceleration = vect(0,0,0);
01816				GotoState('Wandering', 'Pausing');
01817				return;
01818			}
01819			Focus = Destination;
01820			if (PickWallAdjust())
01821				GotoState('Wandering', 'AdjustFromWall');
01822			else
01823				MoveTimer = -1.0;
01824		}
01825	
01826		function bool TestDirection(vector dir, out vector pick)
01827		{	
01828			local vector HitLocation, HitNormal, dist;
01829			local float minDist;
01830			local actor HitActor;
01831	
01832			minDist = FMin(150.0, 4*CollisionRadius);
01833			pick = dir * (minDist + (450 + WanderDistance * CollisionRadius) * FRand());
01834	
01835			HitActor = Trace(HitLocation, HitNormal, Location + pick + 1.5 * CollisionRadius * dir , Location, false);
01836			if (HitActor != None)
01837			{
01838				pick = HitLocation + (HitNormal - dir) * 2 * CollisionRadius;
01839				HitActor = Trace(HitLocation, HitNormal, pick , Location, false);
01840				if (HitActor != None)
01841					return false;
01842			}
01843			else
01844			{	// Trace hit nothing
01845				if (Physics == PHYS_Swimming)
01846				{
01847					if (!pointReachable(Location+pick))
01848					{
01849						return false;
01850					}
01851				}
01852				pick = Location + pick;
01853			}
01854	
01855	 
01856			dist = pick - Location;
01857			if (Physics == PHYS_Walking)
01858				dist.Z = 0;
01859			
01860			return (VSize(dist) > minDist); 
01861		}
01862		
01863		function PickDestination()
01864		{
01865			local vector pick, pickdir;
01866			local bool success;
01867			local float XY;
01868	
01869			// Try to get back to Roaming
01870			OrderObject = FindRandomDest();
01871			if (OrderObject != None)
01872			{
01873				GotoState('Roaming');
01874				return;
01875			}
01876			
01877			//Favor XY alignment
01878			XY = FRand();
01879			if (XY < 0.3)
01880			{
01881				pickdir.X = 1;
01882				pickdir.Y = 0;
01883			}
01884			else if (XY < 0.6)
01885			{
01886				pickdir.X = 0;
01887				pickdir.Y = 1;
01888			}
01889			else
01890			{
01891				pickdir.X = 2 * FRand() - 1;
01892				pickdir.Y = 2 * FRand() - 1;
01893			}
01894	
01895			if (Physics == PHYS_Swimming)
01896			{
01897				pickdir.Z = 2 * FRand() - 1;
01898				pickdir = Normal(pickdir);
01899			}
01900			else if (Physics == PHYS_Flying)
01901			{
01902				pickdir.Z = 2 * FRand() - 1;
01903				pickdir = Normal(pickdir);
01904			}
01905			else
01906			{
01907				pickdir.Z = 0;
01908				if (XY >= 0.6)
01909					pickdir = Normal(pickdir);
01910			}	
01911	
01912			success = TestDirection(pickdir, pick);
01913			if (!success)
01914				success = TestDirection(-1 * pickdir, pick);
01915	
01916			if (success)
01917				Destination = pick;
01918			else
01919				GotoState('Wandering', 'Turn');
01920		}
01921		
01922	Begin:
01923		if(debugstates) slog(name@"Wandering");
01924	Wander: 
01925		TweenToMoving(0.15);
01926		WaitForLanding();
01927		PickDestination();
01928		FinishAnim();
01929		PlayMoving();
01930	
01931	Moving:
01932		Enable('HitWall');
01933		MoveTo(Destination, MovementSpeed);
01934	
01935	Pausing:
01936		SoundChance(RoamSound, 0.3);
01937		if ((!bGlider) &&
01938			MaxStopWait > 0)
01939		{
01940			Acceleration = vect(0,0,0);
01941	
01942			if ( NearWall(2 * CollisionRadius + 50) )
01943			{
01944				PlayTurning();
01945				TurnTo(Focus);
01946			}
01947	
01948			TweenToWaiting(0.2);
01949			FinishAnim();
01950			PlayWaiting();
01951			Sleep(RandRange(MinStopWait, MaxStopWait));
01952		}
01953		Goto('Wander');
01954	
01955	
01956	ContinueWander:
01957		FinishAnim();
01958		PlayMoving();
01959		SoundChance(RoamSound, 0.3);
01960		if (FRand() < 0.2)
01961			Goto('Turn');
01962		Goto('Wander');
01963	
01964	Turn:
01965		Acceleration = vect(0,0,0);
01966		PlayTurning();
01967		TurnTo(Location + 20 * VRand());
01968		Goto('Pausing');
01969	
01970	AdjustFromWall:
01971		StrafeTo(Destination, Focus); 
01972		Destination = Focus; 
01973		Goto('Moving');
01974	}
01975	
01976	
01977	//================================================
01978	//
01979	// Scripting
01980	//
01981	// Requires a ScriptPoint OrderObject
01982	//================================================
01983	State() Scripting
01984	{
01985	ignores PowerupFire, PowerupBlaze, PowerupStone, PowerupIce, PowerupFriend, CheckForEnemies, ProtectAlly;//, SetOnFire;
01986	
01987		function Breath()	{}
01988	
01989		// Override some animations so these events don't takeover animations between scriptpoints
01990		function PlayLanding(optional float tween)		{	PlayMoving(tween);	}
01991		function PlayJumping(optional float tween)		{	PlayMoving(tween);	}
01992		function PlayInAir(optional float tween)		{	PlayMoving(tween);	}
01993	
01994		function BeginState()
01995		{
01996			Acceleration=vect(0,0,0);
01997		}
01998	
01999		function EndState()
02000		{
02001			bOverrideLookTarget=false;
02002		}
02003	
02004		function bool CanGotoPainState()
02005		{
02006			return(false);
02007		}
02008	
02009		function AmbientSoundTimer()
02010		{	// Don't play it, just reset timer for next one
02011			AmbientSoundTime = (0.5 + FRand()*0.5) * AmbientWaitSoundDelay;
02012		}
02013	
02014		//============================================
02015		// ScriptPoint support functions
02016		//============================================
02017	
02018		function ExecuteScriptPoint()
02019		{
02020			local ScriptPoint Dest;
02021			local actor A;
02022	
02023			teststring = "ExecuteScriptPointScript";
02024	
02025			Dest = ScriptPoint(OrderObject);
02026			if (Dest != None)
02027			{
02028				SoundChance(Dest.ArriveSound, 1.0);
02029				
02030				FireEvent(Dest.ArriveEvent);
02031	
02032				NextState = Dest.NextOrder;
02033				NextLabel = '';//Dest.NextOrderLabel;
02034				NextPoint = ActorTagged( Dest.NextOrderTag );
02035				
02036				bTaskLocked = !Dest.bReleaseUponArrival;
02037	
02038				if (Dest.ArriveState != '')
02039				{
02040					OrderObject = ActorTagged( Dest.ArriveStateTag );
02041					GotoState( Dest.ArriveState, Dest.ArriveStateLabel );
02042				}
02043				else if (NextState != '')
02044				{
02045					OrderObject = NextPoint;
02046					GotoState(NextState, NextLabel);
02047				}
02048				else
02049				{	// Release to normal AI
02050					bTaskLocked = false;
02051					OrderObject = None;
02052					GotoState('Waiting');
02053				}
02054			}
02055		}
02056	
02057		//============================================
02058		// ScriptAction support functions
02059		//============================================
02060	
02061		function ExecuteScriptAction()
02062		{
02063			local ScriptAction Action;
02064	
02065			Action = ScriptAction(OrderObject);
02066			if (Action != None)
02067			{
02068				NextState = Action.NextOrder;
02069				NextLabel = '';
02070				NextPoint = ActorTagged( Action.NextOrderTag );
02071				
02072				bTaskLocked = !Action.bReleaseUponCompletion;
02073	
02074				if (NextState != '')
02075				{
02076					OrderObject = NextPoint;
02077					GotoState(NextState, NextLabel);
02078				}
02079				else
02080				{	// Release to normal AI
02081					bTaskLocked = false;
02082					OrderObject = None;
02083					GotoState('Waiting');
02084				}
02085			}
02086		}
02087	
02088		function SpeechTimer()
02089		{
02090			local string letter;
02091			local float alpha;
02092			local ScriptAction Action;
02093			local ScriptDispatcher Dispatch;
02094	
02095			if (ScriptAction(OrderObject) != None)
02096			{
02097				Action = ScriptAction(OrderObject);
02098	
02099				// parse control strings
02100				if (Len(Action.ControlMouth) > 0)
02101				{
02102					letter = Mid(Action.ControlMouth, SpeechPos, 1);
02103					alpha = float(Asc(letter) - Asc("A"))/25.0;
02104					OpenMouth(FClamp(alpha, 0, 1), 1.0);
02105				}
02106				if (Len(Action.ControlHead) > 0)
02107				{
02108					letter = Mid(Action.ControlHead, SpeechPos, 1);
02109					alpha = float(Asc(letter) - Asc("A"))/25.0;
02110					bOverrideLookTarget=true;
02111					targetangle.Yaw = MaxHeadAngle.Yaw * (alpha*2-1);
02112					targetangle.Pitch = 0;
02113					targetangle.Roll = 0;
02114				}
02115				SpeechTime = Action.ControlTimeGranularity;
02116				SpeechPos++;
02117			}
02118			else if (ScriptDispatcher(OrderObject) != None)
02119			{
02120				Dispatch = ScriptDispatcher(OrderObject);
02121	
02122				// parse control strings
02123				if (Len(Dispatch.ControlMouth[DispatchAction-1]) > 0)
02124				{
02125					letter = Mid(Dispatch.ControlMouth[DispatchAction-1], SpeechPos, 1);
02126				//slog("Speaking letter"@letter);
02127					alpha = float(Asc(letter) - Asc("A"))/25.0;
02128					OpenMouth(FClamp(alpha, 0, 1), 1.0);
02129				}
02130				if (Len(Dispatch.ControlHead[DispatchAction-1]) > 0)
02131				{
02132					letter = Mid(Dispatch.ControlHead[DispatchAction-1], SpeechPos, 1);
02133					alpha = float(Asc(letter) - Asc("A"))/25.0;
02134					bOverrideLookTarget=true;
02135					targetangle.Yaw = MaxHeadAngle.Yaw * (alpha*2-1);
02136					targetangle.Pitch = 0;
02137					targetangle.Roll = 0;
02138				}
02139				SpeechTime = Dispatch.ControlTimeGranularity;
02140				SpeechPos++;
02141			}
02142		}
02143	
02144		function Timer()
02145		{
02146			if (ScriptAction(OrderObject)!=None && ScriptAction(OrderObject).SoundToPlay != None)
02147				PlaySound(ScriptAction(OrderObject).SoundToPlay, SLOT_None,,true);
02148		}
02149	
02150		function FinishScriptDispatcher()
02151		{
02152			local ScriptDispatcher SD;
02153	
02154			SD = ScriptDispatcher(OrderObject);
02155			if (SD != None)
02156			{
02157				NextState = SD.NextOrder;
02158				NextLabel = '';
02159				NextPoint = ActorTagged( SD.NextOrderTag );
02160				
02161				if (NextState != '')
02162				{
02163					OrderObject = NextPoint;
02164					GotoState(NextState, NextLabel);
02165				}
02166				else
02167				{	// Release to normal AI
02168					bTaskLocked = false;
02169					OrderObject = None;
02170					GotoState('Waiting');
02171				}
02172			}
02173		}
02174	
02175		function ExecuteScriptDispatcherAction(int i)
02176		{
02177			local ScriptDispatcher SD;
02178	
02179			SD = ScriptDispatcher(OrderObject);
02180	
02181	
02182			if (SD.LookTarget[i] != '' ||
02183				SD.Actions[i].EventToFire != '' ||
02184				SD.Actions[i].AnimToPlay != '' ||
02185				SD.Actions[i].SoundToPlay != None)
02186			{
02187				bTaskLocked = SD.Actions[i].bTaskLocked;
02188			}
02189	
02190			if (SD.LookTarget[i] != '')
02191			{	// Look at looktarget
02192				bOverrideLookTarget=false;
02193				LookAt(ActorTagged(SD.LookTarget[i]));
02194				SD.ControlHead[i] = "";
02195			}
02196	
02197			if (SD.ControlTimeGranularity > 0)
02198			{	// Setup for Sync controls
02199				SpeechPos = 0;
02200				SpeechTime = SD.ControlTimeGranularity;
02201				SD.ControlMouth[i] = Caps(SD.ControlMouth[i]);
02202				SD.ControlHead[i] = Caps(SD.ControlHead[i]);
02203			}
02204	
02205			FireEvent(SD.Actions[i].EventToFire);
02206	
02207			if (SD.Actions[i].SoundToPlay != None)
02208				PlaySound(SD.Actions[i].SoundToPlay,SLOT_None,,true);
02209	
02210			if (SD.Actions[i].AnimToPlay != '')
02211			{
02212				LoopAnim(SD.Actions[i].AnimToPlay, 1.0, 0.1);
02213			}
02214		}
02215	
02216	HandleScriptDispatcher:
02217		if (ScriptDispatcher(OrderObject)!=None)
02218		{
02219			if (ScriptDispatcher(OrderObject).bWaitToBeTriggered)
02220			{	// Pend until trigger fires
02221				ScriptDispatcher(OrderObject).WaitingScripter = self;
02222				WaitForRelease();
02223			}
02224	
02225			for (DispatchAction=0; DispatchAction<12; DispatchAction++)
02226			{
02227				if (ScriptDispatcher(OrderObject).Actions[DispatchAction].Delay > 0)
02228					Sleep(ScriptDispatcher(OrderObject).Actions[DispatchAction].Delay);
02229	
02230				ExecuteScriptDispatcherAction(DispatchAction);
02231			}
02232	
02233			FinishScriptDispatcher();
02234		}
02235	
02236		Goto('done');
02237	
02238	HandleScriptAction:
02239		teststring = "HandleScriptAction";
02240	
02241		if (ScriptAction(OrderObject)!=None)
02242		{
02243			//slog(name@"scripting"@OrderObject.tag);
02244	
02245			if (ScriptAction(OrderObject).bWaitToBeTriggered)
02246			{	// Pend until trigger fires
02247				ScriptAction(OrderObject).WaitingScripter = self;
02248				
02249				teststring = "WaitingToBeReleased";
02250				WaitForRelease();
02251				teststring = "Released";
02252			}
02253	
02254			if (ScriptAction(OrderObject).LookTarget != '')
02255			{	// Look at looktarget
02256				bOverrideLookTarget=false;
02257				LookAt(ActorTagged(ScriptAction(OrderObject).LookTarget));
02258				ScriptAction(OrderObject).ControlHead = "";
02259			}
02260	
02261			if (ScriptAction(OrderObject).ControlTimeGranularity > 0)
02262			{	// Setup for Sync controls
02263				SpeechPos = 0;
02264				SpeechTime = ScriptAction(OrderObject).ControlTimeGranularity;
02265				ScriptAction(OrderObject).ControlMouth = Caps(ScriptAction(OrderObject).ControlMouth);
02266				ScriptAction(OrderObject).ControlHead = Caps(ScriptAction(OrderObject).ControlHead);
02267			}
02268	
02269			if (ScriptAction(OrderObject).bTurnToRotation)
02270			{	// Turn to ScriptAction's Rotation
02271			Acceleration=vect(0,0,0);	// jim said they were moving while turning
02272				DesiredRotation = OrderObject.Rotation;
02273				PlayTurning(0.2);
02274				FinishAnim();
02275			}
02276	
02277			if (ScriptAction(OrderObject).bFireEventImmediately)
02278			{	// Fire pre trigger
02279				//slog("triggering"@OrderObject.event);
02280				FireEvent(OrderObject.Event);
02281			}
02282	
02283			// Queue the sound
02284			if (ScriptAction(OrderObject).SoundToPlay != None)
02285				SetTimer(ScriptAction(OrderObject).PauseBeforeSound, false);
02286	
02287			if (ScriptAction(OrderObject).AnimToPlay != '')
02288			{	// Play/Loop the anim
02289				if (ScriptAction(OrderObject).AnimTimeToLoop > 0)
02290				{
02291					LoopAnim(ScriptAction(OrderObject).AnimToPlay, 1.0, 0.1);
02292					Sleep(ScriptAction(OrderObject).AnimTimeToLoop);
02293				}
02294				else
02295				{
02296					PlayAnim(ScriptAction(OrderObject).AnimToPlay, 1.0, 0.1);
02297					FinishAnim();
02298				}
02299			}
02300	
02301			if (!ScriptAction(OrderObject).bFireEventImmediately)
02302			{	// Fire post trigger
02303				//slog("triggering"@OrderObject.event);
02304				FireEvent(OrderObject.Event);
02305			}
02306		}
02307	
02308		ExecuteScriptAction();
02309		Goto('done');
02310	
02311	HandleScriptPoint:
02312		teststring = "HandleScriptPoint";
02313		PlayMoving(0.2);
02314		if (ScriptPoint(OrderObject) != None && ScriptPoint(OrderObject).LookTarget != '')
02315		{	// Look at looktarget
02316			bOverrideLookTarget=false;
02317			LookAt(ActorTagged(ScriptPoint(OrderObject).LookTarget));
02318		}
02319		
02320	ContinueScripting:
02321		if (actorReachable(OrderObject))
02322		{
02323		teststring = "reachable";
02324			MoveToward(OrderObject, MovementSpeed);
02325	
02326			if (ScriptPoint(OrderObject)!=None)
02327			{
02328				if (ScriptPoint(OrderObject).bTurnToRotation)
02329				{	// Turn to ScriptPoint rotation
02330					TweenToTurning(0.2);
02331					FinishAnim();
02332	
02333					DesiredRotation = OrderObject.Rotation;
02334					PlayTurning(0.2);
02335					FinishAnim();
02336				}
02337	
02338				if (ScriptPoint(OrderObject).ArriveAnim != '')
02339				{	// Play anim
02340					if (ScriptPoint(OrderObject).ArrivePause > 0)
02341					{
02342						LoopAnim(ScriptPoint(OrderObject).ArriveAnim, 1.0, 0.1);
02343						Sleep(ScriptPoint(OrderObject).ArrivePause);
02344					}
02345					else
02346					{
02347						PlayAnim(ScriptPoint(OrderObject).ArriveAnim, 1.0, 0.1);
02348						FinishAnim();
02349					}
02350				}
02351			}
02352			ExecuteScriptPoint();
02353		}
02354		else if (FindBestPathToward(OrderObject))
02355		{
02356		teststring = "pathable";
02357			MoveToward(MoveTarget, MovementSpeed);
02358			Goto('ContinueScripting');
02359		}
02360		else
02361		{
02362		teststring = Orderobject@"unpathable";
02363			TweenToWaiting(0.2);
02364			FinishAnim();
02365			PlayWaiting();
02366			Sleep(1);
02367			Goto('HandleScriptPoint');
02368		}
02369		Goto('done');
02370	
02371	Begin:
02372		if (debugstates) slog(name@"Scripting to"@OrderObject.Name);
02373	
02374		if (ScriptPoint(OrderObject) != None)
02375		{
02376			bHurrying = !ScriptPoint(OrderObject).bWalkToThisPoint;
02377			UpdateMovementSpeed();
02378			Goto('HandleScriptPoint');
02379		}
02380		else if (ScriptAction(OrderObject) != None)
02381		{
02382			Goto('HandleScriptAction');
02383		}
02384		else if (ScriptDispatcher(OrderObject) != None)
02385		{
02386			Goto('HandleScriptDispatcher');
02387		}
02388	
02389	Done:
02390	}
02391	
02392	
02393	//================================================
02394	//
02395	// Fleeing
02396	//
02397	//================================================
02398	State Fleeing
02399	{
02400	ignores SeePlayer, HearNoise, EnemyAcquired;
02401	
02402		function BeginState()
02403		{
02404			bHurrying = true;
02405			UpdateMovementSpeed();
02406			FireEvent(Event);
02407			Event = '';
02408		}
02409	
02410		function HitWall(vector HitNormal, actor Wall)
02411		{
02412			global.HitWall(HitNormal, Wall);
02413			GotoState('Cower');
02414		}
02415	
02416		function Landed(vector HitNormal, actor HitActor)
02417		{
02418			global.Landed(HitNormal, HitActor);
02419			GotoState('Fleeing', 'HandleLanded');
02420		}
02421	
02422		function PickDestination()
02423		{
02424			if (Enemy != None)
02425			{
02426				if (VSize(Enemy.Location - Location) > 1000 && !PlayerCanSeeMe())
02427				{
02428					FightOrFlight = 0.0;
02429					GotoState('Waiting');
02430				}
02431				else
02432				{
02433					OrderObject = FindPathAwayFrom(Enemy, LastPointVisited);
02434				}
02435			}
02436			else
02437			{
02438				OrderObject = FindPathAwayFrom(self, LastPointVisited);
02439			}
02440		}
02441	
02442	HandleLanded:
02443		FinishAnim();
02444		Goto('Flee');
02445	
02446	Begin:
02447		if(debugstates) SLog(name@"Fleeing");
02448	
02449	Flee:
02450		TweenToMoving(0.2);
02451		FinishAnim();
02452		PlayMoving();
02453		Goto('Move');
02454	
02455	Move:
02456		PickDestination();
02457	
02458		if (actorReachable(OrderObject))
02459		{
02460			MoveToward(OrderObject, MovementSpeed);
02461			SoundChance(HitSound1, 0.8);
02462			LastPointVisited = CurrentPoint;
02463			CurrentPoint = OrderObject;
02464		}
02465		else if (FindBestPathToward(OrderObject))
02466		{
02467			MoveToward(MoveTarget, MovementSpeed);
02468			SoundChance(HitSound1, 0.6);
02469			LastPointVisited = CurrentPoint;
02470			CurrentPoint = MoveTarget;
02471		}
02472		else if (Enemy != None)
02473		{	// no paths, pick random spot
02474			Destination = Location + Normal(Location-Enemy.Location)*50;
02475			MoveTo(Destination, MovementSpeed);
02476			Goto('Move');
02477		}
02478		
02479		if (FRand() < 0.1)
02480		{
02481			TweenToWaiting(0.2);
02482			FinishAnim();
02483			PlayWaiting();
02484			FinishAnim();
02485			PlayMoving(0.2);
02486		}
02487	
02488		Goto('Move');
02489	}
02490	
02491	
02492	//================================================
02493	//
02494	// Cower
02495	//
02496	//================================================
02497	State Cower
02498	{
02499	Begin:
02500		Acceleration = vect(0,0,0);
02501		PlayCower();
02502	}
02503	
02504	//================================================
02505	//
02506	// GoingHome
02507	//
02508	//================================================
02509	State GoingHome
02510	{
02511		function BeginState()
02512		{
02513			if (bRoamHome)
02514			{
02515				GotoState('Roaming');
02516				return;
02517			}
02518	
02519			bHurrying = false;
02520			UpdateMovementSpeed();
02521		}
02522	
02523	Begin:
02524		if(debugstates) SLog(name@"GoingHome");
02525		
02526	Home:
02527		if (pointReachable(HomeBase))
02528		{	// Directly reachable
02529			PlayMoving();
02530			MoveTo(HomeBase, MovementSpeed);
02531			FinishAnim();
02532	
02533			PlayTurning();
02534			DesiredRotation = HomeRot;
02535			FinishAnim();
02536	
02537			GotoState('Startup', 'Wake');
02538		}
02539		else
02540		{
02541			MoveTarget = FindPathTo(HomeBase);
02542			if (MoveTarget != None)
02543			{	// Pathable
02544				PlayMoving();
02545				MoveToward(MoveTarget, MovementSpeed);
02546				FinishAnim();
02547			}
02548			else
02549			{	// Unreachable
02550				GotoState('Waiting');
02551			}
02552		}
02553		Goto('Home');
02554	}
02555	
02556	
02557	//================================================
02558	//
02559	// Acquisition
02560	//
02561	//================================================
02562	State Acquisition
02563	{
02564	ignores EnemyAcquired, SeePlayer, HearNoise;
02565	
02566		function BeginState()
02567		{
02568			LookAt(Enemy);
02569		}
02570	
02571		function InformTeammates()
02572		{
02573			local ScriptPawn A;
02574			foreach AllActors(class'ScriptPawn', A, TeamTag)
02575			{
02576				if (A.Enemy != Enemy)
02577				{
02578					A.SetEnemy(Enemy);
02579				}
02580			}
02581		}
02582		
02583	Begin:
02584		if(debugstates) slog(name@"Acquiring"@Enemy.Name);
02585	
02586	Acquire:
02587	//	Sleep(RandRange(0.5, 1.0));
02588		SetMovementPhysics();
02589		if (Enemy != None)
02590			LastSeenPos = Enemy.Location;
02591		if ((!bGlider) &&
02592			MaxStopWait > 0)
02593		{
02594			Acceleration = vect(0,0,0);
02595	
02596			// Turn to see your new enemy
02597			if (NeedToTurn(LastSeenPos))
02598			{	
02599				PlayTurning();
02600				TurnTo(LastSeenPos);
02601			}
02602			DesiredRotation = Rotator(LastSeenPos - Location);
02603		}
02604		Goto('SpecialAcquire');
02605	
02606	SpecialAcquire:		// To be overridden
02607		Goto('InformTeam');
02608	
02609	InformTeam:
02610		PlaySound(AcquireSound, SLOT_Misc,,,, 1.0 + FRand()*0.2-0.1);
02611	
02612		if (Enemy != None)
02613			Enemy.MakeNoise(1);
02614	/*	if (bTeamLeader)
02615		{
02616			InformTeammates();
02617		}*/
02618		
02619		GotoState('TacticalDecision');
02620	}
02621	
02622	
02623	//================================================
02624	//
02625	// TacticalDecision
02626	//
02627	// All attack behaviors stem from here
02628	//================================================
02629	State TacticalDecision
02630	{
02631	ignores EnemyAcquired, SeePlayer, HearNoise;
02632	
02633		function BeginState()
02634		{
02635			if (TeamTag == '')
02636			{
02637				TeamTag = name;
02638				bTeamLeader = true;
02639			}
02640		}
02641	
02642		function AssimilateVagrants()
02643		{
02644			local ScriptPawn A, TeamMember;
02645			foreach VisibleActors(class'ScriptPawn', A)
02646			{
02647				if (bWillJoin && A.TeamTag == '')
02648				{
02649					A.TeamTag = TeamTag;
02650					A.bTeamLeader = false;
02651				}
02652			}
02653		}
02654	
02655		function CheckElectLeader()
02656		{
02657			local ScriptPawn A, TeamMember;
02658			foreach AllActors(class'ScriptPawn', A)
02659			{
02660				if (A.TeamTag == TeamTag)
02661				{
02662					TeamMember = A;
02663					if (A.bTeamLeader)
02664					{
02665						return;
02666					}
02667				}
02668			}
02669			TeamMember.bTeamLeader = true;
02670		}
02671	
02672		function bool IAmLeader()
02673		{
02674			return bTeamLeader;
02675		}
02676	
02677		function float Evaluate(Pawn A)
02678		{
02679			local float strength;
02680	
02681			if (A.Health <= 0)
02682				return 0;
02683			strength = 1;
02684			strength += A.Health  * 0.5;
02685			strength += A.Mass    * 0.01;
02686			return strength;
02687		}
02688	
02689		function DecideOrders()
02690		{
02691			local float TeamStrength, EnemyStrength, Advantage;
02692			local float TeamNumbers, EnemyNumbers;
02693			local pawn A;
02694			local ScriptPawn S;
02695	
02696			// Evaluate Team Strength
02697			TeamStrength = 0;
02698			foreach AllActors(class'ScriptPawn', S)
02699			{
02700				if (S.TeamTag != TeamTag)
02701					continue;
02702	
02703				TeamStrength += Evaluate(S);
02704				TeamNumbers += 1;
02705			}
02706	
02707			// Evaluate Enemy Strength
02708			EnemyStrength = 0;
02709			foreach VisibleActors(class'Pawn', A)
02710			{
02711				if (ScriptPawn(A) != None && ScriptPawn(A).TeamTag == TeamTag)
02712					continue;
02713	
02714				EnemyStrength += Evaluate(A);
02715				EnemyNumbers += 1;
02716			}
02717	
02718			// Null out orders of troops
02719			GiveNullOrders();
02720	
02721			Advantage = TeamStrength / EnemyStrength;
02722			if(debugstates) SLog(TeamStrength$"/"$EnemyStrength$":"@Advantage@TeamNumbers);
02723	
02724			if (Advantage < -1 || AttitudeTo(Enemy) == ATTITUDE_Fear)
02725				GiveFleeOrders(TeamNumbers);		// Outmatched, Flee
02726			else
02727				GiveAttackOrders(TeamNumbers);		// Equally matched, offer battle
02728		}
02729	
02730		function GiveNullOrders()
02731		{
02732			local ScriptPawn TeamMember;
02733			
02734			foreach AllActors(class'ScriptPawn', TeamMember)
02735			{
02736				if (TeamMember.TeamTag != TeamTag)
02737					continue;
02738					
02739				TeamMember.AttackOrders = '';
02740			}
02741		}
02742		function GiveAttackOrders(int TeamNumbers)
02743		{
02744			local ScriptPawn TeamMember;
02745	
02746			foreach AllActors(class'ScriptPawn', TeamMember)
02747			{
02748				if (TeamMember.TeamTag != TeamTag)
02749					continue;
02750				if (TeamMember.AttackOrders != '')
02751					continue;
02752	
02753				if ( TeamMember.CanDirectlyThreaten() )
02754					TeamMember.AttackOrders = 'Charging';
02755				else
02756					TeamMember.AttackOrders = 'Hunting';
02757			}
02758		}
02759		function GiveFleeOrders(int TeamNumbers)
02760		{
02761			local ScriptPawn TeamMember;
02762			foreach AllActors(class'ScriptPawn', TeamMember)
02763			{
02764				if (TeamMember.TeamTag != TeamTag)
02765					continue;
02766				TeamMember.AttackOrders = 'Fleeing';
02767			}
02768		}
02769	
02770	Begin:
02771	if(debugstates) slog(name@"Tactical");
02772	
02773	Think:
02774	/*	if ( IAmLeader())
02775		{
02776			if (Intelligence > BRAINS_REPTILE)
02777			{
02778				// If any non-team members of my class around, comandeer them for my team
02779				AssimilateVagrants();
02780				
02781				DecideOrders();
02782			}
02783			else
02784			{	// Stupid creatures blindly attack
02785				GiveAttackOrders(1);
02786			}
02787		}
02788		else	// I am a follower
02789		{
02790			// If there is no leader elect one
02791			CheckElectLeader();
02792		}*/
02793		//Paul: Test attempt at speedup
02794		if ( CanDirectlyThreaten() )
02795			AttackOrders = 'Charging';
02796		else
02797			AttackOrders = 'Hunting';
02798	
02799		if (FRand() > FightOrFlight)
02800			GotoState('Fleeing');
02801	
02802	Wait:
02803		if (AttackOrders == '')
02804		{
02805			Sleep(1);
02806			Goto('Think');
02807		}
02808		GotoState(AttackOrders);
02809	}
02810	
02811	
02812	//================================================
02813	//
02814	// Threatening
02815	//
02816	// Stay within hunt radius and threaten
02817	//================================================
02818	State Threatening
02819	{
02820		ignores SeePlayer, HearNoise, EnemyAcquired;
02821	
02822		function BeginState()
02823		{
02824			bHurrying = true;
02825			UpdateMovementSpeed();
02826			SetTimer(1, true);
02827		}
02828	
02829		function EndState()
02830		{
02831			SetTimer(0, false);
02832		}
02833	
02834		function Timer()
02835		{
02836			if (FRand() < 0.1)
02837				PlayThreatening(0.1);
02838		}
02839	
02840		function EnemyNotVisible()
02841		{
02842			GotoState('GoingHome');
02843		}
02844	
02845		function bool PickDestination()
02846		{
02847			if (HuntDistance == 0)
02848				return false;
02849	
02850			if (VSize(Enemy.Location-HomeBase) < HuntDistance &&
02851				(VSize(Location-HomeBase) < HuntDistance))
02852			{
02853				GotoState('Charging');
02854			}
02855			else
02856			{
02857				Destination = HomeBase + Normal(Enemy.Location - HomeBase) * (HuntDistance);
02858	
02859				if (VSize(Destination - Location) < 50)
02860				{	// Don't do small moves
02861					return false;
02862				}
02863			}
02864			return true;
02865		}
02866	
02867	Begin:
02868		if(debugstates) SLog(name@"Threatening");
02869		Acceleration = vect(0,0,0);
02870	
02871		if ( !ValidEnemy() )
02872			GotoState('GoingHome');
02873	
02874	StayInRadius:
02875		if (PickDestination())
02876		{
02877			PlayMoving(0.1);
02878			MoveTo(Destination, MovementSpeed);
02879			PlayWaiting(0.1);
02880	
02881			// turn to face
02882			if (NeedToTurn(Enemy.Location))
02883			{	
02884				PlayTurning();
02885				TurnTo(Enemy.Location);
02886			}
02887		}
02888		else
02889		{
02890			PlayWaiting(0.1);
02891		}
02892	
02893		Sleep(1);
02894		Goto('StayInRadius');
02895	}
02896	
02897	
02898	//================================================
02899	//
02900	// StakeOut
02901	//
02902	//================================================
02903	state StakeOut
02904	{
02905		ignores SeePlayer, HearNoise, EnemyAcquired;
02906	
02907		function BeginState()
02908		{
02909			bHurrying = true;
02910			UpdateMovementSpeed();
02911			SetTimer(1.0+FRand(), true);
02912			LastPathTime = Level.TimeSeconds;
02913		}
02914	
02915		function EndState()
02916		{
02917			SetTimer(0, false);
02918		}
02919	
02920		function Timer()
02921		{
02922			CheckReachable();
02923		}
02924	
02925		function CheckReachable()
02926		{
02927			if (Level.TimeSeconds - LastPathTime < 1)
02928				return;
02929			if (Enemy == None)
02930				return;
02931	
02932			if (HuntDistance > 0 && VSize(HomeBase-Enemy.Location)>HuntDistance)
02933			{	// Disallow if seen is outside hunt radius
02934				teststring2 = Enemy.name @"is outside huntdistance";
02935				return;
02936			}
02937	
02938			if (actorReachable(Enemy))
02939			{
02940				teststring2 = Enemy.name @"is actorreachable";
02941				GotoState('Charging');
02942				return;
02943			}
02944	
02945			LastPathTime = Level.TimeSeconds;
02946	
02947			if ( FindBestPathToward(Enemy) )
02948			{
02949				if (HuntDistance > 0 && VSize(HomeBase-Destination)>HuntDistance)
02950				{	// Disallow if path to seen is outside hunt radius
02951					teststring2 = "path is outside huntdistance";
02952					return;
02953				}
02954	
02955				teststring2 = Enemy.name @"is pathable - hunting";
02956				GotoState('Charging');
02957				return;
02958			}
02959	
02960			teststring2 = Enemy.name @"unpathable";
02961		}
02962	
02963	Begin:
02964		if(debugstates) SLog(name@"Stakeout");
02965		Acceleration = vect(0,0,0);
02966		SetPhysics(PHYS_Falling);	// So they don't just sit in the air
02967	
02968	Stay:
02969		if ( !ValidEnemy() )
02970			GotoState('GoingHome');
02971	
02972		PlayWaiting(0.1);
02973		Sleep(2);
02974		Goto('Stay');
02975	}
02976	
02977	
02978	//================================================
02979	//
02980	// Hunting
02981	//
02982	//================================================
02983	State Hunting
02984	{
02985		function BeginState()
02986		{
02987			bHurrying = true;
02988			UpdateMovementSpeed();
02989			bAvoidLedges = true;
02990		}
02991	
02992		function EndState()
02993		{
02994			bHunting = false;
02995			bAvoidLedges = false;
02996			if ( JumpZ > 0 )
02997				bCanJump = true;
02998		}
02999	
03000		function HearNoise(float Loudness, Actor NoiseMaker)
03001		{
03002			if ( SetEnemy(NoiseMaker.instigator) )
03003				LastSeenPos = Enemy.Location; 
03004		}
03005	
03006		function TryChargeEnemy()
03007		{
03008			DesiredRotation = Rotator(Enemy.Location - Location);
03009			if (actorReachable(Enemy))
03010				GotoState('Charging');
03011		}
03012	
03013		function HitWall(vector HitNormal, actor Wall)
03014		{
03015			global.HitWall(HitNormal, Wall);
03016			if (Physics == PHYS_Falling)
03017				return;
03018			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
03019			{
03020				GotoState('Hunting', 'SpecialNavig');
03021				return;
03022			}
03023			Focus = Destination;
03024			if (PickWallAdjust())
03025				GotoState('Hunting', 'AdjustFromWall');
03026			else
03027				MoveTimer = -1.0;
03028		}
03029	
03030		function MayFall()
03031		{	// Only jump if reachable
03032			if (intelligence==BRAINS_None)
03033				bCanJump = true;
03034			else if (MoveTarget!=None)
03035				bCanJump = actorReachable(MoveTarget);
03036			else if (Enemy!=None)
03037				bCanJump = actorReachable(Enemy);
03038	//		if (bFrustrated)
03039	//			bCanJump = true;
03040		}
03041	
03042		function Landed(vector HitNormal, actor HitActor)
03043		{
03044			Super.Landed(HitNormal, HitActor);
03045		}
03046	
03047		function PickDestination()
03048		{
03049			local NavigationPoint path;
03050			local actor HitActor;
03051			local vector HitNormal, HitLocation, nextSpot, ViewSpot;
03052			local float posZ, elapsed;
03053			local bool bCanSeeLastSeen;
03054	
03055			//TEMP: Everything hunts forever
03056			HuntTime = 9999;
03057	
03058			// If no enemy, or I should see him but don't, then give up		
03059			if ( !ValidEnemy() )
03060			{
03061				GotoState('GoingHome');
03062				return;
03063			}
03064	
03065			bAvoidLedges = false;
03066			elapsed = Level.TimeSeconds - HuntStartTime;
03067			if ( elapsed > HuntTime )
03068			{	// Don't hunt too long
03069				GotoState('GoingHome');
03070				return;
03071			}
03072	
03073			// Don't stray too far from home
03074			if ( HuntDistance > 0 && VSize(HomeBase-Location)>HuntDistance)
03075			{	// If outside of hunt radius, return home
03076				GotoState('GoingHome');
03077				return;
03078			}
03079	
03080	//		if ( HuntDistance > 0 && VSize(HomeBase-Enemy.Location) > HuntDistance)
03081	//		{	// If enemy is outside of hunt radius
03082	//			GotoState('Stakeout');
03083	//			return;
03084	//		}
03085	
03086			numHuntPaths++;
03087			if ( JumpZ > 0 )
03088				bCanJump = true;
03089	
03090			// Enemy is directly reachable
03091			if ( ActorReachable(Enemy) )
03092			{
03093				teststring = "enemy is directly reachable";
03094				Destination = Enemy.Location;
03095				MoveTarget = None;
03096				return;
03097			}
03098	
03099			// Try to find a path toward enemy
03100			ViewSpot = Location + EyeHeight * vect(0,0,1);
03101			bCanSeeLastSeen = false;
03102			if ( intelligence > BRAINS_Reptile )
03103			{
03104				HitActor = Trace(HitLocation, HitNormal, LastSeenPos, ViewSpot, false);
03105				bCanSeeLastSeen = (HitActor == None);
03106				if ( bCanSeeLastSeen )
03107				{
03108					HitActor = Trace(HitLocation, HitNormal, LastSeenPos, Enemy.Location, false);
03109					bHunting = (HitActor != None);
03110				}
03111				else
03112					bHunting = true;
03113				if ( FindBestPathToward(Enemy) )
03114				{
03115					teststring = "found a path toward";
03116					return;
03117				}
03118			}
03119	
03120			// If hit a wall, adjust
03121			MoveTarget = None;
03122			if ( bFromWall )
03123			{
03124				teststring = "From Wall";
03125				bFromWall = false;
03126				if ( !PickWallAdjust() )
03127				{	// Stuck on wall?
03128					teststring = "Couldnt wall adjust";
03129					MoveTarget = FindRandomDest();
03130					if (MoveTarget != None)
03131						Destination = MoveTarget.Location;
03132				}
03133				return;
03134			}
03135	
03136			// Resort to Last Seen Position
03137			bFrustrated = true;
03138			teststring = "resorting to last seen";
03139			bAvoidLedges = ( (CollisionRadius > 42) && (Intelligence < BRAINS_Human) );
03140			posZ = LastSeenPos.Z + CollisionHeight - Enemy.CollisionHeight;
03141			nextSpot = LastSeenPos - Normal(Enemy.Location - Enemy.OldLocation) * CollisionRadius;
03142			nextSpot.Z = posZ;
03143			HitActor = Trace(HitLocation, HitNormal, nextSpot , ViewSpot, false);
03144			if ( HitActor == None )
03145				Destination = nextSpot;
03146			else if ( bCanSeeLastSeen )
03147				Destination = LastSeenPos;
03148			else
03149			{
03150				Destination = LastSeenPos;
03151				HitActor = Trace(HitLocation, HitNormal, LastSeenPos , ViewSpot, false);
03152				if ( HitActor != None )
03153				{
03154					// check if could adjust and see it
03155					if ( PickWallAdjust() || FindViewSpot() )
03156						GotoState('Hunting', 'AdjustFromWall');
03157					else
03158					{	// Stuck on wall?
03159						teststring = "Could not wall adjust";
03160						MoveTarget = FindRandomDest();
03161						if (MoveTarget != None)
03162							Destination = MoveTarget.Location;
03163						return;
03164					}
03165				}
03166			}
03167			LastSeenPos = Enemy.Location;
03168		}	
03169	
03170		function bool FindViewSpot()
03171		{
03172			local vector X,Y,Z, HitLocation, HitNormal;
03173			local actor HitActor;
03174			local bool bAlwaysTry;
03175			GetAxes(Rotation,X,Y,Z);
03176	
03177			// try left and right
03178			// if frustrated, always move if possible
03179			bAlwaysTry = bFrustrated;
03180			bFrustrated = false;
03181			
03182			HitActor = Trace(HitLocation, HitNormal, Enemy.Location, Location + 2 * Y * CollisionRadius, false);
03183			if ( HitActor == None )
03184			{
03185				Destination = Location + 2.5 * Y * CollisionRadius;
03186				return true;
03187			}
03188	
03189			HitActor = Trace(HitLocation, HitNormal, Enemy.Location, Location - 2 * Y * CollisionRadius, false);
03190			if ( HitActor == None )
03191			{
03192				Destination = Location - 2.5 * Y * CollisionRadius;
03193				return true;
03194			}
03195			if ( bAlwaysTry )
03196			{
03197				if ( FRand() < 0.5 )
03198					Destination = Location - 2.5 * Y * CollisionRadius;
03199				else
03200					Destination = Location - 2.5 * Y * CollisionRadius;
03201				return true;
03202			}
03203	
03204			return false;
03205		}
03206	
03207		function LookAtEndPoint(int index)
03208		{
03209			local actor endpoint;
03210			endpoint = NavigationPoint(MoveTarget).PathEndPoint(index);
03211			LookToward(endpoint.Location);
03212		}
03213	
03214	AdjustFromWall:
03215		PlayMoving();	//todo: determine direction and play strafing
03216		StrafeTo(Destination, Focus);
03217		Destination = Focus;
03218		if ( MoveTarget != None )
03219			Goto('SpecialNavig');
03220		else
03221			Goto('Follow');
03222	
03223	Begin:
03224		if(debugstates) SLog(name@"Hunting");
03225		Acceleration=vect(0,0,0);
03226	
03227	Hunt:
03228	
03229		numHuntPaths = 0;
03230		HuntStartTime = Level.TimeSeconds;
03231	
03232	AfterFall:
03233		TweenToMoving(0.15);
03234		bFromWall = false;
03235		FinishAnim();
03236	
03237	Follow:
03238		WaitForLanding();
03239		if ( CanSee(Enemy) )
03240			TryChargeEnemy();	// Exit to charge
03241		PickDestination();
03242	
03243		if (!bMoveWhenUnreachable)
03244		{
03245			if (MoveTarget == None)
03246			{
03247				if (!pointReachable(Destination))
03248				{
03249					teststring2 = string(destination)@"failed pointReachable";
03250					GotoState('StakeOut');
03251				}
03252			}
03253			else
03254			{
03255				if (!actorReachable(MoveTarget))
03256				{
03257					teststring2 = MoveTarget.Name@"failed actorReachable";
03258					GotoState('StakeOut');
03259				}
03260			}
03261		}
03262	
03263		LookToward(Destination);
03264	
03265		// Turn to Destination
03266		if (AngleTo(Destination) > ANGLE_45)
03267		{
03268			DesiredRotation.Yaw = Rotator(Destination - Location).Yaw;
03269			PlayTurning();
03270			FinishAnim();
03271		}
03272	
03273		if (HuntDistance>0 && VSize(HomeBase-Destination)>HuntDistance)
03274		{	// Destination outside hunt radius
03275			GotoState('Stakeout');
03276		}
03277	
03278		TweenToMoving(0.15);
03279		WaitForLanding();
03280		FinishAnim();
03281		PlayMoving();
03282		StopLookingToward();
03283	
03284	SpecialNavig:
03285		if (MoveTarget == None)
03286		{
03287			MoveTo(Destination, MovementSpeed);
03288	//		SoundChance(HuntSound, 1.0);
03289		}
03290		else
03291		{
03292			MoveToward(MoveTarget, MovementSpeed);
03293			
03294			// Look down some paths if not a straight shot
03295			if ((!bGlider) &&
03296				MaxStopWait > 0 &&
03297				NavigationPoint(MoveTarget) != None &&
03298				NavigationPoint(MoveTarget).NumPaths() > 2 &&
03299				NavigationPoint(MoveTarget) != LastNodeVisited &&
03300				FRand() > 0.8)
03301			{
03302				TweenToHuntStop(0.3);
03303				Acceleration = vect(0,0,0);
03304				FinishAnim();
03305			
03306				lookindex = 0;
03307				while (lookIndex < NavigationPoint(MoveTarget).NumPaths())
03308				{
03309					PlayHuntStop();
03310					SoundChance(RoamSound, 0.3);
03311					LookAtEndPoint(lookIndex);
03312					lookIndex++;
03313					Sleep(RandRange(MinStopWait, MaxStopWait));
03314					FinishAnim();
03315				}
03316			}
03317			
03318			LastNodeVisited	= NavigationPoint(MoveTarget);
03319		}
03320		
03321	/*	if ( Intelligence < BRAINS_Human )
03322		{
03323			if (!SoundChance(RoamSound, 0.3))
03324				SoundChance(ThreatenSound, 0.7);
03325		}*/
03326		Goto('Follow');
03327	}
03328	
03329	
03330	//================================================
03331	//
03332	// Charging
03333	//
03334	//================================================
03335	State Charging
03336	{
03337	ignores EnemyAcquired, SeePlayer, HearNoise;
03338	
03339		function BeginState()
03340		{
03341			StopLookingToward();
03342			LookAt(Enemy);
03343			SetTimer(0.1, true);
03344			bHurrying = true;
03345			UpdateMovementSpeed();
03346		}
03347		
03348		function EndState()
03349		{
03350			LookAt(None);
03351			SetTimer(0, false);
03352			if ( JumpZ > 0 )		// This may have been set false in Mayfall, so reset
03353				bCanJump = true;
03354		}
03355	
03356		function Timer()
03357		{
03358			if ( !ValidEnemy() )
03359				GotoState('GoingHome');
03360			else if ( Enemy.Region.Zone.bWaterZone && !bCanSwim)
03361			{	// Enemy entered water zone and I can't
03362				GotoState('TacticalDecision');
03363			}
03364			else if (!Enemy.Region.Zone.bWaterZone && !bCanFly && !bCanWalk)
03365			{	// Enemy left water zone and I can't
03366				GotoState('GoingHome');
03367			}
03368			else
03369			{
03370				if ((Enemy.bSwingingHigh || Enemy.bSwingingLow) && InMeleeRange(Enemy))
03371					GotoState('Fighting', 'CheckDefend');
03372				else if (InAttackRange(Enemy))
03373					GotoState('Fighting');
03374			}
03375		}
03376	
03377		function EnemyNotVisible()
03378		{
03379			GotoState('Hunting');
03380		}
03381		
03382		function HitWall(vector HitNormal, actor Wall)
03383		{
03384			global.HitWall(HitNormal, Wall);
03385	
03386			if (Physics == PHYS_Falling)
03387				return;
03388	
03389			//TODO: Test whether climbable, then set falling
03390			if (bCanGrabEdges && bCanStrafe)
03391			{
03392				SetPhysics(PHYS_Falling);
03393				return;
03394			}
03395			
03396			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
03397			{
03398				if ( SpecialPause > 0 )
03399					Acceleration = vect(0,0,0);
03400				GotoState('Charging', 'SpecialNavig');
03401				return;
03402			}
03403			Focus = Destination;
03404			if (PickWallAdjust())
03405				GotoState('Charging', 'AdjustFromWall');
03406			else
03407				MoveTimer = -1.0;
03408		}
03409	
03410		function vector CheckDestination(vector loc)
03411		{
03412			if (VSize(loc - HomeBase) > HuntDistance)
03413			{	// Destination is outside radius
03414				return (Normal(loc-HomeBase)*(HuntDistance*0.95));
03415			}
03416			// Don't stray too far from home when charging
03417			if ( HuntDistance > 0 && VSize(HomeBase-Location)>HuntDistance)
03418			{	// Outside hunt radius
03419				GotoState('Threatening');
03420			}
03421			return loc;
03422		}
03423	
03424	
03425	AdjustFromWall:
03426		StrafeTo(Destination, Focus); 
03427		Goto('CheckEnemy');
03428	
03429	ResumeFromFighting:
03430		Timer();
03431	Begin:
03432		if(debugstates) slog(name@"Charging");
03433		Goto('Threaten');
03434	
03435	Threaten:	// Override in classes that threaten
03436		Enemy=Enemy;
03437		Goto('TweenIn');
03438	
03439	TweenIn:
03440		TweenToMoving(0.15);
03441		FinishAnim();
03442		PlayMoving();
03443	
03444	Charge:
03445		MoveTimer = 0.0;
03446		bFromWall = false;
03447		if ( JumpZ > 0 )		// This may have been set false in Mayfall, so reset
03448			bCanJump = true;
03449	
03450	CheckEnemy:
03451		// Check enemy for exit conditions
03452		Timer();
03453		
03454	CloseIn:
03455		if (Physics == PHYS_Falling)
03456		{	// If falling, wait for land
03457			if (NeedToTurn(Enemy.Location))
03458			{
03459				DesiredRotation = Rotator(Enemy.Location - Location);
03460				Focus = Enemy.Location;
03461				Destination = Enemy.Location;
03462			}
03463			WaitForLanding();
03464		}
03465	
03466		if( actorReachable(Enemy) )
03467		{
03468			// Handle HuntDistance
03469			if (HuntDistance > 0 && VSize(Enemy.Location - HomeBase) > HuntDistance)
03470			{	// Destination is outside radius
03471				Destination = HomeBase + (Normal(Enemy.Location-HomeBase)*(HuntDistance*0.95));
03472				if (VSize(Destination-Location) > 50)
03473				{	// Get as close as possible while still within radius
03474					PlayMoving(0.1);
03475					if (bCanStrafe)
03476						StrafeFacing(Destination, Enemy);
03477					else
03478						MoveTo(Destination, MovementSpeed);
03479	
03480					if (NeedToTurn(Enemy.Location))
03481					{
03482						PlayTurning(0.1);
03483						TurnTo(Enemy.Location);
03484					}
03485					PlayWaiting(0.1);
03486					Sleep(0.5);
03487					Acceleration=vect(0,0,0);
03488				}
03489				else
03490				{	// Small move, just wait
03491					Acceleration=vect(0,0,0);
03492					PlayWaiting(0.1);
03493					Sleep(0.5);
03494				}
03495			}
03496			else if ( HuntDistance > 0 && VSize(HomeBase-Location)>HuntDistance)
03497			{	// Already Outside hunt radius
03498				Destination = HomeBase + (Normal(Enemy.Location-HomeBase)*(HuntDistance*0.95));
03499				if (VSize(Destination-Location) > 50)
03500				{	// Get as close as possible while still within radius
03501					PlayMoving(0.1);
03502					if (bCanStrafe)
03503						StrafeFacing(Destination, Enemy);
03504					else
03505						MoveTo(Destination, MovementSpeed);
03506					if (NeedToTurn(Enemy.Location))
03507					{
03508						PlayTurning(0.1);
03509						TurnTo(Enemy.Location);
03510					}
03511					PlayWaiting(0.1);
03512					Sleep(0.5);
03513					Acceleration=vect(0,0,0);
03514				}
03515				else
03516				{	// Small move, just wait
03517					Acceleration=vect(0,0,0);
03518					PlayWaiting(0.1);
03519					Sleep(0.5);
03520				}
03521			}
03522			else
03523			{	// Normal Charge
03524				if (HuntDistance > 0)	// could have been reset to waiting
03525					PlayMoving(0.1);
03526	
03527				MoveToward(Enemy, MovementSpeed);
03528				if (bFromWall)
03529				{
03530					bFromWall = false;
03531					if (PickWallAdjust())
03532						StrafeFacing(Destination, Enemy);
03533					else
03534						GotoState('TacticalDecision');
03535				}
03536			}
03537		}
03538		else
03539		{
03540			bCanSwing = false;
03541			bFromWall = false;
03542			if (!FindBestPathToward(Enemy))
03543			{	// unreachable and unpathable
03544				GotoState('Hunting');
03545			}
03546	
03547			if ( HuntDistance > 0)
03548			{
03549				if (VSize(HomeBase-Location)>HuntDistance)
03550				{	// Outside hunt radius
03551					GotoState('StakeOut');
03552				}
03553				else if (VSize(HomeBase-MoveTarget.Location)>HuntDistance)
03554				{	// Destination outside hunt radius
03555					GotoState('StakeOut');
03556				}
03557			}
03558	
03559			PlayMoving(0.1);
03560			if (VSize(MoveTarget.Location - Location) < 2.5 * CollisionRadius)
03561			{
03562				bCanSwing = true;
03563				StrafeFacing(MoveTarget.Location, Enemy);
03564			}
03565			else
03566			{
03567				if ( !bCanStrafe || !LineOfSightTo(Enemy) ||
03568					(Skill - 2 * FRand() + (Normal(Enemy.Location - Location - vect(0,0,1) * (Enemy.Location.Z - Location.Z)) 
03569						Dot Normal(MoveTarget.Location - Location - vect(0,0,1) * (MoveTarget.Location.Z - Location.Z))) < 0) )
03570				{
03571					MoveToward(MoveTarget, MovementSpeed);
03572				}
03573				else
03574				{
03575					bCanSwing = true;
03576					StrafeFacing(MoveTarget.Location, Enemy);	
03577				}
03578	//			if ( !bFromWall )
03579	//				SoundChance(ThreatenSound, 0.8);
03580			}
03581		}
03582		Goto('Charge');
03583	}
03584	
03585	
03586	//================================================
03587	//
03588	// Fighting
03589	//
03590	// Override in children
03591	//================================================
03592	State Fighting
03593	{
03594	ignores EnemyAcquired;
03595	
03596		function BeginState()
03597		{
03598			LookAt(Enemy);
03599			bHurrying = true;
03600			UpdateMovementSpeed();
03601		}
03602	
03603		function EndState()
03604		{
03605			bSwingingHigh = false;
03606			if (Weapon!=None)
03607				Weapon.FinishAttack();
03608			LookAt(None);
03609		}
03610	
03611		function AmbientSoundTimer()
03612		{
03613			PlayAmbientFightSound();
03614		}
03615	
03616	Begin:
03617		if(debugstates) SLog(name@"Fighting");
03618		Acceleration = vect(0,0,0);
03619		GetEnemyProximity();
03620	
03621		// Turn to face enemy
03622		DesiredRotation.Yaw = rotator(Enemy.Location-Location).Yaw;
03623	
03624	Fight:
03625		bSwingingHigh = true;
03626		PlayMeleeHigh(0.1);
03627		FinishAnim();
03628		bSwingingHigh = false;
03629		Sleep(TimeBetweenAttacks);
03630	
03631		GotoState('Charging', 'ResumeFromFighting');
03632	
03633	CheckDefend:
03634		GotoState('Charging', 'ResumeFromFighting');
03635	}
03636	
03637	
03638	
03639	
03640	//==================================================================
03641	//
03642	// Sub-states and Misc states
03643	//
03644	// require NextState to be set
03645	//==================================================================
03646	
03647	
03648	//============================================================
03649	//
03650	// Drowning
03651	//
03652	//============================================================
03653	state Drowning
03654	{
03655	ignores SeePlayer, EnemyNotVisible, HearNoise, KilledBy, Trigger, Bump, HitWall, FootZoneChange, ZoneChange, Falling, WarnTarget, LongFall, Landed, EnemyAcquired, PlayTakeHit;
03656	
03657		function BeginState()
03658		{
03659			Buoyancy = 0.98 * Mass;
03660			DropWeapon();
03661			DropShield();
03662		}
03663	
03664		function EndState()
03665		{
03666			Buoyancy = 0.75 * Mass;
03667			SetPhysics(PHYS_Falling);
03668			if (Health <= 0)
03669			{
03670				Breath();
03671				Breath();
03672				Breath();
03673			}
03674		}
03675	
03676		function PainTimer()
03677		{
03678			Super.PainTimer();
03679			Breath();
03680		}
03681	
03682		function HeadZoneChange(ZoneInfo newHeadZone)
03683		{
03684			if (!newHeadZone.bWaterZone)
03685			{
03686				GotoState('Waiting');
03687			}
03688		}
03689	
03690	Begin:
03691		Acceleration = vect(0,0,0);
03692		PlayDrowning(0.1);
03693	}
03694	
03695	
03696	//============================================================
03697	//
03698	// Dying
03699	//
03700	//============================================================
03701	state Dying
03702	{
03703	ignores SeePlayer, EnemyNotVisible, HearNoise, KilledBy, Trigger, Bump, HitWall, HeadZoneChange, FootZoneChange, ZoneChange, Falling, WarnTarget, Died, LongFall, PainTimer, Landed, EnemyAcquired, CheckForEnemies;
03704	
03705		function SpawnBloodSplot()
03706		{
03707			local vector headLoc;
03708			local vector start, end;
03709			local vector loc, norm;
03710			
03711			//	if (BodyPartMissing(BODYPART_HEAD))
03712			if (!HeadRegion.Zone.bWaterZone)
03713			{
03714				headLoc = GetJointPos(JointNamed('head'));
03715				start = headLoc;
03716				end = headLoc - vect(0, 0, 50);
03717				if(Trace(loc, norm, end, start) != None)
03718				{
03719		    		loc = loc + norm * FRand(); // Pull the decal a bit out from the surface
03720					Spawn(class'DecalBlood4',,,loc, rotator(-norm));
03721				}
03722			}
03723		}
03724	
03725		function AmbientSoundTimer()
03726		{
03727			// Don't reset timer
03728		}
03729	
03730		function Timer()
03731		{
03732			Super.Timer();
03733			SpawnBloodSplot();
03734		}
03735	}
03736	
03737	
03738	
03739	//================================================
03740	//
03741	// FallingState
03742	//
03743	//================================================
03744	State FallingState
03745	{
03746	//	ignores Landed, SeePlayer, EnemyNotVisible, HearNoise, KilledBy, Trigger, Bump, HitWall, HeadZoneChange, FootZoneChange, ZoneChange, Falling, WarnTarget, PainTimer, SetFall;
03747		ignores Landed, SeePlayer, EnemyNotVisible, HearNoise, KilledBy, Trigger, Bump, HitWall, Falling, WarnTarget, SetFall;
03748	
03749		function BeginState()
03750		{
03751			MoveTarget = None;
03752		}
03753	
03754		function bool CanGotoPainState()
03755		{ // Do not allow the creature to enter the painstate when falling
03756			return(false);
03757		}
03758	
03759		event PainTimer()
03760		{
03761			// Pain timer just expired:
03762			//  Check what zone I'm in (and which parts are)
03763			//  based on that cause damage, and reset PainTime
03764	
03765			if((Health < 0) || (Level.NetMode == NM_Client))
03766				return;
03767				
03768			if(FootRegion.Zone.bPainZone)
03769			{
03770				PainTime = 1.0;
03771			}
03772			else if ( HeadRegion.Zone.bWaterZone )
03773			{
03774				PainTime = 2.0;
03775			}
03776		}
03777	
03778		function AnimEnd()
03779		{
03780			if(AnimSequence == A_PullUp)
03781			{
03782				PlayStepUp();
03783			}
03784			else if(AnimSequence == A_StepUp)
03785			{		
03786				PlayWaiting();
03787				SetPhysics(PHYS_Falling);
03788				GotoState(NextState);
03789			}
03790		}
03791	
03792		function bool GrabEdge(float grabDistance, vector grabNormal)
03793		{
03794			local float dist;
03795	
03796			Velocity = vect(0, 0, 0);
03797			Acceleration = vect(0, 0, 0);
03798	
03799			GrabLocationUp.X = Location.X;
03800			GrabLocationUp.Y = Location.Y;
03801			GrabLocationUp.Z = Location.Z + grabDistance + 2;
03802		
03803			GrabLocationIn.X = Location.X + grabNormal.X * (CollisionRadius + 5);
03804			GrabLocationIn.Y = Location.Y + grabNormal.Y * (CollisionRadius + 5);
03805			GrabLocationIn.Z = GrabLocationUp.Z + CollisionHeight + 4;
03806	
03807	
03808			dist = GrabLocationUp.Z - Location.Z;
03809			SetLocation(GrabLocationIn);
03810	
03811			if(dist > 20 && A_PullUp != 'None') 
03812			{
03813				PlayPullup(0.0); // No tween
03814			}
03815			else if(A_StepUp != 'None')
03816			{
03817				PlayStepup(0.0); // No tween
03818			}
03819			
03820	//		GotoState('FallingState', 'EdgeHanging');
03821	
03822			return(true);
03823		}
03824	
03825		//choose a jump velocity
03826		function adjustJump()
03827		{
03828			local float velZ;
03829			local vector FullVel;
03830			local vector LedgeDown, HitLoc, HitNorm;
03831			local actor A;
03832	
03833			velZ = Velocity.Z;
03834			FullVel = Normal(Velocity) * GroundSpeed;
03835	
03836			// Trace down to see if we can jump step down to floor
03837			LedgeDown = vect(0,0,0);
03838			LedgeDown.Z = -(CollisionHeight + 100);
03839			A = Trace(HitLoc, HitNorm, Location+LedgeDown, Location, true);
03840			if (A != None)
03841			{	// There's something to land on, don't jump
03842				Destination = Location + vector(rotation)*CollisionRadius;
03843				GotoState('FallingState', 'WalkOverEdge');
03844				return;
03845			}
03846	
03847			// If far above destination
03848			If (Location.Z > Destination.Z + CollisionHeight + 2 * MaxStepHeight)
03849			{
03850				Velocity = FullVel;
03851				Velocity.Z = velZ;
03852				Velocity = EAdjustJump();
03853				Velocity.Z = 0;
03854				if ( VSize(Velocity) < 0.9 * GroundSpeed )
03855				{
03856					Velocity.Z = velZ;
03857					return;
03858				}
03859			}
03860	
03861			Velocity = FullVel;
03862			Velocity.Z = JumpZ + velZ;
03863			Velocity = EAdjustJump();
03864		}
03865	
03866		function DoLanded(vector HitNormal)
03867		{
03868			bJustLanded = true;
03869			PlayLanded(Velocity.Z);
03870			if (Velocity.Z < -1.4 * JumpZ || bUpAndOut)
03871			{
03872				MakeNoise(-0.5 * Velocity.Z/(FMax(JumpZ, 150.0)));
03873	
03874				// Falling damage
03875				if (Velocity.Z <= -1100)
03876				{
03877					if ( (Velocity.Z < -2000) && (ReducedDamageType != 'All') )
03878						JointDamaged(1000, None, Location, vect(0,0,0), 'fell', 0);
03879					else if ( Role == ROLE_Authority )
03880						JointDamaged(-0.15 * (Velocity.Z + 1050), None, Location, vect(0,0,0), 'fell', 0);
03881				}
03882			}
03883			else if ( Velocity.Z < -0.8 * JumpZ )
03884			{
03885				PlayLanded(Velocity.Z);
03886				GotoState('FallingState', 'FastLanded');
03887			}
03888		}
03889		
03890		function EnemyAcquired()
03891		{
03892			NextState = 'Acquisition';
03893			NextLabel = 'Begin';
03894		}
03895	
03896	/* Obsolete, as pulling up is now handed with two animations, and using AnimEnd -- cjr
03897	EdgeHanging:
03898		teststring = "Edge Hanging";
03899	
03900		if(GrabLocationUp.Z - Location.Z > 20)
03901		{
03902	//		PlaySound(EdgeGrabSound, SLOT_Talk, 1.0, false, 1200, FRand() * 0.4 + 0.8);
03903			SetLocation(GrabLocationIn);
03904			PlayPullup(0.0); // No tween
03905			FinishAnim();
03906	
03907	//		PlaySound(StepupSound, SLOT_Talk, 1.0, false, 1200, FRand() * 0.4 + 0.8);
03908	
03909			PlayStepup(0.1);
03910			FinishAnim();
03911		}
03912		else
03913		{
03914			SetLocation(GrabLocationIn);
03915	
03916	//		PlaySound(StepupSound, SLOT_Talk, 1.0, false, 1200, FRand() * 0.4 + 0.8);
03917	
03918			PlayStepup(0.0); // No tween
03919			FinishAnim();	
03920		}
03921	
03922		PlayWaiting(); // Necessary?
03923		AirSpeed = Default.AirSpeed;
03924	
03925		SetPhysics(PHYS_Falling);
03926		WaitForLanding();
03927		GotoState(NextState);
03928	*/
03929	
03930	WalkOverEdge:
03931		teststring = "WalkOverEdge";
03932		AirSpeed = 225;
03933		SetPhysics(PHYS_Flying);
03934		MoveTo(Destination);
03935		AirSpeed = Default.AirSpeed;
03936		SetPhysics(PHYS_Falling);
03937		Goto('Falling');
03938		
03939	FastLanded:
03940		teststring = "FastLanded";
03941		Velocity=vect(0,0,0);
03942	//	FinishAnim();
03943		Goto('Done');
03944	
03945	Done:
03946		GotoState(NextState, NextLabel);
03947	
03948	Begin:
03949		if(debugstates) SLog(name@"Falling"@"NextState="$NextState);
03950		teststring = "Begin";
03951		if (Region.Zone.bWaterZone)
03952		{
03953			if (bCanSwim)
03954				SetPhysics(PHYS_Swimming);
03955			Goto('Done');
03956		}
03957		AdjustJump();
03958	
03959	Falling:
03960		teststring = "Falling";
03961		if (Physics != PHYS_Falling)
03962			Goto('Done');
03963		WaitForLanding();
03964		DoLanded(vect(0,0,1));
03965	
03966	Landed:
03967		teststring = "Landed";
03968		if ( !bIsPlayer ) //bots act like players
03969			Acceleration = vect(0,0,0);
03970		FinishAnim();
03971		Goto('Done');
03972	}
03973	
03974	
03975	//===============================================================================
03976	// Scriptable States
03977	//===============================================================================
03978	
03979	//================================================
03980	//
03981	// Statue
03982	//
03983	//================================================
03984	State() Statue
03985	{
03986	ignores HearNoise, EnemyAcquired, Bump, PowerupFire, PowerupBlaze, PowerupStone, PowerupIce, PowerupFriend, SetOnFire, CheckForEnemies, ProtectAlly, WeaponActivate, SwipeEffectStart;
03987	
03988		function AmbientSoundTimer()
03989		{
03990			// Don't play it, just reset timer for next one
03991			AmbientSoundTime = (0.5 + FRand()*0.5) * AmbientWaitSoundDelay;
03992		}
03993	
03994		function EMatterType MatterForJoint(int joint)
03995		{
03996			return MATTER_STONE;
03997		}
03998	
03999		function bool CanBeStatued()
04000		{
04001			return false;
04002		}
04003	
04004		function SpawnDebris(vector Momentum)
04005		{
04006			local int numchunks;
04007			local debris d;
04008			local vector loc;
04009			local float scale;
04010	
04011			// Spawn cloud
04012			DebrisCloud();
04013	
04014			// Find appropriate size of chunks
04015			numchunks = Clamp(Mass/10, 2, 15);
04016			scale = (CollisionRadius*CollisionRadius*CollisionHeight) / (numchunks*500);
04017			scale = scale ** 0.3333333;
04018	
04019			// Spawn debris
04020			for (i=0; i<numchunks; i++)
04021			{
04022				loc = Location;
04023				loc.X += (FRand()*2-1)*CollisionRadius;
04024				loc.Y += (FRand()*2-1)*CollisionRadius;
04025				loc.Z += (FRand()*2-1)*CollisionHeight;
04026				d = Spawn(class'debrisstone',,,loc);
04027				if (d != None)
04028				{
04029					d.SetSize(scale);
04030					d.SetMomentum(Momentum);
04031				}
04032			}
04033		}
04034	
04035		function bool JointDamaged(int Damage, Pawn EventInstigator, vector HitLoc, vector Momentum, name DamageType, int joint)
04036		{
04037			local actor A;
04038	
04039			if (DamageType=='sever' || DamageType=='bluntsever' ||
04040				DamageType=='thrownweaponsever' || DamageType=='thrownweaponbluntsever' ||
04041				DamageType=='fire' || DamageType=='electricity' ||
04042				DamageType=='stomped')
04043				return false;
04044	
04045			if (bStatueDestructible)
04046			{
04047				if( Event != '' )
04048					foreach AllActors( class 'Actor', A, Event )
04049						A.Trigger( Self, EventInstigator );
04050				Health = 0;
04051				PlaySound(Sound'WeaponsSnd.impcrashes.crashxstone01', SLOT_Pain);
04052				SpawnDebris(Momentum);
04053				Destroy();
04054			}
04055	
04056			return false;
04057		}
04058	
04059		function Trigger(actor Other, pawn EventInstigator)
04060		{
04061			if (bStatueCanWake)
04062				GotoState('Statue', 'Wake');
04063		}
04064	
04065		function CreatureStatue()
04066		{
04067			local int ix;
04068	
04069			for (ix=0; ix<16; ix++)
04070			{
04071				if (SkelGroupSkins[ix] != None)
04072					SkelGroupSkins[ix] = texture'statues.sb_body_stone';
04073			}
04074		}
04075	
04076		function DebrisCloud()
04077		{	// Spawn obscuring cloud
04078			local DebrisCloud c;
04079			c = Spawn(class'DebrisCloud');
04080			c.SetRadius(Max(CollisionRadius,CollisionHeight));
04081		}
04082	
04083		function CreatureNormal()
04084		{
04085			local int ix;
04086	
04087			// Transform creature to default skin
04088			for (ix=0; ix<16; ix++)
04089				SkelGroupSkins[ix] = None;
04090			SetDefaultPolygroups();
04091		}
04092	
04093		function InventoryStatue()
04094		{
04095			local Inventory inv;
04096			local int ix;
04097	
04098			// Transform inventory if visible
04099			inv = Inventory;
04100			while (inv != None)
04101			{
04102				if (!inv.bHidden)
04103				{
04104					inv.bSweepable=false;
04105					for (ix=0; ix<16; ix++)
04106					{
04107						if (inv.SkelGroupSkins[ix] != None)
04108						{
04109							inv.SkelGroupSkins[ix] = texture'statues.sb_body_stone';
04110						}
04111					}
04112				}
04113				inv = inv.Inventory;
04114			}
04115		}
04116	
04117		function InventoryNormal()
04118		{
04119			local Inventory inv;
04120			local int ix;
04121	
04122			// Transform inventory if visible
04123			inv = Inventory;
04124			while (inv != None)
04125			{
04126				if (!inv.bHidden)
04127				{
04128					for (ix=0; ix<16; ix++)
04129						inv.SkelGroupSkins[ix] = None;
04130					inv.SetDefaultPolygroups();
04131					inv.bSweepable=inv.Default.bSweepable;
04132				}
04133				inv = inv.Inventory;
04134			}
04135		}
04136	
04137		function Tick(float DeltaTime)
04138		{
04139			if (bCanLook)
04140			{
04141				Super.Tick(DeltaTime);
04142			}
04143		}
04144	
04145		function Timer()
04146		{
04147			// Once in a while, check if player can see me
04148			if (PlayerCanSeeMe())
04149			{
04150				bCanLook = false;
04151			}
04152			else
04153			{
04154				bCanLook = true;
04155				LookAt(Target);
04156			}
04157		}
04158	
04159		function SeePlayer(Actor Seen)
04160		{
04161			if (bStatueCanWake && Target == None)
04162			{
04163				Target = Seen;
04164				SetTimer(0.5, true);
04165			}
04166		}
04167	
04168		function BeginState()
04169		{
04170			bCanLook = false;
04171			Target = None;
04172			bProjTarget = false;
04173			if (Weapon!=None)
04174				Weapon.FinishAttack();
04175		}
04176	
04177		function EndState()
04178		{
04179			bCanLook = Default.bCanLook;
04180			SetTimer(0, false);
04181			bMovable = Default.bMovable;
04182			bProjTarget = Default.bProjTarget;
04183		}
04184	
04185		function Landed(vector HitNormal, actor HitActor)
04186		{
04187			bMovable = false;
04188			global.Landed(HitNormal, HitActor);
04189		}
04190	
04191	Wake:
04192		DebrisCloud();
04193		SetTimer(0, false);
04194	//	PlayThreatening(0.1);
04195		CreatureNormal();
04196		InventoryNormal();
04197	//	FinishAnim();
04198		OrderFinished();
04199		GotoState('Waiting');
04200	
04201	Begin:
04202		Acceleration=vect(0,0,0);
04203		SetPhysics(PHYS_Falling);
04204	
04205		CreatureStatue();
04206		InventoryStatue();
04207	
04208		if (StatueAnimSeq != '')
04209		{
04210			TweenAnim(StatueAnimSeq, 0.2);
04211			FinishAnim();
04212	
04213			for(i=0; i<20; i++)
04214			{
04215				AnimFrame = (AnimFrame + 3*StatueAnimFrame)*0.25;
04216				Sleep(0.1);
04217			}
04218		}
04219		else
04220		{
04221			// Slow to end of sequence
04222			for(i=0; i<20; i++)
04223			{
04224				AnimRate *= 0.75;
04225				Sleep(0.1);
04226			}
04227		}
04228		AnimRate=0;
04229		bMovable = false;
04230	}
04231	
04232	
04233	//================================================
04234	//
04235	// IceStatue
04236	//
04237	// Used only for Ice Powerup
04238	//================================================
04239	State() IceStatue
04240	{
04241	ignores HearNoise, EnemyAcquired, Bump, PowerupFire, PowerupBlaze, PowerupStone, PowerupIce, PowerupFriend, SetOnFire, CheckForEnemies, ProtectAlly, WeaponActivate, SwipeEffectStart;
04242	
04243		function AmbientSoundTimer()
04244		{
04245			// Don't play it, just reset timer for next one
04246			AmbientSoundTime = (0.5 + FRand()*0.5) * AmbientWaitSoundDelay;
04247		}
04248	
04249		function EMatterType MatterForJoint(int joint)
04250		{
04251			return MATTER_ICE;
04252		}
04253	
04254		function bool CanBeStatued()
04255		{
04256			return false;
04257		}
04258	
04259		function SpawnDebris(vector Momentum)
04260		{
04261			local int numchunks;
04262			local debris d;
04263			local vector loc;
04264			local float scale;
04265	
04266			// Find appropriate size of chunks
04267			numchunks = Clamp(Mass/10, 2, 15);
04268			scale = (CollisionRadius*CollisionRadius*CollisionHeight) / (numchunks*500);
04269			scale = scale ** 0.3333333;
04270	
04271			// Spawn debris
04272			for (i=0; i<numchunks; i++)
04273			{
04274				loc = Location;
04275				loc.X += (FRand()*2-1)*CollisionRadius;
04276				loc.Y += (FRand()*2-1)*CollisionRadius;
04277				loc.Z += (FRand()*2-1)*CollisionHeight;
04278				d = Spawn(class'debrisice',,,loc);
04279				if (d != None)
04280				{
04281					d.SetSize(scale);
04282					d.SetMomentum(Momentum);
04283				}
04284			}
04285		}
04286	
04287		function bool JointDamaged(int Damage, Pawn EventInstigator, vector HitLoc, vector Momentum, name DamageType, int joint)
04288		{
04289			local actor A;
04290	
04291			if (DamageType=='sever' || DamageType=='bluntsever' ||
04292				DamageType=='thrownweaponsever' || DamageType=='thrownweaponbluntsever' ||
04293				DamageType=='fire' || DamageType=='electricity')
04294				return false;
04295	
04296			if (bStatueDestructible)
04297			{
04298				if( Event != '' )
04299					foreach AllActors( class 'Actor', A, Event )
04300						A.Trigger( Self, EventInstigator );
04301				Health = 0;
04302				PlaySound(Sound'WeaponsSnd.impcrashes.crashglass02', SLOT_Pain);
04303				SpawnDebris(Momentum);
04304				Destroy();
04305			}
04306	
04307			return false;
04308		}
04309	
04310		function Trigger(actor Other, pawn EventInstigator)
04311		{
04312			if (bStatueCanWake)
04313				GotoState('IceStatue', 'Wake');
04314		}
04315	
04316		function CreatureStatue()
04317		{
04318			local int ix;
04319	
04320			for (ix=0; ix<16; ix++)
04321			{
04322				if (SkelGroupSkins[ix] != None)
04323					SkelGroupSkins[ix] = texture'statues.ice1';
04324			}
04325		}
04326	
04327		function CreatureNormal()
04328		{
04329			local int ix;
04330	
04331			// Transform creature to default skin
04332			for (ix=0; ix<16; ix++)
04333				SkelGroupSkins[ix] = None;
04334			SetDefaultPolygroups();
04335		}
04336	
04337		function InventoryStatue()
04338		{
04339			local Inventory inv;
04340			local int ix;
04341	
04342			// Transform inventory if visible
04343			inv = Inventory;
04344			while (inv != None)
04345			{
04346				if (!inv.bHidden)
04347				{
04348					for (ix=0; ix<16; ix++)
04349					{
04350						if (inv.SkelGroupSkins[ix] != None)
04351						{
04352							inv.SkelGroupSkins[ix] = texture'statues.ice1';
04353							inv.bSweepable=false;
04354						}
04355					}
04356				}
04357				inv = inv.Inventory;
04358			}
04359		}
04360	
04361		function InventoryNormal()
04362		{
04363			local Inventory inv;
04364			local int ix;
04365	
04366			// Transform inventory if visible
04367			inv = Inventory;
04368			while (inv != None)
04369			{
04370				if (!inv.bHidden)
04371				{
04372					for (ix=0; ix<16; ix++)
04373						inv.SkelGroupSkins[ix] = None;
04374					inv.SetDefaultPolygroups();
04375					inv.bSweepable=inv.Default.bSweepable;
04376	;
04377				}
04378				inv = inv.Inventory;
04379			}
04380		}
04381	
04382		function Timer()
04383		{	// After time expires, come back to life (timer set by powerup)
04384			GotoState('IceStatue', 'Wake');
04385		}
04386	
04387		function BeginState()
04388		{
04389			bCanLook = false;
04390			bProjTarget = false;
04391			if (Weapon!=None)
04392				Weapon.FinishAttack();
04393		}
04394	
04395		function EndState()
04396		{
04397			bCanLook = Default.bCanLook;
04398			SetTimer(0, false);
04399			bMovable = Default.bMovable;
04400			bProjTarget = Default.bProjTarget;
04401		}
04402	
04403		function Landed(vector HitNormal, actor HitActor)
04404		{
04405			bMovable = false;
04406			global.Landed(HitNormal, HitActor);
04407		}
04408	
04409	Wake:
04410		SpawnDebris(vect(0,0,0));
04411		PlaySound(Sound'WeaponsSnd.impcrashes.crashglass02', SLOT_Pain);
04412		SetTimer(0, false);
04413		CreatureNormal();
04414		InventoryNormal();
04415		OrderFinished();
04416		GotoState('Waiting');
04417	
04418	Begin:
04419		Acceleration=vect(0,0,0);
04420		SetPhysics(PHYS_Falling);
04421	
04422		CreatureStatue();
04423		InventoryStatue();
04424	
04425		if (StatueAnimSeq != '')
04426		{
04427			TweenAnim(StatueAnimSeq, 0.2);
04428			FinishAnim();
04429	
04430			for(i=0; i<20; i++)
04431			{
04432				AnimFrame = (AnimFrame + 3*StatueAnimFrame)*0.25;
04433				Sleep(0.1);
04434			}
04435		}
04436		else
04437		{
04438			// Slow to end of sequence
04439			bAnimLoop=false;
04440			for(i=0; i<7; i++)
04441			{
04442				AnimRate *= 0.75;
04443				Sleep(0.1);
04444			}
04445	
04446			while (AnimFrame < AnimLast)
04447				Sleep(0.1);
04448		}
04449		AnimRate=0;
04450		bMovable = false;
04451	}
04452	
04453	
04454	//================================================
04455	// Frozen
04456	// Used to make a quiet creature in scripting, trigger to get out
04457	//================================================
04458	State() Frozen
04459	{
04460		function BeginState()
04461		{
04462			bQuiet=true;
04463			SetPhysics(PHYS_None);
04464		}
04465	
04466		function EndState()
04467		{
04468			bQuiet=false;
04469			SetMovementPhysics();
04470		}
04471	
04472	Begin:
04473	}
04474	
04475	
04476	//================================================
04477	//
04478	// Attack (not finished)
04479	//
04480	// Attack OrderObject
04481	// That order object should have an event to trigger this guy, which in
04482	// turn will fire the next state
04483	//================================================
04484	State() Attack
04485	{
04486	Begin:
04487		Enemy = Pawn(OrderObject);
04488		if (Enemy != None)
04489			GotoState('Acquisition');
04490	
04491		// put next queued state in Trigger orders to be fired when enemy dies
04492		TriggerOrders = NextState;
04493		if (NextPoint != None)
04494			TriggerOrdersTag = NextPoint.Tag;
04495	
04496	}
04497	
04498	
04499	//================================================
04500	// Debug
04501	//================================================
04502	simulated function Debug(canvas Canvas, int mode)
04503	{
04504		local int sx,sy;
04505		local vector offset;
04506		local string text;
04507		
04508		Super.Debug(Canvas, mode);
04509		
04510		Canvas.DrawText("ScriptPawn:");
04511		Canvas.CurY -= 8;
04512		Canvas.DrawText("  NavList:      "$Level.NavigationPointList);
04513		Canvas.CurY -= 8;
04514	
04515		text = "  ";
04516		if (bCanWalk)
04517			text = text@"bCanWalk";
04518		if (bCanJump)
04519			text = text@"bCanJump";
04520		if (bCanSwim)
04521			text = text@"bCanSwim";
04522		if (bCanFly)
04523			text = text@"bCanFly";
04524		Canvas.DrawText(text);
04525		Canvas.CurY -= 8;
04526	
04527		Canvas.DrawText("  bHurrying:    "$bHurrying);
04528		Canvas.CurY -= 8;
04529		Canvas.DrawText("  MovementSpeed:"$MovementSpeed);
04530		Canvas.CurY -= 8;
04531		Canvas.DrawText("  bTaskLocked:  "$bTaskLocked);
04532		Canvas.CurY -= 8;
04533		Canvas.DrawText("  Orders:       "$Orders);
04534		Canvas.CurY -= 8;
04535		Canvas.DrawText("  OrdersTag:    "$OrdersTag);
04536		Canvas.CurY -= 8;
04537		Canvas.SetColor(255, 255, 255);
04538		if (HuntDistance>0)
04539		{
04540			Canvas.DrawText("  HuntDistance: "$HuntDistance);
04541			Canvas.CurY -= 8;
04542		}
04543		Canvas.DrawText("  TestStr: "$teststring);
04544		Canvas.CurY -= 8;
04545		Canvas.DrawText("  TestStr2:"$teststring2);
04546		Canvas.CurY -= 8;
04547		Canvas.SetColor(255, 0, 0);
04548	
04549		Canvas.DrawText("	bStopMoveIfCombatRange:"$bStopMoveIfCombatRange);
04550		Canvas.CurY -= 8;
04551	
04552		switch(EnemyFacing)
04553		{
04554		case FACE_FRONT:	Canvas.DrawText("	Enemy Facing:    FRONT");	break;
04555		case FACE_BACK:		Canvas.DrawText("	Enemy Facing:    BACK");	break;
04556		case FACE_SIDE:		Canvas.DrawText("	Enemy Facing:    SIDE");	break;
04557		}
04558		Canvas.CurY -= 8;
04559	
04560		switch(EnemyVertical)
04561		{
04562		case VERT_ABOVE:	Canvas.DrawText("	Enemy Vertical:  ABOVE");	break;
04563		case VERT_BELOW:	Canvas.DrawText("	Enemy Vertical:  BELOW");	break;
04564		case VERT_LEVEL:	Canvas.DrawText("	Enemy Vertical:  LEVEL");	break;
04565		}
04566		Canvas.CurY -= 8;
04567	
04568		switch(EnemyMovement)
04569		{
04570		case MOVE_CLOSER:		Canvas.DrawText("	Enemy Movement:  CLOSER");			break;
04571		case MOVE_FARTHER:		Canvas.DrawText("	Enemy Movement:  FARTHER");			break;
04572		case MOVE_STRAFE_LEFT:	Canvas.DrawText("	Enemy Movement:  STRAFE_LEFT");		break;
04573		case MOVE_STRAFE_RIGHT:	Canvas.DrawText("	Enemy Movement:  STRAFE_RIGHT");	break;
04574		case MOVE_STANDING:		Canvas.DrawText("	Enemy Movement:  STANDING");		break;
04575		}
04576		Canvas.CurY -= 8;
04577	
04578		switch(EnemyIncidence)
04579		{
04580		case INC_FRONT:		Text="INC_FRONT";	break;
04581		case INC_BACK:		Text="INC_BACK";	break;
04582		case INC_LEFT:		Text="INC_LEFT";	break;
04583		case INC_RIGHT:		Text="INC_RIGHT";	break;
04584		}
04585		Canvas.DrawText("	Enemy Incidence: "$Text);
04586		Canvas.CurY -= 8;
04587	
04588		switch(LastAction)
04589		{
04590		case AA_WAIT:			Text="AA_WAIT";				break;
04591		case AA_CHARGE:			Text="AA_CHARGE";			break;
04592		case AA_STRAFE_LEFT:	Text="AA_STRAFE_LEFT";		break;
04593		case AA_STRAFE_RIGHT:	Text="AA_STRAFE_RIGHT";		break;
04594		case AA_LUNGE:			Text="AA_LUNGE";			break;
04595		case AA_JUMP:			Text="AA_JUMP";				break;
04596		case AA_BACKUP:			Text="AA_BACKUP";			break;
04597		case AA_ATTACKMELEE1:	Text="AA_ATTACKMELEE1";		break;
04598		case AA_ATTACKMELEE2:	Text="AA_ATTACKMELEE2";		break;
04599		case AA_ATTACKMELEE3:	Text="AA_ATTACKMELEE3";		break;
04600		case AA_ATTACKMISSILE1:	Text="AA_ATTACKMISSILE1";	break;
04601		case AA_ATTACKMISSILE2:	Text="AA_ATTACKMISSILE2";	break;
04602		}
04603		Canvas.DrawText("   LastAction:      " $ Text);
04604		Canvas.CurY -= 8;
04605	
04606		switch(AttackAction)
04607		{
04608		case AA_WAIT:			Text="AA_WAIT";				break;
04609		case AA_CHARGE:			Text="AA_CHARGE";			break;
04610		case AA_STRAFE_LEFT:	Text="AA_STRAFE_LEFT";		break;
04611		case AA_STRAFE_RIGHT:	Text="AA_STRAFE_RIGHT";		break;
04612		case AA_LUNGE:			Text="AA_LUNGE";			break;
04613		case AA_JUMP:			Text="AA_JUMP";				break;
04614		case AA_BACKUP:			Text="AA_BACKUP";			break;
04615		case AA_ATTACKMELEE1:	Text="AA_ATTACKMELEE1";		break;
04616		case AA_ATTACKMELEE2:	Text="AA_ATTACKMELEE2";		break;
04617		case AA_ATTACKMELEE3:	Text="AA_ATTACKMELEE3";		break;
04618		case AA_ATTACKMISSILE1:	Text="AA_ATTACKMISSILE1";	break;
04619		case AA_ATTACKMISSILE2:	Text="AA_ATTACKMISSILE2";	break;
04620		}
04621		Canvas.DrawText("   AttackAction:    " $ Text);
04622		Canvas.CurY -= 8;
04623		
04624		// Destination is red
04625		offset = Destination;
04626		Canvas.DrawLine3D(offset + vect(10, 0, 0), offset + vect(-10, 0, 0), 255, 0, 0);
04627		Canvas.DrawLine3D(offset + vect(0, 10, 0), offset + vect(0, -10, 0), 255, 0, 0);
04628		Canvas.DrawLine3D(offset + vect(0, 0, 10), offset+ vect(0, 0, -10), 255, 0, 0);
04629		Canvas.DrawLine3D(Location, Destination, 255, 0, 0);
04630	
04631		// MoveTarget is Green
04632		if (MoveTarget != None)
04633			Canvas.DrawLine3D(Location, MoveTarget.Location, 0, 0, 255);
04634			
04635		// OrderObject is Blue
04636		if (OrderObject != None)
04637			Canvas.DrawLine3D(Location, OrderObject.Location, 0, 0, 255);
04638	}
04639	
04640	defaultproperties
04641	{
04642	     bFallAtStartup=True
04643	     ThrowTrajectory=8192
04644	     HuntTime=30.000000
04645	     bStatueCanWake=True
04646	     bStatueDestructible=True
04647	     AmbientWaitSoundDelay=15.000000
04648	     AmbientFightSoundDelay=10.000000
04649	     MinStopWait=0.500000
04650	     MaxStopWait=1.500000
04651	     bWaitLook=True
04652	     bBurnable=True
04653	     AllyMaxTime=30.000000
04654	     WanderDistance=12.000000
04655	     CarcassType=Class'RuneI.RuneCarcass'
04656	     WalkingSpeed=50.000000
04657	     GibCount=8
04658	     GibClass=Class'RuneI.DebrisFlesh'
04659	     FootStepWood(0)=Sound'FootstepsSnd.Wood.footwood02'
04660	     FootStepWood(1)=Sound'FootstepsSnd.Wood.footlandwood02'
04661	     FootStepWood(2)=Sound'FootstepsSnd.Wood.footwood05'
04662	     FootStepMetal(0)=Sound'FootstepsSnd.Metal.footmetal01'
04663	     FootStepMetal(1)=Sound'FootstepsSnd.Metal.footmetal02'
04664	     FootStepMetal(2)=Sound'FootstepsSnd.Metal.footmetal05'
04665	     FootStepStone(0)=Sound'FootstepsSnd.Earth.footgravel09'
04666	     FootStepStone(1)=Sound'FootstepsSnd.Earth.footgravel10'
04667	     FootStepStone(2)=Sound'FootstepsSnd.Earth.footgravel09'
04668	     FootStepFlesh(0)=Sound'FootstepsSnd.Earth.footsquish02'
04669	     FootStepFlesh(1)=Sound'FootstepsSnd.Earth.footsquish07'
04670	     FootStepFlesh(2)=Sound'FootstepsSnd.Earth.footsquish09'
04671	     FootStepIce(0)=Sound'FootstepsSnd.Ice.footice01'
04672	     FootStepIce(1)=Sound'FootstepsSnd.Ice.footice02'
04673	     FootStepIce(2)=Sound'FootstepsSnd.Ice.footice03'
04674	     FootStepEarth(0)=Sound'FootstepsSnd.Earth.footgravel01'
04675	     FootStepEarth(1)=Sound'FootstepsSnd.Earth.footgravel02'
04676	     FootStepEarth(2)=Sound'FootstepsSnd.Earth.footgravel04'
04677	     FootStepSnow(0)=Sound'FootstepsSnd.Snow.footsnow01'
04678	     FootStepSnow(1)=Sound'FootstepsSnd.Snow.footsnow02'
04679	     FootStepSnow(2)=Sound'FootstepsSnd.Snow.footsnow04'
04680	     FootStepWater(0)=Sound'FootstepsSnd.Water.footwaterwaist01'
04681	     FootStepWater(1)=Sound'FootstepsSnd.Water.footwaterwaist02'
04682	     FootStepWater(2)=Sound'FootstepsSnd.Water.footwaterwaist03'
04683	     FootStepMud(0)=Sound'FootstepsSnd.Mud.footmud01'
04684	     FootStepMud(1)=Sound'FootstepsSnd.Mud.footmud02'
04685	     FootStepMud(2)=Sound'FootstepsSnd.Mud.footmud03'
04686	     FootStepLava(0)=Sound'FootstepsSnd.Lava.footlava02'
04687	     FootStepLava(1)=Sound'FootstepsSnd.Lava.footlava03'
04688	     FootStepLava(2)=Sound'FootstepsSnd.Lava.footlava07'
04689	     LandSoundWood=Sound'FootstepsSnd.Earth.footlandearth01'
04690	     LandSoundMetal=Sound'FootstepsSnd.Metal.footmetal04'
04691	     LandSoundStone=Sound'FootstepsSnd.Earth.footlandearth04'
04692	     LandSoundFlesh=Sound'FootstepsSnd.Earth.footsquish06'
04693	     LandSoundIce=Sound'FootstepsSnd.Earth.footlandearth02'
04694	     LandSoundSnow=Sound'FootstepsSnd.Snow.footlandsnow05'
04695	     LandSoundEarth=Sound'FootstepsSnd.Earth.footlandearth05'
04696	     LandSoundWater=Sound'FootstepsSnd.Water.footlandwater02'
04697	     LandSoundMud=Sound'FootstepsSnd.Mud.footlandmud01'
04698	     LandSoundLava=Sound'FootstepsSnd.Lava.footlava01'
04699	     WeaponJoint=Weapon
04700	     ShieldJoint=Shield
04701	     bFootsteps=True
04702	     bFrameNotifies=True
04703	     DrawType=DT_SkeletalMesh
04704	}

End Source Code