Those guys basically roam around the level avoiding walls and non-targetable Actors, and if they find some target, they rush to it! Boom!, Die b@%#&! Their code isn't very short either, sporting some functions, and even some netcode resulted from server tests with my father.
Code: Select all
//=============================================================================
// AutoRocket.
//=============================================================================
class AutoRocket expands UT_SeekingRocket;
var() float SeekAcceleration, SeekSpeed, TargetAcquiringRadius, WallAvoidSpeed, WallPredictionFactor, WallAvoidSlowdown;
var Pawn User;
var int Slowdown;
var bool bFoundUser;
replication
{
reliable if ( Role == ROLE_Authority )
User,
Slowdown,
bFoundUser;
}
simulated event Vector EstimateLocation(Actor Other)
{
return Other.Location + (Other.Velocity / VSize(Velocity)) * 0.5;
}
simulated event HomeAt(Actor Other)
{
Velocity *= VSize(Velocity cross (EstimateLocation(Seeking) - Location)) * SeekAcceleration;
Acceleration = (EstimateLocation(Seeking) - Location) * SeekSpeed;
}
simulated event Actor WillHitWall(optional out Vector Location, optional out Vector OutNormal)
{
return Trace(Location, OutNormal, Self.Location + Velocity * WallPredictionFactor);
}
simulated event bool CheckSeekable(Pawn Other, Pawn FireAgent)
{
if
(
Other != None
&&
(
FireAgent == None
||
(
FastTrace(Other.Location)
&& Other != FireAgent
&& !Other.IsInState('Dying')
&&
(
(
ScriptedPawn(FireAgent) != None
&& ScriptedPawn(FireAgent).AttitudeTo(Other) != ATTITUDE_Friendly
)
||
(
ScriptedPawn(Other) != None
&& ScriptedPawn(Other).AttitudeTo(FireAgent) != ATTITUDE_Friendly
)
||
(
PlayerPawn(FireAgent) != None
&& Other.AttitudeToPlayer != ATTITUDE_Friendly
&& Other.PlayerReplicationInfo.Team != FireAgent.PlayerReplicationInfo.Team
)
|| Other.PlayerReplicationInfo.Team != FireAgent.PlayerReplicationInfo.Team
)
)
)
)
{
return True;
}
return False;
}
function BeginPlay()
{
if ( Owner == None )
return;
if ( Pawn(Owner) != None )
User = Pawn(Owner);
else
User = Pawn(Owner.Owner);
if ( User == None )
return;
}
simulated function SetTarget(Pawn Other)
{
Seeking = Other;
Log(self@"is now seeking after:"@Other);
}
simulated event Timer()
{
local Pawn P;
local Pawn Target;
local Actor A;
local Vector WallNorm, WallLoc;
SetRotation(Rotator(Velocity));
A = WillHitWall(WallLoc, WallNorm);
if ( A != None && ( LevelInfo(A) != None || !CheckSeekable(Pawn(A), User) ) && WallNorm dot (Velocity + Acceleration) < 0.9 )
{
Velocity /= WallAvoidSlowdown;
Slowdown *= WallAvoidSlowdown;
Acceleration -= WallAvoidSpeed * 2 * (WallNorm dot Velocity) * WallNorm;
}
else if ( Slowdown > 1 )
{
Velocity *= Slowdown;
Slowdown = 1;
}
else bFoundUser = True;
if ( Seeking != None && ( Pawn(Seeking) == None || !Pawn(Seeking).IsInState('Dying') || ( ScriptedPawn(Seeking) == None && User != None && ScriptedPawn(Seeking).AttitudeTo(User) == ATTITUDE_Friendly ) ) )
{
HomeAt(Seeking);
}
else
{
foreach RadiusActors(class'Pawn', P, TargetAcquiringRadius)
{
if ( Target == None || VSize(P.Location - Location) < VSize(Target.Location - Location) )
{
Target = P;
if ( CheckSeekable(Target, User) )
SetTarget(Target);
}
}
}
}
Please remember it will work as desired only if fired from a weapon! (For example, AutoRocketGun below.)
Code: Select all
//=============================================================================
// AutoRocketGun.
//=============================================================================
class AutoRocketGun expands minigun2;
var() float ShootDistance;
var() Vector FireError;
function TraceFire(float Accuracy)
{
Spawn(ProjectileClass, self, '', Pawn(Owner).Location + Normal(Vector(Pawn(Owner).ViewRotation)) * (Owner.CollisionRadius + ProjectileClass.default.CollisionRadius + ShootDistance) + FireOffset + VRand() * Accuracy, Rotator(Vector(Pawn(Owner).ViewRotation) + VRand() * FireError));
}