//============================================================================= // KFAIController_ZedSiren //============================================================================= // The Siren's NPC controller //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC //============================================================================= class KFAIController_ZedSiren extends KFAIController_Monster; /** Delay before next scream */ var float ScreamDelayTime; /** Cooldown between screams */ var float ScreamCooldown; /** Last stored time of successful scream */ var transient float LastScreamTime; /** Push the sirens scream onto the stack before starting a move */ function PreMoveToEnemy() { if( `TimeSince(CreationTime) > 1.f ) { DoScream( true ); } else { SetTimer( 1.f, false, nameOf(DoScream) ); } } /** Executes a scream */ function DoScream( optional bool bCalledFromPreMove=false ) { if( MyKFPawn == none || !MyKFPawn.IsCombatCapable() || GetIsInZedVictoryState() ) { return; } if( `TimeSince(LastScreamTime) > ScreamCooldown ) { if( FindCommandOfClass(class'AICommand_Siren_Scream') == none ) { class'AICommand_Siren_Scream'.static.Scream( self ); } // Start moving to enemy if( !bCalledFromPreMove ) { SetEnemyMoveGoal( self, true ); } } else { SetTimer( 1.f, false, nameOf(DoScream) ); } } /** Gets the best target based on aggro or distance, moves towards them, and then (optionally) screams */ function AcquireEnemyAndScream( optional bool bStartScreamTimer, optional float ScreamTimer=1.0f ) { local Pawn BestTarget; local bool bScreamActive; if( MyKFPawn == none || !MyKFPawn.IsCombatCapable() || IsTimerActive(nameOf(DoScream)) || GetIsInZedVictoryState() ) { //If this failed due to a combat failure, loop back to it in a bit if (MyKFPawn != None && !MyKFPawn.IsCombatCapable()) { SetTimer(1.f, false, nameof(AcquireEnemyAndScream)); } return; } // Get a new enemy if needed if( Enemy == none ) { BestTarget = MyKFPawn.GetBestAggroEnemy(); if( BestTarget == none ) { BestTarget = GetClosestEnemy(); } if( BestTarget != none ) { ChangeEnemy( BestTarget, true ); } } bScreamActive = FindCommandOfClass( class'AICommand_Siren_Scream' ) != none; if( bStartScreamTimer ) { if( bScreamActive ) { ScreamDelayTime = WorldInfo.TimeSeconds + ScreamTimer; } else { SetTimer( ScreamTimer, false, nameOf(DoScream) ); } } else if( !bScreamActive ) { DoScream(); } } /** If an incap special move just started, abort our scream command */ function NotifySpecialMoveStarted( KFSpecialMove SM ) { local AICommand_Siren_Scream ScreamCommand; if( MyKFPawn == none || !MyKFPawn.IsAliveAndWell() ) { return; } if( !MyKFPawn.IsCombatCapable() ) { ScreamCommand = FindCommandOfClass( class'AICommand_Siren_Scream' ); if( ScreamCommand != none ) { AbortCommand( ScreamCommand ); } } super.NotifySpecialMoveStarted( SM ); } /** Scream after an incap */ function NotifySpecialMoveEnded( KFSpecialMove SM ) { super.NotifySpecialMoveEnded( SM ); // Allow a bit of time to blend out of incaps if( SM.Handle == 'KFSM_Stumble' || SM.Handle == 'KFSM_Stunned' || SM.Handle == 'KFSM_Frozen' || SM.Handle == 'KFSM_RecoverFromRagdoll' ) { AcquireEnemyAndScream( true, 0.5f ); } } /** Notification from the move command that we've arrived at an intermediate destination */ function NotifyReachedLatentMoveGoal() { AcquireEnemyAndScream( false ); } /** Notification from KFSM_MeleeAttack that it has completed */ function NotifyMeleeAttackFinished() { super.NotifyMeleeAttackFinished(); if( MyKFPawn != none ) { MyKFPawn.NotifyMeleeAttackFinished(); } } /** Notification from AICommand::Popped that it has completed */ function NotifyCommandFinished( AICommand FinishedCommand ) { // Need to set our scream timer after a panic wander or else Siren will likely never scream again if( AICommand_PanicWander(FinishedCommand) != none ) { AcquireEnemyAndScream( true, 0.75f ); } super.NotifyCommandFinished( FinishedCommand ); } /** If we're in a panic state, kill the scream command */ function DoPanicWander() { local GameAICommand ActiveCommand; local AICommand_Siren_Scream ScreamCommand; ActiveCommand = GetActiveCommand(); if( ActiveCommand != none && AICommand_PanicWander(ActiveCommand) != none ) { return; } ScreamCommand = FindCommandOfClass( class'AICommand_Siren_Scream' ); if( ScreamCommand != none ) { AbortCommand( ScreamCommand ); } super.DoPanicWander(); } /** Stop screaming in victory state */ function EnterZedVictoryState() { local AICommand_Siren_Scream ScreamCommand; if( IsTimerActive(nameOf(DoScream)) ) { ClearTimer( nameOf(DoScream) ); } ScreamCommand = FindCommandOfClass( class'AICommand_Siren_Scream' ); if( ScreamCommand != none ) { AbortCommand( ScreamCommand ); } super.EnterZedVictoryState(); } DefaultProperties { // AI / Navigation SprintWithinEnemyRange=(X=600.f,Y=1200.f) MeleeCommandClass=class'AICommand_Base_Zed' DefaultCommandClass=class'AICommand_Base_Zed' bIsProbingMeleeRangeEvents=true LowIntensityAttackCooldown=7.0 bCanTeleportCloser=false // Scream ScreamCooldown=4.0 }