RuneI
Class Goblin

source: c:\runehov\RuneI\Classes\Goblin.uc
Core.Object
   |
   +--Engine.Actor
      |
      +--Engine.Pawn
         |
         +--RuneI.ScriptPawn
            |
            +--RuneI.Goblin
Direct Known Subclasses:GoblinCrazy, GoblinFemale, GoblinMohawk, GoblinWarrior, GoblinWeakling

class Goblin
extends RuneI.ScriptPawn

//============================================================================= // Goblin. //=============================================================================
Variables
 Actor CurrentPoint
 EGoblinHead GoblinHead
 EGoblinLimb GoblinLimb
 EGoblinTorso GoblinTorso
 bool bGoblinMask
 int breathcounter

States
DodgeBackflip, DodgeLeft, DodgeRight, DodgeBack, DodgeForward, Fighting, Charging, StayOnPaths, ThrowRocks, Excited, AlertSquawk, ThrowTest, MineRocksPartner2, MineRocksPartner1, MineRocks

Function Summary
 void ApplyGoreCap(int BodyPart)
     
//============================================================
//
// ApplyGoreCap
//
//============================================================
 void AttachMask()
     
//============================================================
//
// AttachMask
//
//============================================================
 eAttitude AttitudeToCreature(Pawn Other)
     
//------------------------------------------------
//
// AttitudeToCreature
//
//------------------------------------------------
 bool BodyPartCritical(int BodyPart)
     
//============================================================
//
// BodyPartCritical
//
//============================================================
 int BodyPartForJoint(int joint)
     
//============================================================
//
// BodyPartForJoint
//
// Returns the body part a joint is associated with
//============================================================
 int BodyPartForPolyGroup(int polygroup)
     
//============================================================
//
// BodyPartForPolyGroup
//
//============================================================
 bool BodyPartSeverable(int BodyPart)
     
//============================================================
//
// BodyPartSeverable
//
//============================================================
 void Breath()
     
//------------------------------------------------
//
// Sound Functions
//
//------------------------------------------------
 void Bump(Actor Other)
     
//===================================================================
//
// Bump
//
// If Pawns bump a destroyable decoration, they should smash it
//===================================================================
 bool CanPickup(Inventory item)
     
//============================================================
//
// CanPickup
//
// Let's pawn dictate what it can pick up
//============================================================
 void LimbSevered(int BodyPart, vector Momentum)
     
//============================================================
//
// LimbSevered
//
//============================================================
 Texture PainSkin(int BodyPart)
     
//============================================================
//
// PainSkin
//
// returns the pain skin for a given polygroup
//============================================================
 void PlayBackDeath(name DamageType)
 void PlayBlockHigh(optional float)
 void PlayBlockLow(optional float)
 void PlayCower(optional float)
 void PlayDeath(name DamageType)
 void PlayDodgeBack(optional float)
 void PlayDodgeBackflip(optional float)
 void PlayDodgeDuck(optional float)
 void PlayDodgeForward(optional float)
 void PlayDodgeLeft(optional float)
 void PlayDodgeRight(optional float)
 void PlayDrownDeath(name DamageType)
 void PlayDrowning(optional float)
 void PlayFrontHit(float tween)
 void PlayHeadDeath(name DamageType)
 void PlayHuntStop(optional float)
 void PlayInAir(optional float)
 void PlayJumping(optional float)
 void PlayLeftDeath(name DamageType)
 void PlayMeleeHigh(optional float)
 void PlayMeleeLow(optional float)
 void PlayMoving(optional float)
 void PlayRightDeath(name DamageType)
 void PlaySkewerDeath(name DamageType)
 void PlayTaunting(optional float)
 void PlayThrowing(optional float)
 void PlayTurning(optional float)
 void PlayWaiting(optional float)
     
//============================================================
// Animation functions
//============================================================
 void PreBeginPlay()
     
//============================================================
//
// PreBeginPlay
//
//============================================================
 void SetGoblinSkins(EGoblinLimb arms, EGoblinTorso torso, EGoblinHead head)
     
//============================================================
//
// SetGoblinSkins
//
//============================================================
 void SetupGoblin()
     
//============================================================
//
// SetupGoblin
//
//============================================================
 void TweenToHuntStop(float time)
 void TweenToJumping(float time)
 void TweenToMeleeHigh(float time)
 void TweenToMeleeLow(float time)
 void TweenToMoving(float time)
 void TweenToThrowing(float time)
 void TweenToTurning(float time)
 void TweenToWaiting(float time)
     
// Tween functions


State DodgeBackflip Function Summary
 void AmbientSoundTimer()


State DodgeLeft Function Summary
 void AmbientSoundTimer()


State DodgeRight Function Summary
 void AmbientSoundTimer()


State DodgeBack Function Summary
 void AmbientSoundTimer()


State DodgeForward Function Summary
 void AmbientSoundTimer()


State Fighting Function Summary
 bool InDangerFromAttack()
 bool ShouldDefend()
 void Timer()
 bool BlockRatherThanDodge()
 void AmbientSoundTimer()
 void EndState()
 void BeginState()


State Charging Function Summary
 void HitWall(vector HitNormal, Actor Wall)
 void PickPaceDest()
 void EnemyNotVisible()
 void Timer()
 void MayFall()
 void BeginState()
     
// Inherits EndState()


State StayOnPaths Function Summary
 void MayFall()
 void HitWall(vector norm, Actor wall)
 void PickDestination()
     
// Consider all destinations from OrderObject to stay close to enemy
 void EndState()
 void BeginState()


State ThrowRocks Function Summary
 bool EnemyInRange(float range)
 void SpawnRock()
 void EnemyNotVisible()


State Excited Function Summary
 void EnemyNotVisible()


State AlertSquawk Function Summary


State ThrowTest Function Summary
 void SpawnRock()
 void Timer()


State MineRocksPartner2 Function Summary


State MineRocksPartner1 Function Summary


State MineRocks Function Summary



Source Code


