/** * Interface for path objects which need to interface with the navmesh * - mostly provides a linkage from edges in the mesh to an object in the game. Useful for specifc edges which * need special handling (e.g. ladders). This interface does not provide methods for altaring the mesh at runtime, for that * see Interface_navMeshPathObstacle (which can be used in conjunction with this interface) * * Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. */ interface Interface_NavMeshPathObject native(AI); cpptext { /************************************************************************ * Runtime Edge Query interface * ************************************************************************/ virtual void InitGuid( TArray& ExistingNavGuids ) { AActor* Actor = Cast(GetUObjectInterfaceInterface_NavMeshPathObject()); if( Actor != NULL ) { FGuid* pGuid = Actor->GetGuid(); if( pGuid ) { if( !pGuid->IsValid() || ExistingNavGuids.ContainsItem(*pGuid) ) { *pGuid = appCreateGuid(); } else { // save the existing guid to check for duplicates ExistingNavGuids.AddItem(*pGuid); } } } } /** * Called from edges linked to this PO * @param Interface - the navhandle interface of the entity pathing * @param PreviousPoint - the previous point in the path search (e.g. the point along the predecessor edge) * @param out_PathEdgePoint - the point we used along this edge to determine the cost * @param Edge - the edge linked to this PO which needs to compute cost * @return - the cost for traversing this edge */ virtual INT CostFor( const FNavMeshPathParams& PathParams, const FVector& PreviousPoint, FVector& out_PathEdgePoint, FNavMeshPathObjectEdge* Edge, FNavMeshPolyBase* SourcePoly ) { return Edge->FNavMeshEdgeBase::CostFor(PathParams, PreviousPoint, out_PathEdgePoint, SourcePoly); } /** * passthru function from edge to determine if the passed searcher supports this pathobject's movement * @param Interface - the interface of the searcher * @param CurPoly - the poly being checked for support * @param Edge - the edge linked to this path object which is being considered * @param PredecessorEdge - the edge we are coming from to get to 'Edge' * @return TRUE if this path object supports the searcher */ virtual UBOOL Supports( const FNavMeshPathParams& PathParams, FNavMeshPolyBase* CurPoly, FNavMeshPathObjectEdge* Edge, FNavMeshEdgeBase* PredecessorEdge) { return Edge->FNavMeshEdgeBase::Supports(PathParams,CurPoly,PredecessorEdge); } /** * called when an entity is about to move through an edge linked to this path object. Allows the path object to * trigger animations, etc related to this PO * @param Interface - the entity which is traversing this PO * @param out_MovePt - the point generated by GetEdgeDestination which the entity is moving toward * @param Edge - the edge linked to this PathObject which is being moved through * @return - whether or not the move was successful (true means we're done with this edge, remove it from path) */ virtual UBOOL PrepareMoveThru( class IInterface_NavigationHandle* Interface, FVector& out_MovePt, FNavMeshPathObjectEdge* Edge ) { return FALSE; } /** * called to determine the optimal position along this edge for the passed parameters (e.g. string pulling) * @param EntityRadius - the radius of the entity we are computing the position for * @param InfluencePosition - the position we are trying to get close to (e.g. previous point in path) * @param EntityPosition - the current position of the entity (e.g. starting position of bot) * @param Edge - the edge which is associated with this PO which we're being asked about * @param out_EdgeDest - the destination we want to use * @return whether this PO modified the destination or not (FALSE indicates the default edge functionality should be used) */ virtual UBOOL GetEdgeDestination( const FNavMeshPathParams& PathParams, FLOAT EntityRadius, const FVector& InfluencePosition, const FVector& EntityPosition, FVector& out_EdgeDest, FNavMeshPathObjectEdge* Edge, UNavigationHandle* Handle) { return FALSE; } /** * called to allow this PO to draw custom stuff for edges linked to it * @param DRSP - the sceneproxy we're drawing for * @param DrawOffset - offset from the actual location we should be drawing * @param Edge - the edge we're drawing * @return - whether this PO is doing custom drawing for the passed edge (FALSE indicates the default edge drawing functionality should be used) */ virtual UBOOL DrawEdge( FDebugRenderSceneProxy* DRSP, FColor C, FVector DrawOffset, FNavMeshPathObjectEdge* Edge ) { return FALSE; } /** * allows path objects to dictate when it's OK for GetNextMoveLocation to push the current edge past this one in the AI's path. * (normally the edge the AI is running to is figured out automatically based on which polygon the AI is in) * @param PathParams - the parameters associated with the AI wondering if it is OK to run normal behavior * @param bInPoly0 - whether the AI is currently in poly0 of this edge * @param bInPoly1 - whether the AI is currently in poly1 of this edge * @return - TRUE if it's OK to automatically progress the edge past this one */ virtual UBOOL AllowMoveToNextEdge(FNavMeshPathParams& PathParams, UBOOL bInPoly0, UBOOL bInPoly1){ return TRUE; } /** * this function is called after a poly linked to this edge is replaced with a submesh for a pathobstacle * allows special edges to have a chance to add extra data after the mesh is split * @params Edge - the edge that we are updating for * @param Poly - the poly that was just disabled and replaced with a submesh * @param NewSubMesh - the submesh that now represents the poly */ virtual void PostSubMeshUpdateForOwningPoly(FNavMeshPathObjectEdge* Edge, FNavMeshPolyBase* Poly, UNavigationMeshBase* New_SubMesh){} /** * This is a helper function which is useful when this pathobject is being used in conjunction with the pathobstacle interface * - just does all the work normally done in AddObstacleEdge, except convenienty tuned for use with pathobjects * @param Status - current status of edges (e.g. what still needs adding) * @param inV1 - vertex location of first vert in the edge * @param inV2 - vertex location of second vert in the edge * @param ConnectedPolys - the polys this edge links * @param bEdgesNeedToBeDynamic - whether or not added edges need to be dynamic (e.g. we're adding edges between meshes) * @param PolyAssocatedWithThisPO - the index into the connected polys array of the poly which is associated with this path object * @param POOwner - the owner of the pathobject interface (e.g. the actor that implements interface) * @param SupportedEdgeWidth - widht of unit this edge supports (defaults to -1.0 in which case the length of the edge will be used) * @(optional) param EdgeGroupID - ID of the edgegroup this edge is a part of (defaults to no group) * @return returns an enum describing what just happened (what actions did we take) - used to determien what accompanying actions need to be taken * by other obstacles and calling code */ EEdgeHandlingStatus AddObstacleEdgeForObstacle( EEdgeHandlingStatus Status, const FVector& inV1, const FVector& inV2, TArray& ConnectedPolys, UBOOL bEdgesNeedToBeDynamic, INT PolyAssocatedWithThisPO, AActor* POOwner, FLOAT SupportedEdgeWidth=-1.0f, BYTE EdgeGroupID=MAXBYTE) { // if an edge has already been added in the direction we want to add an edge then there is probably a conflicting pathobstacle (e.g. we're butted // up against another obstacle which has already added an edge.. so just bail) if(Status == EHS_AddedBothDirs) { return Status; } // if there is already an edge point back into this PO from the other poly, bail if( (PolyAssocatedWithThisPO == 0 && Status == EHS_Added1to0) || (PolyAssocatedWithThisPO == 1 && Status == EHS_Added0to1) ) { return Status; } TArray ReversedConnectedPolys=ConnectedPolys; // so we want to add an edge back into the poly associated with this PO, so swap the order if we need to if(PolyAssocatedWithThisPO == 0) { ReversedConnectedPolys.SwapItems(0,1); } UNavigationMeshBase* Mesh = ReversedConnectedPolys(0)->NavMesh; if( Mesh == NULL ) { return Status; } FNavMeshPathObjectEdge* NewEdge = NULL; if( bEdgesNeedToBeDynamic ) { TArray CreatedEdges; Mesh->AddDynamicCrossPylonEdge(inV1,inV2,ReversedConnectedPolys,SupportedEdgeWidth,EdgeGroupID,TRUE, &CreatedEdges); NewEdge = (CreatedEdges.Num() > 0) ? CreatedEdges(0) : NULL; checkSlowish(CreatedEdges.Num() <2); } else { if (! Mesh->AddOneWayCrossPylonEdgeToMesh(inV1,inV2,ReversedConnectedPolys,SupportedEdgeWidth,EdgeGroupID,&NewEdge) ) { // we failed to add an edge for some reason.. if it returns true, we added an edge or there was already an identical edge there return Status; } } // bind new edge to this avoidance vol if(NewEdge != NULL) { NewEdge->PathObject = POOwner; NewEdge->InternalPathObjectID = 0; } // indicate that we added an edge from dest poly to src poly if(Status == EHS_AddedNone) { if(PolyAssocatedWithThisPO == 0) { return EHS_Added1to0; } else { return EHS_Added0to1; } } else { // if we get here that means someone should have already added an edge in the opposite direction return EHS_AddedBothDirs; } } /************************************************************************ * Mesh generation interface * ************************************************************************/ /** * this function describes a 'cookie cutter' edge that will be used to split the mesh beneath it * for example if you have a cost inflicting volume that needs to conform the mesh to itself * you could return an array of verts along the bottom boundary of the volume, and a height up to the top of the volume * and then you have poly boundaries exactly along the border of the volume with which you can add * edges which affect cost * @param Poly - array of vectors that describe bottom of 'cookie cutter' volume (should be CW and convex) * @param PolyHeight - height above the poly within which polys should be split (extrude passed poly up by this amount and use faces for clip) * @return - TRUE if this shape should be used to split the mesh FALSE if no splitting is necessary */ virtual UBOOL GetMeshSplittingPoly( TArray& Poly, FLOAT& PolyHeight ){ return FALSE; } /** * called for each pylon's exploration to see if this path object needs to be consulted during exploration * @param Py - the pylon being explored that we need to know if this PO should be asked about * @return - TRUE if this path object needs to have its IsExplorationAllowed function considered during path exploration */ virtual UBOOL NeedsForbidExploreCheck(APylon* Py) { return FALSE; } /** * called during mesh exploration, allows this PO to forbid exploration through specific areas * @param Py - the pylon being explored right now * @param TestPosition - the position of the new poly we are asking about * @return - TRUE if exploration is allowed */ virtual UBOOL IsExplorationAllowed( APylon* Py, const FVector& TestPosition) { return TRUE; } /** * called after initial exploration, gives this PO a chance to add seeds to the exploration process * @param SeedPointList - reference to the main array of seeds that need to be expanded after initial explore * @param Py - the pylon which is being expanded */ virtual void AddAuxSeedPoints( APylon* Py ){} /** * called after edge creation is complete for each pylon to allow this PO to add edges for itself * @param Py - the pylon which we are creating edges for */ virtual void CreateEdgesForPathObject( APylon* Py ){} /** * Function to be used from within CreateEdgesForPathObject * @param Interface_Owner - the actor which owns this interface * @param StartPoly - Source poly for edge * @param EndPoly - Destination poly for edge * @param Vert0 - One end point of the edge * @param Vert1 - Another end point of the edge * @param InternalPOID - optional param for extra identifier which is attached to the edge being added (to distinguish between edges linked to this PO) * @return - T/F indicating success */ UBOOL AddEdgeForThisPO(AActor* Interface_Owner, APylon* Py, FNavMeshPolyBase* StartPoly, FNavMeshPolyBase* EndPoly, const FVector& Vert0, const FVector& Vert1, INT InternalPOID=-1, UBOOL bForce=FALSE) { UNavigationMeshBase* Mesh = Py->GetNavMesh(); if( Mesh == NULL ) { return FALSE; } if( StartPoly == EndPoly || StartPoly == NULL || EndPoly == NULL ) { warnf(NAME_Warning,TEXT("WARNING! A pathobject (%s) tried to add an edge that links a poly to itself, or links a poly to nothing. This add is being IGNORED!"), *Interface_Owner->GetName()); return FALSE; } TArray ConnectedPolys; ConnectedPolys.AddItem(StartPoly); ConnectedPolys.AddItem(EndPoly); FNavMeshPathObjectEdge* NewEdge = NULL; if( Mesh->AddOneWayCrossPylonEdgeToMesh(Vert0,Vert1,ConnectedPolys,-1.0f,MAXBYTE,&NewEdge,bForce) ) { if(NewEdge != NULL) { NewEdge->PathObject = Interface_Owner; NewEdge->InternalPathObjectID = InternalPOID; } // still want to return true if newedge is null, false indicates we ran out of vert indices return TRUE; } return FALSE; } /** * This is called offline when edges are about to be added from the exterior of the pathobject to the interior or vice versa * Default behavior just a normal edge, override to add special costs or behavior * @param Status - current status of edges (e.g. what still needs adding) * @param inV1 - vertex location of first vert in the edge * @param inV2 - vertex location of second vert in the edge * @param ConnectedPolys - the polys this edge links * @param PolyAssocatedWithThisPO - the index into the connected polys array parmaeter which tells us which poly from that array is associated with this path object * @(optional) param SupportedEdgeWidth - width of unit that this edge supports, defaults to -1.0f meaning the length of the edge itself will be used * @(optional) param EdgeGroupID - ID of the edgegroup this edge is a part of (defaults to no group) * @return returns an enum describing what just happened (what actions did we take) - used to determien what accompanying actions need to be taken * by other obstacles and calling code */ virtual EEdgeHandlingStatus AddStaticEdgeIntoThisPO( EEdgeHandlingStatus Status, const FVector& inV1, const FVector& inV2, TArray& ConnectedPolys, INT PolyAssocatedWithThisPO, FLOAT SupportedEdgeWidth=-1.0f, BYTE EdgeGroupID=MAXBYTE); /** * Allows this path object to modify the final path generated for a bot when the path * uses an edge linked to this path object.. default is to do nothing * @param Handle - the navigation handle whose path we are molesting * @param Idx - the index into the pathcache that the edge associated with this path object is at * @return - TRUE if we modified the path */ virtual UBOOL ModifyFinalPath( UNavigationHandle* Handle, INT Idx ) { return FALSE; } /** * For debugging. Verifies that this pathobject is still alive and well and not orphaned or deleted * @return - TRUE If this path object is in good working order */ virtual UBOOL Verify() { return FALSE; } }