bool AI_NodeHasTimedOut( edict_t *self ) { if( self->ai->goal_node == NODE_INVALID ) return true; if( !GS_MatchPaused() ) self->ai->node_timeout += game.frametime; // Try again? if( self->ai->node_timeout > NODE_TIMEOUT || self->ai->next_node == NODE_INVALID ) { if( self->ai->tries++ > 3 ) return true; else AI_SetGoal( self, self->ai->goal_node ); } if( self->ai->current_node == NODE_INVALID || self->ai->next_node == NODE_INVALID ) return true; return false; }
//========================================== // AI_PickLongRangeGoal // // Evaluate the best long range goal and send the bot on // its way. This is a good time waster, so use it sparingly. // Do not call it for every think cycle. // // jal: I don't think there is any problem by calling it, // now that we have stored the costs at the nav.costs table (I don't do it anyway) //========================================== void AI_PickLongRangeGoal( edict_t *self ) { #define WEIGHT_MAXDISTANCE_FACTOR 20000.0f #define COST_INFLUENCE 0.5f int i; float weight, bestWeight = 0.0; int current_node; float cost; float dist; nav_ents_t *goalEnt, *bestGoalEnt = NULL; AI_ClearGoal( self ); if( G_ISGHOSTING( self ) ) return; if( self->ai->longRangeGoalTimeout > level.time ) return; if( !self->r.client->ps.pmove.stats[PM_STAT_MAXSPEED] ) { return; } self->ai->longRangeGoalTimeout = level.time + AI_LONG_RANGE_GOAL_DELAY + brandom( 0, 1000 ); // look for a target current_node = AI_FindClosestReachableNode( self->s.origin, self, ( ( 1 + self->ai->nearest_node_tries ) * NODE_DENSITY ), NODE_ALL ); self->ai->current_node = current_node; if( current_node == NODE_INVALID ) { if( nav.debugMode && bot_showlrgoal->integer ) G_PrintChasersf( self, "%s: LRGOAL: Closest node not found. Tries:%i\n", self->ai->pers.netname, self->ai->nearest_node_tries ); self->ai->nearest_node_tries++; // extend search radius with each try return; } self->ai->nearest_node_tries = 0; // Run the list of potential goal entities FOREACH_GOALENT( goalEnt ) { i = goalEnt->id; if( !goalEnt->ent ) continue; if( !goalEnt->ent->r.inuse ) { goalEnt->node = NODE_INVALID; continue; } if( goalEnt->ent->r.client ) { if( G_ISGHOSTING( goalEnt->ent ) || goalEnt->ent->flags & (FL_NOTARGET|FL_BUSY) ) goalEnt->node = NODE_INVALID; else goalEnt->node = AI_FindClosestReachableNode( goalEnt->ent->s.origin, goalEnt->ent, NODE_DENSITY, NODE_ALL ); } if( goalEnt->ent->item ) { if( !G_Gametype_CanPickUpItem( goalEnt->ent->item ) ) continue; } if( goalEnt->node == NODE_INVALID ) continue; weight = self->ai->status.entityWeights[i]; if( weight <= 0.0f ) continue; // don't try to find cost for too far away objects dist = DistanceFast( self->s.origin, goalEnt->ent->s.origin ); if( dist > WEIGHT_MAXDISTANCE_FACTOR * weight/* || dist < AI_GOAL_SR_RADIUS*/ ) continue; cost = AI_FindCost( current_node, goalEnt->node, self->ai->status.moveTypesMask ); if( cost == NODE_INVALID ) continue; cost -= brandom( 0, 2000 ); // allow random variations clamp_low( cost, 1 ); weight = ( 1000 * weight ) / ( cost * COST_INFLUENCE ); // Check against cost of getting there if( weight > bestWeight ) { bestWeight = weight; bestGoalEnt = goalEnt; } } if( bestGoalEnt ) { self->ai->goalEnt = bestGoalEnt; AI_SetGoal( self, bestGoalEnt->node ); if( self->ai->goalEnt != NULL && nav.debugMode && bot_showlrgoal->integer ) G_PrintChasersf( self, "%s: selected a %s at node %d for LR goal. (weight %f)\n", self->ai->pers.netname, self->ai->goalEnt->ent->classname, self->ai->goalEnt->node, bestWeight ); return; } if( nav.debugMode && bot_showlrgoal->integer ) G_PrintChasersf( self, "%s: did not find a LR goal.\n", self->ai->pers.netname ); #undef WEIGHT_MAXDISTANCE_FACTOR #undef COST_INFLUENCE }
//========================================== // AI_FollowPath // Move closer to goal by pointing the bot to the next node // that is closer to the goal //jabot092 (path-> to path.) //========================================== qboolean AI_FollowPath( edict_t *self ) { vec3_t v; float dist; if( self->ai->goal_node == INVALID ) return false; // Try again? if(self->ai->node_timeout++ > 30) { if(self->ai->tries++ > 3) return false; else AI_SetGoal( self, self->ai->goal_node ); } // Are we there yet? VectorSubtract( self->s.origin, nodes[self->ai->next_node].origin, v ); dist = VectorLength(v); //special lower plat reached check if( dist < 64 && nodes[self->ai->current_node].flags & NODEFLAGS_PLATFORM && nodes[self->ai->next_node].flags & NODEFLAGS_PLATFORM && self->groundentity && self->groundentity->use == Use_Plat) dist = 16; if( (dist < 32 && nodes[self->ai->next_node].flags != NODEFLAGS_JUMPPAD && nodes[self->ai->next_node].flags != NODEFLAGS_TELEPORTER_IN) || (self->ai->status.jumpadReached && nodes[self->ai->next_node].flags & NODEFLAGS_JUMPPAD) || (self->ai->status.TeleportReached && nodes[self->ai->next_node].flags & NODEFLAGS_TELEPORTER_IN) ) { // reset timeout self->ai->node_timeout = 0; if( self->ai->next_node == self->ai->goal_node ) { //if(AIDevel.debugChased && bot_showlrgoal->value) // G_PrintMsg (AIDevel.chaseguy, PRINT_HIGH, "%s: GOAL REACHED!\n", self->ai->pers.netname); //if botroam, setup a timeout for it if( nodes[self->ai->goal_node].flags & NODEFLAGS_BOTROAM ) { int i; for( i=0; i<nav.num_broams; i++) { //find the broam if( nav.broams[i].node != self->ai->goal_node ) continue; //if(AIDevel.debugChased && bot_showlrgoal->integer) // G_PrintMsg (AIDevel.chaseguy, PRINT_HIGH, "%s: BotRoam Time Out set up for node %i\n", self->ai->pers.netname, nav.broams[i].node); //Com_Printf( "%s: BotRoam Time Out set up for node %i\n", self->ai->pers.netname, nav.broams[i].node); self->ai->status.broam_timeouts[i] = level.time + 15.0; break; } } // Pick a new goal if (self->ai->state != BOT_STATE_CAMP) AI_PickLongRangeGoal(self); // if (self->ai->next_node == self->ai->current_node) // { //gi.dprintf(DEVELOPER_MSG_GAME, "next_node\n"); //AI_PickLongRangeGoal(self); // } } else { self->ai->current_node = self->ai->next_node; if( self->ai->path.numNodes ) self->ai->path.numNodes--; self->ai->next_node = self->ai->path.nodes[self->ai->path.numNodes]; } } if(self->ai->current_node == -1 || self->ai->next_node == -1) return false; // Set bot's movement vector VectorSubtract( nodes[self->ai->next_node].origin, self->s.origin , self->ai->move_vector ); return true; }