//============================================================================= // AICommand_Base_Zed //============================================================================= // Base AI command for Zeds. Zeds don't wander (yet?) - they simply choose a // valid enemy and use this command to begin moving to their enemy and to // start melee attacks if they happen to be in range once the move to enemy // command pops and this command resumes. // // Zeds who need special handling can subclass this. //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC //============================================================================= class AICommand_Base_Zed extends AICommand_Base_Combat within KFAIController_Monster implements(LatentActionObserver) native(AI); var name CachedAttackTag; var byte AttackFlags; var bool bFailedPathfind; var bool bWaitingOnMovementPlugIn; /********************************************************************************************* * Initialization ********************************************************************************************* */ function Pushed() { Super.Pushed(); EnableSeePlayer(); `AILog( self$" "$GetFuncName()$"() using AttackRange of "$AttackRange, 'Command_Base' ); GotoState( DefaultStateName ); } function Paused( GameAICommand NewCommand ) { `AILog( self$" "$GetFuncName()$"() Paused for "$NewCommand, 'Command_Base' ); Super.Paused( NewCommand ); } function Resumed( name OldCommandName ) { `AILog( self$" "$GetFuncName()$"() (OldCommandName: "$OldCommandName$")", 'Command_Base' ); Super.Resumed( OldCommandName ); EnableProbingMeleeRangeEvents( true ); } /********************************************************************************************* * Movement & Pathing ********************************************************************************************* */ native function OnLatentFinished(BaseAIController Observer, Object Action, byte FinishResult); function bool NotifyBump( Actor Other, Vector HitNormal ) { // To be enabled if( CachedChildCommand != None ) { `AILog( GetFuncName()$"() Other: "$Other$" HitNormal: "$HitNormal$" notifying "$CachedChildCommand$" and letting it handle the event.", 'SeePlayer' ); return CachedChildCommand.NotifyBump( Other, HitNormal ); } return false; } function NotifyEnemyChanged( optional Pawn OldEnemy ) { super.NotifyEnemyChanged( OldEnemy ); GotoState( DefaultStateName ); } /********************************************************************************************* * Combat ********************************************************************************************* */ function bool NotifyPlayerBecameVisible( Pawn VisiblePlayer ) { if( CachedChildCommand != None ) { `AILog( GetFuncName()$"() Seen: "$VisiblePlayer$" notifying "$CachedChildCommand$" and letting it handle the event.", 'SeePlayer' ); return CachedChildCommand.NotifyPlayerBecameVisible( VisiblePlayer ); } `AILog( GetFuncName()$"() : "$VisiblePlayer$" ignoring this event", 'SeePlayer' ); //DisableSeePlayer(); return false; } function bool ShouldAttackWhileMoving() { `AILog( self$" ShouldAttackWhileMoving() returning true", 'Command_Base' ); return true; } function bool ShouldSelectTarget() { if( Enemy == none || !Enemy.IsAliveAndWell() ) { `AILog( self$" "$GetFuncName()$"() returning TRUE", 'Command_Base' ); return true; } return false; } /********************************************************************************************* * Base Zed Command State ********************************************************************************************* */ state ZedBaseCommand `DEBUGSTATE { event BeginState( name PreviousStateName ) { } event EndState( name NextStateName ) { } Begin: `AILog( self$" "$GetStateName()$" [Begin Label]", 'Command_Base' ); if( Pawn.Physics == PHYS_Falling ) { DisableMeleeRangeEventProbing(); WaitForLanding(); } // Don't do any sort of AI processing in incap states if( MyKFPawn.IsIncapacitated() ) { DisableMeleeRangeEventProbing(); Sleep( 0.1f ); Goto( 'Begin' ); } EnableMeleeRangeEventProbing(); // Check for any interrupt transitions CheckInterruptCombatTransitions(); // Select nearest enemy if current enemy is invalid if (!IsValidAttackTarget(KFPawn(Enemy)) || !Enemy.IsAliveAndWell() || !Enemy.CanAITargetThisPawn(MyKFPawn.Controller)) { SelectEnemy(); } // If enemy is still invalid or melee range events are disabled, pause and loop back if( (Enemy == none && DoorEnemy == none) || !bIsProbingMeleeRangeEvents ) { `AILog( self$" Enemy: "$Enemy$" bIsProbingMeleeRangeEvents: "$bIsProbingMeleeRangeEvents, 'Command_Base' ); Sleep( 0.1f + FRand() * 0.3f ); Goto( 'Begin' ); } // Handle special case if I'm supposed to be attacking a door if( DoorEnemy != none && DoorEnemy.Health > 0 && VSizeSq( DoorEnemy.Location - Pawn.Location ) < (DoorMeleeDistance * DoorMeleeDistance) ) //200UU { `AILog( self$" DoorEnemy: "$DoorEnemy$" starting melee attack", 'Command_Base' ); UpdateHistoryString( "[Attacking : "$DoorEnemy$" at "$WorldInfo.TimeSeconds$"]" ); class'AICommand_Attack_Melee'.static.Melee( Outer, DoorEnemy ); } if( IsValidAttackTarget(KFPawn(Enemy)) ) { `AILog( "Calling SetEnemyMoveGoal [Dist:"$VSize(Enemy.Location - Pawn.Location)$"] using offset of "$AttackRange$", because IsWithinBasicMeleeRange() returned false ", 'Command_Base' ); bWaitingOnMovementPlugIn = true; SetEnemyMoveGoal(self, true,,, ShouldAttackWhileMoving() ); while( bWaitingOnMovementPlugIn && bUsePluginsForMovement ) { Sleep(0.03); } `AiLog("Back from waiting for the movement plug in!!!"); if( Enemy == none ) { Sleep( FRand() + 0.1f ); Goto( 'Begin' ); } } else { `AILog("Enemy is invalid melee target" @ Enemy, 'Command_Base'); bFailedToMoveToEnemy = true; } // Check combat transitions CheckCombatTransition(); if( bFailedToMoveToEnemy ) { if( bFailedPathfind ) { bFailedPathfind = false; Sleep( 0.f ); } else { Sleep( 0.f ); } SetEnemy( GetClosestEnemy( Enemy ) ); } else { Sleep(0.f); } Goto('Begin'); } DefaultProperties { DefaultStateName=ZedBaseCommand bAllowedToAttack=true bDisableMovementPluginOnPushed=false }