//============================================================================= // 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)} }