//============================================================================= // AICommand_SM_Attack //============================================================================= // Base AICommand for NPC attack commands linked to special moves //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC //============================================================================= class AICommand_SM_Attack extends AICommand_SpecialMove within KFAIController abstract native(AI); /** Special move type to use for this attack */ var ESpecialMove AttackSpecialMove; /** If none, NPC's enemy will be used. Allows using temporary non-pawn targets, such as doors */ var actor AttackTarget; /** Pop this command when attack is finished - don't repeat the attack from within this command. */ var bool bSingleAttack; /** Extra latent time slept through when special move has completed */ var float PostSpecialMoveSleepTime; /** Extra latent time slept through when special move has been aborted (in case extra time is needed for blending) * PostSpecialMoveSleepTime will be used if this value is 0 */ var float PostSpecialMoveAbortedSleepTime; /** Can this attack be aborted? */ var bool bCanBeAborted; /** Set to true if attack aborted, since command does not end immediately when interrupted */ var bool bAttackAborted; /** Time that has passed since command started, used to determine if enough time has passed * to allow the attack to be interrupted */ var protected float TimePlayed; /** Prevent NPC movement during this attack? */ var bool bLockDownAI; /** Zero pawn's acceleration when this command is popped */ var bool bZeroPawnAccelWhenPopped; /** Set when getting a valid attack anim from animgroup, and passed on to associated special move */ var byte SMFlags; var bool bFinishRotationBeforeAttack; /********************************************************************************************* * Initialization/Resume/Pause **********************************************************************************************/ function Pushed() { Super.Pushed(); if( bLockDownAI ) { // Prevent latent AI movement (bPreparing move is set to true, etc.) LockdownAI(); } // Disable incoming "in attack range" events DisableMeleeRangeEventProbing(); // Do attack immediately GotoState( DefaultStartState ); } /** Attack paused - currently, this should not happen! */ function Paused( GameAICommand NewCommand ) { //`warn( "Warning! Paused() for "$self$" - paused by "$NewCommand ); Super( AICommand ).Paused( NewCommand ); } function Popped() { // Turn off "in range" notifications for new attacks if( !Outer.bHasDebugCommand ) { ResetProbingMeleeRangeEvents(); } if( bLockDownAI ) { UnlockAI(); } if( bZeroPawnAccelWhenPopped && MyKFPawn != none ) { MyKFPawn.Acceleration = vect(0,0,0); } Super.Popped(); } /********************************************************************************************* * Action Transitions **********************************************************************************************/ function bool AllowTransitionTo( class AttemptCommand ) { `AILog( self$" AllowTransitionTo: "$AttemptCommand$" called", 'Command_Attack' ); // Let the child command, if any, make the decision if( ChildCommand != none ) { return ChildCommand.AllowTransitionTo( AttemptCommand ); } return IsSpecialMoveComplete(); } /********************************************************************************************* * Timers **********************************************************************************************/ function bool ShouldSelectTarget() { `AILog( self$" "$GetFuncName()$"() returning false", 'Command_Attack' ); return false; } function bool ShouldIgnoreTimeTransitions() { `AILog( GetFuncName()$" returning TRUE - ignoring time transitions", 'Command_Attack' ); return true; } /********************************************************************************************* * Default state related code **********************************************************************************************/ function ESpecialMove GetSpecialMove() { return AttackSpecialMove; } /** Return true if it's outside the Command_SpecialMove state */ function bool IsSpecialMoveComplete() { return true; } state Command_SpecialMove { function BeginState( Name PreviousStateName ) { Super.BeginState( PreviousStateName ); if( !bHasDebugCommand ) { // Rotate to AttackTarget if( AttackTarget != None && AttackTarget != Pawn ) { Focus = AttackTarget; SetDesiredRotation( Rotator(AttackTarget.Location - Pawn.Location) ); } else if( Enemy != none ) { Focus = Enemy; } } // Reset time played in case this isn't a single attack TimePlayed = 0.f; } function bool ShouldFinishRotation() { return bFinishRotationBeforeAttack; } function ESpecialMove GetSpecialMove() { return AttackSpecialMove; } /** Delay between time special move finishes and AICommand is popped */ function float GetPostSpecialMoveSleepTime() { // In case extra blend out time is needed if attack special move was aborted if( bAttackAborted ) { return PostSpecialMoveAbortedSleepTime; } else { return PostSpecialMoveSleepTime; } } function bool IsSpecialMoveComplete() { if( MyKFPawn == none || !MyKFPawn.IsAliveAndWell() || !MyKFPawn.IsDoingSpecialMove( GetSpecialMove() ) ) { return true; } return Super.IsSpecialMoveComplete(); } function FinishedSpecialMove() { Status = 'Success'; Super.FinishedSpecialMove(); } } DefaultProperties { bSingleAttack=true DefaultStartState=Command_SpecialMove bIgnoreNotifies=true bIgnoreStepAside=true bShouldCheckSpecialMove=false bLockDownAI=true AttackSpecialMove=SM_MeleeAttack }