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

440 lines
18 KiB
Ucode

//=============================================================================
// KFAIController_ZedDAR
//=============================================================================
// Base AI controller for evil DAR robots.
//=============================================================================
// Killing Floor 2
// Copyright (C) 2018 Tripwire Interactive LLC
//=============================================================================
class KFAIController_ZedDAR extends KFAIController_Ranged
abstract;
var float MinRangeAttackDist;
var float MaxRangeAttackDist;
var float LastRangeAttackTime;
var float TimeBetweenRangeAttacks;
var float BaseTimeBetweenRangeAttacks;
var float TimeBetweenRangeAttackVariance;
var float CheckSpecialMoveTime;
var float LastCheckSpecialMoveTime;
var name FiringSocketName;
var protected float RangeAttackIntervalNormal;
var protected float RangeAttackIntervalHard;
var protected float RangeAttackIntervalSuicidal;
var protected float RangeAttackIntervalHellOnEarth;
var float HeadLossAttackTimeDebuff;
var float HeadLossAccuracyDebuffPct;
var float AimError;
var bool bAllowedToSprint;
var bool bCanEvade;
var int ArmorLossStumblePower;
event PostBeginPlay()
{
Super.PostBeginPlay();
BaseTimeBetweenRangeAttacks = GetAttackIntervalByDifficulty();
TimeBetweenRangeAttacks = BaseTimeBetweenRangeAttacks;
if(TimeBetweenRangeAttackVariance > 0)
{
TimeBetweenRangeAttacks += RandRange(-TimeBetweenRangeAttackVariance, TimeBetweenRangeAttackVariance);
}
}
function float GetAttackIntervalByDifficulty()
{
if (Skill == class'KFGameDifficultyInfo'.static.GetDifficultyValue(0)) // Normal
{
return RangeAttackIntervalNormal;
}
else if (Skill <= class'KFGameDifficultyInfo'.static.GetDifficultyValue(1)) // Hard
{
return RangeAttackIntervalHard;
}
else if (Skill <= class'KFGameDifficultyInfo'.static.GetDifficultyValue(2)) // Suicidal
{
return RangeAttackIntervalSuicidal;
}
else if (Skill <= class'KFGameDifficultyInfo'.static.GetDifficultyValue(3)) // Hell on Earth
{
return RangeAttackIntervalHellOnEarth;
}
return RangeAttackIntervalNormal;
}
function bool CanDoRangedAttack(float DistanceToTargetSqr)
{
if (!CheckOverallCooldownTimer())
{
return false;
}
return ((LastRangeAttackTime == 0 || (`TimeSince(LastRangeAttackTime) > TimeBetweenRangeAttacks)) &&
DistanceToTargetSqr >= Square(MinRangeAttackDist) &&
DistanceToTargetSqr <= Square(MaxRangeAttackDist) &&
MyKFPawn.CanDoSpecialMove(SM_StandAndShootAttack));
}
simulated function Tick(float DeltaTime)
{
local float DistToTargetSqr;
Super.Tick(DeltaTime);
if (Role == ROLE_Authority && Enemy != none && MyKFPawn != none)
{
if (`TimeSince(LastCheckSpecialMoveTime) >= CheckSpecialMoveTime && !MyKFPawn.IsDoingSpecialMove() )
{
if (GetActiveCommand() != none && !GetActiveCommand().IsA('AICommand_SpecialMove'))
{
// Trace from worldinfo, open doors ignore traces from zeds
if (WorldInfo.FastTrace(Enemy.Location, Pawn.Location, , true))
{
DistToTargetSqr = VSizeSq(Enemy.Location - Pawn.Location);
if (CanDoRangedAttack(DistToTargetSqr))
{
if (KFGameInfo(WorldInfo.Game) != none && KFGameInfo(WorldInfo.Game).GameConductor != none)
{
KFGameInfo(WorldInfo.Game).GameConductor.UpdateOverallAttackCoolDowns(self);
}
StartRangedAttack();
if(TimeBetweenRangeAttackVariance > 0)
{
TimeBetweenRangeAttacks = BaseTimeBetweenRangeAttacks + RandRange(-TimeBetweenRangeAttackVariance, TimeBetweenRangeAttackVariance);
}
}
}
}
LastCheckSpecialMoveTime = WorldInfo.TimeSeconds;
}
if (MyKFPawn.IsDoingSpecialMove(SM_StandAndShootAttack))
{
Focus = none;
SetFocalPoint(Enemy.Location);
}
else if (Focus == none)
{
Focus = Enemy;
}
}
}
event bool SetEnemy(Pawn NewEnemy)
{
if (MyKFPawn == none || MyKFPawn.IsDoingSpecialMove(SM_StandAndShootAttack))
{
if (MyKFPawn.NeedToTurn(NewEnemy.Location))
{
`AILog( GetFuncName() $ "() rejecting " $ NewEnemy $ " because current enemy (" $ Enemy $ ") we would need to turn to see it", 'SetEnemy' );
return false;
}
}
return super.SetEnemy(NewEnemy);
}
function StartRangedAttack();
function FireRangedAttack(class<KFProjectile> RangedProjectileClass, vector ProjectileOffset)
{
local vector SocketLocation, DirToEnemy, HitLocation, HitNormal;
local KFProjectile SpawnedProjectile;
local actor HitActor;
local Vector AimLocation, AimDispalcement;
local vector displacementToHitLoc;
local float distanceToHitLoc;
if (MyKFPawn == none)
{
return;
}
if (bDebugAimError)
{
// Render debug lines and simulate several shots at once to evaluate aimerror
DebugAimError(FiringSocketName);
return;
}
SocketLocation = GetStartFireLocation() + (ProjectileOffset >> Pawn.GetViewRotation());
if (MyKFPawn.Health > 0.f && Role == ROLE_Authority && MyKFPawn.IsDoingSpecialMove(SM_StandAndShootAttack))
{
AimLocation = Enemy.Location;
HitActor = WorldInfo.Trace(HitLocation, HitNormal, AimLocation, SocketLocation, true);
// Don't shoot if it's too close
if (HitActor == Enemy)
{
if (!bCanLeadTarget)
{
DirToEnemy = normal(AimLocation - SocketLocation);
`AILog_Ext( GetFuncName() @ "Leading - HitActor: " @ HitActor @ " Is My Enemy: " @ Enemy, 'FireBall', self );
}
else
{
DirToEnemy = normal(CalcAimLocToHit(AimLocation, SocketLocation, RangedProjectileClass.default.Speed, RangedProjectileClass.default.Speed, Enemy.Velocity) - SocketLocation);
`AILog_Ext( GetFuncName() @ "Not Leading - HitActor: " @ HitActor @ " Is My Enemy: " @ Enemy, 'FireBall', self );
}
}
else
{
// If our path to the enemy is blocked, try his last known position
AimLocation = AICommand_RangedAttack(GetActiveCommand()).LastKnownEnemyLocation;
HitActor = Trace(HitLocation, HitNormal, AimLocation, SocketLocation, true);
displacementToHitLoc = HitLocation - SocketLocation;
distanceToHitLoc = VSize(displacementToHitLoc);
// If there is an object in front of us, cancel the attack
if (distanceToHitLoc < MinRangeAttackDist)
{
`AILog_Ext( GetFuncName() @ "- HitActor: " @ HitActor @ " Is NOT My Enemy: " @ Enemy @ " and distanceToHitLoc: " @ distanceToHitLoc @ " is too close so not firing!!!", 'FireBall', self );
MyKFPawn.SpecialMoves[SM_StandAndShootAttack].AbortedByAICommand();
LastRangeAttackTime = WorldInfo.TimeSeconds;
return;
}
else // Otherwise fire at the enemies last known position
{
DirToEnemy = normal((AimLocation - SocketLocation));
`AILog_Ext( GetFuncName() @ "- Fire at enemy last known position : " @ Enemy @ AimLocation, 'FireBall', self );
}
}
AimDispalcement = VRand();
DirToEnemy += (AimDispalcement * AimError);
// Shoot the fireball
SpawnedProjectile = Spawn(RangedProjectileClass, MyKFPawn, , SocketLocation, Rotator(DirToEnemy));
SpawnedProjectile.Instigator = MyKFPawn;
SpawnedProjectile.InstigatorController = self;
// Fire
SpawnedProjectile.Init(DirToEnemy);
LastRangeAttackTime = WorldInfo.TimeSeconds;
}
}
function vector GetStartFireLocation()
{
return MyKFPawn.GetPawnViewLocation();
}
function bool ShouldSprint()
{
return bAllowedToSprint && super.ShouldSprint();
}
function bool CanEvade(optional bool bOverrideSpecialMove)
{
return bCanEvade && super.CanEvade(bOverrideSpecialMove);
}
function OnArmorLoss(name ArmorName)
{
MyKFPawn.AfflictionHandler.AccrueAffliction(AF_Stumble, ArmorLossStumblePower);
if(ArmorName == 'head')
{
BaseTimeBetweenRangeAttacks += HeadLossAttackTimeDebuff;
AimError *= (1.f + HeadLossAccuracyDebuffPct);
}
else if (ArmorName == 'front')
{
bCanSprint = false;
bAllowedToSprint = false;
bCanEvade = false;
MyKFPawn.bIsSprinting = false;
}
}
defaultproperties
{
// ---------------------------------------------
// Danger Evasion Settings
DangerEvadeSettings.Empty
DangerEvadeSettings(0)={(ClassName="KFProj_Bullet_Pellet",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.3, 0.5, 0.7),
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(1)={(ClassName="KFProj_Nail_Nailgun",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.3, 0.5, 0.7),
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(2)={(ClassName="KFProj_Bullet_DragonsBreath",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.3, 0.5, 0.7),
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(3)={(ClassName="KFProj_HighExplosive_M79",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.3, 0.5, 0.7),
ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(4)={(ClassName="KFProj_HighExplosive_M32",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.3, 0.5, 0.7),
ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(5)={(ClassName="KFProj_Rocket_RPG7",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.3, 0.5, 0.7),
ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(6)={(ClassName="KFDT_Explosive_M16M203",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.3, 0.5, 0.7),
ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(7)={(ClassName="KFDT_Explosive_HRGIncendiaryRifle",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.3, 0.5, 0.7),
ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
SoloChanceMultiplier=1.0)}
//shooting fire
DangerEvadeSettings(8)={(ClassName="KFProj_CaulkNBurn_GroundFire",
Cooldowns=(3.0, 2.0, 1.5, 1.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.3, 0.5, 0.7),
ReactionDelayRanges={((X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(9)={(ClassName="KFProj_FlameThrower_GroundFire",
Cooldowns=(3.0, 2.0, 1.5, 1.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.4, 0.5, 0.6, 0.7),
ReactionDelayRanges={((X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(10)={(ClassName="KFWeap_Flame_CaulkBurn",
Cooldowns=(3.0, 2.0, 1.5, 1.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.4, 0.5, 0.6, 0.7),
ReactionDelayRanges={((X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(11)={(ClassName="KFWeap_Flame_Flamethrower",
Cooldowns=(3.0, 2.0, 1.5, 1.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.4, 0.5, 0.6, 0.7),
ReactionDelayRanges={((X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(12)={(ClassName="KFWeap_Beam_Microwave",
Cooldowns=(3.0, 2.0, 1.5, 1.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.4, 0.5, 0.6, 0.7),
ReactionDelayRanges={((X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5))},
SoloChanceMultiplier=1.0)}
//Aimed weapons it dodges //sharpshooter
DangerEvadeSettings(13)={(ClassName="KFWeap_Bow_Crossbow",
Cooldowns=(2.3, 2.3, 2.3, 1.3), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.08, 0.1, 0.2, 0.35),
ForcedEvadeChances={((FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(14)={(ClassName="KFWeap_Rifle_M14EBR",
Cooldowns=(2.3, 2.3, 2.3, 1.3), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.08, 0.1, 0.2, 0.35),
ForcedEvadeChances={((FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(15)={(ClassName="KFWeap_Rifle_Winchester1894",
Cooldowns=(2.3, 2.3, 2.3, 1.3), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.08, 0.1, 0.2, 0.35),
ForcedEvadeChances={((FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(16)={(ClassName="KFWeap_Rifle_RailGun",
Cooldowns=(2.3, 2.3, 2.3, 1.3), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.08, 0.1, 0.2, 0.35),
ForcedEvadeChances={((FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15))},
SoloChanceMultiplier=1.0)}
//Grenades
DangerEvadeSettings(17)={(ClassName="KFProj_FragGrenade",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.2, 0.5, 0.8),
ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(18)={(ClassName="KFProj_MolotovGrenade",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.2, 0.5, 0.8),
ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(19)={(ClassName="KFProj_DynamiteGrenade",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.2, 0.5, 0.8),
ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(20)={(ClassName="KFProj_NailBombGrenade",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.2, 0.5, 0.8),
ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(21)={(ClassName="KFProj_HEGrenade",
Cooldowns=(3.0, 2.0, 1.0, 0.75), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.2, 0.5, 0.8),
ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
SoloChanceMultiplier=1.0)}
// Ground Fire
DangerEvadeSettings(22)={(ClassName="KFProj_Flame_HRGIncendiaryRifle",
Cooldowns=(3.0, 2.0, 1.5, 1.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.1, 0.3, 0.5, 0.7),
ReactionDelayRanges={((X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5), (X=0.3, Y=0.5))},
SoloChanceMultiplier=1.0)}
BaseShapeOfProjectileForCalc=(X=10,Y=10,Z=10)
bUseDesiredRotationForMelee=false
bCanTeleportCloser=false
// ---------------------------------------------
// Behaviors
EvadeGrenadeChance=0.9f
CheckSpecialMoveTime=0.25f
SprintWithinEnemyRange=(X=500.f,Y=5000.f) //X=800.f,Y=1500
RangeAttackIntervalNormal=7.0
RangeAttackIntervalHard=6.0
RangeAttackIntervalSuicidal=5.0
RangeAttackIntervalHellOnEarth=4.0
TimeBetweenRangeAttackVariance=1.0
MinRangeAttackDist=300
MaxRangeAttackDist=4000
HeadLossAttackTimeDebuff=2.f
HeadLossAccuracyDebuffPct=2.0f //0.25
AimError=0.03f
ArmorLossStumblePower=100
bAllowedToSprint=true
bCanEvade=true
}