qboolean NPC_GetMoveDirectionAltRoute( vec3_t out, float *distance, qboolean tryStraight ) { vec3_t angles; NPCInfo->aiFlags &= ~NPCAI_BLOCKED; //Clear the struct memset( &frameNavInfo, 0, sizeof( frameNavInfo ) ); //Get our movement, if any if ( NPC_GetMoveInformation( frameNavInfo.direction, &frameNavInfo.distance ) == qfalse ) return qfalse; //Setup the return value *distance = frameNavInfo.distance; //For starters VectorCopy( frameNavInfo.direction, frameNavInfo.pathDirection ); //If on a ladder, move appropriately if ( NPC->watertype & CONTENTS_LADDER ) { NPC_LadderMove( frameNavInfo.direction ); return qtrue; } //Attempt a straight move to goal if ( !tryStraight || NPC_ClearPathToGoal( frameNavInfo.direction, NPCInfo->goalEntity ) == qfalse ) {//blocked //Can't get straight to goal, use macro nav if ( NAVNEW_MoveToGoal( NPC, &frameNavInfo ) == WAYPOINT_NONE ) { //Can't reach goal, just face vectoangles( frameNavInfo.direction, angles ); NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); VectorCopy( frameNavInfo.direction, out ); *distance = frameNavInfo.distance; return qfalse; } //else we are on our way frameNavInfo.flags |= NIF_MACRO_NAV; } else {//we have no architectural problems, see if there are ents inthe way and try to go around them //not blocked if ( d_altRoutes.integer ) {//try macro nav navInfo_t tempInfo; memcpy( &tempInfo, &frameNavInfo, sizeof( tempInfo ) ); if ( NAVNEW_AvoidCollision( NPC, NPCInfo->goalEntity, &tempInfo, qtrue, 5 ) == qfalse ) {//revert to macro nav //Can't get straight to goal, dump tempInfo and use macro nav if ( NAVNEW_MoveToGoal( NPC, &frameNavInfo ) == WAYPOINT_NONE ) { //Can't reach goal, just face vectoangles( frameNavInfo.direction, angles ); NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); VectorCopy( frameNavInfo.direction, out ); *distance = frameNavInfo.distance; return qfalse; } //else we are on our way frameNavInfo.flags |= NIF_MACRO_NAV; } else {//otherwise, either clear or can avoid memcpy( &frameNavInfo, &tempInfo, sizeof( frameNavInfo ) ); } } else {//OR: just give up if ( NAVNEW_AvoidCollision( NPC, NPCInfo->goalEntity, &frameNavInfo, qtrue, 30 ) == qfalse ) {//give up return qfalse; } } } //Setup the return values VectorCopy( frameNavInfo.direction, out ); *distance = frameNavInfo.distance; return qtrue; }
/* ------------------------- NAVNEW_MoveToGoal ------------------------- */ int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t *info ) { int bestNode = WAYPOINT_NONE; qboolean foundClearPath = qfalse; vec3_t origin; navInfo_t tempInfo; qboolean setBlockedInfo = qtrue; qboolean /*inBestWP, */inGoalWP/*, goalWPFailed = qfalse*/; int numTries = 0; memcpy( &tempInfo, info, sizeof( tempInfo ) ); //Must have a goal entity to move there if( self->NPC->goalEntity == NULL ) return WAYPOINT_NONE; if ( self->waypoint == WAYPOINT_NONE && self->noWaypointTime > level.time ) {//didn't have a valid one in about the past second, don't look again just yet return WAYPOINT_NONE; } if ( self->NPC->goalEntity->waypoint == WAYPOINT_NONE && self->NPC->goalEntity->noWaypointTime > level.time ) {//didn't have a valid one in about the past second, don't look again just yet return WAYPOINT_NONE; } if ( self->noWaypointTime > level.time && self->NPC->goalEntity->noWaypointTime > level.time ) {//just use current waypoints bestNode = trap->Nav_GetBestNodeAltRoute2( self->waypoint, self->NPC->goalEntity->waypoint, bestNode ); } //FIXME!!!!: this is making them wiggle back and forth between waypoints else if ( (bestNode = trap->Nav_GetBestPathBetweenEnts( (sharedEntity_t *)self, (sharedEntity_t *)self->NPC->goalEntity, NF_CLEAR_PATH )) == NODE_NONE )//!NAVNEW_GetWaypoints( self, qtrue ) ) {//one of us didn't have a valid waypoint! if ( self->waypoint == NODE_NONE ) {//don't even try to find one again for a bit self->noWaypointTime = level.time + Q_irand( 500, 1500 ); } if ( self->NPC->goalEntity->waypoint == NODE_NONE ) {//don't even try to find one again for a bit self->NPC->goalEntity->noWaypointTime = level.time + Q_irand( 500, 1500 ); } return WAYPOINT_NONE; } else { if ( self->NPC->goalEntity->noWaypointTime < level.time ) { self->NPC->goalEntity->noWaypointTime = level.time + Q_irand( 500, 1500 ); } } while( !foundClearPath ) { /*inBestWP = */inGoalWP = qfalse; /* bestNode = trap->Nav_GetBestNodeAltRoute( self->waypoint, self->NPC->goalEntity->waypoint, bestNode ); */ if ( bestNode == WAYPOINT_NONE ) { goto failed; } //see if we can get directly to the next node off bestNode en route to goal's node... //NOTE: shouldn't be necc. now /* int oldBestNode = bestNode; bestNode = NAV_TestBestNode( self, self->waypoint, bestNode, qtrue );//, self->NPC->goalEntity->waypoint );// //NOTE: Guaranteed to return something if ( bestNode != oldBestNode ) {//we were blocked somehow if ( setBlockedInfo ) { self->NPC->aiFlags |= NPCAI_BLOCKED; trap->Nav_GetNodePosition( oldBestNode, NPCInfo->blockedDest ); } } */ trap->Nav_GetNodePosition( bestNode, origin ); /* if ( !goalWPFailed ) {//we haven't already tried to go straight to goal or goal's wp if ( bestNode == self->NPC->goalEntity->waypoint ) {//our bestNode is the goal's wp if ( NAV_HitNavGoal( self->r.currentOrigin, self->r.mins, self->r.maxs, origin, trap->Nav_GetNodeRadius( bestNode ), FlyingCreature( self ) ) ) {//we're in the goal's wp inGoalWP = qtrue; //we're in the goalEntity's waypoint already //so head for the goalEntity since we know it's clear of architecture //FIXME: this is pretty stupid because the NPCs try to go straight // towards their goal before then even try macro_nav... VectorCopy( self->NPC->goalEntity->r.currentOrigin, origin ); } } } */ if ( !inGoalWP ) {//not heading straight for goal if ( bestNode == self->waypoint ) {//we know it's clear or architecture //trap->Nav_GetNodePosition( self->waypoint, origin ); /* if ( NAV_HitNavGoal( self->r.currentOrigin, self->r.mins, self->r.maxs, origin, trap->Nav_GetNodeRadius( bestNode ), FlyingCreature( self ) ) ) {//we're in the wp we're heading for already inBestWP = qtrue; } */ } else {//heading to an edge off our confirmed clear waypoint... make sure it's clear //it it's not, bestNode will fall back to our waypoint int oldBestNode = bestNode; bestNode = NAV_TestBestNode( self, self->waypoint, bestNode, qtrue ); if ( bestNode == self->waypoint ) {//we fell back to our waypoint, reset the origin self->NPC->aiFlags |= NPCAI_BLOCKED; trap->Nav_GetNodePosition( oldBestNode, NPCS.NPCInfo->blockedDest ); trap->Nav_GetNodePosition( bestNode, origin ); } } } //Com_Printf( "goalwp = %d, mywp = %d, node = %d, origin = %s\n", self->NPC->goalEntity->waypoint, self->waypoint, bestNode, vtos(origin) ); memcpy( &tempInfo, info, sizeof( tempInfo ) ); VectorSubtract( origin, self->r.currentOrigin, tempInfo.direction ); VectorNormalize( tempInfo.direction ); //NOTE: One very important thing NAVNEW_AvoidCollision does is // it actually CHANGES the value of "direction" - it changes it to // whatever dir you need to go in to avoid the obstacle... foundClearPath = NAVNEW_AvoidCollision( self, self->NPC->goalEntity, &tempInfo, setBlockedInfo, 5 ); if ( !foundClearPath ) {//blocked by an ent if ( inGoalWP ) {//we were heading straight for the goal, head for the goal's wp instead trap->Nav_GetNodePosition( bestNode, origin ); foundClearPath = NAVNEW_AvoidCollision( self, self->NPC->goalEntity, &tempInfo, setBlockedInfo, 5 ); } } if ( foundClearPath ) {//clear! //If we got set to blocked, clear it NPC_ClearBlocked( self ); //Take the dir memcpy( info, &tempInfo, sizeof( *info ) ); if ( self->s.weapon == WP_SABER ) {//jedi if ( info->direction[2] * info->distance > 64 ) { self->NPC->aiFlags |= NPCAI_BLOCKED; VectorCopy( origin, NPCS.NPCInfo->blockedDest ); goto failed; } } } else {//blocked by ent! if ( setBlockedInfo ) { self->NPC->aiFlags |= NPCAI_BLOCKED; trap->Nav_GetNodePosition( bestNode, NPCS.NPCInfo->blockedDest ); } //Only set blocked info first time setBlockedInfo = qfalse; if ( inGoalWP ) {//we headed for our goal and failed and our goal's WP and failed if ( self->waypoint == self->NPC->goalEntity->waypoint ) {//our waypoint is our goal's waypoint, nothing we can do //remember that this node is blocked trap->Nav_AddFailedNode( (sharedEntity_t *)self, self->waypoint ); goto failed; } else {//try going for our waypoint this time inGoalWP = qfalse; } } else if ( bestNode != self->waypoint ) {//we headed toward our next waypoint (instead of our waypoint) and failed if ( d_altRoutes.integer ) {//mark this edge failed and try our waypoint //NOTE: don't assume there is something blocking the direct path // between my waypoint and the bestNode... I could be off // that path because of collision avoidance... if ( d_patched.integer &&//use patch-style navigation ( !trap->Nav_NodesAreNeighbors( self->waypoint, bestNode ) || NAVNEW_TestNodeConnectionBlocked( self->waypoint, bestNode, self, self->NPC->goalEntity->s.number, qfalse, qtrue ) ) ) {//the direct path between these 2 nodes is blocked by an ent trap->Nav_AddFailedEdge( self->s.number, self->waypoint, bestNode ); } bestNode = self->waypoint; } else { //we should stop goto failed; } } else {//we headed for *our* waypoint and couldn't get to it if ( d_altRoutes.integer ) { //remember that this node is blocked trap->Nav_AddFailedNode( (sharedEntity_t *)self, self->waypoint ); //Now we should get our waypoints again //FIXME: cache the trace-data for subsequent calls as only the route info would have changed //if ( (bestNode = trap->Nav_GetBestPathBetweenEnts( self, self->NPC->goalEntity, NF_CLEAR_PATH )) == NODE_NONE )//!NAVNEW_GetWaypoints( self, qfalse ) ) {//one of our waypoints is WAYPOINT_NONE now goto failed; } } else { //we should stop goto failed; } } if ( ++numTries >= 10 ) { goto failed; } } } //finish: //Draw any debug info, if requested if ( NAVDEBUG_showEnemyPath ) { vec3_t dest, start; //Get the positions trap->Nav_GetNodePosition( self->NPC->goalEntity->waypoint, dest ); trap->Nav_GetNodePosition( bestNode, start ); //Draw the route G_DrawNode( start, NODE_START ); if ( bestNode != self->waypoint ) { vec3_t wpPos; trap->Nav_GetNodePosition( self->waypoint, wpPos ); G_DrawNode( wpPos, NODE_NAVGOAL ); } G_DrawNode( dest, NODE_GOAL ); G_DrawEdge( dest, self->NPC->goalEntity->r.currentOrigin, EDGE_PATH ); G_DrawNode( self->NPC->goalEntity->r.currentOrigin, NODE_GOAL ); trap->Nav_ShowPath( bestNode, self->NPC->goalEntity->waypoint ); } self->NPC->shoveCount = 0; //let me keep this waypoint for a while if ( self->noWaypointTime < level.time ) { self->noWaypointTime = level.time + Q_irand( 500, 1500 ); } return bestNode; failed: //FIXME: What we should really do here is have a list of the goal's and our // closest clearpath waypoints, ranked. If the first set fails, try the rest // until there are no alternatives. trap->Nav_GetNodePosition( self->waypoint, origin ); //do this to avoid ping-ponging? return WAYPOINT_NONE; /* //this was causing ping-ponging if ( DistanceSquared( origin, self->r.currentOrigin ) < 16 )//woo, magic number {//We're right up on our waypoint, so that won't help, return none //Or maybe find the nextbest here? return WAYPOINT_NONE; } else {//Try going to our waypoint bestNode = self->waypoint; VectorSubtract( origin, self->r.currentOrigin, info.direction ); VectorNormalize( info.direction ); } goto finish; */ }