1
0
KF2-Dev-Scripts/KFGame/Classes/KFAIController_ZedCrawler.uc

617 lines
27 KiB
Ucode
Raw Normal View History

2020-12-13 15:01:13 +00:00
//=============================================================================
// KFAIController_ZedCrawler
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================
// Base controller for the Crawler, and responsible for updating its Crawler
// pawn's rotation updates when it's using PHYS_Spider.
//
// * Note!
// * Normally AIControllers receive NotifyHitWall() events, and then the
// * pawn's HitWall() event is called depending on NotifyHitWall's return value.
// * The Crawler receives these events differently. The Crawler pawn first receives
// * its HitWall() event, and it will call its controller's NotifyHitWall() only
// * it doesn't know what else to do about the obstruction. NotifyHitWall() will
// * not be called from native code, however (see usage of the Pawn bDirectHitWall
// * flag).
//=============================================================================
class KFAIController_ZedCrawler extends KFAIController_Monster
native(AI);
/** Last NavigationPoint used to ambush enemy from (see AICommand_Crawler_MoveToGoal) */
var Actor LastAmbushNode;
/** Used in emergence related code after rootmotion has stopped */
var vector LastRMVelocity;
/** Min/Max pathfinding cost applied to pathnodes (the higher the number, the more Crawlers will prefer using WallPathNodes
(10000000 should make a Crawler never accept a path that isn't composed entirely of wall pathnodes). */
var int MinExtraCostForFloorNodes;
var int MaxExtraCostForFloorNodes;
/** Used in translation adjustments (see Tick() below) */
var vector OriginalMeshTranslation;
/*********************************************************************************************
* Emergency teleport handling:
* When FailedToFindAnchorCount exceeds MaxFailedToFindAnchorCount, Crawler will attempt to
* teleport to the nearest navigation point. This fixes problems like badly-placed spawners
* causing Crawlers to sometimes finish KFSM_Emerge partially embedded in a wall, etc. Ideally,
* FitCollision() should fix this, but it's called later for a Crawler than it is for other
* emerged NPCs.
********************************************************************************************* */
/** Tracks how many times I completely failed to build a path */
var int FailedToFindAnchorCount;
/** Max number of times I can completely fail a path within a short duration before I give up and teleport */
var int MaxFailedToFindAnchorCount;
/** See usage in UpdatePawnRotation() and CalcCrawlerX() */
var vector LastCrawlerX;
/** Pawn's previous floor vector */
var vector OldFloor;
cpptext
{
// Combat
virtual UBOOL TickMeleeCombatDecision( FLOAT DeltaTime );
// Spider Physics
virtual void UpdatePawnRotation();
virtual FVector CalcSpiderX();
}
/** Don't turn on steering for Crawlers */
// Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm
//simulated function StartSteering() {}
/** Completely failed to build a path, so increment the failed anchor count and if it's
exceeded max allowed within 1.5 seconds, the Crawler will attempt to teleport to the
best nearby navigation point */
event FailedToFindAnchor()
{
`AILog( "Failed to Find Anchor! Phys: "$MyKFPawn.GetPhysicsName(), 'PathWarning' );
FailedToFindAnchorCount++;
if( !IsTimerActive(nameof(Timer_ResetFailedToFindAnchorCount)) )
{
SetTimer( 1.5f, false, nameof(Timer_ResetFailedToFindAnchorCount) );
}
if( FailedToFindAnchorCount > MaxFailedToFindAnchorCount )
{
AttemptToTeleport();
}
}
/** Resets FailedToFindAnchorCount every 1.5 seconds. */
function Timer_ResetFailedToFindAnchorCount()
{
FailedToFindAnchorCount = 0;
}
function bool AmIAllowedToStillUsePathNodes()
{
return true;
}
/** My current enemy is blocked from me, probably because other Zeds are in the way */
// Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm P.S. doesn't seem to be called anywhere anyway.
//function bool HandleEnemyBlocked()
//{
// local KFPathnode KFPN;
//
// if( (Enemy == none || AICommand_Pause(GetActiveCommand()) != none) && !IsDoingLatentMove() )
// {
// return true;
// }
//
// if( VSizeSq(Enemy.Location - Pawn.Location) > 160000 )
// {
// return false;
// }
//
// DisableMeleeRangeEventProbing();
// SetTimer( 1.5 + (2.f * FRand()), false, nameof(Timer_EnableMeleeRangeEventProbing), self );
//
// /*
// React to being blocked from reaching enemy by moving to a KFPathnode with a clear
// view to my enemy, or by trying to move to a nearby wall (and re-pathing from there), or just
// kill time by pausing and occasionally taunting.
// */
//
// if( FRand() < 0.25f )
// {
// foreach WorldInfo.AllNavigationPoints( class'KFPathnode', KFPN )
// {
// // Try to move to a reachable KFPathnode within 500 units of my enemy and that's in a location where my enemy is still visible to me.
// if( VSize(KFPN.Location - Enemy.Location) < 500.f && CanSeeByPoints( KFPN.Location + (vect(0,0,1) * MyKFPawn.BaseEyeHeight), Enemy.Location, Rotator(Enemy.Location - KFPN.Location)) )
// {
// // TODO: Better to use in state code for benefit of latency
// SetMoveGoal( KFPN,, false, 128.f, false, true, true, false, true );
// return true;
// }
// }
// }
// // Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm
//// else if( FRand() < 0.5f )
//// {
//// // Abort my current movement command(s) and try to move to a nearby wall.
//// AbortMovementCommands();
//// AbortMovementPlugIns();
//// AICommand_Base_Crawler( GetActiveCommand() ).BeginMoveToWall();
//// return true;
//// }
// else if( MyKFPawn.CanDoSpecialMove(SM_Taunt) && FRand() < 0.22 && `TimeSince(LastTauntTime) > 2.f )
// {
// // Taunt the enemy I'm blocked from reaching.
// class'AICommand_TauntEnemy'.static.Taunt( self, KFPawn(Enemy), TAUNT_Standard );
// return true;
// }
// else
// {
// // Just stay idle and pause.
// DoPauseAI( 1.f + (0.7f * FRand()), true );
// return true;
// }
//
// return false;
//}
/*********************************************************************************************
* Movement Methods
********************************************************************************************* */
/**
* ShouldSprint()
* Timer function called during latent moves that determines whether NPC should sprint or
* stop sprinting.
*/
function bool ShouldSprint()
{
if( bCanSprint || (bCanSprintWhenDamaged && MyKFPawn.Health < MyKFPawn.HealthMax) )
{
return true;
}
return false;
}
/** Overridden to handle fractured mesh actors - setting physics to falling in case
the Crawler was on a wall */
// Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm
//event bool NotifyBump( Actor Other, vector HitNormal )
//{
// DisableBump( 0.12f );
//
// /** If we bumped into a glass window, break it */
// if( Other.bCanBeDamaged && KFFracturedMeshGlass(Other) != none )
// {
// KFFracturedMeshGlass( Other ).BreakOffAllFragments();
// MyKFPawn.SetPhysics( PHYS_Falling );
// return false;
// }
//
// return Super.NotifyBump( Other, HitNormal );
//}
//
///** Notification from pawn that it has received a BaseChange event */
//function bool NotifyBaseChange( actor NewBase, vector NewFloor )
//{
// /** If I'm using PHYS_Spider and my floor has changed and is considered 'walkable'... */
// if( MyKFPawn != none && NewFloor != OldFloor && NewFloor.Z > 0.98f && MyKFPawn.Physics == PHYS_Spider )
// {
// Pawn.SetPhysics( PHYS_Walking );
// }
//
// return super.NotifyBaseChange( NewBase, NewFloor );
//}
//
///** Set PHYS_Walking just in case, and find a new anchor if I don't already have one */
//event bool NotifyLanded( vector HitNormal, Actor FloorActor )
//{
// if( HitNormal.Z == 1.f )
// {
// Pawn.SetPhysics( PHYS_Walking );
// if( MyKFPawn.Anchor == none )
// {
// SetBestAnchor();
// }
// }
// return super.NotifyLanded( HitNormal, FloorActor );
//}
//
///** Notification that I've run into a wall. If the pawn's bDirectHitWall flag is true, this will be bypassed and
// the pawn's HitWall() event will be called directly. */
//event bool NotifyHitWall( vector HitNormal, actor Wall )
//{
// local AICommand ActiveCommand;
// local bool bCanBaseOnWall;
//
// if( KFDoorActor(Wall) != none )
// {
// Super.NotifyHitWall(HitNormal, Wall);
// }
//
// RecordHitWall( Wall );
//
// if( Wall.bWorldGeometry && Wall.bCrawlable && Wall != Base ) // 1/8/2014
// //if( Wall.bWorldGeometry && Wall != Base )
// {
// // New as of 1/9/2014
// ActiveCommand = AICommand( GetActiveCommand() );
// //if( Physics == PHYS_Falling && (bWallJumping || (ActiveCommand != none && AICommand_Crawler_LeapToWall(ActiveCommand) != none)) )
// if( Pawn.Physics == PHYS_Falling && ActiveCommand != none && AICommand_Crawler_LeapToWall(ActiveCommand) != none )
// {
// bCanBaseOnWall = true;
// }
//
// ActiveCommand = AICommand( GetActiveCommand() );
// if( (Pawn.Physics == PHYS_Walking || bCanBaseOnwall) )
// {
// if( (NavigationPoint(MoveTarget) != none && MoveTarget.Base == Wall) )
// {
// if( KFPathNode(MoveTarget).GetUpVector() == vect(0,0,0) )
// {
// return false;
// }
// }
//
// if( Pawn.Physics != PHYS_Spider )
// {
// `AILog_Ext( GetFuncName()$" Wall: "$Wall$" HitNormal: "$HitNormal$" setting phys to spider & base to wall!", 'Crawler', self );
// Pawn.SetPhysics( PHYS_Spider );
// Pawn.SetBase( Wall, HitNormal );
// return false;
// }
// }
// }
// else if( !Wall.bCrawlable )
// {
// `AILog_Ext( self$" Rejecting "$Wall$" because it's not crawlable! "$GetFuncName(), 'Crawler', self );
// }
//
// return Super.NotifyHitWall( HitNormal, Wall );
//}
//
///** Find a nearby navigation point that's preferably on a wall and make sure I can see my
// enemy from its location. Then I'll build a path and move to it and pounce on my enemy
// from there. */
//function bool FindAmbushNode( optional bool bOnlyWallNodes )
//{
// // Apply 1000 extra cost to navigation points which aren't on walls.
// class'Path_PreferWalls'.static.PreferWalls( Pawn, true, 1000 );
// // The end goal should be on a wall, and I must be able to see my enemy from that location.
// class'Goal_WallToEnemy'.static.WallToEnemy( Pawn );
// class'Goal_Null'.static.GoUntilBust( Pawn, 2024 );
//
// if( FindPathToward( Enemy ) != None )
// {
// // Found the ambush node - but don't use it if I'm already on a wall and it's
// // the same as my LastAmbushNode.
// if( MyKFPawn.Physics != PHYS_Spider || RouteGoal != LastAmbushNode )
// {
// //PauseAndShowMsg( "PATH: "$RouteGoal$" RouteCache Length: "$RouteCache.Length );
// return true;
// }
// }
// return false;
//}
// Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm P.S. doesn't seem to be called anywhere.
//function bool TestPath()
//{
// //class'Path_PreferWalls'.static.PreferWalls( Pawn, true );
// class'Path_PreferWalls'.static.PreferWalls( Pawn );
// class'Goal_WallToEnemy'.static.WallToEnemy( Pawn );
// if( FindPathToward( Enemy ) != None )
// {
// //PauseAndShowMsg( "PATH: "$RouteGoal$" RouteCache Length: "$RouteCache.Length );
// return true;
// }
// else
// {
// return false;
// }
//}
/**
* GeneratePathTo()
* Set up path constraints and attempt to build a path to Goal actor.
* Overridden for Crawlers to prefer using wall pathnodes when possible.
*
* @param Goal Goal actor to build path to
* @param Distance Optional offset distance from goal
* @param bAllowPartialPath Allow a partial path to be used if a full path fails to generate
*
* @return Actor Next navigation point to move to, if a path was generated
*/
// Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm
//event Actor GeneratePathTo( Actor Goal, optional float Distance, optional bool bAllowPartialPath )
//{
// local actor PathResult;
//
// AddBasePathConstraints();
//
// // Build a path toward my goal, applying an extra cost to any non-wallpathnodes evaluated
// class'Path_TowardGoal'.static.TowardGoal( Pawn, Goal );
// class'Path_PreferWalls'.static.PreferWalls( Pawn, false, RandRange(MinExtraCostForFloorNodes, MaxExtraCostForFloorNodes) );
// class'Goal_Null'.static.GoUntilBust( Pawn, 2024 );
//
// // Attempt to build the path.
// PathResult = FindPathToward( Goal );
// Pawn.ClearConstraints();
//
// if( PathResult == None )
// {
// `AILog( GetFuncName()$"() failed to build a path to "$Goal$", offset distance was "$Distance$", bAllowPartialPath was "$bAllowPartialPath, 'PathWarning' );
// }
// return PathResult;
//}
/**
* MayFall() - Called when a pawn is about to fall off a ledge, and allows the controller to prevent a fall by toggling
* bCanJump. This event is also passed if a floor is found over the edge, and the normal of the floor if so.
* Called by PhysWalking() and CheckForLedges()
*
* @param bFloor True if a floor was found below gap
* @param FloorNormal Normal of potential new floor
*/
// Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm
//event MayFall( bool bFloor, vector FloorNormal )
//{
// // Allow the fall
// Pawn.bCanJump = true;
//}
/**
* Overridden to use AICommand_Crawler_MoveToGoal rather than AICommand_MoveToGoal.
* @param NewMoveGoal What I'll be moving toward
* @param NewMoveFocus Optional focus override while moving
* @param bInterruptable Whether or not this move can be interrupted
* @param OffsetDist Optional offset distance from move point
* @param bIsValidCache Whether routecache is currently valid
* @param bInCanPathfind When false, NPC will only move if goal is directly reachable
* @param bForce Forces the move regardless of interruptability
* @param bAllowedToAttack Whether or not NPC is permitted to attack during this move
* @param bAllowPartialPath Whether or not to allow a partial path
*/
// Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm
//event SetMoveGoal( Actor NewMoveGoal, optional Actor NewMoveFocus,
// optional bool bInterruptable, optional float OffsetDist,
// optional bool bIsValidCache, optional bool bInCanPathfind = true,
// optional bool bForce, optional bool bAllowedToAttack=true,
// optional bool bAllowPartialPath )
//{
// MoveGoal = NewMoveGoal;
// MoveFocus = NewMoveFocus;
// MoveOffset = OffsetDist;
//
// bMoveGoalInterruptable = bInterruptable;
// SetBasedPosition( MovePosition, vect(0,0,0) );
// `AILog( GetFuncName()$"() initializing AICommand_Crawler_MoveToGoal"@NewMoveGoal@NewMoveFocus@bInterruptable@bAllowedToAttack, 'InitAICommand' );
//
// if( NewMoveGoal != None && (MoveIsInterruptable(bForce) || !bInterruptable) )
// {
// class'AICommand_Crawler_MoveToGoal'.static.CrawlerMoveToGoal( self, NewMoveGoal, NewMoveFocus, OffsetDist, bIsValidCache, bInCanPathfind, bAllowedToAttack, bAllowPartialPath );
// }
// else if( NewMoveGoal != none )
// {
// `AILog( GetFuncName() @"!! -- ignoring movegoal because I already have a moveaction, which is non-interruptable, and the new movegoal IS interruptable.. trumped", 'InitAICommand' );
// }
//}
// Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm
//simulated function Tick( float DeltaTime )
//{
// local vector CeilingTrans;
//
//// if( Role == ROLE_Authority && MyKFPawn != none && MyKFPawn.Health > 0 && !MyKFPawn.IsDoingSpecialMove(SM_Emerge) )
//// {
// if( Pawn.Floor.Z == -1 )
// {
// if( Pawn.Floor.Z == -1 )
// {
// CeilingTrans = OriginalMeshTranslation;
// CeilingTrans.X = -36;
// Pawn.Mesh.SetTranslation( CeilingTrans );
// }
//
// }
// else
// {
// Pawn.Mesh.SetTranslation( vect(-36,0,-36) );
// }
//// }
// super.Tick( DeltaTime );
//}
DefaultProperties
{
// ---------------------------------------------
// AI / Navigation
MeleeCommandClass=class'AICommand_Base_Crawler'
DefaultCommandClass=class'AICommand_Base_Crawler'
MaxFailedToFindAnchorCount=3
bNotifyFallingHitWall=true
RotationRate=(Pitch=70000,Yaw=70000,Roll=40000)
EvadeGrenadeChance=0.5f
bEvadeOnRunOverWarning=true
RunOverEvadeDelayScale=0.75f
// Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm
//InUseNodeCostMultiplier=4.f
MinExtraCostForFloorNodes=14000
MaxExtraCostForFloorNodes=25000
// Disabled for Early Access 1/14/15 as part of disabling wall walking - Ramm
//WallWalkingPluginClass=none//"KFAiPluginWallWalkingMovement"
// ---------------------------------------------
// Combat
bIsProbingMeleeRangeEvents=true
// ---------------------------------------------
// Danger Evasion Settings
DangerEvadeSettings.Empty
DangerEvadeSettings(0)={(ClassName="KFProj_Bullet_Pellet",
Cooldowns=(3.0, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 0.3, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 1.0, 1.0, 1.0),
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(9)={(ClassName="KFProj_FlameThrower_GroundFire",
Cooldowns=(3.0, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 1.0, 1.0, 1.0),
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(10)={(ClassName="KFWeap_Flame_CaulkBurn",
Cooldowns=(3.0, 0.3, 0.1, 0.1), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 1.0, 1.0),
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.00))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(11)={(ClassName="KFWeap_Flame_Flamethrower",
Cooldowns=(3.0, 1.0, 0.1, 0.1), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 1.0, 1.0),
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.00))},
SoloChanceMultiplier=1.0)}
DangerEvadeSettings(12)={(ClassName="KFWeap_Beam_Microwave",
Cooldowns=(3.0, 3.0, 2.5, 1.5), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.3, 0.5, 0.8),
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)}
//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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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(22)={(ClassName="KFProj_HighExplosive_M16M203",
Cooldowns=(3.0, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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(23)={(ClassName="KFProj_Grenade_HRGIncendiaryRifle",
Cooldowns=(3.0, 1.0, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.9, 1.0),
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)}
// Flashlight
DangerEvadeSettings(24)={(ClassName="KFFlashlightAttachment",
Cooldowns=(3.0, 1.0, 0.75, 0.5), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 0.5, 0.8, 0.9),
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.5), (X=0.0, Y=0.5), (X=0.0, Y=0.5))},
SoloChanceMultiplier=1.0)}
// Ground Fire
DangerEvadeSettings(25)={(ClassName="KFProj_Flame_HRGIncendiaryRifle",
Cooldowns=(3.0, 0.3, 0.1, 0.0), // Normal, Hard, Suicidal, HoE
EvadeChances=(0.0, 1.0, 1.0, 1.0),
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)}
}