/* ------------------------- NAVNEW_Bypass ------------------------- */ qboolean NAVNEW_Bypass( gentity_t *self, gentity_t *blocker, vec3_t blocked_dir, float blocked_dist, vec3_t movedir, qboolean setBlockedInfo ) { vec3_t moveangles, right; //Draw debug info if requested if ( NAVDEBUG_showCollision ) { G_DrawEdge( self->r.currentOrigin, blocker->r.currentOrigin, EDGE_NORMAL ); } vectoangles( movedir, moveangles ); moveangles[2] = 0; AngleVectors( moveangles, NULL, right, NULL ); //Check to see what dir the other guy is moving in (if any) and pick the opposite dir if ( NAVNEW_DanceWithBlocker( self, blocker, movedir, right ) ) { return qtrue; } //Okay, so he's not moving to my side, see which side of him is most clear if ( NAVNEW_SidestepBlocker( self, blocker, blocked_dir, blocked_dist, movedir, right ) ) { return qtrue; } //Neither side is clear, tell him to step aside NAVNEW_PushBlocker( self, blocker, right, setBlockedInfo ); return qfalse; }
qboolean NPC_TryJump_Final() { vec3_t targetDirection; float targetDistanceXY; float targetDistanceZ; int sideTryCount; qboolean WithinForceJumpRange; // Get The Direction And Distances To The Target //----------------------------------------------- VectorSubtract(NPCInfo->jumpDest, NPC->r.currentOrigin, targetDirection); targetDirection[2] = 0.0f; targetDistanceXY = VectorNormalize(targetDirection); targetDistanceZ = NPCInfo->jumpDest[2] - NPC->r.currentOrigin[2]; if ((targetDistanceXY>NPCInfo->jumpMaxXYDist) || (targetDistanceZ<NPCInfo->jumpMazZDist)) { return qfalse; } // Test To See If There Is A Wall Directly In Front Of Actor, If So, Backup Some //------------------------------------------------------------------------------- if (TIMER_Done(NPC, "jumpBackupDebounce")) { vec3_t actorProjectedTowardTarget; VectorMA(NPC->r.currentOrigin, NPC_JUMP_PREP_BACKUP_DIST, targetDirection, actorProjectedTowardTarget); trap_Trace(&mJumpTrace, NPC->r.currentOrigin, vec3_origin, vec3_origin, actorProjectedTowardTarget, NPC->s.number, NPC->clipmask); if ((mJumpTrace.fraction < 1.0f) || (mJumpTrace.allsolid) || (mJumpTrace.startsolid)) { if (NAVDEBUG_showCollision) { G_DrawEdge(NPC->r.currentOrigin, actorProjectedTowardTarget, EDGE_RED_TWOSECOND); // TryJump } // TODO: We may want to test to see if it is safe to back up here? NPCInfo->jumpBackupTime = level.time + 1000; TIMER_Set(NPC, "jumpBackupDebounce", 5000); return qtrue; } } // bool Wounded = (NPC->health < 150); // bool OnLowerLedge = ((targetDistanceZ<-80.0f) && (targetDistanceZ>-200.0f)); // bool WithinNormalJumpRange = ((targetDistanceZ<32.0f) && (targetDistanceXY<200.0f)); WithinForceJumpRange = (qboolean)(((float)fabs(targetDistanceZ)>0) || (targetDistanceXY>128)); /* if (Wounded && OnLowerLedge) { ucmd.forwardmove = 127; VectorClear(NPC->client->ps.moveDir); TIMER_Set(NPC, "duck", -level.time); return qtrue; } if (WithinNormalJumpRange) { ucmd.upmove = 127; ucmd.forwardmove = 127; VectorClear(NPC->client->ps.moveDir); TIMER_Set(NPC, "duck", -level.time); return qtrue; } */ if (!WithinForceJumpRange) { return qfalse; } // If There Is Any Chance That This Jump Will Land On An Enemy, Try 8 Different Traces Around The Target //------------------------------------------------------------------------------------------------------- if (NPCInfo->jumpTarget) { float minSafeRadius = (NPC->r.maxs[0]*1.5f) + (NPCInfo->jumpTarget->r.maxs[0]*1.5f); float minSafeRadiusSq = (minSafeRadius * minSafeRadius); if (DistanceSquared(NPCInfo->jumpDest, NPCInfo->jumpTarget->r.currentOrigin)<minSafeRadiusSq) { vec3_t startPos; vec3_t floorPos; VectorCopy(NPCInfo->jumpDest, startPos); floorPos[2] = NPCInfo->jumpDest[2] + (NPC->r.mins[2]-32); for (sideTryCount=0; sideTryCount<8; sideTryCount++) { NPCInfo->jumpSide++; if ( NPCInfo->jumpSide > 7 ) { NPCInfo->jumpSide = 0; } switch ( NPCInfo->jumpSide ) { case 0: NPCInfo->jumpDest[0] = startPos[0] + minSafeRadius; NPCInfo->jumpDest[1] = startPos[1]; break; case 1: NPCInfo->jumpDest[0] = startPos[0] + minSafeRadius; NPCInfo->jumpDest[1] = startPos[1] + minSafeRadius; break; case 2: NPCInfo->jumpDest[0] = startPos[0]; NPCInfo->jumpDest[1] = startPos[1] + minSafeRadius; break; case 3: NPCInfo->jumpDest[0] = startPos[0] - minSafeRadius; NPCInfo->jumpDest[1] = startPos[1] + minSafeRadius; break; case 4: NPCInfo->jumpDest[0] = startPos[0] - minSafeRadius; NPCInfo->jumpDest[1] = startPos[1]; break; case 5: NPCInfo->jumpDest[0] = startPos[0] - minSafeRadius; NPCInfo->jumpDest[1] = startPos[1] - minSafeRadius; break; case 6: NPCInfo->jumpDest[0] = startPos[0]; NPCInfo->jumpDest[1] = startPos[1] - minSafeRadius; break; case 7: NPCInfo->jumpDest[0] = startPos[0] + minSafeRadius; NPCInfo->jumpDest[1] = startPos[1] -=minSafeRadius; break; } floorPos[0] = NPCInfo->jumpDest[0]; floorPos[1] = NPCInfo->jumpDest[1]; trap_Trace(&mJumpTrace, NPCInfo->jumpDest, NPC->r.mins, NPC->r.maxs, floorPos, (NPCInfo->jumpTarget)?(NPCInfo->jumpTarget->s.number):(NPC->s.number), (NPC->clipmask|CONTENTS_BOTCLIP)); if ((mJumpTrace.fraction<1.0f) && (!mJumpTrace.allsolid) && (!mJumpTrace.startsolid)) { break; } if ( NAVDEBUG_showCollision ) { G_DrawEdge( NPCInfo->jumpDest, floorPos, EDGE_RED_TWOSECOND ); } } // If All Traces Failed, Just Try Going Right Back At The Target Location //------------------------------------------------------------------------ if ((mJumpTrace.fraction>=1.0f) || (mJumpTrace.allsolid) || (mJumpTrace.startsolid)) { VectorCopy(startPos, NPCInfo->jumpDest); } } } // Now, Actually Try The Jump To The Dest Target //----------------------------------------------- if (NPC_Jump(NPCInfo->jumpDest, (NPCInfo->jumpTarget)?(NPCInfo->jumpTarget->s.number):(NPC->s.number))) { // We Made IT! //------------- NPC_JumpAnimation(); NPC_JumpSound(); NPC->client->ps.fd.forceJumpZStart = NPC->r.currentOrigin[2]; //NPC->client->ps.pm_flags |= PMF_JUMPING; NPC->client->ps.weaponTime = NPC->client->ps.torsoTimer; NPC->client->ps.fd.forcePowersActive |= ( 1 << FP_LEVITATION ); ucmd.forwardmove = 0; NPCInfo->jumpTime = 1; VectorClear(NPC->client->ps.moveDir); TIMER_Set(NPC, "duck", -level.time); return qtrue; } return qfalse; }
static qboolean NPC_Jump( vec3_t dest, int goalEntNum ) {//FIXME: if land on enemy, knock him down & jump off again float targetDist, travelTime, impactDist, bestImpactDist = Q3_INFINITE;//fireSpeed, float originalShotSpeed, shotSpeed, speedStep = 50.0f, minShotSpeed = 30.0f, maxShotSpeed = 500.0f; qboolean belowBlocked = qfalse, aboveBlocked = qfalse; vec3_t targetDir, shotVel, failCase; trace_t trace; trajectory_t tr; qboolean blocked; int elapsedTime, timeStep = 250, hitCount = 0, aboveTries = 0, belowTries = 0, maxHits = 10; vec3_t lastPos, testPos, bottom; VectorSubtract( dest, NPC->r.currentOrigin, targetDir ); targetDist = VectorNormalize( targetDir ); //make our shotSpeed reliant on the distance originalShotSpeed = targetDist;//DistanceHorizontal( dest, NPC->currentOrigin )/2.0f; if ( originalShotSpeed > maxShotSpeed ) { originalShotSpeed = maxShotSpeed; } else if ( originalShotSpeed < minShotSpeed ) { originalShotSpeed = minShotSpeed; } shotSpeed = originalShotSpeed; while ( hitCount < maxHits ) { VectorScale( targetDir, shotSpeed, shotVel ); travelTime = targetDist/shotSpeed; shotVel[2] += travelTime * 0.5 * NPC->client->ps.gravity; if ( !hitCount ) {//save the first one as the worst case scenario VectorCopy( shotVel, failCase ); } if ( 1 )//tracePath ) {//do a rough trace of the path blocked = qfalse; VectorCopy( NPC->r.currentOrigin, tr.trBase ); VectorCopy( shotVel, tr.trDelta ); tr.trType = TR_GRAVITY; tr.trTime = level.time; travelTime *= 1000.0f; VectorCopy( NPC->r.currentOrigin, lastPos ); //This may be kind of wasteful, especially on long throws... use larger steps? Divide the travelTime into a certain hard number of slices? Trace just to apex and down? for ( elapsedTime = timeStep; elapsedTime < floor(travelTime)+timeStep; elapsedTime += timeStep ) { if ( (float)elapsedTime > travelTime ) {//cap it elapsedTime = floor( travelTime ); } BG_EvaluateTrajectory( &tr, level.time + elapsedTime, testPos ); //F**K IT, always check for do not enter... trap_Trace( &trace, lastPos, NPC->r.mins, NPC->r.maxs, testPos, NPC->s.number, NPC->clipmask|CONTENTS_BOTCLIP ); /* if ( testPos[2] < lastPos[2] && elapsedTime < floor( travelTime ) ) {//going down, haven't reached end, ignore botclip gi.trace( &trace, lastPos, NPC->mins, NPC->maxs, testPos, NPC->s.number, NPC->clipmask ); } else {//going up, check for botclip gi.trace( &trace, lastPos, NPC->mins, NPC->maxs, testPos, NPC->s.number, NPC->clipmask|CONTENTS_BOTCLIP ); } */ if ( trace.allsolid || trace.startsolid ) {//started in solid if ( NAVDEBUG_showCollision ) { G_DrawEdge( lastPos, trace.endpos, EDGE_RED_TWOSECOND ); } return qfalse;//you're hosed, dude } if ( trace.fraction < 1.0f ) {//hit something if ( NAVDEBUG_showCollision ) { G_DrawEdge( lastPos, trace.endpos, EDGE_RED_TWOSECOND ); // TryJump } if ( trace.entityNum == goalEntNum ) {//hit the enemy, that's bad! blocked = qtrue; /* if ( g_entities[goalEntNum].client && g_entities[goalEntNum].client->ps.groundEntityNum == ENTITYNUM_NONE ) {//bah, would collide in mid-air, no good blocked = qtrue; } else {//he's on the ground, good enough, I guess //Hmm, don't want to land on him, though...? } */ break; } else { if ( trace.contents & CONTENTS_BOTCLIP ) {//hit a do-not-enter brush blocked = qtrue; break; } if ( trace.plane.normal[2] > 0.7 && DistanceSquared( trace.endpos, dest ) < 4096 )//hit within 64 of desired location, should be okay {//close enough! break; } else {//FIXME: maybe find the extents of this brush and go above or below it on next try somehow? impactDist = DistanceSquared( trace.endpos, dest ); if ( impactDist < bestImpactDist ) { bestImpactDist = impactDist; VectorCopy( shotVel, failCase ); } blocked = qtrue; break; } } } else { if ( NAVDEBUG_showCollision ) { G_DrawEdge( lastPos, testPos, EDGE_WHITE_TWOSECOND ); // TryJump } } if ( elapsedTime == floor( travelTime ) ) {//reached end, all clear if ( trace.fraction >= 1.0f ) {//hmm, make sure we'll land on the ground... //FIXME: do we care how far below ourselves or our dest we'll land? VectorCopy( trace.endpos, bottom ); bottom[2] -= 128; trap_Trace( &trace, trace.endpos, NPC->r.mins, NPC->r.maxs, bottom, NPC->s.number, NPC->clipmask ); if ( trace.fraction >= 1.0f ) {//would fall too far blocked = qtrue; } } break; } else { //all clear, try next slice VectorCopy( testPos, lastPos ); } } if ( blocked ) {//hit something, adjust speed (which will change arc) hitCount++; //alternate back and forth between trying an arc slightly above or below the ideal if ( (hitCount%2) && !belowBlocked ) {//odd belowTries++; shotSpeed = originalShotSpeed - (belowTries*speedStep); } else if ( !aboveBlocked ) {//even aboveTries++; shotSpeed = originalShotSpeed + (aboveTries*speedStep); } else {//can't go any higher or lower hitCount = maxHits; break; } if ( shotSpeed > maxShotSpeed ) { shotSpeed = maxShotSpeed; aboveBlocked = qtrue; } else if ( shotSpeed < minShotSpeed ) { shotSpeed = minShotSpeed; belowBlocked = qtrue; } } else {//made it! break; } } else {//no need to check the path, go with first calc break; } } if ( hitCount >= maxHits ) {//NOTE: worst case scenario, use the one that impacted closest to the target (or just use the first try...?) return qfalse; //NOTE: or try failcase? //VectorCopy( failCase, NPC->client->ps.velocity ); //return qtrue; } VectorCopy( shotVel, NPC->client->ps.velocity ); 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; */ }
/* ------------------------- NAVNEW_AvoidCollision ------------------------- */ qboolean NAVNEW_AvoidCollision( gentity_t *self, gentity_t *goal, navInfo_t *info, qboolean setBlockedInfo, int blockedMovesLimit ) { vec3_t movedir; vec3_t movepos; //Cap our distance if ( info->distance > MAX_COLL_AVOID_DIST ) { info->distance = MAX_COLL_AVOID_DIST; } //Get an end position VectorMA( self->r.currentOrigin, info->distance, info->direction, movepos ); VectorCopy( info->direction, movedir ); //Now test against entities if ( NAV_CheckAhead( self, movepos, &info->trace, CONTENTS_BODY ) == qfalse ) { //Get the blocker info->blocker = &g_entities[ info->trace.entityNum ]; info->flags |= NIF_COLLISION; //Ok to hit our goal entity if ( goal == info->blocker ) return qtrue; if ( setBlockedInfo ) { if ( self->NPC->consecutiveBlockedMoves > blockedMovesLimit ) { if ( d_patched.integer ) {//use patch-style navigation self->NPC->consecutiveBlockedMoves++; } NPC_SetBlocked( self, info->blocker ); return qfalse; } self->NPC->consecutiveBlockedMoves++; } //See if we're moving along with them //if ( NAVNEW_TrueCollision( self, info->blocker, movedir, info->direction ) == qfalse ) // return qtrue; //Test for blocking by standing on goal if ( NAV_TestForBlocked( self, goal, info->blocker, info->distance, &info->flags ) == qtrue ) return qfalse; //If the above function said we're blocked, don't do the extra checks /* if ( info->flags & NIF_BLOCKED ) return qtrue; */ //See if we can get that entity to move out of our way if ( NAVNEW_ResolveEntityCollision( self, info->blocker, movedir, info->pathDirection, setBlockedInfo ) == qfalse ) return qfalse; VectorCopy( movedir, info->direction ); return qtrue; } else { if ( setBlockedInfo ) { self->NPC->consecutiveBlockedMoves = 0; } } //Our path is clear, just move there if ( NAVDEBUG_showCollision ) { G_DrawEdge( self->r.currentOrigin, movepos, EDGE_MOVEDIR ); } return qtrue; }