//============================================================================= // NavigationPoint. // // NavigationPoints are organized into a network to provide AIControllers // the capability of determining paths to arbitrary destinations in a level // // Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. //============================================================================= class NavigationPoint extends Actor hidecategories(Lighting,LightColor,Force) dependson(ReachSpec) ClassGroup(Navigation) native; const INFINITE_PATH_COST = 10000000; //------------------------------------------------------------------------------ // NavigationPoint variables var transient bool bEndPoint; // used by C++ navigation code var transient bool bTransientEndPoint; // set right before a path finding attempt, cleared afterward. var transient bool bHideEditorPaths; // don't show paths to this node in the editor var transient bool bCanReach; // used during paths review in editor /** structure for inserting things into the navigation octree */ struct native NavigationOctreeObject { /** the bounding box to use */ var Box BoundingBox; /** cached center of that box */ var vector BoxCenter; /** if this object is in the octree, pointer to the node it's in, otherwise NULL */ var native transient const pointer OctreeNode{class FNavigationOctreeNode}; /** UObject that owns the entry in the octree */ var noexport const Object Owner; /** bitfield representing common classes of Owner so we can avoid casts */ var noexport const byte OwnerType; structcpptext { enum ENavOctreeObjectType { NAV_NavigationPoint = 0x01, NAV_ReachSpec = 0x02, }; private: UObject* Owner; BYTE OwnerType; public: /** constructor, makes sure OctreeNode is NULL */ FNavigationOctreeObject() : OctreeNode(NULL), Owner(NULL), OwnerType(0) {} /** destructor, removes us from the octree if we're still there */ ~FNavigationOctreeObject(); /** sets the object's owner and initializes the OwnerType for fast cast to common types */ void SetOwner(UObject* InOwner); /** sets the object's bounding box * if the object is currently in the octree, re-adds it * @param InBoundingBox the new bounding box to use */ void SetBox(const FBox& InBoundingBox); /** overlap check function called after the axis aligned bounding box check succeeds * allows for more precise checks for certain object types * @param TestBox the box to check * @return true if the box doesn't overlap this object, false if it does */ UBOOL OverlapCheck(const FBox& TestBox); /** templated accessor to Owner, to avoid casting for common cases * @note T must be UObject or a subclass, or this function will not compile */ template FORCEINLINE T* GetOwner() { return Cast(Owner); } //@note the specializations for this template are in UnPath.h because they must be outside the struct definition void Serialize(FArchive& Ar); } }; var native transient const NavigationOctreeObject NavOctreeObject; var() bool bBlocked; // this node is currently unuseable var() bool bOneWayPath; // reachspecs from this path only in the direction the path is facing (180 degrees) var bool bNeverUseStrafing; // shouldn't use bAdvancedTactics going to this point var bool bAlwaysUseStrafing; // shouldn't use bAdvancedTactics going to this point var const bool bForceNoStrafing;// override any LD changes to bNeverUseStrafing var const bool bAutoBuilt; // placed during execution of "PATHS BUILD" var bool bSpecialMove; // if true, pawn will call SuggestMovePreparation() when moving toward this node `if(`__TW_PATHFINDING_) var() bool bAllowPathConnections; var() editconst bool bWallNode; /** Brightness of light(s) affecting this navigation point (calculated when building paths) */ var() editconst float Luminance; var LinearColor Intensity; var() bool bNoAutoConnect; // don't connect this path to others except with special conditions (used by LiftCenter, for example) /** Tag identifier that can be used when debugging AI. For example, the 'AIPathTo ' will force a debug NPC to path to the NavigatioNPoint with a matching DebugTag */ var(KFPathnode) duplicatetransient name DebugTag; /** Notification will be sent to pawn when/if this node is added to its RouteCache, to allow early special handling */ var(KFPathnode) duplicatetransient bool bNotifyOnAddToRouteCache; `else var bool bNoAutoConnect; // don't connect this path to others except with special conditions (used by LiftCenter, for example) `endif var const bool bNotBased; // used by path builder - if true, no error reported if node doesn't have a valid base var const bool bPathsChanged; // used for incremental path rebuilding in the editor var() bool bDestinationOnly; // used by path building - means no automatically generated paths are sourced from this node var bool bSourceOnly; // used by path building - means this node is not the destination of any automatically generated path var bool bSpecialForced; // paths that are forced should call the SpecialCost() and SuggestMovePreparation() functions var bool bMustBeReachable; // used for PathReview code var bool bBlockable; // true if path can become blocked (used by pruning during path building) var bool bFlyingPreferred; // preferred by flying creatures var bool bMayCausePain; // set in C++ if in PhysicsVolume that may cause pain var transient bool bAlreadyVisited; // internal use var() bool bVehicleDestination; // if true, forced paths to this node will have max width to accomodate vehicles var() bool bMakeSourceOnly; var bool bMustTouchToReach; // if true. reach tests are based on whether pawn can move to overlap this NavigationPoint (only valid if bCollideActors=true) /** whether walking on (being based on) this NavigationPoint counts as reaching it */ var bool bCanWalkOnToReach; /** if true, attempt to build long range (> MAXPATHDIST) paths to/from this node */ var bool bBuildLongPaths; /** indicates vehicles cannot use this node */ var(VehicleUsage) bool bBlockedForVehicles; /** vehicles with bUsePreferredVehiclePaths set (large vehicles, usually) will prioritize using these nodes */ var(VehicleUsage) bool bPreferredVehiclePath; var() editinline const editconst duplicatetransient array PathList; //index of reachspecs (used by C++ Navigation code) /** List of navigation points to prevent paths being built to */ var() editoronly duplicatetransient array EditorProscribedPaths; /** List of navigation points to force paths to be built to */ var() editoronly duplicatetransient array EditorForcedPaths; /** List of volumes containing this navigation point relevant for gameplay */ var() const editconst array Volumes; var int visitedWeight; var const int bestPathWeight; var const private NavigationPoint nextNavigationPoint; var const NavigationPoint nextOrdered; // for internal use during route searches var const NavigationPoint prevOrdered; // for internal use during route searches var const NavigationPoint previousPath; var int Cost; // added cost to visit this pathnode var() int ExtraCost; // Extra weight added by level designer var transient int TransientCost; // added right before a path finding attempt, cleared afterward. var transient int FearCost; // extra weight diminishing over time (used for example, to mark path where bot died) /** Mapping of Cost/Description for costs of this node */ struct native DebugNavCost { var string Desc; var int Cost; structcpptext { /** constructors */ FDebugNavCost() {} FDebugNavCost(EEventParm) { appMemzero(this, sizeof(FDebugNavCost)); } UBOOL operator==(const FDebugNavCost& Other) { return (Other.Cost == Cost && Other.Desc == Desc); } } }; var transient array CostArray; var DroppedPickup InventoryCache; // used to point to dropped weapons var float InventoryDist; var const float LastDetourWeight; var CylinderComponent CylinderComponent; /** path size of the largest ReachSpec in this node's PathList */ var() editconst const Cylinder MaxPathSize; /** GUID used for linking paths across levels */ var() editconst const duplicatetransient guid NavGuid; /** Normal editor sprite */ var const transient SpriteComponent GoodSprite; /** Used to draw bad collision intersection in editor */ var const transient SpriteComponent BadSprite; /** Does this nav point point to others in separate levels? */ var const bool bHasCrossLevelPaths; /** Which navigation network does this navigation point connect to? */ var() editconst const int NetworkID; /** Pawn that is currently anchor to this navigation point */ var transient Pawn AnchoredPawn; /** Last time a pawn was anchored to this navigation point - set when Pawn chooses a new anchor */ var transient float LastAnchoredPawnTime; /** whether we need to save this in checkpoints because it has been modified by Kismet */ var transient bool bShouldSaveForCheckpoint; struct CheckpointRecord { var bool bDisabled; var bool bBlocked; }; `if(`__TW_PATHFINDING_) var(KFPathNode) bool bConnectToSameFloorOnly; /** When true, this NavigationPoint will not allow other NavigationPoints to connect to it, and it will not allow itself to connect to other NavigationPoints - unless it is a forced path connection. */ var() bool bNoAutoConnectBiDirectional; /** Flagged during pathbuilding for navigation points with no reachspecs. */ var() editconst bool bBadPlacement; /** Flagged during pathbuilding for nodes that are considered unnecessary. */ var() editconst bool bUnnecessaryNavPoint; `endif cpptext { virtual UClass* GetReachSpecClass( ANavigationPoint* Nav, UClass* DefaultReachSpecClass ) { return DefaultReachSpecClass; } virtual void ClearPaths(); virtual void FindBase(); virtual void PostScriptDestroyed(); protected: virtual void UpdateComponentsInternal(UBOOL bCollisionUpdate = FALSE); public: void PostEditMove(UBOOL bFinished); void Spawned(); void UpdateMaxPathSize(); UBOOL FindAlternatePath(UReachSpec* StraightPath, INT AccumulatedDistance); virtual UBOOL ShouldBeBased(); /** Checks to make sure the navigation is at a valid point */ virtual void Validate(); virtual void TogglePathRendering(UBOOL bShouldDrawPaths); #if WITH_EDITOR virtual void CheckForErrors(); /** * Sets the network ID for this nav and all connected navs. */ virtual void SetNetworkID(INT InNetworkID); static void BuildNetworkIDs(); virtual void ReviewPath(APawn* Scout); virtual UBOOL CheckSatisfactoryConnection(ANavigationPoint* Other); void CleanUpPruned(); INT PrunePaths(); // more aggressive (and expensive) path pruning routine ( should only be called from editor ) INT AggressivePrunePaths(); INT SecondPassAggressivePrunePaths(); virtual UBOOL CanPrunePath(INT index) { return TRUE; } virtual void AddForcedSpecs( AScout *Scout ); #if __TW_PATHFINDING_ virtual UReachSpec* ManualForcePathTo( ANavigationPoint *Nav, AScout *Scout = NULL, UClass* ReachSpecClass = NULL, UBOOL bJump = FALSE ); #endif virtual UReachSpec* ForcePathTo(ANavigationPoint *Nav, AScout *Scout = NULL, UClass* ReachSpecClass = NULL ); virtual UBOOL ProscribePathTo(ANavigationPoint *Nav, AScout *Scout = NULL); /** builds long range paths (> MAXPATHDIST) between this node and all other reachable nodes * for which a straight path would be significantly shorter or the only way to reach that node * done in a separate pass at the end because it's expensive and so we want to early out in the maximum number * of cases (e.g. if suitable short range paths already get there) */ void AddLongPaths(AScout* Scout, UBOOL bOnlyChanged); virtual void addReachSpecs(class AScout *Scout, UBOOL bOnlyChanged=0); virtual INT AddMyMarker(AActor *S); /** returns whether a ReachSpec can be built from this node to Nav * @param Nav the NavigationPoint to check if we can build a path to * @param bCheckDistance whether or not we should check if Nav is close enough (MAXPATHDIST) * @return true if a ReachSpec can be built from this node to Nav, false otherwise */ virtual UBOOL CanConnectTo(ANavigationPoint* Nav, UBOOL bCheckDistance); virtual void OnAddToPrefab(); #if __TW_PATHFINDING_ virtual UBOOL PreserveForcedPath( ANavigationPoint* DestNav ); virtual UBOOL IsNecessaryForPath() { return FALSE; } #endif //__TW_PATHFINDING_ #endif //WITH_EDITOR virtual void InitForPathFinding() {}; virtual void ClearForPathFinding(); UBOOL CanReach(ANavigationPoint *Dest, FLOAT Dist, UBOOL bUseFlag, UBOOL bAllowFlying); virtual class APickupFactory* GetAPickupFactory() { return NULL; } virtual void SetVolumes(const TArray& Volumes); virtual void SetVolumes(); virtual UBOOL ReachedBy(APawn* P, const FVector& TestPosition, const FVector& Dest); virtual UBOOL TouchReachSucceeded(APawn *P, const FVector& TestPosition); virtual UBOOL GetUpDir( FVector &V ) { return 0; } virtual void AddToNavigationOctree(); virtual void RemoveFromNavigationOctree(); virtual UBOOL PlaceScout(class AScout *Scout); /** returns the position the AI should move toward to reach this actor */ FVector GetDestination(AController* C); /** sorts the PathList by distance, shortest first */ void SortPathList(); /** * Fills the array of any nav references needing to be fixed up. */ virtual void GetActorReferences(TArray &ActorRefs, UBOOL bIsRemovingLevel); virtual void ClearCrossLevelReferences(); virtual FGuid* GetGuid() { return &NavGuid; } virtual ANavigationPoint* SpecifyEndAnchor(APawn* RouteFinder); /** * Works through the component arrays marking entries as pending kill so references to them * will be NULL'ed. * * @param bAllowComponentOverride Whether to allow component to override marking the setting */ virtual void MarkComponentsAsPendingKill(UBOOL bAllowComponentOverride = FALSE); #if __TW_PATHFINDING_ virtual ANavigationPoint* GetANavigationPoint() { return this; } virtual const ANavigationPoint* GetANavigationPoint() const { return this; } #endif } native function GetBoundingCylinder(out float CollisionRadius, out float CollisionHeight) const; `if(`__TW_PATHFINDING_) native final function ReachSpec GetReachSpecTo( NavigationPoint Nav, optional class SpecClass, optional Pawn CheckPawn ); `else native final function ReachSpec GetReachSpecTo( NavigationPoint Nav, optional class SpecClass ); `endif /** returns whether this NavigationPoint is valid to be considered as an Anchor (start or end) for pathfinding by the given Pawn * @param P the Pawn doing pathfinding * @return whether or not we can be an anchor */ native function bool IsUsableAnchorFor( Pawn P ); /** returns whether this NavigationPoint is a teleporter that can teleport the given Actor */ native function bool CanTeleport(Actor A); event int SpecialCost(Pawn Seeker, ReachSpec Path); // Accept an actor that has teleported in. // used for random spawning and initial placement of creatures event bool Accept( actor Incoming, actor Source ) { local bool bResult; // Move the actor here. bResult = Incoming.SetLocation( Location ); if (bResult) { Incoming.Velocity = vect(0,0,0); Incoming.SetRotation(Rotation); } Incoming.PlayTeleportEffect(true, false); return bResult; } /* DetourWeight() value of this path to take a quick detour (usually 0, used when on route to distant objective, but want to grab inventory for example) */ event float DetourWeight(Pawn Other,float PathWeight); /* SuggestMovePreparation() Optionally tell Pawn any special instructions to prepare for moving to this goal (called by Pawn.PrepareForMove() if this node's bSpecialMove==true */ event bool SuggestMovePreparation( Pawn Other ) { // If special move was taken to get to this link return Other.SpecialMoveTo(Other.Anchor, self, Other.Controller.MoveTarget); } /* ProceedWithMove() Called by Controller to see if move is now possible when a mover reports to the waiting pawn that it has completed its move */ function bool ProceedWithMove(Pawn Other) { return true; } /** * Returns the nearest valid navigation point to the given actor. */ static final function NavigationPoint GetNearestNavToActor(Actor ChkActor, optional class RequiredClass,optional array ExcludeList,optional float MinDist) { local NavigationPoint Nav, BestNav; local float Dist, BestDist; if (ChkActor != None) { // iterate through all points in the level foreach ChkActor.WorldInfo.AllNavigationPoints(class'NavigationPoint',Nav) { // if no filter class specified, and // if nav is available to the check actor, and // if the nav isn't part of the excluded list, if ((RequiredClass == None || Nav.class == RequiredClass) && ExcludeList.Find(Nav) == INDEX_NONE) { // pick the closest Dist = VSize(Nav.Location-ChkActor.Location); if (Dist > MinDist) { if (BestNav == None || Dist < BestDist) { BestNav = Nav; BestDist = Dist; } } } } } return BestNav; } /** * Returns the nearest valid navigation point to the given point. */ static final function NavigationPoint GetNearestNavToPoint(Actor ChkActor,vector ChkPoint, optional class RequiredClass,optional array ExcludeList) { local NavigationPoint Nav, BestNav; local float Dist, BestDist; if (ChkActor != None) { // iterate through all points in the level foreach ChkActor.WorldInfo.AllNavigationPoints(class'NavigationPoint',Nav) { // if no filter class specified, and // if nav is available to the check actor, and // if the nav isn't part of the excluded list, if ((RequiredClass == None || Nav.class == RequiredClass) && ExcludeList.Find(Nav) == INDEX_NONE) { // pick the closest Dist = VSize(Nav.Location-ChkPoint); if (BestNav == None || Dist < BestDist) { BestNav = Nav; BestDist = Dist; } } } } return BestNav; } /** * Returns all navigation points near the ChkPoint specified by Radius. */ static native final function bool GetAllNavInRadius( Actor ChkActor, Vector ChkPoint, float Radius, out array out_NavList, optional bool bSkipBlocked, optional int inNetworkID=-1, optional Cylinder MinSize ); /** Returns if this navigation point is on a different network than the given */ native final function bool IsOnDifferentNetwork( NavigationPoint Nav ); /** * Toggle the blocked state of a navigation point. */ function OnToggle(SeqAct_Toggle inAction) { if (inAction.InputLinks[0].bHasImpulse) { bBlocked = false; } else if (inAction.InputLinks[1].bHasImpulse) { bBlocked = true; } else if (inAction.InputLinks[2].bHasImpulse) { bBlocked = !bBlocked; } WorldInfo.Game.NotifyNavigationChanged(self); bShouldSaveForCheckpoint = true; } simulated event ShutDown() { Super.ShutDown(); bBlocked = TRUE; WorldInfo.Game.NotifyNavigationChanged(self); bShouldSaveForCheckpoint = true; } function bool ShouldSaveForCheckpoint() { return bShouldSaveForCheckpoint; } function CreateCheckpointRecord(out CheckpointRecord Record) { Record.bBlocked = bBlocked; } function ApplyCheckpointRecord(const out CheckpointRecord Record) { bBlocked = Record.bBlocked; bShouldSaveForCheckpoint = true; } /** @return Debug abbrev for hud printing */ simulated event string GetDebugAbbrev() { return "NP?"; } defaultproperties { Begin Object Class=SpriteComponent Name=Sprite Sprite=Texture2D'EditorResources.S_NavP' HiddenGame=true HiddenEditor=false AlwaysLoadOnClient=False AlwaysLoadOnServer=False SpriteCategoryName="Navigation" End Object Components.Add(Sprite) GoodSprite=Sprite Begin Object Class=SpriteComponent Name=Sprite2 Sprite=Texture2D'EditorResources.Bad' HiddenGame=true HiddenEditor=true AlwaysLoadOnClient=False AlwaysLoadOnServer=False SpriteCategoryName="Navigation" Scale=0.25 End Object Components.Add(Sprite2) BadSprite=Sprite2 Begin Object Class=ArrowComponent Name=Arrow ArrowColor=(R=150,G=200,B=255) ArrowSize=1 bTreatAsASprite=True HiddenGame=true AlwaysLoadOnClient=False AlwaysLoadOnServer=False SpriteCategoryName="Navigation" End Object Components.Add(Arrow) Begin Object Class=CylinderComponent Name=CollisionCylinder LegacyClassName=NavigationPoint_NavigationPointCylinderComponent_Class CollisionRadius=+0050.000000 CollisionHeight=+0050.000000 End Object CollisionComponent=CollisionCylinder CylinderComponent=CollisionCylinder Components.Add(CollisionCylinder) Begin Object Class=PathRenderingComponent Name=PathRenderer End Object Components.Add(PathRenderer) bMayCausePain=true bStatic=true bNoDelete=true bHidden=FALSE bCollideWhenPlacing=true bMustTouchToReach=true // Long range reachspecs are the source of all evil bBuildLongPaths=false bCollideActors=false // default to no network id NetworkID=-1 // NavigationPoints are generally server side only so we don't need to worry about client simulation bForceAllowKismetModification=true bAllowPathConnections=true }