void BotInterbreedGoalFuzzyLogic( int parent1, int parent2, int child ) { bot_goalstate_t* p1 = BotGoalStateFromHandle( parent1 ); bot_goalstate_t* p2 = BotGoalStateFromHandle( parent2 ); bot_goalstate_t* c = BotGoalStateFromHandle( child ); InterbreedWeightConfigs( p1->itemweightconfig, p2->itemweightconfig, c->itemweightconfig ); }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotInterbreedGoalFuzzyLogic( int parent1, int parent2, int child ) { bot_goalstate_t *p1, *p2, *c; p1 = BotGoalStateFromHandle( parent1 ); p2 = BotGoalStateFromHandle( parent2 ); c = BotGoalStateFromHandle( child ); InterbreedWeightConfigs( p1->itemweightconfig, p2->itemweightconfig, c->itemweightconfig ); } //end of the function BotInterbreedingGoalFuzzyLogic
void BotEmptyGoalStack( int goalstate ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } gs->goalstacktop = 0; }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotSaveGoalFuzzyLogic( int goalstate, char *filename ) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle( goalstate ); //WriteWeightConfig(filename, gs->itemweightconfig); } //end of the function BotSaveGoalFuzzyLogic
void BotSetAvoidGoalTime( int goalstate, int number, float avoidtime ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } if ( avoidtime < 0 ) { if ( !itemconfig ) { return; } for ( levelitem_t* li = levelitems; li; li = li->next ) { if ( li->number == number ) { avoidtime = itemconfig->iteminfo[ li->iteminfo ].respawntime; if ( !avoidtime ) { avoidtime = AVOID_DEFAULT_TIME; } if ( avoidtime < AVOID_MINIMUM_TIME ) { avoidtime = AVOID_MINIMUM_TIME; } BotAddToAvoidGoals( gs, number, avoidtime ); return; } } return; } else { BotAddToAvoidGoals( gs, number, avoidtime ); } }
int BotLoadItemWeights( int goalstate, const char* filename ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return GGameType & GAME_Quake3 ? Q3BLERR_CANNOTLOADITEMWEIGHTS : WOLFBLERR_CANNOTLOADITEMWEIGHTS; } //load the weight configuration if ( GGameType & GAME_ET ) { PS_SetBaseFolder( BOTFILESBASEFOLDER ); } gs->itemweightconfig = ReadWeightConfig( filename ); if ( GGameType & GAME_ET ) { PS_SetBaseFolder( "" ); } if ( !gs->itemweightconfig ) { BotImport_Print( PRT_FATAL, "couldn't load weights\n" ); return GGameType & GAME_Quake3 ? Q3BLERR_CANNOTLOADITEMWEIGHTS : WOLFBLERR_CANNOTLOADITEMWEIGHTS; } //if there's no item configuration if ( !itemconfig ) { return GGameType & GAME_Quake3 ? Q3BLERR_CANNOTLOADITEMWEIGHTS : WOLFBLERR_CANNOTLOADITEMWEIGHTS; } //create the item weight index gs->itemweightindex = ItemWeightIndex( gs->itemweightconfig, itemconfig ); //everything went ok return BLERR_NOERROR; }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotMutateGoalFuzzyLogic( int goalstate, float range ) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle( goalstate ); EvolveWeightConfig( gs->itemweightconfig ); } //end of the function BotMutateGoalFuzzyLogic
void BotResetAvoidGoals( int goalstate ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } Com_Memset( gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof ( int ) ); Com_Memset( gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof ( float ) ); }
void BotPopGoal( int goalstate ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } if ( gs->goalstacktop > 0 ) { gs->goalstacktop--; } }
void BotResetGoalState( int goalstate ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } Com_Memset( gs->goalstack, 0, MAX_GOALSTACK * sizeof ( bot_goal_t ) ); gs->goalstacktop = 0; BotResetAvoidGoals( goalstate ); }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotResetAvoidGoals( int goalstate ) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } memset( gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof( int ) ); memset( gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof( float ) ); } //end of the function BotResetAvoidGoals
int BotGetTopGoalET( int goalstate, bot_goal_et_t* goal ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return false; } if ( !gs->goalstacktop ) { return false; } memcpy( goal, &gs->goalstack[ gs->goalstacktop ], sizeof ( bot_goal_et_t ) ); return true; }
int BotGetSecondGoalET( int goalstate, bot_goal_et_t* goal ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return false; } if ( gs->goalstacktop <= 1 ) { return false; } Com_Memcpy( goal, &gs->goalstack[ gs->goalstacktop - 1 ], sizeof ( bot_goal_et_t ) ); return true; }
void BotDumpGoalStack( int goalstate ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } for ( int i = 1; i <= gs->goalstacktop; i++ ) { char name[ 32 ]; BotGoalName( gs->goalstack[ i ].number, name, 32 ); Log_Write( "%d: %s", i, name ); } }
void BotFreeItemWeights( int goalstate ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } if ( gs->itemweightconfig ) { FreeWeightConfig( gs->itemweightconfig ); } if ( gs->itemweightindex ) { Mem_Free( gs->itemweightindex ); } }
void BotRemoveFromAvoidGoals( int goalstate, int number ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } for ( int i = 0; i < MAX_AVOIDGOALS; i++ ) { if ( gs->avoidgoals[ i ] == number && gs->avoidgoaltimes[ i ] >= AAS_Time() ) { gs->avoidgoaltimes[ i ] = 0; return; } } }
float BotAvoidGoalTime( int goalstate, int number ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return 0; } for ( int i = 0; i < MAX_AVOIDGOALS; i++ ) { if ( gs->avoidgoals[ i ] == number && gs->avoidgoaltimes[ i ] >= AAS_Time() ) { return gs->avoidgoaltimes[ i ] - AAS_Time(); } } return 0; }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotGetSecondGoal( int goalstate, bot_goal_t *goal ) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return qfalse; } if ( gs->goalstacktop <= 1 ) { return qfalse; } memcpy( goal, &gs->goalstack[gs->goalstacktop - 1], sizeof( bot_goal_t ) ); return qtrue; } //end of the function BotGetSecondGoal
static void BotPushGoal( int goalstate, const bot_goal_t* goal, size_t size ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } if ( gs->goalstacktop >= MAX_GOALSTACK - 1 ) { BotImport_Print( PRT_ERROR, "goal heap overflow\n" ); BotDumpGoalStack( goalstate ); return; } gs->goalstacktop++; Com_Memcpy( &gs->goalstack[ gs->goalstacktop ], goal, size ); }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotFreeItemWeights( int goalstate ) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } if ( gs->itemweightconfig ) { FreeWeightConfig( gs->itemweightconfig ); } if ( gs->itemweightindex ) { FreeMemory( gs->itemweightindex ); } } //end of the function BotFreeItemWeights
void BotDumpAvoidGoals( int goalstate ) { bot_goalstate_t* gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } for ( int i = 0; i < MAX_AVOIDGOALS; i++ ) { if ( gs->avoidgoaltimes[ i ] >= AAS_Time() ) { char name[ 32 ]; BotGoalName( gs->avoidgoals[ i ], name, 32 ); Log_Write( "avoid goal %s, number %d for %f seconds", name, gs->avoidgoals[ i ], gs->avoidgoaltimes[ i ] - AAS_Time() ); } } }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotPushGoal( int goalstate, bot_goal_t *goal ) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } if ( gs->goalstacktop >= MAX_GOALSTACK - 1 ) { botimport.Print( PRT_ERROR, "goal heap overflow\n" ); BotDumpGoalStack( goalstate ); return; } //end if gs->goalstacktop++; memcpy( &gs->goalstack[gs->goalstacktop], goal, sizeof( bot_goal_t ) ); } //end of the function BotPushGoal
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void BotDumpGoalStack( int goalstate ) { int i; bot_goalstate_t *gs; char name[32]; gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return; } for ( i = 1; i <= gs->goalstacktop; i++ ) { BotGoalName( gs->goalstack[i].number, name, 32 ); Log_Write( "%d: %s", i, name ); } //end for } //end of the function BotDumpGoalStack
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== float BotAvoidGoalTime( int goalstate, int number ) { int i; bot_goalstate_t *gs; gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return 0; } //don't use the goals the bot wants to avoid for ( i = 0; i < MAX_AVOIDGOALS; i++ ) { if ( gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time() ) { return gs->avoidgoaltimes[i] - AAS_Time(); } //end if } //end for return 0; } //end of the function BotAvoidGoalTime
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int BotLoadItemWeights( int goalstate, char *filename ) { bot_goalstate_t *gs; gs = BotGoalStateFromHandle( goalstate ); if ( !gs ) { return BLERR_CANNOTLOADITEMWEIGHTS; } //load the weight configuration gs->itemweightconfig = ReadWeightConfig( filename ); if ( !gs->itemweightconfig ) { botimport.Print( PRT_FATAL, "couldn't load weights\n" ); return BLERR_CANNOTLOADITEMWEIGHTS; } //end if //if there's no item configuration if ( !itemconfig ) { return BLERR_CANNOTLOADITEMWEIGHTS; } //create the item weight index gs->itemweightindex = ItemWeightIndex( gs->itemweightconfig, itemconfig ); //everything went ok return BLERR_NOERROR; } //end of the function BotLoadItemWeights
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; }
//=========================================================================== // // 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