//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_PointReachabilityAreaIndex( vec3_t origin ) { int areanum, cluster, i, index; if (!aasworld.initialized) return 0; if ( !origin ) { index = 0; for (i = 0; i < aasworld.numclusters; i++) { index += aasworld.clusters[i].numreachabilityareas; } //end for return index; } //end if areanum = AAS_PointAreaNum( origin ); if ( !areanum || !AAS_AreaReachability(areanum) ) return 0; cluster = aasworld.areasettings[areanum].cluster; areanum = aasworld.areasettings[areanum].clusterareanum; if (cluster < 0) { cluster = aasworld.portals[-cluster].frontcluster; areanum = aasworld.portals[-cluster].clusterareanum[0]; } //end if index = 0; for (i = 0; i < cluster; i++) { index += aasworld.clusters[i].numreachabilityareas; } //end for index += areanum; return index; } //end of the function AAS_PointReachabilityAreaIndex
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags, aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, int type) { #ifndef ENABLE_ALTROUTING return 0; #else int i, j, bestareanum; int numaltroutegoals, nummidrangeareas; int starttime, goaltime, goaltraveltime; float dist, bestdist; vec3_t mid, dir; #ifdef ALTROUTE_DEBUG int startmillisecs; startmillisecs = Sys_MilliSeconds(); #endif if (!startareanum || !goalareanum) return 0; //travel time towards the goal area goaltraveltime = AAS_AreaTravelTimeToGoalArea(startareanum, start, goalareanum, travelflags); //clear the midrange areas Com_Memset(midrangeareas, 0, aasworld.numareas * sizeof(midrangearea_t)); numaltroutegoals = 0; // nummidrangeareas = 0; // for (i = 1; i < aasworld.numareas; i++) { // if (!(type & ALTROUTEGOAL_ALL)) { if (!(type & ALTROUTEGOAL_CLUSTERPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL))) { if (!(type & ALTROUTEGOAL_VIEWPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL))) { continue; } //end if } //end if } //end if //if the area has no reachabilities if (!AAS_AreaReachability(i)) continue; //tavel time from the area to the start area starttime = AAS_AreaTravelTimeToGoalArea(startareanum, start, i, travelflags); if (!starttime) continue; //if the travel time from the start to the area is greater than the shortest goal travel time if (starttime > (float) 1.1 * goaltraveltime) continue; //travel time from the area to the goal area goaltime = AAS_AreaTravelTimeToGoalArea(i, NULL, goalareanum, travelflags); if (!goaltime) continue; //if the travel time from the area to the goal is greater than the shortest goal travel time if (goaltime > (float) 0.8 * goaltraveltime) continue; //this is a mid range area midrangeareas[i].valid = qtrue; midrangeareas[i].starttime = starttime; midrangeareas[i].goaltime = goaltime; Log_Write("%d midrange area %d", nummidrangeareas, i); nummidrangeareas++; } //end for // for (i = 1; i < aasworld.numareas; i++) { if (!midrangeareas[i].valid) continue; //get the areas in one cluster numclusterareas = 0; AAS_AltRoutingFloodCluster_r(i); //now we've got a cluster with areas through which an alternative route could go //get the 'center' of the cluster VectorClear(mid); for (j = 0; j < numclusterareas; j++) { VectorAdd(mid, aasworld.areas[clusterareas[j]].center, mid); } //end for VectorScale(mid, 1.0 / numclusterareas, mid); //get the area closest to the center of the cluster bestdist = 999999; bestareanum = 0; for (j = 0; j < numclusterareas; j++) { VectorSubtract(mid, aasworld.areas[clusterareas[j]].center, dir); dist = VectorLength(dir); if (dist < bestdist) { bestdist = dist; bestareanum = clusterareas[j]; } //end if } //end for //now we've got an area for an alternative route //FIXME: add alternative goal origin VectorCopy(aasworld.areas[bestareanum].center, altroutegoals[numaltroutegoals].origin); altroutegoals[numaltroutegoals].areanum = bestareanum; altroutegoals[numaltroutegoals].starttraveltime = midrangeareas[bestareanum].starttime; altroutegoals[numaltroutegoals].goaltraveltime = midrangeareas[bestareanum].goaltime; altroutegoals[numaltroutegoals].extratraveltime = (midrangeareas[bestareanum].starttime + midrangeareas[bestareanum].goaltime) - goaltraveltime; numaltroutegoals++; // #ifdef ALTROUTE_DEBUG AAS_ShowAreaPolygons(bestareanum, 1, qtrue); #endif //don't return more than the maximum alternative route goals if (numaltroutegoals >= maxaltroutegoals) break; } //end for #ifdef ALTROUTE_DEBUG botimport.Print(PRT_MESSAGE, "alternative route goals in %d msec\n", Sys_MilliSeconds() - startmillisecs); #endif return numaltroutegoals; #endif } //end of the function AAS_AlternativeRouteGoals
int AAS_AlternativeRouteGoals(vec3_t start, vec3_t goal, int travelflags, aas_altroutegoal_t *altroutegoals, int maxaltroutegoals, int color) { #ifndef ENABLE_ALTROUTING return 0; #else int i, j, startareanum, goalareanum, bestareanum; int numaltroutegoals, nummidrangeareas; int starttime, goaltime, goaltraveltime; float dist, bestdist; vec3_t mid, dir; int reachnum, time; int a1, a2; /*#ifdef DEBUG int startmillisecs; startmillisecs = Sys_MilliSeconds(); #endif*/ startareanum = AAS_PointAreaNum(start); if (!startareanum) { return 0; } goalareanum = AAS_PointAreaNum(goal); if (!goalareanum) { VectorCopy(goal, dir); dir[2] += 30; goalareanum = AAS_PointAreaNum(dir); if (!goalareanum) { return 0; } } //travel time towards the goal area goaltraveltime = AAS_AreaTravelTimeToGoalArea(startareanum, start, goalareanum, travelflags); //clear the midrange areas memset(midrangeareas, 0, (*aasworld).numareas * sizeof(midrangearea_t)); numaltroutegoals = 0; // nummidrangeareas = 0; // for (i = 1; i < (*aasworld).numareas; i++) { // if (!((*aasworld).areasettings[i].contents & AREACONTENTS_ROUTEPORTAL) && !((*aasworld).areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) { continue; } //if the area has no reachabilities if (!AAS_AreaReachability(i)) { continue; } //tavel time from the area to the start area starttime = AAS_AreaTravelTimeToGoalArea(startareanum, start, i, travelflags); if (!starttime) { continue; } //if the travel time from the start to the area is greater than the shortest goal travel time if (starttime > 500 + 3.0 * goaltraveltime) { continue; } //travel time from the area to the goal area goaltime = AAS_AreaTravelTimeToGoalArea(i, NULL, goalareanum, travelflags); if (!goaltime) { continue; } //if the travel time from the area to the goal is greater than the shortest goal travel time if (goaltime > 500 + 3.0 * goaltraveltime) { continue; } //this is a mid range area midrangeareas[i].valid = qtrue; midrangeareas[i].starttime = starttime; midrangeareas[i].goaltime = goaltime; Log_Write("%d midrange area %d", nummidrangeareas, i); nummidrangeareas++; } //end for // for (i = 1; i < (*aasworld).numareas; i++) { if (!midrangeareas[i].valid) { continue; } //get the areas in one cluster numclusterareas = 0; AAS_AltRoutingFloodCluster_r(i); //now we've got a cluster with areas through which an alternative route could go //get the 'center' of the cluster VectorClear(mid); for (j = 0; j < numclusterareas; j++) { VectorAdd(mid, (*aasworld).areas[clusterareas[j]].center, mid); } //end for VectorScale(mid, 1.0 / numclusterareas, mid); //get the area closest to the center of the cluster bestdist = 999999; bestareanum = 0; for (j = 0; j < numclusterareas; j++) { VectorSubtract(mid, (*aasworld).areas[clusterareas[j]].center, dir); dist = VectorLength(dir); if (dist < bestdist) { bestdist = dist; bestareanum = clusterareas[j]; } //end if } //end for // make sure the route to the destination isn't in the same direction as the route to the source if (!AAS_AreaRouteToGoalArea(bestareanum, (*aasworld).areawaypoints[bestareanum], goalareanum, travelflags, &time, &reachnum)) { continue; } a1 = (*aasworld).reachability[reachnum].areanum; if (!AAS_AreaRouteToGoalArea(bestareanum, (*aasworld).areawaypoints[bestareanum], startareanum, travelflags, &time, &reachnum)) { continue; } a2 = (*aasworld).reachability[reachnum].areanum; if (a1 == a2) { continue; } //now we've got an area for an alternative route //FIXME: add alternative goal origin VectorCopy((*aasworld).areawaypoints[bestareanum], altroutegoals[numaltroutegoals].origin); altroutegoals[numaltroutegoals].areanum = bestareanum; altroutegoals[numaltroutegoals].starttraveltime = midrangeareas[bestareanum].starttime; altroutegoals[numaltroutegoals].goaltraveltime = midrangeareas[bestareanum].goaltime; altroutegoals[numaltroutegoals].extratraveltime = (midrangeareas[bestareanum].starttime + midrangeareas[bestareanum].goaltime) - goaltraveltime; numaltroutegoals++; // /*#ifdef DEBUG botimport.Print(PRT_MESSAGE, "alternative route goal area %d, numclusterareas = %d\n", bestareanum, numclusterareas); if (color) { AAS_DrawPermanentCross((*aasworld).areas[bestareanum].center, 10, color); } //end if //AAS_ShowArea(bestarea, qtrue); #endif*/ //don't return more than the maximum alternative route goals if (numaltroutegoals >= maxaltroutegoals) { break; } } //end for //botimport.Print(PRT_MESSAGE, "%d alternative route goals\n", numaltroutegoals); #ifdef DEBUG // botimport.Print(PRT_MESSAGE, "alternative route goals in %d msec\n", Sys_MilliSeconds() - startmillisecs); #endif return numaltroutegoals; #endif } //end of the function AAS_AlternativeRouteGoals
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_NumberClusterAreas(int clusternum) { int i, portalnum; aas_cluster_t *cluster; aas_portal_t *portal; aasworld.clusters[clusternum].numareas = 0; aasworld.clusters[clusternum].numreachabilityareas = 0; //number all areas in this cluster WITH reachabilities for (i = 1; i < aasworld.numareas; i++) { // if (aasworld.areasettings[i].cluster != clusternum) continue; // if (!AAS_AreaReachability(i)) continue; // aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas; //the cluster has an extra area aasworld.clusters[clusternum].numareas++; aasworld.clusters[clusternum].numreachabilityareas++; } //end for //number all portals in this cluster WITH reachabilities cluster = &aasworld.clusters[clusternum]; for (i = 0; i < cluster->numportals; i++) { portalnum = aasworld.portalindex[cluster->firstportal + i]; portal = &aasworld.portals[portalnum]; if (!AAS_AreaReachability(portal->areanum)) continue; if (portal->frontcluster == clusternum) { portal->clusterareanum[0] = cluster->numareas++; aasworld.clusters[clusternum].numreachabilityareas++; } //end if else { portal->clusterareanum[1] = cluster->numareas++; aasworld.clusters[clusternum].numreachabilityareas++; } //end else } //end for //number all areas in this cluster WITHOUT reachabilities for (i = 1; i < aasworld.numareas; i++) { // if (aasworld.areasettings[i].cluster != clusternum) continue; // if (AAS_AreaReachability(i)) continue; // aasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas; //the cluster has an extra area aasworld.clusters[clusternum].numareas++; } //end for //number all portals in this cluster WITHOUT reachabilities cluster = &aasworld.clusters[clusternum]; for (i = 0; i < cluster->numportals; i++) { portalnum = aasworld.portalindex[cluster->firstportal + i]; portal = &aasworld.portals[portalnum]; if (AAS_AreaReachability(portal->areanum)) continue; if (portal->frontcluster == clusternum) { portal->clusterareanum[0] = cluster->numareas++; } //end if else { portal->clusterareanum[1] = cluster->numareas++; } //end else } //end for } //end of the function AAS_NumberClusterAreas
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotChooseNBGItem( int goalstate, vec3_t origin, int *inventory, int travelflags, bot_goal_t *ltg, float maxtime ) { int areanum, t, weightnum, ltg_time; float weight, bestweight, avoidtime; iteminfo_t *iteminfo; itemconfig_t *ic; levelitem_t *li, *bestitem; bot_goal_t goal; bot_goalstate_t *gs; gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return qfalse; } if ( !gs->itemweightconfig ) { return qfalse; } //get the area the bot is in areanum = BotReachabilityArea( origin, gs->client ); //if the bot is in solid or if the area the bot is in has no reachability links if ( !areanum || !AAS_AreaReachability( areanum ) ) { //use the last valid area the bot was in areanum = gs->lastreachabilityarea; } //end if //remember the last area with reachabilities the bot was in gs->lastreachabilityarea = areanum; //if still in solid if ( !areanum ) { return qfalse; } // if ( ltg ) { ltg_time = AAS_AreaTravelTimeToGoalArea( areanum, origin, ltg->areanum, travelflags ); } else { ltg_time = 99999;} //the item configuration ic = itemconfig; if ( !itemconfig ) { return qfalse; } //best weight and item so far bestweight = 0; bestitem = NULL; memset( &goal, 0, sizeof( bot_goal_t ) ); //go through the items in the level for ( li = levelitems; li; li = li->next ) { if ( g_gametype == GT_SINGLE_PLAYER ) { if ( li->notsingle ) { continue; } } else if ( g_gametype >= GT_TEAM ) { if ( li->notteam ) { continue; } } else { if ( li->notfree ) { continue; } } //if the item is in a possible goal area if ( !li->goalareanum ) { continue; } //get the fuzzy weight function for this item iteminfo = &ic->iteminfo[li->iteminfo]; weightnum = gs->itemweightindex[iteminfo->number]; if ( weightnum < 0 ) { continue; } //if this goal is in the avoid goals if ( BotAvoidGoalTime( goalstate, li->number ) > 0 ) { continue; } // #ifdef UNDECIDEDFUZZY weight = FuzzyWeightUndecided( inventory, gs->itemweightconfig, weightnum ); #else weight = FuzzyWeight( inventory, gs->itemweightconfig, weightnum ); #endif //UNDECIDEDFUZZY #ifdef DROPPEDWEIGHT //HACK: to make dropped items more attractive if ( li->timeout ) { weight += 1000; } #endif //DROPPEDWEIGHT if ( weight > 0 ) { //get the travel time towards the goal area t = AAS_AreaTravelTimeToGoalArea( areanum, origin, li->goalareanum, travelflags ); //if the goal is reachable if ( t > 0 && t < maxtime ) { weight /= (float) t * TRAVELTIME_SCALE; // if ( weight > bestweight ) { t = 0; if ( ltg && !li->timeout ) { //get the travel time from the goal to the long term goal t = AAS_AreaTravelTimeToGoalArea( li->goalareanum, li->goalorigin, ltg->areanum, travelflags ); } //end if //if the travel back is possible and doesn't take too long if ( t <= ltg_time ) { bestweight = weight; bestitem = li; } //end if } //end if } //end if } //end if } //end for //if no goal item found if ( !bestitem ) { return qfalse; } //create a bot goal for this item iteminfo = &ic->iteminfo[bestitem->iteminfo]; VectorCopy( bestitem->goalorigin, goal.origin ); VectorCopy( iteminfo->mins, goal.mins ); VectorCopy( iteminfo->maxs, goal.maxs ); goal.areanum = bestitem->goalareanum; goal.entitynum = bestitem->entitynum; goal.number = bestitem->number; goal.flags = GFL_ITEM; goal.iteminfo = bestitem->iteminfo; //add the chosen goal to the goals to avoid for a while avoidtime = iteminfo->respawntime * 0.5; if ( avoidtime < 10 ) { avoidtime = AVOID_TIME; } //if it's a dropped item if ( bestitem->timeout ) { avoidtime = AVOIDDROPPED_TIME; } BotAddToAvoidGoals( gs, bestitem->number, avoidtime ); //push the goal on the stack BotPushGoal( goalstate, &goal ); // #ifdef DEBUG_AI_GOAL if ( bestitem->timeout ) { botimport.Print( PRT_MESSAGE, "new nbg dropped item %s\n", ic->iteminfo[bestitem->iteminfo].classname ); } //end if iteminfo = &ic->iteminfo[bestitem->iteminfo]; botimport.Print( PRT_MESSAGE, "new nbg \"%s\"\n", iteminfo->classname ); #endif //DEBUG_AI_GOAL return qtrue; } //end of the function BotChooseNBGItem
static bool BotChooseNBGItem( int goalstate, const vec3_t origin, const int* inventory, int travelflags, const bot_goal_t* ltg, float maxtime ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return false; } if ( !gs->itemweightconfig ) { return false; } //get the area the bot is in int areanum = BotReachabilityArea( origin, gs->client ); //if the bot is in solid or if the area the bot is in has no reachability links if ( !areanum || !AAS_AreaReachability( areanum ) ) { //use the last valid area the bot was in areanum = gs->lastreachabilityarea; } //remember the last area with reachabilities the bot was in gs->lastreachabilityarea = areanum; //if still in solid if ( !areanum ) { return false; } int ltg_time; if ( ltg ) { ltg_time = AAS_AreaTravelTimeToGoalArea( areanum, origin, ltg->areanum, travelflags ); } else { ltg_time = 99999; } //the item configuration itemconfig_t* ic = itemconfig; if ( !itemconfig ) { return false; } //best weight and item so far float bestweight = 0; levelitem_t* bestitem = NULL; //go through the items in the level for ( levelitem_t* li = levelitems; li; li = li->next ) { if ( GGameType & GAME_ET ) { if ( g_singleplayer ) { if ( li->flags & IFL_NOTSINGLE ) { continue; } } } else { if ( g_gametype == Q3GT_SINGLE_PLAYER ) { if ( li->flags & IFL_NOTSINGLE ) { continue; } } else if ( g_gametype >= Q3GT_TEAM ) { if ( li->flags & IFL_NOTTEAM ) { continue; } } else { if ( li->flags & IFL_NOTFREE ) { continue; } } } if ( GGameType & GAME_Quake3 && li->flags & IFL_NOTBOT ) { continue; } //if the item is in a possible goal area if ( !li->goalareanum ) { continue; } //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk) if ( GGameType & GAME_Quake3 && !li->entitynum && !( li->flags & IFL_ROAM ) ) { continue; } //get the fuzzy weight function for this item iteminfo_t* iteminfo = &ic->iteminfo[ li->iteminfo ]; int weightnum = gs->itemweightindex[ iteminfo->number ]; if ( weightnum < 0 ) { continue; } //if this goal is in the avoid goals if ( !( GGameType & GAME_Quake3 ) && BotAvoidGoalTime( goalstate, li->number ) > 0 ) { continue; } float weight = FuzzyWeightUndecided( inventory, gs->itemweightconfig, weightnum ); //HACK: to make dropped items more attractive if ( li->timeout ) { weight += GGameType & GAME_Quake3 ? droppedweight->value : 1000; } //use weight scale for item_botroam if ( GGameType & GAME_Quake3 && li->flags & IFL_ROAM ) { weight *= li->weight; } if ( weight > 0 ) { //get the travel time towards the goal area int t = AAS_AreaTravelTimeToGoalArea( areanum, origin, li->goalareanum, travelflags ); //if the goal is reachable if ( t > 0 && t < maxtime ) { if ( GGameType & GAME_Quake3 ) { //if this item won't respawn before we get there float avoidtime = BotAvoidGoalTime( goalstate, li->number ); if ( avoidtime - t * 0.009 > 0 ) { continue; } } weight /= ( float )t * TRAVELTIME_SCALE; if ( weight > bestweight ) { t = 0; if ( ltg && !li->timeout ) { //get the travel time from the goal to the long term goal t = AAS_AreaTravelTimeToGoalArea( li->goalareanum, li->goalorigin, ltg->areanum, travelflags ); } //if the travel back is possible and doesn't take too long if ( t <= ltg_time ) { bestweight = weight; bestitem = li; } } } } } //if no goal item found if ( !bestitem ) { return false; } //create a bot goal for this item bot_goal_t goal; Com_Memset( &goal, 0, sizeof ( bot_goal_t ) ); iteminfo_t* iteminfo = &ic->iteminfo[ bestitem->iteminfo ]; VectorCopy( bestitem->goalorigin, goal.origin ); VectorCopy( iteminfo->mins, goal.mins ); VectorCopy( iteminfo->maxs, goal.maxs ); goal.areanum = bestitem->goalareanum; goal.entitynum = bestitem->entitynum; goal.number = bestitem->number; goal.flags = GFL_ITEM; if ( GGameType & GAME_Quake3 ) { if ( bestitem->timeout ) { goal.flags |= GFL_DROPPED; } if ( bestitem->flags & IFL_ROAM ) { goal.flags |= GFL_ROAM; } } goal.iteminfo = bestitem->iteminfo; float avoidtime; if ( GGameType & GAME_Quake3 ) { //if it's a dropped item if ( bestitem->timeout ) { avoidtime = AVOID_DROPPED_TIME; } else { avoidtime = iteminfo->respawntime; if ( !avoidtime ) { avoidtime = AVOID_DEFAULT_TIME; } if ( avoidtime < AVOID_MINIMUM_TIME ) { avoidtime = AVOID_MINIMUM_TIME; } } } else { avoidtime = iteminfo->respawntime * 0.5; if ( avoidtime < 10 ) { avoidtime = AVOID_DEFAULT_TIME; } //if it's a dropped item if ( bestitem->timeout ) { avoidtime = AVOID_DROPPED_TIME_WOLF; } } //add the chosen goal to the goals to avoid for a while BotAddToAvoidGoals( gs, bestitem->number, avoidtime ); //push the goal on the stack BotPushGoal( goalstate, &goal, sizeof ( goal ) ); return true; }