//=============================================================================
// 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<class T> FORCEINLINE T* GetOwner()
		{
			return Cast<T>(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 <DebugTag>' 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<ReachSpec> PathList; //index of reachspecs (used by C++ Navigation code)
/** List of navigation points to prevent paths being built to */
var() editoronly duplicatetransient array<ActorReference> EditorProscribedPaths;
/** List of navigation points to force paths to be built to */
var() editoronly duplicatetransient array<ActorReference> EditorForcedPaths;
/** List of volumes containing this navigation point relevant for gameplay */
var() const editconst  array<ActorReference>	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<DebugNavCost> 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<class AVolume*>& 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<FActorReference*> &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<ReachSpec> SpecClass, optional Pawn CheckPawn );
`else
native final function ReachSpec GetReachSpecTo( NavigationPoint Nav, optional class<ReachSpec> 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<NavigationPoint> RequiredClass,optional array<NavigationPoint> 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<NavigationPoint> RequiredClass,optional array<NavigationPoint> 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<NavigationPoint> 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
}