00001	//=============================================================================
00002	// Goblin.
00003	//=============================================================================
00004	class Goblin expands ScriptPawn;
00005	
00006	/* Description:
00007	
00008	   Walk speed = 54 @ 20Hx
00009	   Run speed = 260 @ 15Hz
00010	   
00011	   ANIMS:
00012	   	HuntStop Animation (sniffing)
00013	   	Threaten anim
00014	
00015	   TODO:
00016	*/
00017	
00018	var(Sounds) sound		RollSound;
00019	var(Sounds) sound		PaceSound;
00020	
00021	
00022	enum EGoblinLimb
00023	{
00024		LIMB_RANDOM,
00025		LIMB_RANDOMWARPAINT,
00026		LIMB_NORMAL,
00027		LIMB_WARPAINT1
00028	};
00029	
00030	enum EGoblinTorso
00031	{
00032		TORSO_RANDOM,
00033		TORSO_RANDOMWARPAINT,
00034		TORSO_NORMAL,
00035		TORSO_WARPAINT1,
00036		TORSO_WARPAINT2,
00037		TORSO_WARPAINT3
00038	};
00039	
00040	enum EGoblinHead
00041	{
00042		HEAD_RANDOM,
00043		HEAD_RANDOMWARPAINT,
00044		HEAD_NORMAL,
00045		HEAD_WARPAINT1,
00046		HEAD_WARPAINT2,
00047		HEAD_WARPAINT3
00048	};
00049	
00050	
00051	var() EGoblinLimb	GoblinLimb;
00052	var() EGoblinTorso	GoblinTorso;
00053	var() EGoblinHead	GoblinHead;
00054	var() bool			bGoblinMask;
00055	
00056	var Actor CurrentPoint;
00057	var private int breathcounter;
00058	
00059	
00060	//===================================================================
00061	//					Functions
00062	//===================================================================
00063	
00064	//============================================================
00065	//
00066	// PreBeginPlay
00067	//
00068	//============================================================
00069	function PreBeginPlay()
00070	{
00071		Super.PreBeginPlay();
00072	
00073		SetupGoblin();
00074	}
00075	
00076	//------------------------------------------------
00077	//
00078	// AttitudeToCreature
00079	//
00080	//------------------------------------------------
00081	function eAttitude AttitudeToCreature(Pawn Other)
00082	{
00083		if (Other.IsA('Dwarf') || Other.IsA('Viking') || Other.IsA('Zombie') || Other.IsA('Sark'))
00084			return ATTITUDE_Hate;
00085		else if (Other.IsA('Snowbeast'))
00086			return ATTITUDE_Fear;
00087		else
00088			return Super.AttitudeToCreature(Other);
00089	}
00090	
00091	
00092	//============================================================
00093	//
00094	// SetupGoblin
00095	//
00096	//============================================================
00097	function SetupGoblin()
00098	{
00099		local EGoblinLimb limbs;
00100		local EGoblinTorso torso;
00101		local EGoblinHead head;
00102	
00103		limbs = GoblinLimb;
00104		switch(GoblinLimb)
00105		{
00106			case LIMB_RANDOM:
00107				switch(Rand(2))
00108				{
00109					case 0:	limbs = LIMB_NORMAL;	break;
00110					case 1:	limbs = LIMB_WARPAINT1;	break;
00111				}
00112				break;
00113			case LIMB_RANDOMWARPAINT:
00114				limbs = LIMB_WARPAINT1;
00115				break;
00116		}
00117	
00118		torso = GoblinTorso;
00119		switch(GoblinTorso)
00120		{
00121			case TORSO_RANDOM:
00122				switch(Rand(4))
00123				{
00124					case 0:	torso = TORSO_NORMAL;		break;
00125					case 1:	torso = TORSO_WARPAINT1;	break;
00126					case 2:	torso = TORSO_WARPAINT2;	break;
00127					case 3:	torso = TORSO_WARPAINT3;	break;
00128				}
00129				break;
00130			case TORSO_RANDOMWARPAINT:
00131				switch(Rand(3))
00132				{
00133					case 0:	torso = TORSO_WARPAINT1;	break;
00134					case 1:	torso = TORSO_WARPAINT2;	break;
00135					case 2:	torso = TORSO_WARPAINT3;	break;
00136				}
00137				break;
00138		}
00139		
00140		head = GoblinHead;
00141		switch(GoblinHead)
00142		{
00143			case HEAD_RANDOM:
00144				switch(Rand(4))
00145				{
00146					case 0:	head = HEAD_NORMAL;		break;
00147					case 1:	head = HEAD_WARPAINT1;	break;
00148					case 2:	head = HEAD_WARPAINT2;	break;
00149					case 3:	head = HEAD_WARPAINT3;	break;
00150				}
00151				break;
00152			case HEAD_RANDOMWARPAINT:
00153				switch(Rand(3))
00154				{
00155					case 0:	head = HEAD_WARPAINT1;	break;
00156					case 1:	head = HEAD_WARPAINT2;	break;
00157					case 2:	head = HEAD_WARPAINT3;	break;
00158				}
00159				break;
00160		}
00161	
00162		SetGoblinSkins(limbs, torso, head);
00163	
00164		if (bGoblinMask)
00165			AttachMask();
00166	}
00167	
00168	
00169	//============================================================
00170	//
00171	// AttachMask
00172	//
00173	//============================================================
00174	function AttachMask()
00175	{
00176		local actor mask;
00177		mask = Spawn(class'GoblinMask',,, GetJointPos(JointNamed('head')),);
00178		if (mask != None)
00179		{
00180			AttachActorToJoint(mask, JointNamed('head'));
00181			mask.PlayAnim('on_face', 1.0, 0.1);
00182		}
00183		else slog("No spawn mask");
00184	}
00185	
00186	
00187	//============================================================
00188	//
00189	// SetGoblinSkins
00190	//
00191	//============================================================
00192	function SetGoblinSkins(EGoblinLimb arms, EGoblinTorso torso, EGoblinHead head)
00193	{
00194		switch(arms)
00195		{
00196			case LIMB_NORMAL:
00197				break;
00198			case LIMB_WARPAINT1:
00199				SkelGroupSkins[2] = texture'runei.goblinpaintedarmleg';
00200				SkelGroupSkins[12] = texture'runei.goblinpaintedarmleg';
00201				SkelGroupSkins[8] = texture'runei.goblinpaintedarmleg';
00202				SkelGroupSkins[11] = texture'runei.goblinpaintedarmleg';
00203				break;
00204		}
00205		switch(torso)
00206		{
00207			case TORSO_NORMAL:
00208				break;
00209			case TORSO_WARPAINT1:
00210				SkelGroupSkins[7] = texture'runei.goblinpaintedbody';
00211				break;
00212			case TORSO_WARPAINT2:
00213				SkelGroupSkins[7] = texture'runei.goblinpaintedbody2';
00214				break;
00215			case TORSO_WARPAINT3:
00216				SkelGroupSkins[7] = texture'runei.goblinpaintedbody3';
00217				break;
00218		}
00219		switch(head)
00220		{
00221			case HEAD_NORMAL:
00222				break;
00223			case HEAD_WARPAINT1:
00224				SkelGroupSkins[1] = texture'runei.goblinpaintedhead';
00225				SkelGroupSkins[6] = texture'runei.goblinpaintedhead';
00226				break;
00227			case HEAD_WARPAINT2:
00228				SkelGroupSkins[1] = texture'runei.goblinpaintedhead2';
00229				SkelGroupSkins[6] = texture'runei.goblinpaintedhead2';
00230				break;
00231			case HEAD_WARPAINT3:
00232				SkelGroupSkins[1] = texture'runei.goblinpaintedhead3';
00233				SkelGroupSkins[6] = texture'runei.goblinpaintedhead3';
00234				break;
00235		}
00236	}
00237	
00238	
00239	//============================================================
00240	//
00241	// CanPickup
00242	//
00243	// Let's pawn dictate what it can pick up
00244	//============================================================
00245	function bool CanPickup(Inventory item)
00246	{
00247		if (Health <= 0)
00248			return false;
00249	
00250		if (item.IsA('Weapon') && (BodyPartHealth[BODYPART_RARM1] > 0) && (Weapon == None))
00251		{
00252			return (item.IsA('axe') || item.IsA('hammer') || item.IsA('Torch'));
00253		}
00254		else if (item.IsA('Shield') && (BodyPartHealth[BODYPART_LARM1] > 0) && (Shield == None))
00255		{
00256			return item.IsA('Shield');
00257		}
00258		return(false);
00259	}
00260	
00261	
00262	//============================================================
00263	//
00264	// PainSkin
00265	//
00266	// returns the pain skin for a given polygroup
00267	//============================================================
00268	function Texture PainSkin(int BodyPart)
00269	{
00270		local Texture newpage;
00271		local Texture oldpage;
00272		
00273		switch(BodyPart)
00274		{
00275			case BODYPART_TORSO:
00276				oldpage = SkelGroupSkins[7];
00277				if (oldpage == Texture'creatures.goblinbody')
00278					newpage = Texture'creatures.goblinpainbody';
00279				else if (oldpage == Texture'creatures.goblinpaintedbody')
00280					newpage = Texture'creatures.goblinpainbodypainted1';
00281				else if (oldpage == Texture'creatures.goblinpaintedbody2')
00282					newpage = Texture'creatures.goblinpainbodypainted2';
00283				else if (oldpage == Texture'creatures.goblinpaintedbody3')
00284					newpage = Texture'creatures.goblinpainbodypainted3';
00285				else
00286					newpage = oldpage;
00287				SkelGroupSkins[7] = newpage;
00288				break;
00289			case BODYPART_HEAD:
00290				oldpage = SkelGroupSkins[1];
00291				if (oldpage == Texture'creatures.goblinhead')
00292					newpage = Texture'creatures.goblinpainhead';
00293				else if (oldpage == Texture'creatures.goblinpaintedhead')
00294					newpage = Texture'creatures.goblinpainheadpainted';
00295				else if (oldpage == Texture'creatures.goblinpaintedhead2')
00296					newpage = Texture'creatures.goblinpainheadpainted2';
00297				else if (oldpage == Texture'creatures.goblinpaintedhead3')
00298					newpage = Texture'creatures.goblinpainheadpainted3';
00299				else
00300					newpage = oldpage;
00301				SkelGroupSkins[1] = newpage;
00302				SkelGroupSkins[6] = newpage;
00303				break;
00304			case BODYPART_LARM1:
00305				oldpage = SkelGroupSkins[2];
00306				if (oldpage == Texture'creatures.goblinarmleg')
00307					newpage = Texture'creatures.goblinpainarmleg';
00308				else if (oldpage == Texture'creatures.goblinpaintedarmleg')
00309					newpage = Texture'creatures.goblinpainarmlegpainted';
00310				else
00311					newpage = oldpage;
00312				SkelGroupSkins[2] = newpage;
00313				SkelGroupSkins[8] = newpage;
00314				break;
00315			case BODYPART_RARM1:
00316				oldpage = SkelGroupSkins[11];
00317				if (oldpage == Texture'creatures.goblinarmleg')
00318					newpage = Texture'creatures.goblinpainarmleg';
00319				else if (oldpage == Texture'creatures.goblinpaintedarmleg')
00320					newpage = Texture'creatures.goblinpainarmlegpainted';
00321				else
00322					newpage = oldpage;
00323				SkelGroupSkins[11] = newpage;
00324				SkelGroupSkins[12] = newpage;
00325				break;
00326			case BODYPART_LLEG1:
00327				oldpage = SkelGroupSkins[3];
00328				if (oldpage == Texture'creatures.goblinarmleg')
00329					newpage = Texture'creatures.goblinpainarmleg';
00330				else if (oldpage == Texture'creatures.goblinpaintedarmleg')
00331					newpage = Texture'creatures.goblinpainarmlegpainted';
00332				else
00333					newpage = oldpage;
00334				SkelGroupSkins[3] = newpage;
00335				break;
00336			case BODYPART_RLEG1:
00337				oldpage = SkelGroupSkins[4];
00338				if (oldpage == Texture'creatures.goblinarmleg')
00339					newpage = Texture'creatures.goblinpainarmleg';
00340				else if (oldpage == Texture'creatures.goblinpaintedarmleg')
00341					newpage = Texture'creatures.goblinpainarmlegpainted';
00342				else
00343					newpage = oldpage;
00344				SkelGroupSkins[4] = newpage;
00345				break;
00346		}
00347		return None;
00348	}
00349	
00350	
00351	//============================================================
00352	//
00353	// BodyPartForJoint
00354	//
00355	// Returns the body part a joint is associated with
00356	//============================================================
00357	function int BodyPartForJoint(int joint)
00358	{
00359		switch(joint)
00360		{
00361			case 9:						return BODYPART_LARM1;
00362			case 15:					return BODYPART_RARM1;
00363			case 24:					return BODYPART_RLEG1;
00364			case 27:					return BODYPART_LLEG1;
00365			case 20:					return BODYPART_HEAD;
00366			case 1: case 4: case 5:		return BODYPART_TORSO;
00367			default:					return BODYPART_BODY;
00368		}
00369	}
00370	
00371	//============================================================
00372	//
00373	// BodyPartForPolyGroup
00374	//
00375	//============================================================
00376	function int BodyPartForPolyGroup(int polygroup)
00377	{
00378		switch(polygroup)
00379		{
00380			case 1: case 6:						return BODYPART_HEAD;
00381			case 2:								return BODYPART_LARM1;
00382			case 12:							return BODYPART_RARM1;
00383			case 3:								return BODYPART_LLEG1;
00384			case 4:								return BODYPART_RLEG1;
00385			case 5: case 7: case 8: case 9:
00386				case 10: case 11:				return BODYPART_TORSO;
00387		}
00388		return BODYPART_BODY;
00389	}
00390	
00391	//============================================================
00392	//
00393	// BodyPartSeverable
00394	//
00395	//============================================================
00396	function bool BodyPartSeverable(int BodyPart)
00397	{
00398		switch(BodyPart)
00399		{
00400			case BODYPART_HEAD:
00401			case BODYPART_LARM1:
00402			case BODYPART_RARM1:
00403				return true;
00404		}
00405		return false;
00406	}
00407	
00408	//============================================================
00409	//
00410	// BodyPartCritical
00411	//
00412	//============================================================
00413	function bool BodyPartCritical(int BodyPart)
00414	{
00415		return (BodyPart == BODYPART_HEAD);
00416	}
00417	
00418	//============================================================
00419	//
00420	// ApplyGoreCap
00421	//
00422	//============================================================
00423	function ApplyGoreCap(int BodyPart)
00424	{
00425		switch(BodyPart)
00426		{
00427			case BODYPART_LARM1:
00428				SkelGroupSkins[9] = Texture'runefx.gore_bone';
00429				SkelGroupFlags[9] = SkelGroupFlags[9] & ~POLYFLAG_INVISIBLE;
00430				break;
00431			case BODYPART_RARM1:
00432				SkelGroupSkins[10] = Texture'runefx.gore_bone';
00433				SkelGroupFlags[10] = SkelGroupFlags[10] & ~POLYFLAG_INVISIBLE;
00434				break;
00435			case BODYPART_HEAD:
00436				SkelGroupSkins[5] = Texture'runefx.gore_bone';
00437				SkelGroupFlags[5] = SkelGroupFlags[5] & ~POLYFLAG_INVISIBLE;
00438				break;
00439		}
00440	}
00441	
00442	//================================================
00443	//
00444	// SeveredLimbClass
00445	//
00446	//================================================
00447	function class<Actor> SeveredLimbClass(int BodyPart)
00448	{
00449		switch(BodyPart)
00450		{
00451			case BODYPART_LARM1:
00452				return class'GoblinLArm';
00453			case BODYPART_RARM1:
00454				return class'GoblinRArm';
00455			case BODYPART_HEAD:
00456				return class'GoblinHead';
00457		}
00458	
00459		return None;
00460	}
00461	
00462	
00463	//============================================================
00464	//
00465	// LimbSevered
00466	//
00467	//============================================================
00468	function LimbSevered(int BodyPart, vector Momentum)
00469	{
00470		local int joint;
00471		local actor part;
00472		local vector X,Y,Z,pos;
00473		local vector cameralocation;
00474		local actor dummyactor;
00475		local rotator camerarotation;
00476		local class<actor> partclass;
00477	
00478		Super.LimbSevered(BodyPart, Momentum);
00479		
00480		ApplyGoreCap(BodyPart);
00481		partclass = SeveredLimbClass(BodyPart);
00482	
00483		switch(BodyPart)
00484		{
00485			case BODYPART_LARM1:
00486				joint = JointNamed('lshould');
00487				pos = GetJointPos(joint);
00488				GetAxes(Rotation, X, Y, Z);
00489				part = Spawn(partclass,,, pos, Rotation);
00490				if(part != None)
00491				{
00492					part.Velocity = -Y * 100 + vect(0, 0, 175);
00493					part.GotoState('Drop');
00494				}
00495				part = Spawn(class'BloodSpurt', self,, pos, Rotation);
00496				if(part != None)
00497				{
00498					AttachActorToJoint(part, joint);
00499				}
00500				break;
00501		
00502			case BODYPART_RARM1:
00503				joint = JointNamed('rshould');
00504				pos = GetJointPos(joint);
00505				GetAxes(Rotation, X, Y, Z);
00506				part = Spawn(partclass,,, pos, Rotation);
00507				if(part != None)
00508				{
00509					part.Velocity = Y * 100 + vect(0, 0, 175);
00510					part.GotoState('Drop');
00511				}
00512				part = Spawn(class'BloodSpurt', self,, pos, Rotation);
00513				if(part != None)
00514				{
00515					AttachActorToJoint(part, joint);
00516				}
00517				break;
00518			case BODYPART_HEAD:
00519				joint = JointNamed('head');
00520				pos = GetJointPos(joint);
00521				part = Spawn(partclass,,, pos, Rotation);
00522				if(part != None)
00523				{
00524	/*
00525					if (Enemy != None && PlayerPawn(Enemy) != None && VSize(Enemy.Location-part.Location)<1000)
00526					{
00527						PlayerPawn(Enemy).PlayerCalcView(dummyactor, cameralocation, camerarotation);
00528						part.Velocity = CalcArcVelocity(8192, part.Location, cameralocation);
00529						part.Velocity *= 0.6;//testfloat;
00530					}
00531					else
00532					{
00533						part.Velocity = 0.75 * (momentum / Mass) + vect(0, 0, 300);
00534					}
00535	*/
00536					part.Velocity = 0.75 * (momentum / Mass) + vect(0, 0, 300);
00537	
00538					part.GotoState('Drop');
00539					part.SkelGroupSkins[4] = SkelGroupSkins[6]; // Set the Head texture properly
00540				}
00541				part = Spawn(class'BloodSpurt', self,, pos, Rotation);
00542				if(part != None)
00543				{
00544					AttachActorToJoint(part, joint);
00545				}
00546				break;
00547		}
00548	}
00549	
00550	//===================================================================
00551	//
00552	// Bump
00553	//
00554	// If Pawns bump a destroyable decoration, they should smash it
00555	//===================================================================
00556	
00557	function Bump(Actor Other)
00558	{
00559		if(Other.IsA('DecorationRune') && DecorationRune(Other).bDestroyable && Health>0)
00560		{
00561			if(Weapon == None)
00562				PlayUninterruptedAnim('swipe');
00563			else
00564				PlayUninterruptedAnim('attackb');
00565		}
00566		else
00567		{
00568			Super.Bump(Other);
00569		}
00570	}
00571	
00572	
00573	//============================================================
00574	// Animation functions
00575	//============================================================
00576	
00577	function PlayWaiting(optional float tween)
00578	{
00579		if (BodyPartHealth[BODYPART_LARM1] <= 0 ^^
00580			BodyPartHealth[BODYPART_RARM1] <= 0)
00581		{
00582			if (BodyPartHealth[BODYPART_LARM1]<=0)
00583				LoopAnim  ('idleA_leftpain',  RandRange(0.8, 1.2), 0.1);
00584			else
00585				LoopAnim  ('idleA_rightpain', RandRange(0.8, 1.2), 0.1);
00586		}
00587		else if (Weapon==None)
00588			LoopAnim  ('idleA',           RandRange(0.8, 1.2), 0.1);
00589		else if (Weapon.IsA('Torch'))
00590			LoopAnim  ('idleA_torch',     RandRange(0.8, 1.2), 0.1);
00591		else
00592			LoopAnim  ('w_idleA',         RandRange(0.8, 1.2), 0.1);
00593	}
00594	function PlayMoving(optional float tween)
00595	{
00596		if (bHurrying)
00597		{
00598			if (Weapon==None)						LoopAnim  ('w_gallopB',		1.0, tween);
00599			else if (Weapon.IsA('Torch'))			LoopAnim  ('w_gallopB_torch', 1.0, tween);
00600			else									LoopAnim  ('w_gallopB_weapon',1.0, tween);
00601		}
00602		else
00603		{
00604			if (Weapon==None)						LoopAnim  ('walkB',			1.0, tween);
00605			else									LoopAnim  ('walkB',			1.0, tween);
00606		}
00607	}
00608	function PlayJumping(optional float tween)    { PlayAnim  ('jump',			1.0, tween);   }
00609	function PlayHuntStop(optional float tween)   { LoopAnim  ('w_idleA',		1.0, tween);   }
00610	function PlayMeleeHigh(optional float tween)
00611	{
00612		if (Weapon==None)							PlayAnim  ('swipe',			1.0, tween);
00613		else										PlayAnim  ('attackb',		1.0, tween);
00614	}
00615	function PlayMeleeLow(optional float tween)
00616	{
00617		if (Weapon==None)							PlayAnim  ('swipe',			1.0, tween);
00618		else										PlayAnim  ('attackb',		1.0, tween);
00619	}
00620	function PlayTurning(optional float tween)
00621	{
00622		local int YawErr;
00623		YawErr = Abs(DesiredRotation.Yaw - Rotation.Yaw);
00624		//TODO: based on turn rate, turn amount, vary length of hop
00625													PlayAnim  ('hopA',			1.0, tween);
00626	}
00627	function PlayCower(optional float tween)      { LoopAnim  ('cower',			1.0, tween);   }
00628	function PlayThrowing(optional float tween)   { PlayAnim  ('throwA',		1.0, tween);   }
00629	function PlayTaunting(optional float tween)   { PlayAnim  ('pain',			1.0, tween);   }
00630	function PlayInAir(optional float tween)
00631	{
00632		LoopAnim  ('blockhigh',	1.0, tween);
00633	}
00634	
00635	function PlayDodgeLeft(optional float tween)  { PlayAnim  ('rollleft',		1.0, tween);   }
00636	function PlayDodgeRight(optional float tween) { PlayAnim  ('rollright',		1.0, tween);   }
00637	function PlayDodgeForward(optional float tween){PlayAnim  ('hopA',			1.0, tween);   }
00638	function PlayDodgeBack(optional float tween)  { PlayAnim  ('dodgeback',		1.0, tween);   }
00639	function PlayDodgeBackflip(optional float tween){PlayAnim ('flip2',			1.0, tween);   }
00640	function PlayDodgeDuck(optional float tween)  { PlayAnim  ('duck',			1.0, tween);   }
00641	function PlayBlockHigh(optional float tween)  { LoopAnim  ('blocklow',		1.0, tween);   }
00642	function PlayBlockLow(optional float tween)   { LoopAnim  ('blocklow',		1.0, tween);   }
00643	
00644	function PlayFrontHit(float tween)            { PlayAnim  ('pain',			1.0, tween);   }
00645	function PlayDrowning(optional float tween)   { LoopAnim  ('drown',			1.0, tween);   }
00646	
00647	function PlayDeath(name DamageType)
00648	{
00649		if (FRand()<0.5)
00650												  PlayAnim('death',			1.0, 0.1);	
00651		else
00652												  PlayAnim('deathB',		1.0, 0.1);	
00653	}
00654	function PlayBackDeath(name DamageType)		{ PlayAnim('deathS',		1.0, 0.1);	}
00655	function PlayLeftDeath(name DamageType)		{ PlayAnim('deathR',		1.0, 0.1);	}
00656	function PlayRightDeath(name DamageType)	{ PlayAnim('deathB',		1.0, 0.1);	}
00657	function PlayHeadDeath(name DamageType)		{ PlayAnim('deathF',		1.0, 0.1);	}
00658	function PlaySkewerDeath(name DamageType)	{ PlayAnim('death',			1.0, 0.1);	}
00659	function PlayDrownDeath(name DamageType)	{ PlayAnim('drown_death',	1.0, 0.1);	}
00660	
00661	// Tween functions
00662	function TweenToWaiting(float time)
00663	{	if (BodyPartHealth[BODYPART_LARM1] <= 0 ^^
00664			BodyPartHealth[BODYPART_RARM1] <= 0)
00665		{	if (BodyPartHealth[BODYPART_LARM1]<=0)	TweenAnim ('idleA_leftpain',	time);
00666			else									TweenAnim ('idleA_rightpain',	time);
00667		}
00668		else if (Weapon==None)						TweenAnim ('idleA',				time);
00669		else if (Weapon.IsA('Torch'))				TweenAnim ('idleA_torch',		time);
00670		else										TweenAnim ('w_idleA',			time);
00671	}
00672	function TweenToMoving(float time)
00673	{
00674		if (bHurrying)
00675		{
00676			if (Weapon==None)						TweenAnim ('w_gallopB',			time);
00677			else if (Weapon.IsA('Torch'))			TweenAnim ('w_gallopB_torch',	time);
00678			else									TweenAnim ('w_gallopB_weapon',	time);
00679		}
00680		else
00681		{
00682			if (Weapon==None)						TweenAnim ('walkB',				time);
00683			else									TweenAnim ('walkB',				time);
00684		}
00685	}
00686	function TweenToTurning(float time)           {	TweenAnim ('hopA',				time);	}
00687	function TweenToJumping(float time)           {	TweenAnim ('jump',				time);	}
00688	function TweenToHuntStop(float time)          { TweenAnim ('w_idlaA',			time);	}
00689	function TweenToMeleeHigh(float time)
00690	{
00691		if (Weapon==None)							TweenAnim ('swipe',				time);
00692		else										TweenAnim ('attackb',			time);
00693	}
00694	function TweenToMeleeLow(float time)
00695	{
00696		if (Weapon==None)							TweenAnim ('swipe',				time);
00697		else										TweenAnim ('attackb',			time);
00698	}
00699	function TweenToThrowing(float time)          { TweenAnim ('throwA',			time);	}
00700	
00701	
00702	//------------------------------------------------
00703	//
00704	// Sound Functions
00705	//
00706	//------------------------------------------------
00707	function Breath()
00708	{
00709		local int joint;
00710		local vector l;
00711		local actor A;
00712	
00713		if (++breathcounter > 2)
00714		{
00715			breathcounter = 0;
00716			OpenMouth(0.5, 0.5);
00717	
00718			if (HeadRegion.Zone.bWaterZone)
00719			{
00720				// Spawn Bubbles
00721				joint = JointNamed('jaw');
00722				l = GetJointPos(joint);
00723				if(FRand() < 0.3)
00724				{
00725					Spawn(class'BubbleSystemOneShot',,, l,);
00726				}
00727			}
00728			else
00729			{
00730				PlaySound(BreathSound, SLOT_Interface,,,, 1.0 + FRand()*0.2-0.1);
00731			}
00732		}
00733		else
00734		{
00735			OpenMouth(0.0, 0.3);
00736		}
00737	}
00738	
00739	
00740	//===================================================================
00741	//					States
00742	//===================================================================
00743	State MineRocks
00744	{
00745	Begin:
00746		LoopAnim('attackb', 1.0, 0.3);
00747		Sleep(3);
00748		FinishAnim();
00749	
00750		TossWeapon();
00751	
00752		OrderObject = NextPoint;
00753		GotoState(NextState, NextLabel);
00754	}
00755	
00756	
00757	//============================================================
00758	//
00759	// MinRocksPartner1
00760	//
00761	//============================================================
00762	state MineRocksPartner1
00763	{
00764		ignores SeePlayer, HearNoise, EnemyAcquired;
00765	
00766	Begin:
00767	Swing:
00768		PlayAnim('attackb', 1.0, 0.2);
00769		FinishAnim();
00770		ReleaseTagged('miner2');
00771	WaitForPartner:
00772		LoopAnim('w_idleA', 1.0, 0.2);
00773		WaitForRelease();
00774		FinishAnim();
00775		goto('Swing');
00776	}
00777	
00778	
00779	//============================================================
00780	//
00781	// MineRocksPartner2
00782	//
00783	//============================================================
00784	state MineRocksPartner2
00785	{
00786		ignores SeePlayer, HearNoise, EnemyAcquired;
00787	
00788	Begin:
00789		Goto('WaitForPartner');
00790	Swing:
00791		PlayAnim('attackb', 1.0, 0.2);
00792		FinishAnim();
00793		ReleaseTagged('miner1');
00794	WaitForPartner:
00795		LoopAnim('w_idleA', 1.0, 0.2);
00796		WaitForRelease();
00797		FinishAnim();
00798		goto('Swing');
00799	}
00800	
00801	
00802	//============================================================
00803	//
00804	// ThrowTest
00805	//
00806	//============================================================
00807	state ThrowTest
00808	{
00809		function Timer()
00810		{
00811			if (DesiredMouthRot == 0)
00812				OpenMouth(0.5, 0.5);
00813			else
00814				OpenMouth(0.0, 0.5);
00815			SetTimer(0.5, false);
00816		}
00817	
00818		function SpawnRock()
00819		{
00820			local RockSmall rock;
00821			rock = Spawn(class'RockSmall');
00822			AttachActorToJoint(rock, JointNamed('weapon'));
00823		}
00824	
00825	Begin:
00826		PlayWaiting(0.2);
00827		OpenMouth(1, 1.0);
00828		Sleep(0.2);
00829		SetTimer(0.2, false);
00830		SpawnRock();
00831		Sleep(5);
00832		NextState = 'ThrowTest';
00833		NextLabel = 'Begin';
00834		OrderObject = ActorTagged('throwtarget');
00835		GotoState('Throwing');
00836	}
00837	
00838	
00839	//============================================================
00840	//
00841	// AlertSquawk
00842	//
00843	//============================================================
00844	State AlertSquawk
00845	{
00846	Begin:
00847		LookAt(Enemy);
00848		SoundChance(AcquireSound, 1.0);
00849		OpenMouth(1, 1);
00850		Sleep(0.3);
00851		OpenMouth(0, 1);
00852		Sleep(2);
00853		Goto('InformTeam');
00854	}
00855	
00856	State Excited
00857	{
00858		function EnemyNotVisible()
00859		{
00860			GotoState('Startup', 'Restart');
00861		}
00862	
00863	Begin:
00864		if (Enemy == None)
00865			Enemy = Pawn(OrderObject);
00866	
00867		LookAt(Enemy);
00868		PlayTurning();
00869		TurnToward(Enemy);
00870		FinishAnim();
00871	
00872	Jump:
00873		if (FRand()<0.5)
00874		{
00875			PlayAnim('hopA', 1.0, 0.2);
00876			AddVelocity(vect(0,0,200));
00877			WaitForLanding();
00878			FinishAnim();
00879		}
00880		else
00881		{
00882			TweenAnim('flip', 0.2);
00883			FinishAnim();
00884			PlayAnim('flip', 1.0);
00885			Sleep(0.2);
00886			AddVelocity(vect(0,0,500));
00887			WaitForLanding();
00888			FinishAnim();
00889		}
00890		if (FRand() < 0.8)
00891			Goto('Jump');
00892	
00893		PlayWaiting();
00894		Sleep(RandRange(1.5, 4));
00895		FinishAnim();
00896		Goto('Jump');
00897	}
00898	
00899	//============================================================
00900	//
00901	// ThrowRocks
00902	//
00903	//============================================================
00904	State ThrowRocks
00905	{
00906	ignores SeePlayer, HearNoise, EnemyAcquired;
00907	
00908		function EnemyNotVisible()
00909		{
00910			bAlerted = false;
00911			OrderFinished();
00912			GotoState('Startup', 'Restart');
00913		}
00914		
00915		function SpawnRock()
00916		{
00917			local RockSmall rock;
00918			rock = Spawn(class'RockSmall');
00919			AttachActorToJoint(rock, JointNamed('weapon'));
00920		}
00921	
00922		function bool EnemyInRange(float range)
00923		{
00924			return VSize2D(Enemy.Location - Location) < range;
00925		}
00926		
00927	Begin:
00928		PlayWaiting(0.2);
00929	
00930		if (EnemyInRange(300))
00931			GotoState('Acquisition');
00932	
00933		if (Enemy == None || !EnemyInRange(1000))
00934		{
00935			Sleep(2);
00936			Goto('Begin');
00937		}
00938		SpawnRock();
00939		Sleep(3);
00940		NextState = 'ThrowRocks';
00941		NextLabel = 'Begin';
00942		OrderObject = Enemy;
00943		GotoState('Throwing');
00944	
00945	}
00946	
00947	
00948	//============================================================
00949	//
00950	// StayOnPaths
00951	//
00952	//============================================================
00953	State StayOnPaths
00954	{
00955	ignores SeePlayer, EnemyNotVisible, HearNoise;
00956	
00957		function BeginState()
00958		{
00959			bHurrying = false;
00960			UpdateMovementSpeed();
00961		}
00962	
00963		function EndState()
00964		{
00965			LookAt(None);
00966		}
00967	
00968		// Consider all destinations from OrderObject to stay close to enemy
00969		function PickDestination()
00970		{
00971			local int numpaths, numpaths2, i, j;
00972			local NavigationPoint dest, dest2, closest;
00973			local float closestdist, dist;
00974	
00975			if (NavigationPoint(CurrentPoint) != None)
00976			{
00977				numpaths = NavigationPoint(CurrentPoint).NumPaths();
00978				closest = NavigationPoint(CurrentPoint);
00979				closestdist = VSize(Enemy.Location - CurrentPoint.Location);
00980				for (i=0; i<numpaths; i++)
00981				{
00982					// Consider one step down the graph
00983					dest = NavigationPoint(NavigationPoint(CurrentPoint).PathEndPoint(i));
00984					if (dest != None)
00985					{
00986						dist = VSize(Enemy.Location - dest.Location);
00987						if (dist < closestdist)
00988						{
00989							closestdist = dist;
00990							closest = dest;
00991						}
00992	
00993						// Consider two steps down the graph
00994						numpaths2 = dest.NumPaths();
00995						for (j=0; j<numpaths2; j++)
00996						{
00997							dest2 = NavigationPoint(dest.PathEndPoint(j));
00998							if (dest2!=None)
00999							{
01000								dist = VSize(Enemy.Location - dest2.Location);
01001								if (dist < closestdist)
01002								{
01003									closestdist = dist;
01004									closest = dest;
01005								}
01006							}
01007						}
01008					}
01009				}
01010				OrderObject = closest;
01011			}
01012		}
01013	
01014		function HitWall(vector norm, actor wall)
01015		{
01016	//		slog("hit wall");
01017		}
01018		
01019		function MayFall()
01020		{
01021	//		slog("may fall");
01022		}
01023		
01024	Begin:
01025		LookAt(Enemy);
01026	OrderObject = None;
01027		// Stay on paths, staying as close to enemy as possible
01028		if (OrderObject == None)
01029			OrderObject = NearestNavPoint();
01030		Goto('path');
01031		
01032		// OrderObject is my final destination
01033		// MoveTarget is my next waypoint
01034		
01035	Move:
01036	Path:
01037	//slog("orderobject="$OrderObject.name);
01038		if (actorReachable(OrderObject))
01039		{
01040	//	slog("..reachable - oo="$OrderObject);
01041			PlayMoving(0.2);
01042			MoveToward(OrderObject, MovementSpeed);
01043			FinishAnim();
01044			CurrentPoint = OrderObject;
01045			Goto('Turn');
01046		}
01047		else if (FindBestPathToward(OrderObject))
01048		{
01049	//	slog("..not reachable - path="$MoveTarget);
01050			PlayMoving(0.1);
01051			MoveToward(MoveTarget, MovementSpeed);
01052			FinishAnim();
01053			CurrentPoint = MoveTarget;
01054			Goto('Path');
01055		}
01056		else
01057		{
01058	//	slog("..not pathable - picking dest");
01059			PickDestination();
01060			if (CurrentPoint != OrderObject && OrderObject != None)
01061			{
01062				Sleep(0);
01063				Goto('Move');
01064			}
01065			FinishAnim();
01066			Acceleration = vect(0,0,0);
01067			Goto('Wait');
01068		}
01069	
01070	Turn:	
01071		PlayTurning();
01072		TurnToward(Enemy);
01073		FinishAnim();
01074		SoundChance(RoamSound, 1.0);
01075	
01076	Wait:
01077		PlayWaiting();
01078		Sleep(1);
01079	
01080	Check:
01081		if (VSize(Enemy.Location-Location) < 4000)
01082		{
01083			OrderObject = Enemy.NearestNavPoint();
01084			if (OrderObject != None && OrderObject != CurrentPoint)
01085			{
01086				Goto('Move');
01087			}
01088		}
01089		
01090		Sleep(2);
01091		Goto('Check');
01092	}
01093	
01094	
01095	//================================================
01096	//
01097	// Charging
01098	//
01099	//================================================
01100	State Charging
01101	{
01102	ignores EnemyAcquired, SeePlayer, HearNoise;
01103	
01104		// Inherits EndState()
01105	
01106		function BeginState()
01107		{
01108			StopLookingToward();
01109			LookAt(Enemy);
01110			SetTimer(0.1, true);
01111			bHurrying = true;
01112			UpdateMovementSpeed();
01113		}
01114		
01115	
01116		function MayFall()
01117		{	// Only jump if reachable
01118			bCanJump = ( (Enemy!=None) && actorReachable(Enemy) );
01119		}
01120	
01121		function Timer()
01122		{
01123			if (Enemy!=None && (Enemy.bSwingingHigh || Enemy.bSwingingLow) && InMeleeRange(Enemy))
01124			{
01125				GotoState('Fighting', 'CheckDefend');
01126			}
01127		}
01128	
01129		function EnemyNotVisible()
01130		{
01131			GotoState('Hunting');
01132		}
01133		
01134		function PickPaceDest()
01135		{
01136			local vector X,Y,Z;
01137			local vector PaceVect;
01138			
01139			GetAxes(Enemy.Rotation, X, Y, Z);
01140			PaceVect = Y * PaceRange * 0.3; // 1/3 the distance -- cjr
01141			if (FRand() < 0.5)
01142			{
01143				Destination = Enemy.Location + PaceVect;
01144			}
01145			else
01146			{
01147				Destination = Enemy.Location - PaceVect;
01148			}
01149		}
01150		
01151		function HitWall(vector HitNormal, actor Wall)
01152		{
01153			if (Physics == PHYS_Falling)
01154				return;
01155				
01156			//TODO: Test whether climbable, then set falling
01157			if (bCanGrabEdges && bCanStrafe)
01158			{
01159				SetPhysics(PHYS_Falling);
01160				return;
01161			}
01162			
01163			if ( Wall.IsA('Mover') && Mover(Wall).HandleDoor(self) )
01164			{
01165				if ( SpecialPause > 0 )
01166					Acceleration = vect(0,0,0);
01167				GotoState('Charging', 'SpecialNavig');
01168				return;
01169			}
01170			Focus = Destination;
01171			if (PickWallAdjust())
01172				GotoState('Charging', 'AdjustFromWall');
01173			else
01174				MoveTimer = -1.0;
01175		}
01176		
01177	AdjustFromWall:
01178		StrafeTo(Destination, Focus); 
01179		Goto('CloseIn');
01180	
01181	ResumeCharge:
01182		PlayMoving();
01183		Goto('Charge');
01184	
01185	Begin:
01186		if(debugstates) slog(name@"GoblinCharging");
01187	
01188	TweenIn:
01189		TweenToMoving(0.15);
01190		FinishAnim();
01191		PlayMoving();
01192	
01193	Charge:
01194		bFromWall = false;
01195		
01196	CloseIn:
01197		if ( JumpZ > 0 )		// This may have been set false in Mayfall, so reset
01198			bCanJump = true;
01199		if ( !ValidEnemy() )
01200			GotoState('GoingHome');
01201	
01202		if ( Enemy.Region.Zone.bWaterZone )
01203		{	// Enemy entered water zone
01204			if (!bCanSwim)
01205				GotoState('TacticalDecision');
01206		}
01207		else if (!bCanFly && !bCanWalk)
01208			// Enemy left water zone
01209			GotoState('GoingHome');
01210	
01211		if (Physics == PHYS_Falling)
01212		{	// If falling, wait for land
01213			if (NeedToTurn(Enemy.Location))
01214			{
01215				DesiredRotation = Rotator(Enemy.Location - Location);
01216				Focus = Enemy.Location;
01217				Destination = Enemy.Location;
01218			}
01219			WaitForLanding();
01220		}
01221	
01222	Pace:
01223		if( actorReachable(Enemy) )
01224		{
01225			if (bPaceAttack && FRand() < 0.75 && InPaceRange(Enemy))
01226			{
01227				PlayMoving(0.2);
01228				PickPaceDest();
01229				MoveTo(Destination, MovementSpeed);
01230				PlayTurning(0.1);
01231				TurnToward(Enemy);
01232				SoundChance(PaceSound, 1.0);
01233				FinishAnim();
01234				PlayMoving(0.2);
01235				if (FRand() < 0.5)
01236				{	// Wait and continue pacing
01237					Acceleration = vect(0,0,0);
01238					SoundChance(ThreatenSound, 0.3);
01239					Goto('Pace');
01240				}
01241			}
01242	
01243			if (bLungeAttack && FRand()<0.3 && InLungeRange(Enemy))
01244			{
01245				Goto('GotThere');
01246			}
01247			
01248			SoundChance(ThreatenSound, 0.3);
01249			MoveToward(Enemy, MovementSpeed);
01250			if (bFromWall)
01251			{
01252				bFromWall = false;
01253				if (PickWallAdjust())
01254					StrafeFacing(Destination, Enemy);
01255				else
01256					GotoState('TacticalDecision');
01257			}
01258		}
01259		else
01260		{
01261	NoReach:
01262			bCanSwing = false;
01263			bFromWall = false;
01264			if (!FindBestPathToward(Enemy))
01265			{	// unreachable and unpathable
01266				GotoState('Hunting');
01267			}
01268	Moving:
01269			if (VSize(MoveTarget.Location - Location) < 2.5 * CollisionRadius)
01270			{
01271				bCanSwing = true;
01272				StrafeFacing(MoveTarget.Location, Enemy);
01273			}
01274			else
01275			{
01276				if ( !bCanStrafe || !LineOfSightTo(Enemy) ||
01277					(Skill - 2 * FRand() + (Normal(Enemy.Location - Location - vect(0,0,1) * (Enemy.Location.Z - Location.Z)) 
01278						Dot Normal(MoveTarget.Location - Location - vect(0,0,1) * (MoveTarget.Location.Z - Location.Z))) < 0) )
01279				{
01280					MoveToward(MoveTarget, MovementSpeed);
01281				}
01282				else
01283				{
01284					bCanSwing = true;
01285					StrafeFacing(MoveTarget.Location, Enemy);	
01286				}
01287				if ( !bFromWall )
01288					SoundChance(ThreatenSound, 0.8);
01289			}
01290		}
01291	
01292	CheckDistance:
01293		if ( (bLungeAttack && !InLungeRange(Enemy)) || !InMeleeRange(Enemy))
01294		{	// Lunge Attack
01295			MoveTimer = 0.0;
01296			bFromWall = false;
01297			Goto('CloseIn');
01298		}
01299		Goto('GotThere');
01300	
01301	ResumeFromFighting:
01302		if ( !ValidEnemy() )
01303			GotoState('GoingHome');
01304		if ( (bLungeAttack && !InLungeRange(Enemy)) || !InMeleeRange(Enemy))
01305		{
01306			MoveTimer = 0.0;
01307			Goto('TweenIn');
01308		}
01309	
01310	GotThere:
01311		Target = Enemy;
01312		GotoState('Fighting');
01313	}
01314	
01315	
01316	//================================================
01317	//
01318	// Fighting
01319	//
01320	//================================================
01321	State Fighting
01322	{
01323	ignores EnemyAcquired;
01324	
01325		function BeginState()
01326		{
01327			LookAt(Enemy);
01328			SetTimer(0.2, true);
01329		}
01330	
01331		function EndState()
01332		{
01333			bSwingingHigh = false;
01334			bSwingingLow  = false;
01335			if (Weapon!=None)
01336			{
01337				Weapon.FinishAttack();
01338				SwipeEffectEnd();
01339			}
01340			LookAt(None);
01341			SetTimer(0, false);
01342		}
01343	
01344		function AmbientSoundTimer()
01345		{
01346			PlayAmbientFightSound();
01347		}
01348	
01349		function bool BlockRatherThanDodge()
01350		{
01351			if (Shield == None)
01352				return false;
01353	
01354			if (EnemyIncidence != INC_FRONT)
01355				return false;
01356	
01357			return (FRand() < BlockChance);
01358		}
01359	
01360		function Timer()
01361		{
01362			if (Enemy != None && ShouldDefend())
01363			{
01364				GotoState('Fighting', 'Defend');
01365			}
01366		}
01367	
01368		function bool ShouldDefend()
01369		{
01370			return (FRand() > FightOrDefend && InDangerFromAttack());
01371		}
01372		
01373		function bool InDangerFromAttack()
01374		{
01375			if ((!Enemy.bSwingingHigh) && (!Enemy.bSwingingLow))
01376				return false;
01377	
01378			GetEnemyProximity();
01379				
01380			if (EnemyDist>CollisionRadius+Enemy.CollisionRadius+Enemy.MeleeRange)
01381				return false;
01382	
01383			return (EnemyVertical==VERT_LEVEL && EnemyFacing==FACE_FRONT);
01384		}
01385	
01386	Begin:
01387		if(debugstates) SLog(name@"GoblinFighting");
01388		Acceleration = vect(0,0,0);
01389		GetEnemyProximity();
01390	
01391		// Turn to face enemy
01392		DesiredRotation.Yaw = rotator(Enemy.Location-Location).Yaw;
01393	
01394	Fight:
01395		// Decide high or low
01396		if (FRand() < HighOrLow)
01397		{
01398			TweenToMeleeHigh(0.1);
01399			bSwingingHigh = true;
01400		}
01401		else
01402		{
01403			TweenToMeleeLow(0.1);
01404			bSwingingLow = true;
01405		}
01406		FinishAnim();
01407	
01408		if (bLungeAttack && !InMeleeRange(Enemy))
01409		{	// Lunge
01410			AddVelocity( CalcArcVelocity(8000, Location, Enemy.Location) );
01411		}
01412	
01413		if (bSwingingHigh)
01414			PlayMeleeHigh(0.1);
01415		else
01416			PlayMeleeLow(0.1);
01417		FinishAnim();
01418		bSwingingHigh = false;
01419		bSwingingLow  = false;
01420	
01421		Sleep(TimeBetweenAttacks);
01422		
01423		// Good chance of dodge back here (after attacking)
01424		if (bDodgeAfterAttack)
01425		{
01426			if (FRand() < 0.5)
01427				GotoState('DodgeBack');
01428			else if (FRand() < 0.5)
01429				GotoState('DodgeLeft');
01430			else
01431				GotoState('DodgeRight');
01432		}
01433	
01434		GotoState('Charging', 'ResumeFromFighting');
01435	
01436	CheckDefend:
01437		Timer();
01438		GotoState('Charging', 'ResumeFromFighting');
01439	
01440	Defend:
01441		if (BlockRatherThanDodge())
01442		{	// Try Block
01443			Disable('Timer');
01444			if (FRand() < HighOrLowBlock)
01445			{	// Block High
01446				ActivateShield(true);
01447				PlayBlockHigh(0.1);
01448				Sleep(RandRange(1, 2));
01449				FinishAnim();
01450				ActivateShield(false);
01451			}
01452			else
01453			{	// Block Low
01454				ActivateShield(true);
01455				PlayBlockLow(0.1);
01456				Sleep(RandRange(1, 2));
01457				FinishAnim();
01458				ActivateShield(false);
01459			}
01460			Enable('Timer');
01461		}
01462		else
01463		{	// Try Dodge
01464		
01465			//TODO: handle above/below/level - high/low/vertical
01466			
01467			switch(EnemyIncidence)
01468			{
01469			case INC_RIGHT:
01470				GotoState('DodgeLeft');
01471				break;
01472			case INC_LEFT:
01473				if (FRand() < 0.5)
01474					GotoState('DodgeRight');
01475				else
01476					GotoState('DodgeBack');
01477				break;
01478			case INC_FRONT:
01479				if (FRand() < 0.5)
01480					GotoState('DodgeBackFlip');
01481				else
01482					GotoState('DodgeBack');
01483				break;
01484			case INC_BACK:
01485				if (FRand() < 0.5)
01486					GotoState('DodgeForward');
01487				else if (FRand() < 0.5)
01488					GotoState('DodgeLeft');
01489				else
01490					GotoState('DodgeRight');
01491				break;
01492			}
01493		}
01494	
01495	BackFromSubState:
01496		GotoState('Charging', 'ResumeFromFighting');
01497	}
01498	
01499	
01500	//================================================
01501	//
01502	// Fighting Sub-States
01503	//
01504	// Impliment in sub-classes
01505	//
01506	//================================================
01507	State DodgeForward
01508	{
01509		ignores SeePlayer, HearNoise, EnemyAcquired;
01510	
01511		function AmbientSoundTimer()
01512		{
01513			PlayAmbientFightSound();
01514		}
01515	
01516	Begin:
01517		if(debugstates) SLog(name@"DodgeForward");
01518		Acceleration = vect(0,0,0);
01519		Velocity = vect(0,0,0);
01520		PlayDodgeForward();
01521		AddLocalVelocity(300, 0, 50);
01522		FinishAnim();
01523		WaitForLanding();
01524		PlayLanded(Velocity.Z);
01525	
01526		GotoState('Fighting', 'BackFromSubstate');
01527	}
01528	State DodgeBack
01529	{
01530		ignores SeePlayer, HearNoise, EnemyAcquired;
01531	
01532		function AmbientSoundTimer()
01533		{
01534			PlayAmbientFightSound();
01535		}
01536	
01537	Begin:
01538		if(debugstates) SLog(name@"DodgeBack");
01539		Acceleration = vect(0,0,0);
01540		Velocity = vect(0,0,0);
01541		PlayDodgeBack();
01542		AddLocalVelocity(-200, 0, 50);
01543		FinishAnim();
01544		WaitForLanding();
01545		PlayLanded(Velocity.Z);
01546	
01547		GotoState('Fighting', 'BackFromSubstate');
01548	}
01549	State DodgeRight
01550	{
01551		ignores SeePlayer, HearNoise, EnemyAcquired;
01552		
01553		function AmbientSoundTimer()
01554		{
01555			PlayAmbientFightSound();
01556		}
01557	
01558	Begin:
01559		if(debugstates) SLog(name@"DodgeRight");
01560		Acceleration = vect(0,0,0);
01561		PlayDodgeRight();
01562		AddLocalVelocity(0, 300, 100);
01563		SoundChance(RollSound, 1.0);
01564		FinishAnim();
01565		WaitForLanding();
01566		GotoState('Fighting', 'BackFromSubstate');
01567	}
01568	State DodgeLeft
01569	{
01570		ignores SeePlayer, HearNoise, EnemyAcquired;
01571		
01572		function AmbientSoundTimer()
01573		{
01574			PlayAmbientFightSound();
01575		}
01576	
01577	Begin:
01578		if(debugstates) SLog(name@"DodgeLeft");
01579		Acceleration = vect(0,0,0);
01580		PlayDodgeLeft();
01581		AddLocalVelocity(0, -300, 100);
01582		SoundChance(RollSound, 1.0);
01583		FinishAnim();
01584		WaitForLanding();
01585		GotoState('Fighting', 'BackFromSubstate');
01586	}
01587	State DodgeBackflip
01588	{
01589		ignores SeePlayer, HearNoise, EnemyAcquired;
01590		
01591		function AmbientSoundTimer()
01592		{
01593			PlayAmbientFightSound();
01594		}
01595	
01596	Begin:
01597		if(debugstates) SLog(name@"DodgeBackflip");
01598		Acceleration = vect(0,0,0);
01599		Velocity = vect(0,0,0);
01600		PlayDodgeBackFlip();
01601		AddLocalVelocity(-200, 0, 400);
01602		FinishAnim();
01603		WaitForLanding();
01604		PlayLanded(Velocity.Z);
01605		
01606		GotoState('Fighting', 'BackFromSubstate');
01607	}
01608	
01609	
01610	//===================================================================
01611	//					Debug (temp)
01612	//===================================================================
01613	
01614	simulated function Debug(canvas Canvas, int mode)
01615	{
01616		Super.Debug(Canvas, mode);
01617	
01618		Canvas.DrawText("Goblin:");
01619		Canvas.CurY -= 8;
01620		Canvas.DrawText(" MoveTimer:"@MoveTimer);
01621		Canvas.CurY -= 8;
01622	}
01623	
01624	defaultproperties
01625	{
01626	     bLungeAttack=True
01627	     FightOrFlight=0.990000
01628	     FightOrDefend=0.500000
01629	     HighOrLow=1.000000
01630	     LatOrVertDodge=0.800000
01631	     HighOrLowBlock=0.500000
01632	     BlockChance=0.500000
01633	     LungeRange=200.000000
01634	     PaceRange=200.000000
01635	     HuntTime=60.000000
01636	     BreathSound=Sound'CreaturesSnd.Goblin.goblinbreath03'
01637	     AcquireSound=Sound'CreaturesSnd.Goblin.goblinamb04'
01638	     AmbientWaitSounds(0)=Sound'CreaturesSnd.Goblin.goblinbreath02'
01639	     AmbientWaitSounds(1)=Sound'CreaturesSnd.Goblin.goblinamb02'
01640	     AmbientWaitSounds(2)=Sound'CreaturesSnd.Goblin.goblinamb01'
01641	     AmbientFightSounds(0)=Sound'CreaturesSnd.Goblin.goblinattack17'
01642	     AmbientFightSounds(1)=Sound'CreaturesSnd.Goblin.goblinattack01'
01643	     AmbientFightSounds(2)=Sound'CreaturesSnd.Goblin.goblinattack18'
01644	     AmbientWaitSoundDelay=7.500000
01645	     AmbientFightSoundDelay=5.000000
01646	     StartWeapon=Class'RuneI.boneclub'
01647	     StartShield=Class'RuneI.GoblinShield'
01648	     ShadowScale=1.000000
01649	     A_PullUp=ClimbB
01650	     A_StepUp=ClimbC
01651	     bCanStrafe=True
01652	     bCanGrabEdges=True
01653	     MeleeRange=30.000000
01654	     GroundSpeed=260.000000
01655	     WaterSpeed=100.000000
01656	     JumpZ=500.000000
01657	     MaxStepHeight=50.000000
01658	     WalkingSpeed=54.000000
01659	     ClassID=5
01660	     Visibility=150
01661	     SightRadius=1500.000000
01662	     PeripheralVision=-0.500000
01663	     Health=50
01664	     BodyPartHealth(1)=35
01665	     BodyPartHealth(3)=35
01666	     BodyPartHealth(5)=40
01667	     UnderWaterTime=2.000000
01668	     Intelligence=BRAINS_HUMAN
01669	     HitSound1=Sound'CreaturesSnd.Goblin.goblinhit08'
01670	     HitSound2=Sound'CreaturesSnd.Goblin.goblinhit16'
01671	     HitSound3=Sound'CreaturesSnd.Goblin.goblinhit28'
01672	     Die=Sound'CreaturesSnd.Goblin.goblindeath06'
01673	     Die2=Sound'CreaturesSnd.Goblin.goblindeath13'
01674	     Die3=Sound'CreaturesSnd.Goblin.goblindeath16'
01675	     LandGrunt=Sound'CreaturesSnd.Goblin.goblinhit32'
01676	     FootStepWood(0)=Sound'FootstepsSnd.Earth.footlandearth08'
01677	     FootStepWood(1)=Sound'FootstepsSnd.Earth.footlandearth08'
01678	     FootStepWood(2)=Sound'FootstepsSnd.Earth.footlandearth08'
01679	     FootStepMetal(0)=Sound'FootstepsSnd.Metal.footmetal07'
01680	     FootStepMetal(1)=Sound'FootstepsSnd.Metal.footmetal07'
01681	     FootStepMetal(2)=Sound'FootstepsSnd.Metal.footmetal07'
01682	     FootStepStone(0)=Sound'FootstepsSnd.Earth.footlandearth09'
01683	     FootStepStone(1)=Sound'FootstepsSnd.Earth.footlandearth09'
01684	     FootStepStone(2)=Sound'FootstepsSnd.Earth.footlandearth09'
01685	     FootStepFlesh(0)=Sound'FootstepsSnd.Earth.footlandearth09'
01686	     FootStepFlesh(1)=Sound'FootstepsSnd.Earth.footlandearth09'
01687	     FootStepFlesh(2)=Sound'FootstepsSnd.Earth.footlandearth09'
01688	     FootStepIce(0)=Sound'FootstepsSnd.Earth.footlandearth09'
01689	     FootStepIce(1)=Sound'FootstepsSnd.Earth.footlandearth09'
01690	     FootStepIce(2)=Sound'FootstepsSnd.Earth.footlandearth09'
01691	     FootStepEarth(0)=Sound'FootstepsSnd.Earth.footlandearth09'
01692	     FootStepEarth(1)=Sound'FootstepsSnd.Earth.footlandearth09'
01693	     FootStepEarth(2)=Sound'FootstepsSnd.Earth.footlandearth09'
01694	     FootStepSnow(0)=Sound'FootstepsSnd.Earth.footlandearth09'
01695	     FootStepSnow(1)=Sound'FootstepsSnd.Earth.footlandearth09'
01696	     FootStepSnow(2)=Sound'FootstepsSnd.Earth.footlandearth09'
01697	     bCanLook=True
01698	     LookDegPerSec=720.000000
01699	     MaxMouthRot=8192
01700	     MaxMouthRotRate=65535
01701	     DeathRadius=32.000000
01702	     DeathHeight=6.000000
01703	     bLeadEnemy=True
01704	     AnimSequence=idleA
01705	     TransientSoundRadius=1000.000000
01706	     CollisionRadius=16.000000
01707	     CollisionHeight=32.000000
01708	     Mass=50.000000
01709	     Buoyancy=35.000000
01710	     RotationRate=(Pitch=0,Yaw=45000,Roll=0)
01711	     Skeletal=SkelModel'creatures.Goblin'
01712	}

End Source Code