//============================================================================= // KFDoorMarker //============================================================================= // Base KF2 NavigationPoint used to mark door locations //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC //============================================================================= class KFDoorMarker extends DoorMarker native(Waypoint) hidecategories(DoorMarker); var(KF2) KFDoorActor MyKFDoor; /** Optionally override the distance at which my door will be considered reached by NPCs */ var(KF2) float AdjustedReachThreshold; /** The computed cost when this door is welded */ var transient int ExtraCostWhenWelded; cpptext { virtual UClass* GetReachSpecClass( ANavigationPoint* Nav, UClass* DefaultReachSpecClass ); // Overriding because DoorMarker returns MyKFDoor which is an InterpActor virtual AActor* AssociatedLevelGeometry(); virtual UBOOL HasAssociatedLevelGeometry(AActor *Other); #if WITH_EDITOR virtual UBOOL IsNecessaryForPath() { return true; } virtual UBOOL CanConnectTo(ANavigationPoint* Nav, UBOOL bCheckDistance); #endif virtual void PrePath(); virtual void PostPath(); virtual void FindBase(); #if WITH_EDITOR virtual void CheckForErrors(); #endif } event bool SuggestMovePreparation( Pawn Other ) { local KFPawn_Monster KFPM; local int count; `AILog_Ext( GetFuncName()$"() "$self$" called for "$Other$" Dist: "$VSize(Location - Other.Location), 'Doors', KFPawn(Other).MyKFAIC ); if( MyKFDoor != none && !MyKFDoor.IsCompletelyOpen() && MyKFDoor.WeldIntegrity > 0 ) { // @Todo We might want to make this check from the position of the marker without the visible check foreach VisibleCollidingActors( class'KFPawn_Monster', KFPM, 220.f, Other.Location,,, true ) { // Stop trying to attack the door if another zed's attacking it, as this event call was probably from adjust around code caused by bumping into the door if( KFPM != Other && KFPM.IsAliveAndWell() && VSizeSq2D(KFPM.Location - Location) < VSizeSq2D(Other.Location - Location) && KFPM.MyKFAIC.DoorEnemy != none && KFPM.MyKFAIC.DoorEnemy == MyKFDoor ) { Count++; } } // Only allow up to 5 attackers if( Count > 5 ) { `AILog_Ext( GetFuncName()$"() "$self$" - telling "$Other$" to wait for "$MyKFDoor$" to open", 'Doors', KFPawn(Other).MyKFAIC ); Other.ZeroMovementVariables(); //DrawDebugSphere( Other.Location, 32, 8, 255, 0, 0, true ); KFPawn_Monster(Other).MyKFAIC.WaitForDoor( MyKFDoor ); return true; } } return false; } function MoverOpened() { bBlocked = (!bInitiallyClosed && bBlockedWhenClosed); bDoorOpen = bInitiallyClosed; WorldInfo.Game.NotifyNavigationChanged(self); } function MoverClosed() { bBlocked = (bInitiallyClosed && bBlockedWhenClosed); bDoorOpen = !bInitiallyClosed; WorldInfo.Game.NotifyNavigationChanged(self); } /** Updates the pathing cost based on weld integrity, number of attackers and number queued */ function UpdatePathingCost( int AttackerCount, int QueuedCount ) { local float IntegrityPct; if( MyKFDoor.WeldIntegrity > 0 ) { IntegrityPct = float(MyKFDoor.WeldIntegrity) / float(MyKFDoor.MaxWeldIntegrity); // If there are simply too many AI waiting on this door, and it's heavily welded, just skip it if( IntegrityPct > 0.75f && AttackerCount + QueuedCount > 10 ) { ExtraCostWhenWelded = 100000; return; } // Initialize at 1000 ExtraCostWhenWelded = 1000; // Add cost based on the strength of the weld (10-1000) ExtraCostWhenWelded += int( IntegrityPct * 100.f ) * 10; // Add cost based on the number of attackers (0-500) ExtraCostWhenWelded += Min( AttackerCount * 100, 500 ); // Add cost based on the number of queued AI (0-1000) ExtraCostWhenWelded += Min( QueuedCount * 100, 1000 ); } else { // Reset to base cost ExtraCostWhenWelded = 1000; } //`log("updating door pathing cost:"@extracostwhenwelded); } function bool ProceedWithMove(Pawn Other) { local KFAIController KFAIC; `if(`__TW_PATHFINDING_) if( KFPawn(Other) != none && !KFPawn(Other).IsHumanControlled() ) { `AILog_Ext( GetFuncName()$" "$self$" for "$Other, 'Doors', KFPawn(Other).MyKFAIC ); } `endif KFAIC = KFAIController( Other.Controller ); if( KFAIC != none && KFAIC.PendingDoor != none && (bWaitUntilCompletelyOpened && KFAIC.PendingDoor.IsCompletelyOpen()) || (!bWaitUntilCompletelyOpened && (KFAIC.PendingDoor.bIsDoorOpen || KFAIC.PendingDoor.bIsDestroyed)) ) { return true; } if( MyKFDoor != none && (bWaitUntilCompletelyOpened && MyKFDoor.IsCompletelyOpen()) || (!bWaitUntilCompletelyOpened && (MyKFDoor.bIsDoorOpen || MyKFDoor.bIsDestroyed)) ) { return true; } if( Other.Controller != none && KFAIController( Other.Controller ) != none && bWaitUntilCompletelyOpened && MyKFDoor != none ) { KFAIController( Other.Controller ).WaitForDoor( MyKFDoor ); } return false; } event Actor SpecialHandling( Pawn Other ) { `AILog_Ext( GetFuncName()$"() "$self$" SpecialHandling event called for "$Other, 'Doors', KFPawn(Other).MyKFAIC ); if ( MyKFDoor == None || MyKFDoor.IsCompletelyOpen() /*|| VSize( Location - Other.Location ) > 200.f*/ ) { return self; } if( KFPawn(Other) != none && !KFPawn(Other).IsHumanControlled() ) { `AILog_Ext( GetFuncName()$" "$self$" calling for "$Other, 'Doors', KFPawn(Other).MyKFAIC ); } return self; // Added 1.24.14 // Disabled 1.24.14, replaced with MoveToGoal command door handling (see ReachedIntermediateGoal) // Door is welded shut // if( MyKFDoor.WeldIntegrity > 0 ) // { // KFAIController( Other.Controller ).WaitForDoor( MyKFDoor ); // KFAIController( Other.Controller ).NotifyAttackDoor( MyKFDoor ); // return self; // } // else // { // KFAIController( Other.Controller ).WaitForDoor( MyKFDoor ); // MyKFDoor.UseDoor( Other ); // return self; // } } DefaultProperties { AdjustedReachThreshold=72.f bBuildLongPaths=false }