1
0
KF2-Dev-Scripts/GameFramework/Classes/GameCrowdAgent.uc
2020-12-13 18:01:13 +03:00

1557 lines
46 KiB
Ucode

/**
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
*/
class GameCrowdAgent extends CrowdAgentBase
native
abstract
hidecategories(Advanced)
hidecategories(Attachment)
hidecategories(Collision)
hidecategories(Object)
implements(Interface_RVO)
dependson(GameCrowdAgentBehavior)
placeable; // placeable only so LDs can create archetypes
/** Agent group this agent is part of */
var GameCrowdGroup MyGroup;
/** Velocity in the absence of other agent interactions */
var Vector PreferredVelocity;
/** Velocity we will take next physics tick */
var Vector PendingVelocity;
/** Current destination */
var GameCrowdDestination CurrentDestination;
/** Last destination where performed Kismet/Behavior. Cleared when have new destination. Used to keep from looping kismet/behavior at destination. */
var GameCrowdDestination BehaviorDestination;
/** where agent is coming from */
var GameCrowdDestination PreviousDestination;
/** If conforming to ground, this is how much to move the agent each frame between line-trace updates. */
var float InterpZTranslation;
/** Current health of agent */
var() int Health;
/** How long dead body stays around */
var(Behavior) float DeadBodyDuration;
/** Pointer to LightEnvironment */
var const editconst DynamicLightEnvironmentComponent LightEnvironment;
/** Used to count how many frames since the last conform trace. */
var transient int ConformTraceFrameCount;
/** Nearby pawns and agents. Updated periodically using main Octree */
var transient array<NearbyDynamicItem> NearbyDynamics;
/** Whether to use same scale variation in all axes */
var bool bUniformScale;
enum EConformType
{
CFM_NavMesh,
CFM_BSP,
CFM_World,
CFM_None,
};
/** How agent conforms to surfaces */
var(Movement) EConformType ConformType;
/** Whether to have obstacle mesh block agents */
var(Pathing) bool bCheckForObstacles;
/** How far to trace to conform agent to the bsp/world. */
var(Movement) float ConformTraceDist;
/** Every how many frames the ground conforming line check is done. */
var(Movement) int ConformTraceInterval;
/** Current conform interval */
var int CurrentConformTraceInterval;
/** TEMP for debugging - last ground conform hit normal Z*/
var float LastGroundZ;
// Agent force stuff
/** Controls how far around an agent the system looks when finding the average speed. */
var(Pathing) float AwareRadius;
/** The radius used to check overlap between agents (basically how big an agent is). */
var(Pathing) float AvoidOtherRadius;
struct native AvoidOtherSampleItem
{
var() int RotOffset;
var() byte NumMagSamples;
var() bool bFallbackOnly;
};
var(Pathing) array<AvoidOtherSampleItem> AvoidOtherSampleList;
var(Pathing) float PENALTY_COEFF_ANGLETOGOAL;
var(Pathing) float PENALTY_COEFF_ANGLETOVEL;
var(Pathing) float PENALTY_COEFF_MAG;
var(Pathing) float MIN_PENALTY_THRESHOLD;
var(Pathing) float LastProgressTime;
var(Pathing) float LastFallbackActiveTime;
/** If TRUE, use navmesh for pathing */
var(Pathing) bool bUseNavMeshPathing;
var(Pathing) float MaxPathLaneValue;
var(Pathing) float CurrentPathLaneValue;
var(Pathing) int ExtraPathCost;
/** When a 'target' action occurs, agent will rotate to face the CrowdAttractor. This controls how fast that turn happens */
var(Movement) float RotateToTargetSpeed;
/** Crowd agents rotate to face the direction they are travelling. This value limits how quickly they turn to do this, to avoid them spinning too quickly */
var(Movement) float MaxYawRate;
/** Min 3D drawscale to apply to the agent mesh */
var(Rendering) vector MeshMinScale3D;
/** Max 3D drawscale to apply to the agent mesh */
var(Rendering) vector MeshMaxScale3D;
/** Note currently only checks if see player when being rendered */
var bool bWantsSeePlayerNotification;
/** Eye Z offset from location */
var float EyeZOffset;
/** Distance to LOD out proximity checks for non-visible agents */
var(LOD) float ProximityLODDist;
/** Distance to LOD out proximity checks for visible agents */
var(LOD) float VisibleProximityLODDist;
/** Last position validated by collision trace */
var vector LastKnownGoodPosition;
/** Distance from ground to agent center (used to adjust foot positioning) */
var(Rendering) float GroundOffset;
/** Current movement destination intermediate to reaching CurrentDestination */
var vector IntermediatePoint;
/** bounding box to use for pathing queries */
var vector SearchExtent;
// Whether agent is allowed to pitch as he rotates toward his current velocity
var(Movement) bool bAllowPitching;
/** Navigation Handle used by agents requesting pathing */
var class<NavigationHandle> NavigationHandleClass;
var NavigationHandle NavigationHandle;
/** flags set for debugging (set each tick) */
var bool bHitObstacle, bBadHitNormal;
var int ObstacleCheckCount;
/** Used for accessing potential obstacles - not an obstacle if hitnormal.Z > WalkableFloorZ */
var float WalkableFloorZ;
/** Last time pathing was attempted for this agent */
var float LastPathingAttempt;
/** Used to limit update frequency of agents that are not visible */
var float LastUpdateTime;
/** Whether to perform crowd simulation this tick on this agent ( updated using ShouldPerformCrowdSimulation() )*/
var bool bSimulateThisTick;
/** how long to wait before killing this agent when it isn't visible */
var(LOD) float NotVisibleLifeSpan;
/** Archetype used to spawn this agent */
var GameCrowdAgent MyArchetype;
/** Max walking speed (if not using root motion velocity)*/
var(Movement) float MaxWalkingSpeed;
/** Max running speed (if not using root motion velocity)*/
var(Movement) float MaxRunningSpeed;
/** Current max speed */
var float MaxSpeed;
struct native RecentInteraction
{
var Name InteractionTag;
var float InteractionDelay;
};
var array<RecentInteraction> RecentInteractions;
/** Max distance to draw debug beacon */
var float BeaconMaxDist;
/** Debug beacon offset from Location */
var vector BeaconOffset;
/** Background texture for debug beacon */
var const Texture2D BeaconTexture;
/** Beacon background color */
var const LinearColor BeaconColor;
/** Ambient Sound cue played by this agent */
var() soundcue AmbientSoundCue;
/** Ambient sound being played */
var AudioComponent AmbientSoundComponent;
/** Current applied behavior instance */
var GameCrowdAgentBehavior CurrentBehavior;
var float CurrentBehaviorActivationTime;
/** Describes a behavior type and its frequency */
struct native BehaviorEntry
{
/** Archetype based on a GameCrowdAgentBehavior class */
var() GameCrowdAgentBehavior BehaviorArchetype;
/** Optional actor to look at when performing this behavior */
var() Actor LookAtActor;
/** How often this behavior is picked = BehaviorFrequency/(sum of BehaviorFrequencies) */
var() float BehaviorFrequency;
/** If true, agent will never repeat this behavior */
var() bool bNeverRepeat;
/** Whether this behavior has been used by this agent */
var bool bHasBeenUsed;
/** Temp Cache whether this behavior can be used */
var bool bCanBeUsed;
structdefaultproperties
{
BehaviorFrequency=1.0
}
};
/** Behaviors to choose from when encounter another agent (only if no current behavior) */
var(Behavior) array<BehaviorEntry> EncounterAgentBehaviors;
/** Set when updating dynamics if agent is potential encounter for updating agent - only valid in HandlePotentialAgentEncounter() event.*/
var bool bPotentialEncounter;
/** Behaviors to choose from when see player (only if no current behavior) */
var(Behavior) array<BehaviorEntry> SeePlayerBehaviors;
/** Calculated from behaviors in SeePlayerList */
var float MaxSeePlayerDistSq;
/** How often see player event can be triggered. If 0, never retriggers */
var(Behavior) float SeePlayerInterval;
/** Behaviors to choose from when agent spawns. */
var(Behavior) array<BehaviorEntry> SpawnBehaviors;
/** Behaviors to choose from when agent panicks. */
var(Behavior) array<BehaviorEntry> UneasyBehaviors;
var(Behavior) array<BehaviorEntry> AlertBehaviors;
var(Behavior) array<BehaviorEntry> PanicBehaviors;
/** Behaviors to choose from randomly at RandomBehaviorInterval. */
var(Behavior) array<BehaviorEntry> RandomBehaviors;
/** Behaviors to choose from when the agent takes damage. */
var(Behavior) array<BehaviorEntry> TakeDamageBehaviors;
/** Average time between random behavior attempt (only if visible to player and no current behavior) */
var(Behavior) float RandomBehaviorInterval;
/** only used for animation now. Need to replace with more appropriately named property */
var bool bIsPanicked;
/** World time when agent was spawned or last rendered */
var float ForceUpdateTime;
/** Random variation in how closely agent must reach destination (0.5 to 1.0)*/
var float ReachThreshold;
/** Whether should idle and wait for other group members */
var bool bWantsGroupIdle;
/** Behaviors to choose from when waiting for other group members. */
var(Behavior) array<BehaviorEntry> GroupWaitingBehaviors;
/** Try to keep Members this close together - probably will be obsolete when have formations */
var(Behavior) float DesiredGroupRadius;
/** Keep square of DesiredGroupRadius for faster testing */
var float DesiredGroupRadiusSq;
/** If true, agent will prefer destinations with line of sight to player if starting from non-L.O.S. destination */
var() bool bPreferVisibleDestination;
/** If true, prefer visible destination only for first destination chosen after spawn */
var() bool bPreferVisibleDestinationOnSpawn;
/** Max distance to keep agents around if they haven't been rendered in NotVisibleLifeSpan, but are still in player's potential line of sight */
var float MaxLOSLifeDistanceSq;
/** Actor with GameCrowdSpawnerInterface which spawned this agent */
var GameCrowdSpawnerInterface MySpawner;
/** True if already notified spawner about destruction */
var bool bHasNotifiedSpawner;
/** true if agent is currently sitting in pop mgr's agent pool */
var bool bIsInSpawnPool;
/** Used for keeping groups spawned together */
var vector SpawnOffset;
/** Initial setting of LastRenderTime (used to see if agent was ever actually rendered) */
var float InitialLastRenderTime;
var(Debug) bool bPaused;
var(Debug) Color DebugAgentColor;
var(Debug) GameCrowdDestination DebugSpawnDest;
cpptext
{
virtual void PreBeginPlay();
virtual void PostBeginPlay();
virtual void PostScriptDestroyed();
virtual void GetActorReferences(TArray<FActorReference*>& ActorRefs, UBOOL bIsRemovingLevel);
virtual UBOOL Tick( FLOAT DeltaSeconds, ELevelTick TickType );
virtual void performPhysics(FLOAT DeltaTime);
virtual UBOOL ShouldTrace(UPrimitiveComponent* Primitive, AActor *SourceActor, DWORD TraceFlags);
virtual void TickSpecial( FLOAT DeltaSeconds );
/**
* If desired, take the current location, and using a line check, update its Z to match the ground better.
* Returns FALSE if could not be conformed, and agent was killed.
*/
UBOOL UpdateInterpZTranslation(const FVector& NewLocation);
/**
* Update NearbyDynamics, RelevantAttractors
* Also checks ReportOverlapsWithClass and calls OverlappedActorEvent if necessary
*/
void UpdateProximityInfo();
void CheckSeePlayer();
virtual void UpdatePendingVelocity( FLOAT DeltaTime );
UBOOL IsVelocityWithinConstraints( const FRotator& Dir, FLOAT Speed, FLOAT DeltaTime );
UBOOL VerifyDestinationIsClear();
UBOOL IsDestinationObstructed( const FVector& Dest );
virtual UBOOL IsValidNearbyDynamic( AActor* A );
virtual FLOAT GetInfluencePct( INT PriB );
virtual UBOOL WantsOverlapCheckWith(AActor* TestActor);
/**
* This will allow subclasses to implement specialized behavior for whether or not to actually simulate.
* Example: You have hundreds of crowd agents and not all can be seen. So doing a distance check before simulating them
* could save CPU. (distance check in stead of LastRender as you might want them moving before the viewer sees them).
**/
virtual UBOOL ShouldPerformCrowdSimulation(FLOAT DeltaTime);
/** Whether agent should end idling now */
virtual UBOOL ShouldEndIdle();
/** Check if reached intermediate point in route to destination */
UBOOL ReachedIntermediatePoint();
/** Clamp velocity to reach destination exactly */
virtual void ExactVelocity(FLOAT DeltaTime);
/** Interface_NavigationHandle implementation to grab search params */
virtual void SetupPathfindingParams( FNavMeshPathParams& out_ParamCache );
virtual void InitForPathfinding();
virtual INT ExtraEdgeCostToAddWhenActive(FNavMeshEdgeBase* Edge);
virtual FVector GetEdgeZAdjust(FNavMeshEdgeBase* Edge);
/**
* This function actually does the work for the GetDetailInfo and is virtual.
* It should only be called from GetDetailedInfo as GetDetailedInfo is safe to call on NULL object pointers
**/
virtual FString GetDetailedInfoInternal() const;
virtual UBOOL IsActiveObstacle() { return TRUE; }
virtual FLOAT GetAvoidRadius();
virtual INT GetInfluencePriority();
virtual FColor GetDebugAgentColor();
}
native function Vector GetCollisionExtent();
/** called when the actor falls out of the world 'safely' (below KillZ and such) */
simulated event FellOutOfWorld(class<DamageType> dmgType)
{
Health = -1;
Lifespan = -0.1;
}
/**
* @RETURNS whether this agent is panicked (true if agent has a CurrentBehavior and CurrentBehavior.bIsPanicked==true
*/
native function bool IsPanicked();
/**
* Pick a behavior from the BehaviorList, for a camera at BestCameraLoc,
* and activate this behavior
* Caller is responsible for setting bHasBeenUsed on picked behavior entry.
*
* @RETURNS true if new behavior was activated
*/
function bool PickBehaviorFrom(array<BehaviorEntry> BehaviorList, optional vector BestCameraLoc=vect(0,0,0) )
{
local vector CameraLoc;
local rotator CameraRot;
local PlayerController PC;
local float BestDistSq, NewDistSq;
local int i;
local float FreqSum, RandPick;
if ( BestCameraLoc == vect(0,0,0) )
{
// if camera location not passed in, find closest camera
BestDistSq = 90000000.0;
ForEach LocalPlayerControllers(class'PlayerController', PC)
{
PC.GetPlayerViewPoint(CameraLoc, CameraRot);
NewDistSq = VSizeSq(CameraLoc - Location);
if ( NewDistSq < BestDistSq )
{
BestDistSq = NewDistSq;
BestCameraLoc = CameraLoc;
}
}
}
// Pick a behavior to activate
for ( i=0; i<BehaviorList.length; i++ )
{
if ( BehaviorList[i].BehaviorArchetype == None )
{
`warn(self@MyArchetype$" No behavior archetype for behavior entry "$i);
}
else
{
BehaviorList[i].bCanBeUsed = (!BehaviorList[i].bHasBeenUsed || !BehaviorList[i].bNeverRepeat) && BehaviorList[i].BehaviorArchetype.CanBeUsedBy(self, BestCameraLoc);
if ( BehaviorList[i].bCanBeUsed )
{
FreqSum += BehaviorList[i].BehaviorFrequency;
}
}
}
// If frequency sum < 1.0, chance no behavior will be picked
RandPick = FMax(1.0,FreqSum) * FRand();
if ( RandPick >= FreqSum )
{
return false;
}
// Activate the selected behavior
for ( i=0; i<BehaviorList.length; i++ )
{
if ( BehaviorList[i].bCanBeUsed )
{
RandPick -= BehaviorList[i].BehaviorFrequency;
if ( RandPick < 0.0 )
{
ActivateBehavior(BehaviorList[i].BehaviorArchetype, BehaviorList[i].LookAtActor);
BehaviorList[i].bHasBeenUsed = true;
return true;
}
}
}
return false;
}
/**
* Too far ahead of group, pick waiting behavior
*/
event WaitForGroupMembers()
{
local int i;
PickBehaviorFrom(GroupWaitingBehaviors);
if ( CurrentBehavior != None )
{
CurrentBehavior.ActionTarget = MyGroup.Members[0];
// look at agent being waited for
for ( i=0; i<MyGroup.Members.Length; i++ )
{
if (MyGroup.Members[i] != None && !MyGroup.Members[i].bDeleteMe && (VSizeSq(MyGroup.Members[i].Location - Location) > DesiredGroupRadiusSq)
&& ((MyGroup.Members[i].Velocity dot (Location - MyGroup.Members[i].Location)) > 0.0))
{
CurrentBehavior.ActionTarget = MyGroup.Members[i];
break;
}
}
}
}
event SetCurrentDestination(GameCrowdDestination NewDest)
{
if( NewDest != CurrentDestination )
{
if ( CurrentBehavior != None )
{
CurrentBehavior.ChangingDestination(NewDest);
}
CurrentDestination = NewDest;
CurrentDestination.IncrementCustomerCount(self);
ReachThreshold = CurrentDestination.bSoftPerimeter ? 0.5 + 0.5*FRand() : 1.0;
}
}
/** Set maximum movement speed */
function SetMaxSpeed()
{
MaxSpeed = IsPanicked()? MaxRunningSpeed : MaxWalkingSpeed;
}
simulated function PostBeginPlay()
{
local vector AgentScale3D;
local int i;
local float MaxSeePlayerDist;
super.PostBeginPlay();
if ( bDeleteMe )
{
return;
}
WorldInfo.bHaveActiveCrowd = true;
// Randomize scale
if( bUniformScale )
{
AgentScale3D = MeshMinScale3D + (FRand() * (MeshMaxScale3D - MeshMinScale3D));
}
else
{
AgentScale3D.X = RandRange(MeshMinScale3D.X, MeshMaxScale3D.X);
AgentScale3D.Y = RandRange(MeshMinScale3D.Y, MeshMaxScale3D.Y);
AgentScale3D.Z = RandRange(MeshMinScale3D.Z, MeshMaxScale3D.Z);
}
SetDrawScale3D(AgentScale3D);
// assume starting point is valid
LastKnownGoodPosition = Location;
LastKnownGoodPosition.Z += EyeZOffset;
ForceUpdateTime = WorldInfo.TimeSeconds;
// init max speed
SetMaxSpeed();
// init ambient sound
if ( AmbientSoundCue != None )
{
AmbientSoundComponent = new(self) class'AudioComponent';
if( AmbientSoundComponent != none )
{
AttachComponent(AmbientSoundComponent);
AmbientSoundComponent.SoundCue = AmbientSoundCue;
AmbientSoundComponent.Play();
}
}
// init see player notification
bWantsSeePlayerNotification = (SeePlayerBehaviors.Length > 0);
for ( i=0; i<SeePlayerBehaviors.Length; i++ )
{
MaxSeePlayerDist = FMax(MaxSeePlayerDist, SeePlayerBehaviors[i].BehaviorArchetype.MaxPlayerDistance);
}
// init convenient/perf squares of agent properties
MaxSeePlayerDistSq = MaxSeePlayerDist*MaxSeePlayerDist;
DesiredGroupRadiusSq = DesiredGroupRadius * DesiredGroupRadius;
if ( RandomBehaviors.Length > 0 )
{
settimer((0.8+0.4*FRand())*RandomBehaviorInterval, true, 'TryRandomBehavior');
}
}
/**
* Kill this agent or add it to population manager's spawn pool
*/
event KillAgent()
{
if ( bIsInSpawnPool )
{
return;
}
LifeSpan = -0.1;
// make sure to tick right away to destroy
TimeSinceLastTick = 1000.0;
}
/**
* Agent is coming out of pool, so rev him up
*/
function ResetPooledAgent()
{
bIsInSpawnPool = false;
SetHidden(false);
BehaviorDestination = None;
PreviousDestination = None;
LifeSpan = 0.0;
Health = default.Health;
TimeSinceLastTick = 0.0;
LastKnownGoodPosition = Location;
LastKnownGoodPosition.Z += EyeZOffset;
ForceUpdateTime = WorldInfo.TimeSeconds;
SetMaxSpeed();
if ( RandomBehaviors.Length > 0 )
{
settimer((0.8+0.4*FRand())*RandomBehaviorInterval, true, 'TryRandomBehavior');
}
}
simulated function Destroyed()
{
super.Destroyed();
if ( (MySpawner != None) && !bHasNotifiedSpawner )
{
bHasNotifiedSpawner = true;
MySpawner.AgentDestroyed(self);
}
if ( CurrentDestination != None )
{
CurrentDestination.DecrementCustomerCount(self);
CurrentDestination = None;
}
if ( MyGroup != None )
{
MyGroup.RemoveMember(self);
}
}
simulated function DisplayDebug(HUD HUD, out float out_YL, out float out_YPos)
{
local string T;
local Canvas Canvas;
super.DisplayDebug(HUD, out_YL, out_YPos);
Canvas = HUD.Canvas;
Canvas.SetPos(4, out_YPos);
Canvas.SetDrawColor(255,0,0);
T = GetDebugName();
if( bDeleteMe )
{
T = T$" DELETED (bDeleteMe == true)";
}
if( T != "" )
{
Canvas.DrawText(T, FALSE);
out_YPos += out_YL;
Canvas.SetPos(4, out_YPos);
}
Canvas.SetDrawColor(255,255,255);
Canvas.DrawText("Location:"@Location@"Rotation:"@Rotation@" Speed: "$VSize(Velocity)@"ZVel"@Velocity.Z, FALSE);
out_YPos += out_YL;
Canvas.SetPos(4,out_YPos);
Canvas.DrawText("Hit obestacle:"@bHitObstacle@"BadHitNormal:"@bBadHitNormal@"count"@ObstacleCheckCount, FALSE);
out_YPos += out_YL;
Canvas.SetPos(4,out_YPos);
Canvas.DrawText("Current conform interval:"@CurrentConformTraceInterval@"Base Conform Interval:"@ConformTraceInterval@" Last Ground Z "@LastGroundZ, FALSE);
out_YPos += out_YL;
Canvas.SetPos(4,out_YPos);
if ( CurrentDestination == None )
{
Canvas.DrawText("NO DESTINATION", FALSE);
out_YPos += out_YL;
Canvas.SetPos(4,out_YPos);
}
else
{
if ( NavigationHandle != None )
{
NavigationHandle.DrawPathCache();
}
T = "DESTINATION "$CurrentDestination;
if ( MyGroup != None )
{
T = T$" Group "$MyGroup;
DrawDebugLine(MyGroup.Members[0].Location, Location, 255, 128, 0, FALSE);
}
Canvas.DrawText(T, FALSE);
out_YPos += out_YL;
Canvas.SetPos(4,out_YPos);
if ( IntermediatePoint == CurrentDestination.Location )
{
DrawDebugLine(IntermediatePoint, Location, 0, 128, 255, FALSE);
}
else
{
DrawDebugLine(IntermediatePoint, Location, 0, 255, 0, FALSE);
DrawDebugLine(CurrentDestination.Location, Location, 255, 255, 0, FALSE);
}
}
}
/**
* Set agent lighting
* @PARAM bEnableLightEnvironment controls whether light environment is enabled
* @PARAM AgentLightingChannel is the lighting channel to use (GameCrowdAgentSkeletal only)
* @PARAM bCastShadows controls whether agent casts shadows (GameCrowdAgentSkeletal only)
*/
simulated function SetLighting(bool bEnableLightEnvironment, LightingChannelContainer AgentLightingChannel, bool bCastShadows)
{
// If desired, enable light env
if(bEnableLightEnvironment)
{
LightEnvironment.SetEnabled(TRUE);
}
// If not, detach to stop it even getting updated
else
{
DetachComponent(LightEnvironment);
}
}
simulated function Vector GetAttemptedSpawnLocation( float Pct, Vector CurPos, float CurRadius, Vector DestPos, float DestRadius )
{
local float MaxLateralOffset, LateralOffset;
local Vector LateralDir;
MaxLateralOffset = CurRadius + Pct * (DestRadius - CurRadius);
LateralDir = Normal((CurPos - DestPos) CROSS vect(0,0,1));
LateralOffset = RandRange( -MaxLateralOffset, MaxLateralOffset );
return (Pct * DestPos) + ((1.f-Pct)*CurPos) + (LateralOffset * LateralDir);
}
/**
* Initialize agent archetype, group, destination, and behavior
*/
simulated function InitializeAgent( Actor SpawnLoc, const out array<CrowdSpawnerPlayerInfo> PlayerInfo, GameCrowdAgent AgentTemplate, GameCrowdGroup NewGroup, float AgentWarmUpTime, bool bWarmupPosition, bool bCheckWarmupVisibility )
{
local bool bGroupDestination, bRealPreferVisible;
local GameCrowdDestination SpawnDest;
local float TryPct, MaxSpawnDist, DestDist, StartDist;
local vector TryLoc;
local Actor HitActor;
local vector HitLocation, HitNormal, NearestViewLocation, YAdjust;
local bool bVisibleTryLoc, bFoundOption;
local int CheckCnt, MaxCheckCnt, OptionIdx;
local array<Vector> TryOptions;
local float SpawnDestRadius, TravelDestRadius;
local int PlayerIdx;
local float NearestViewDistSq, ViewDistSq;
local bool bVisibleOption;
MyArchetype = AgentTemplate;
// let agent "warm up" and simulate a little before going to sleep
LastRenderTime = WorldInfo.TimeSeconds + AgentWarmUpTime * (0.5 + FRand());
InitialLastRenderTime = LastRenderTime;
// set group - maybe get destination from there
if ( NewGroup != None )
{
NewGroup.AddMember(self);
if ( NewGroup.Members.Length > 1 )
{
//already have leader, get destination from him
bGroupDestination = true;
SetCurrentDestination(NewGroup.Members[0].CurrentDestination);
}
}
if ( !bGroupDestination )
{
SpawnDest = GameCrowdDestination(SpawnLoc);
if( SpawnDest != None )
{
DebugSpawnDest = SpawnDest;
// already at destination - pick a next destination from it
SetCurrentDestination(SpawnDest);
// ask for a new destination - with spawn preference for visible destination
bRealPreferVisible = bPreferVisibleDestination;
bPreferVisibleDestination = bPreferVisibleDestinationOnSpawn || !SpawnDest.bWillBeVisible;
LastRenderTime = WorldInfo.TimeSeconds; // Update the agent's render time, so it doesn't get killed on initialization for having not been rendered in a while
CurrentDestination.ReachedDestination(self);
bPreferVisibleDestination = bRealPreferVisible;
if ( CurrentDestination == None )
{
`warn("INITIALIZING - NO CURRENTDESTINATION AFTER REACHING "$SpawnDest);
}
if ( bWarmupPosition )
{
for( PlayerIdx = 0; PlayerIdx < PlayerInfo.Length; PlayerIdx++ )
{
ViewDistSq = VSizeSq(PlayerInfo[PlayerIdx].ViewLocation-SpawnDest.Location);
if( NearestViewDistSq == 0.f || ViewDistSq < NearestViewDistSq )
{
NearestViewDistSq = ViewDistSq;
NearestViewLocation = PlayerInfo[PlayerIdx].ViewLocation;
}
}
if( NewGroup == None || NewGroup.Members.Length == 1 )
{
// group leader or individual agent determines spawn offset
// try randomizing position somewhere between spawn position and destination
TryPct = FRand();
MaxSpawnDist = (MySpawner != None) ? MySpawner.GetMaxSpawnDist() : 0.0;
if( SpawnDest.bIsBeyondSpawnDistance && MySpawner != None )
{
DestDist = VSize(CurrentDestination.Location - NearestViewLocation);
if ( CurrentDestination.bIsBeyondSpawnDistance || (DestDist > MaxSpawnDist) )
{
TryPct = (DestDist < VSizeSq(SpawnDest.Location - NearestViewLocation)) ? 1.0 : 0.0;
}
else
{
// get close to max spawn dist
StartDist = VSize(SpawnDest.Location - NearestViewLocation);
if ( StartDist > DestDist )
{
TryPct = 1.0 - (MaxSpawnDist - DestDist)/(StartDist - DestDist);
TryPct *= 0.9;
}
else
{
TryPct = 0.0;
}
}
}
else if( !SpawnDest.bWillBeVisible )
{
// bias spawning closer to point that will soon be visible, unless player is about to view this area
TryPct = 0.5*TryPct + 0.5;
}
else
{
TryPct *= 0.9;
}
SpawnDestRadius = SpawnDest.GetDestinationRadius();
TravelDestRadius = CurrentDestination != None ? CurrentDestination.GetDestinationRadius() : SpawnDestRadius;
TryLoc = GetAttemptedSpawnLocation( TryPct, SpawnDest.Location, SpawnDestRadius, CurrentDestination.Location, TravelDestRadius );
// make sure no player can see this intermediate spawn position
bVisibleTryLoc = FALSE;
if( NavigationHandle != None )
{
bFoundOption = FALSE;
CheckCnt = 0;
MaxCheckCnt = 4;
while( CheckCnt < MaxCheckCnt && !bFoundOption )
{
TryOptions.Length = 0;
NavigationHandle.GetValidPositionsForBox( TryLoc, 128.f, GetCollisionExtent(), FALSE, TryOptions, 1 );
for( OptionIdx = 0; OptionIdx < TryOptions.Length; OptionIdx++ )
{
bVisibleOption = FALSE;
for( PlayerIdx = 0; PlayerIdx < PlayerInfo.Length; PlayerIdx ++ )
{
HitActor = Trace(HitLocation, HitNormal, PlayerInfo[PlayerIdx].ViewLocation, TryOptions[OptionIdx], FALSE);
if( HitActor == None )
{
// DrawDebugLine( PlayerInfo[PlayerIdx].ViewLocation, TryOptions[OptionIdx], 255, 0, 0, TRUE );
bVisibleOption = TRUE;
break;
}
else
{
// DrawDebugLine( PlayerInfo[PlayerIdx].ViewLocation, TryOptions[OptionIdx], 0, 255, 0, TRUE );
}
}
if( !bVisibleOption )
{
// not visible - allow spawn
bFoundOption = TRUE;
TryLoc = TryOptions[OptionIdx];
break;
}
}
if( !bFoundOption )
{
TryPct *= 0.5f;
TryLoc = GetAttemptedSpawnLocation( TryPct, SpawnDest.Location, SpawnDestRadius, CurrentDestination.Location, TravelDestRadius );
CheckCnt++;
}
}
bVisibleTryLoc = !bFoundOption;
}
if ( !bVisibleTryLoc )
{
SpawnOffset = TryLoc;
// most of the time, agent is fine any way, otherwise will just fall quickly out of the world
SetLocation(TryLoc);
// randomly turn some agents around if their spawn loc is about to become visible, and their current destination is visible
if ( SpawnDest.bWillBeVisible && CurrentDestination.bIsVisible && (FRand()<0.5) )
{
PreviousDestination = CurrentDestination;
CurrentDestination.DecrementCustomerCount(self);
CurrentDestination = None;
BehaviorDestination = None;
SetCurrentDestination(SpawnDest);
}
}
else
{
// If current position is visible, teleport to spawn location, which we know was not visible
for( PlayerIdx = 0; PlayerIdx < PlayerInfo.Length; PlayerIdx ++ )
{
HitActor = Trace(HitLocation, HitNormal, Location, PlayerInfo[PlayerIdx].ViewLocation, FALSE);
if( HitActor == None )
{
SetLocation( SpawnDest.Location );
break;
}
}
}
}
else
{
// group leader already determined offset, other group members will use same offset
TryLoc = SpawnOffset;
// try offsetting the agent so they aren't all in a line
TryPct = 2.0 * FRand() - 1.0;
YAdjust = TryLoc + TryPct * AvoidOtherRadius * Normal((CurrentDestination.Location - SpawnDest.Location) cross Vect(0,0,1));
HitActor = Trace(HitLocation, HitNormal, YAdjust, CurrentDestination.Location, false);
if ( HitActor == None )
{
TryLoc = YAdjust;
}
// find floor below candidate position, and adjust agent there
HitActor = Trace(HitLocation, HitNormal, TryLoc - vect(0.0,0.0,250.0), TryLoc, false);
if ( HitActor != None )
{
TryLoc.Z = HitLocation.Z + GroundOffset + 5.0;
}
// most of the time, agent is fine any way, otherwise will just fall quickly out of the world
SetLocation(TryLoc);
}
}
}
}
LastKnownGoodPosition = Location;
LastKnownGoodPosition.Z += EyeZOffset;
// apply spawn behavior to agent
if ( SpawnBehaviors.Length > 0 )
{
PlaySpawnBehavior();
}
UpdateIntermediatePoint();
InitDebugColor();
}
simulated function OnPlayAgentAnimation(SeqAct_PlayAgentAnimation Action)
{
// non-skeletal agent can't play animation, so get next destination
CurrentDestination.ReachedDestination(self);
}
/**
* Play a looping idle animation
*/
simulated event PlayIdleAnimation();
simulated event StopIdleAnimation();
/**
* Called when agent encounters another agent
* NearbyDynamics list has been updated with agents, and potential encounters have their bPotentialEncounter set to true
*/
event HandlePotentialAgentEncounter()
{
if ( CurrentBehavior == None )
{
PickBehaviorFrom(EncounterAgentBehaviors);
}
}
/**
* Called when agent spawns and has SpawnBehaviors set
*/
function PlaySpawnBehavior()
{
if ( CurrentBehavior == None )
{
PickBehaviorFrom(SpawnBehaviors);
}
}
/**
* Called when see PC's pawn where PC is a local playercontroller,
* Notification only occurs if bHasSeePlayerKismet=true
*/
event NotifySeePlayer(PlayerController PC)
{
local bool bFoundBehavior;
local int i;
bWantsSeePlayerNotification = false;
// FIXMESTEVE - should check if current behavior can be overwritten and/or paused, and if so just pause it (keep it in current state)
if ( CurrentBehavior == None )
{
if ( !PickBehaviorFrom(SeePlayerBehaviors, PC.Pawn.Location) )
{
// maybe all behaviors have been used and can't be re-used
for ( i=0; i<SeePlayerBehaviors.Length; i++ )
{
if ( !SeePlayerBehaviors[i].bNeverRepeat || !SeePlayerBehaviors[i].bHasBeenUsed )
{
bFoundBehavior = true;
break;
}
}
if ( !bFoundBehavior )
{
// no available behaviors, so kill the see player timer
SeePlayerInterval = 0.0;
}
}
}
// set timer to begin requesting see player notification again
if ( SeePlayerInterval > 0.0 )
{
SetTimer( (0.8+0.4*FRand())*SeePlayerInterval, false, 'ResetSeePlayer');
}
}
/**
* Called when random behavior timer expires
* If not currently in behavior AND player can see me, do a random behavior.
*/
function TryRandomBehavior()
{
local bool bFoundBehavior;
local int i;
// FIXMESTEVE - should check if current behavior can be overwritten and/or paused, and if so just pause it (keep it in current state)
if ( (CurrentBehavior == None) && (WorldInfo.TimeSeconds - LastRenderTime < 0.1) )
{
if ( !PickBehaviorFrom(RandomBehaviors) )
{
// maybe all behaviors have been used and can't be re-used
for ( i=0; i<RandomBehaviors.Length; i++ )
{
if ( !RandomBehaviors[i].bNeverRepeat || !RandomBehaviors[i].bHasBeenUsed )
{
bFoundBehavior = true;
break;
}
}
if ( !bFoundBehavior )
{
// no available behaviors, so kill the see player timer
ClearTimer('TryRandomBehavior');
}
}
}
}
function ResetSeePlayer()
{
bWantsSeePlayerNotification = true;
}
/**
* Activate the passed in NewBehaviorArchetype as the new current behavior for this agent
* FIXMESTEVE - currently kills old behavior - instead, should have stack of behaviors
*/
event ActivateBehavior(GameCrowdAgentBehavior NewBehaviorArchetype, optional Actor LookAtActor )
{
StopBehavior();
if ( NewBehaviorArchetype == None )
{
`warn("Illegal behavior "$NewBehaviorArchetype$" for "$self);
return;
}
SetCurrentBehavior(NewBehaviorArchetype);
// Set custom look at actor if it exists
if( LookAtActor != None )
{
CurrentBehavior.ActionTarget = LookAtActor;
}
if( CurrentBehavior != None )
{
// start up behavior
CurrentBehavior.InitBehavior(self);
}
}
/**
* Activate a new behavior that has already been instantiated
*/
function ActivateInstancedBehavior(GameCrowdAgentBehavior NewBehaviorObject)
{
StopBehavior();
CurrentBehavior = NewBehaviorObject;
// start up behavior
CurrentBehavior.InitBehavior(self);
}
event HandleBehaviorEvent( ECrowdBehaviorEvent EventType, Actor InInstigator, bool bViralCause, bool bPropagateViralFlag )
{
local bool bActivatedBehavior;
// this will set CurrentBehavior
switch( EventType )
{
case CBE_Spawn:
bActivatedBehavior = PickBehaviorFrom( SpawnBehaviors );
break;
case CBE_Random:
bActivatedBehavior = PickBehaviorFrom( RandomBehaviors );
break;
case CBE_SeePlayer:
bActivatedBehavior = PickBehaviorFrom( SeePlayerBehaviors );
break;
case CBE_EncounterAgent:
bActivatedBehavior = PickBehaviorFrom( EncounterAgentBehaviors );
break;
case CBE_TakeDamage:
bActivatedBehavior = PickBehaviorFrom( TakeDamageBehaviors );
break;
case CBE_GroupWaiting:
bActivatedBehavior = PickBehaviorFrom( GroupWaitingBehaviors );
break;
case CBE_Uneasy:
bActivatedBehavior = PickBehaviorFrom( UneasyBehaviors );
break;
case CBE_Alert:
bActivatedBehavior = PickBehaviorFrom( AlertBehaviors );
break;
case CBE_Panic:
bActivatedBehavior = PickBehaviorFrom( PanicBehaviors );
break;
}
if( bActivatedBehavior && CurrentBehavior != None )
{
if( bPropagateViralFlag )
{
CurrentBehavior.bIsViralBehavior = bViralCause;
}
CurrentBehavior.ActivatedBy( InInstigator );
}
}
event StopBehavior()
{
if( CurrentBehavior != None )
{
CurrentBehavior.StopBehavior();
CurrentBehavior = None;
}
}
/**
* Instantiate a new behavior using BehaviorArchetype, and set it to be the current behavior.
*/
final native function SetCurrentBehavior(GameCrowdAgentBehavior BehaviorArchetype);
/**
* @RETURNS true if CurrentBehavior and CurrentBehavior.bIdleBehavior is true
*/
native function bool IsIdle();
/**
* Calculate camera view point, when viewing this actor.
*
* @param fDeltaTime delta time seconds since last update
* @param out_CamLoc Camera Location
* @param out_CamRot Camera Rotation
* @param out_FOV Field of View
*
* @return true if Actor should provide the camera point of view.
*/
simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
local vector HitNormal;
local float Radius;
Radius = 20.0;
if (Trace(out_CamLoc, HitNormal, Location - vector(out_CamRot) * Radius * 20, Location, false) == None)
{
out_CamLoc = Location - vector(out_CamRot) * Radius * 20;
}
return false;
}
/**
* Update current intermediate destination point for agent in route to DestinationActor
*/
event UpdateIntermediatePoint(optional Actor DestinationActor)
{
if ( DestinationActor == None )
{
if ( CurrentBehavior != None )
{
DestinationActor = CurrentBehavior.GetDestinationActor();
}
else
{
DestinationActor = CurrentDestination;
}
if ( DestinationActor == None )
{
return;
}
}
if ( !bUseNavMeshPathing )
{
IntermediatePoint = DestinationActor.Location;
}
else
{
IntermediatePoint = GeneratePathToActor(DestinationActor);
if ( IntermediatePoint == vect(0,0,0) )
{
IntermediatePoint = DestinationActor.Location;
}
}
}
/** Stop agent moving and pay death anim */
native function PlayDeath(vector KillMomentum);
/** Death event is script class, so need to call from script */
simulated event FireDeathEvent()
{
TriggerEventClass( class'SeqEvent_Death', self );
}
function TakeDamage(int DamageAmount, Controller EventInstigator, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
{
if ( Health > 0 )
{
Health -= DamageAmount;
if(Health <= 0)
{
Health = -1;
SetCollision(FALSE, FALSE, FALSE); // Turn off all collision when dead.
PlayDeath(normal(Momentum) * DamageType.default.KDamageImpulse + Vect(0,0,1)*DamageType.default.KDeathUpKick);
}
else
{
if ( CurrentBehavior == None )
{
// Agent is still alive and there is no current behavior, start a take damage behavior
PickBehaviorFrom(TakeDamageBehaviors);
}
}
}
}
/** Called when crowd agent overlaps something in the ReportOverlapsWithClass list */
event OverlappedActorEvent(Actor A);
/** spawn and init Navigation Handle */
event InitNavigationHandle()
{
if( NavigationHandleClass != None )
{
NavigationHandle = new(self) NavigationHandleClass;
}
}
/**
* Generate a path to Goal on behalf of the QueryingAgent
*/
event vector GeneratePathToActor( Actor Goal, optional float WithinDistance, optional bool bAllowPartialPath )
{
local vector NextDest;
LastPathingAttempt = WorldInfo.TimeSeconds;
NextDest = Goal.Location;
// make sure we have a valid navigation handle
if ( NavigationHandle == None )
{
InitNavigationHandle();
}
if( (NavigationHandle != None) && !NavigationHandle.ActorReachable(Goal) )
{
class'NavMeshPath_Toward'.static.TowardGoal( NavigationHandle, Goal );
class'NavMeshGoal_At'.static.AtActor( NavigationHandle, Goal, WithinDistance, bAllowPartialPath );
if ( NavigationHandle.FindPath() )
{
NavigationHandle.GetNextMoveLocation(NextDest, SearchExtent.X);
}
NavigationHandle.ClearConstraints();
}
return NextDest;
}
simulated native function NativePostRenderFor(PlayerController PC, Canvas Canvas, vector CameraPosition, vector CameraDir);
/**
PostRenderFor()
Hook to allow agents to render HUD overlays for themselves.
Called only if the agent was rendered this tick.
Assumes that appropriate font has already been set
*/
simulated event PostRenderFor(PlayerController PC, Canvas Canvas, vector CameraPosition, vector CameraDir)
{
local float NameXL, TextXL, BehavXL, TextYL, YL, XL;
local vector ScreenLoc;
local string ScreenName, DestString, BehaviorString;
local FontRenderInfo FontInfo;
// make sure not clipped out
screenLoc = Canvas.Project(Location + BeaconOffset);
if (screenLoc.X < 0 ||
screenLoc.X >= Canvas.ClipX ||
screenLoc.Y < 0 ||
screenLoc.Y >= Canvas.ClipY)
{
return;
}
ScreenName = "Agent"@self;
if ( MyGroup != None )
{
ScreenName = ScreenName$" Group "$MyGroup;
DrawDebugLine(MyGroup.Members[0].Location, Location, 255, 0, 255, FALSE);
}
ScreenName = ScreenName@"Last Rendered"@(WorldInfo.TimeSeconds - LastRenderTime);
Canvas.StrLen(ScreenName, NameXL, TextYL);
XL = FMax(XL, NameXL);
YL += TextYL;
DestString = GetDestString();
Canvas.StrLen(DestString, TextXL, TextYL);
XL = FMax(XL, TextXL);
YL += TextYL;
BehaviorString = GetBehaviorString();
Canvas.StrLen(BehaviorString, BehavXL, TextYL);
XL = FMax(XL, BehavXL);
YL += TextYL;
Canvas.SetPos(ScreenLoc.X-0.7*XL, ScreenLoc.Y-1.8*YL);
Canvas.DrawTile(BeaconTexture, 1.4*XL, 1.2*YL, 0,0,31,31, BeaconColor);
Canvas.DrawColor = class'HUD'.default.GreenColor;
Canvas.SetPos(ScreenLoc.X-0.5*NameXL,ScreenLoc.Y-1.7*YL);
FontInfo.bClipText = true;
Canvas.DrawText(ScreenName, true,,, FontInfo);
Canvas.SetPos(ScreenLoc.X-0.5*TextXL,ScreenLoc.Y-1.7*YL + 1.1*TextYL);
FontInfo.bClipText = true;
Canvas.DrawText(DestString, true,,, FontInfo);
Canvas.SetPos(ScreenLoc.X-0.5*BehavXL,ScreenLoc.Y-1.7*YL + 2.2*TextYL);
FontInfo.bClipText = true;
Canvas.DrawText(BehaviorString, true,,, FontInfo);
// draw line to current destination
if ( CurrentDestination != None )
{
DrawDebugLine(Location, CurrentDestination.Location, 255, 255, 0 , false);
}
}
/**
* Get debug string about agent destination and movement status
*/
function string GetDestString()
{
local string DestString;
DestString = (CurrentDestination == None) ? "NO DESTINATION" : ""$CurrentDestination;
if ( IsIdle() )
{
DestString = ((CurrentDestination != None) && CurrentDestination.ReachedByAgent(self, Location, true)) ? "Idle at "$DestString : "Idle en route to "$DestString;
}
else
{
DestString = "Moving to "$DestString;
}
return DestString;
}
/**
* Get debug string about agent behavior
*/
function string GetBehaviorString()
{
local string BehaviorString;
if ( CurrentBehavior != None )
{
BehaviorString = CurrentBehavior.GetBehaviorString();
}
else
{
BehaviorString = "Moving between Destinations";
}
return BehaviorString;
}
simulated function InitDebugColor()
{
// DebugAgentColor.R = 30 + Rand(220);
DebugAgentColor.G = 50 + Rand(205);
// DebugAgentColor.B = 30 + Rand(220);
}
defaultproperties
{
NavigationHandleClass=class'NavigationHandle'
Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
bEnabled=FALSE
InvisibleUpdateTime=5.0
MinTimeBetweenFullUpdates=2.0
TickGroup=TG_DuringAsyncWork
bCastShadows=true
End Object
Components.Add(MyLightEnvironment)
LightEnvironment=MyLightEnvironment
MeshMinScale3D=(X=1.0,Y=1.0,Z=1.0)
MeshMaxScale3D=(X=1.0,Y=1.0,Z=1.0)
bUniformScale=TRUE
Health=100
ConformTraceDist=35.0
ConformTraceInterval=10
CurrentConformTraceInterval=10
AvoidOtherRadius=32.0
AwareRadius=256.0
// AwareRadius=1024.0
AvoidOtherSampleList.Add((RotOffset=0,NumMagSamples=10)) // 0 degrees
AvoidOtherSampleList.Add((RotOffset=2048,NumMagSamples=8)) // 11 degrees
AvoidOtherSampleList.Add((RotOffset=-2048,NumMagSamples=8))
AvoidOtherSampleList.Add((RotOffset=4096,NumMagSamples=6)) // 22 degrees
AvoidOtherSampleList.Add((RotOffset=-4096,NumMagSamples=6))
AvoidOtherSampleList.Add((RotOffset=6144,NumMagSamples=4)) // 33 degrees
AvoidOtherSampleList.Add((RotOffset=-6144,NumMagSamples=4))
AvoidOtherSampleList.Add((RotOffset=8192,NumMagSamples=4)) // 45 degrees
AvoidOtherSampleList.Add((RotOffset=-8192,NumMagSamples=4))
AvoidOtherSampleList.Add((RotOffset=12288,NumMagSamples=2)) // 67 degrees
AvoidOtherSampleList.Add((RotOffset=-12288,NumMagSamples=2))
AvoidOtherSampleList.Add((RotOffset=16384,NumMagSamples=1,bFallbackOnly=TRUE)) // 90 degrees
AvoidOtherSampleList.Add((RotOffset=-16384,NumMagSamples=1,bFallbackOnly=TRUE))
AvoidOtherSampleList.Add((RotOffset=24576,NumMagSamples=1,bFallbackOnly=TRUE)) // 135 degrees
AvoidOtherSampleList.Add((RotOffset=-24576,NumMagSamples=1,bFallbackOnly=TRUE))
AvoidOtherSampleList.Add((RotOffset=32768,NumMagSamples=1,bFallbackOnly=TRUE)) // 180 degrees
ExtraPathCost=50
MaxPathLaneValue=10.f
PENALTY_COEFF_ANGLETOGOAL=2.5f
PENALTY_COEFF_ANGLETOVEL=1.f
PENALTY_COEFF_MAG=1.f
MIN_PENALTY_THRESHOLD=0.05f
EyeZOffset=40.0
RotateToTargetSpeed=30000.0
MaxYawRate=40000.0
TickGroup=TG_DuringAsyncWork
Physics=PHYS_Interpolating
bStatic=FALSE
bCollideActors=TRUE
bBlockActors=FALSE
bWorldGeometry=FALSE
bCollideWorld=FALSE
bProjTarget=TRUE
bUpdateSimulatedPosition=FALSE
bNoEncroachCheck=TRUE
bPreferVisibleDestinationOnSpawn=TRUE
RemoteRole=ROLE_None
bNoDelete=false
ProximityLODDist=2000.0
VisibleProximityLODDist=5000.0
ConformType=CFM_NavMesh
GroundOffset=86.0
bCheckForObstacles=FALSE
bUseNavMeshPathing=TRUE
SearchExtent=(X=32.0,Y=32.0,Z=86.0)
WalkableFloorZ=0.7
NotVisibleLifeSpan=10.f
MaxLOSLifeDistanceSq=400000000.0
MaxWalkingSpeed=100.0
MaxRunningSpeed=300.0
SupportedEvents.Add(class'SeqEvent_TakeDamage')
SupportedEvents.Add(class'SeqEvent_Death')
BeaconMaxDist=1500.0
BeaconOffset=(x=0.0,y=0.0,z=140.0)
BeaconTexture=Texture2D'EngineResources.WhiteSquareTexture'
BeaconColor=(R=0.5f, G=0.5f, B=0.5f, A=0.5f)
DeadBodyDuration=10.f
SeePlayerInterval=0.0
RandomBehaviorInterval=30.0
ReachThreshold=1.0
DesiredGroupRadius=200.0
}