617 lines
27 KiB
Ucode
617 lines
27 KiB
Ucode
//=============================================================================
|
|
// 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)}
|
|
} |