/* ======================================================================================================================================= FindEnemyByName ======================================================================================================================================= */ int FindEnemyByName(bot_state_t *bs, char *name) { int i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) { maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); } for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (BotSameTeam(bs, i)) { continue; } ClientName(i, buf, sizeof(buf)); if (!Q_stricmp(buf, name)) { return i; } } for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { if (BotSameTeam(bs, i)) { continue; } ClientName(i, buf, sizeof(buf)); if (stristr(buf, name)) { return i; } } return -1; }
/* ================== BotRandomOpponentName ================== */ char *BotRandomOpponentName(bot_state_t *bs) { int i, count; char buf[MAX_INFO_STRING]; int opponents[MAX_CLIENTS], numopponents; static char name[32]; numopponents = 0; opponents[0] = 0; for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; //skip team mates if (BotSameTeam(bs, i)) continue; // opponents[numopponents] = i; numopponents++; } count = random() * numopponents; for (i = 0; i < numopponents; i++) { count--; if (count <= 0) { EasyClientName(opponents[i], name, sizeof(name)); return name; } } EasyClientName(opponents[0], name, sizeof(name)); return name; }
void BotMatch_WrongWall(bot_state_t* bs, bot_match_t *match){ char netname[MAX_MESSAGE_SIZE]; char buf[MAX_INFO_STRING]; int client; if(gametype != GT_SPRAY) return; // talking about me ? (avoid clientfromname, its ambiguous) trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); trap_GetConfigstring(CS_PLAYERS + bs->client, buf, sizeof(buf)); Q_CleanStr( buf ); if (!Q_stricmp(Info_ValueForKey(buf, "n"), netname)){ // could be someone with same name, so make (more) sure if( ClientInSprayroom(bs->client) ){ bs->which_wall = BotChooseCorrectWall(bs); bs->enemy = -1; // chat BotAI_BotInitialChat(bs, "wall_missed", NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); return; } } // check if opposite team client = ClientFromName(netname); if(!BotSameTeam(bs, client)){ float rnd; // flame rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1); if(random() > rnd) return; BotAI_BotInitialChat(bs, "wall_insult", netname, NULL); trap_BotEnterChat(bs->cs, 0, CHAT_ALL); } }
/* ================== FindHumanTeamLeader ================== */ int FindHumanTeamLeader(bot_state_t *bs) { int i; for (i = 0; i < MAX_CLIENTS; i++) { if ( g_entities[i].inuse ) { // if this player is not a bot if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { // if this player is ok with being the leader if (!notleader[i]) { // if this player is on the same team if ( BotSameTeam(bs, i) ) { ClientName(i, bs->teamleader, sizeof(bs->teamleader)); // if not yet ordered to do anything if ( !BotSetLastOrderedTask(bs) ) { // go on defense by default BotVoiceChat_Defend(bs, i, SAY_TELL); } return qtrue; } } } } } return qfalse; }
/* ================== BotChat_Kill ================== */ int BotChat_Kill(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1); // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chat is off if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (bs->lastkilledplayer == bs->client) return qfalse; if (BotNumActivePlayers() <= 1) return qfalse; if (!BotValidChatPosition(bs)) return qfalse; // if (BotVisibleEnemies(bs)) return qfalse; // EasyClientName(bs->lastkilledplayer, name, 32); // bs->chatto = CHAT_ALL; if (TeamPlayIsOn() && BotSameTeam(bs, bs->lastkilledplayer)) { BotAI_BotInitialChat(bs, "kill_teammate", name, NULL); bs->chatto = CHAT_TEAM; } else { //don't chat in teamplay if (TeamPlayIsOn()) { #ifdef MISSIONPACK trap_EA_Command(bs->client, "vtaunt"); #endif return qfalse; // don't wait } // if (bs->enemydeathtype == MOD_GAUNTLET) { BotAI_BotInitialChat(bs, "kill_gauntlet", name, NULL); } else if (bs->enemydeathtype == MOD_RAILGUN) { BotAI_BotInitialChat(bs, "kill_rail", name, NULL); } else if (bs->enemydeathtype == MOD_TELEFRAG) { BotAI_BotInitialChat(bs, "kill_telefrag", name, NULL); } #ifdef MISSIONPACK else if (bs->botdeathtype == MOD_KAMIKAZE && trap_BotNumInitialChats(bs->cs, "kill_kamikaze")) BotAI_BotInitialChat(bs, "kill_kamikaze", name, NULL); #endif //choose between insult and praise else if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1)) { BotAI_BotInitialChat(bs, "kill_insult", name, NULL); } else { BotAI_BotInitialChat(bs, "kill_praise", name, NULL); } } bs->lastchat_time = FloatTime(); return qtrue; }
/* ================== BotVisibleEnemies ================== */ int BotVisibleEnemies(bot_state_t *bs) { float vis; int i; aas_entityinfo_t entinfo; for (i = 0; i < MAX_CLIENTS; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); // if (!entinfo.valid) continue; //if the enemy isn't dead and the enemy isn't the bot self if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue; //if the enemy is invisible and not shooting if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { continue; } //if on the same team if (BotSameTeam(bs, i)) continue; //check if the enemy is visible vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i); if (vis > 0) return qtrue; } return qfalse; }
/* ================== BotNumTeamMates ================== */ int BotNumTeamMates( bot_state_t *bs ) { int i, numplayers; char buf[MAX_INFO_STRING]; static int maxclients; if ( !maxclients ) { maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" ); } numplayers = 0; for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) { trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) ); //if no config string or no name if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) { continue; } //skip spectators if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) { continue; } // if ( BotSameTeam( bs, i ) ) { numplayers++; } } return numplayers; }
/* ================== BotAddressedToBot ================== */ int BotAddressedToBot( bot_state_t *bs, bot_match_t *match ) { char addressedto[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char name[MAX_MESSAGE_SIZE]; char botname[128]; int client; bot_match_t addresseematch; trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) ); client = ClientFromName( netname ); if ( client < 0 ) { return qfalse; } if ( !BotSameTeam( bs, client ) ) { return qfalse; } //if the message is addressed to someone if ( match->subtype & ST_ADDRESSED ) { trap_BotMatchVariable( match, ADDRESSEE, addressedto, sizeof( addressedto ) ); //the name of this bot ClientName( bs->client, botname, 128 ); // while ( trap_BotFindMatch( addressedto, &addresseematch, MTCONTEXT_ADDRESSEE ) ) { if ( addresseematch.type == MSG_EVERYONE ) { return qtrue; } else if ( addresseematch.type == MSG_MULTIPLENAMES ) { trap_BotMatchVariable( &addresseematch, TEAMMATE, name, sizeof( name ) ); if ( strlen( name ) ) { if ( stristr( botname, name ) ) { return qtrue; } if ( stristr( bs->subteam, name ) ) { return qtrue; } } trap_BotMatchVariable( &addresseematch, MORE, addressedto, MAX_MESSAGE_SIZE ); } else { trap_BotMatchVariable( &addresseematch, TEAMMATE, name, MAX_MESSAGE_SIZE ); if ( strlen( name ) ) { if ( stristr( botname, name ) ) { return qtrue; } if ( stristr( bs->subteam, name ) ) { return qtrue; } } break; } } //Com_sprintf(buf, sizeof(buf), "not addressed to me but %s", addressedto); //trap_EA_Say(bs->client, buf); return qfalse; } else { //make sure not everyone reacts to this message if ( random() > (float ) 1.0 / ( NumPlayersOnSameTeam( bs ) - 1 ) ) { return qfalse; } } return qtrue; }
int BotTeamCarrierVisible( bot_state_t *bs ) { int i; float vis; aas_entityinfo_t entinfo; gentity_t *ent; float f, alertness; float squaredist; vec3_t dir; int areanum; alertness = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_ALERTNESS, 0, 1 ); for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) { if ( i == bs->client ) continue; ent = &g_entities[ i ]; if ( !ent->inuse || !ent->target_ent ) continue; BotEntityInfo( ent->target_ent->s.number, &entinfo ); if ( !entinfo.valid || !( entinfo.powerups & ( 1 << PW_BATTLESUIT ) ) ) continue; if ( !BotSameTeam( bs, i ) ) continue; VectorSubtract( entinfo.origin, bs->origin, dir ); squaredist = VectorLengthSquared( dir ); if ( squaredist > Square( 900.0 + alertness * 4000.0 ) ) continue; f = 90 + 90 - ( 90 - ( squaredist > Square( 810 ) ? Square( 810 ) : squaredist ) / ( 810 * 9 ) ); vis = BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, f, entinfo.number ); if ( vis <= 0 ) continue; areanum = BotPointAreaNum( entinfo.origin ); if ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, areanum, bs->tfl ) ) { return entinfo.number; } } return -1; }
int BotVoiceChatCommand(bot_state_t *bs, int mode, char *voiceChat) { int i, clientNum; char *ptr, buf[MAX_MESSAGE_SIZE], *cmd,*cmd2; if (!TeamPlayIsOn()) { return qfalse; } if ( mode == SAY_ALL ) { return qfalse; } Q_strncpyz(buf, voiceChat, sizeof(buf)); cmd = buf; ptr = strchr(buf,' '); *ptr++ = 0; clientNum = atoi(ptr); cmd2 = ptr+2; ptr = strchr(cmd2,' '); *ptr = 0; if (!BotSameTeam(bs, clientNum)) { return qfalse; } for (i = 0; voiceCommands[i].cmd; i++) { if (!Q_stricmp(cmd2, voiceCommands[i].cmd)) { voiceCommands[i].func(bs, clientNum, mode); return qtrue; } } return qfalse; }
/* ================== FindEnemyByName ================== */ int FindEnemyByName(bot_state_t *bs, char *name) { int i; char buf[MAX_INFO_STRING]; for (i = 0; i < level.maxplayers; i++) { if (BotSameTeam(bs, i)) continue; PlayerName(i, buf, sizeof(buf)); if (!Q_stricmp(buf, name)) return i; } for (i = 0; i < level.maxplayers; i++) { if (BotSameTeam(bs, i)) continue; PlayerName(i, buf, sizeof(buf)); if (stristr(buf, name)) return i; } return -1; }
int BotGetTeammates(bot_state_t *bs, int *teammates, int maxteammates) { int i, numteammates; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); //G_Printf("mates: "); numteammates = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // if (BotSameTeam(bs, i)) { // teammates[numteammates++] = i; if (numteammates >= maxteammates) break; //G_Printf("/%s ", Info_ValueForKey(buf, "n")); } } //G_Printf("\n %d mates \n", numteammates); return numteammates; }
/* ================== BotChat_Kill ================== */ int BotChat_Kill( bot_state_t *bs ) { char name[32]; float rnd; if ( bot_nochat.integer ) { return qfalse; } if ( bs->lastchat_time > trap_AAS_Time() - 3 ) { return qfalse; } rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1 ); //if fast chat is off if ( !bot_fastchat.integer ) { if ( random() > rnd ) { return qfalse; } } if ( bs->lastkilledplayer == bs->client ) { return qfalse; } if ( BotNumActivePlayers() <= 1 ) { return qfalse; } if ( !BotValidChatPosition( bs ) ) { return qfalse; } // EasyClientName( bs->lastkilledplayer, name, 32 ); // bs->chatto = CHAT_ALL; if ( TeamPlayIsOn() && BotSameTeam( bs, bs->lastkilledplayer ) ) { BotAI_BotInitialChat( bs, "kill_teammate", name, NULL ); bs->chatto = CHAT_TEAM; } else { //don't chat in teamplay if ( TeamPlayIsOn() ) { return qfalse; } // if ( bs->enemydeathtype == MOD_GAUNTLET ) { BotAI_BotInitialChat( bs, "kill_gauntlet", name, NULL ); } else if ( bs->enemydeathtype == MOD_RAILGUN ) { BotAI_BotInitialChat( bs, "kill_rail", name, NULL ); } else if ( bs->enemydeathtype == MOD_TELEFRAG ) { BotAI_BotInitialChat( bs, "kill_telefrag", name, NULL ); } //choose between insult and praise else if ( random() < trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1 ) ) { BotAI_BotInitialChat( bs, "kill_insult", name, NULL ); } else { BotAI_BotInitialChat( bs, "kill_praise", name, NULL ); } } bs->lastchat_time = trap_AAS_Time(); return qtrue; }
void BotMatch_NewLeader(bot_state_t *bs, bot_match_t *match) { int playernum; char netname[MAX_NETNAME]; BotMatchVariable(match, NETNAME, netname, sizeof(netname)); playernum = FindPlayerByName(netname); if (!BotSameTeam(bs, playernum)) return; Q_strncpyz(bs->teamleader, netname, sizeof(bs->teamleader)); }
void BotMatch_NewLeader(bot_state_t *bs, bot_match_t *match) { int client; char netname[MAX_NETNAME]; trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = FindClientByName(netname); if (!BotSameTeam(bs, client)) return; Q_strncpyz(bs->teamleader, netname, sizeof(bs->teamleader)); }
/* ================== BotSortTeamMatesByBaseTravelTime ================== */ int BotSortTeamMatesByBaseTravelTime( bot_state_t *bs, int *teammates, int maxteammates ) { int i, j, k, numteammates, traveltime; char buf[MAX_INFO_STRING]; static int maxclients; int traveltimes[MAX_CLIENTS]; bot_goal_t *goal; if ( BotCTFTeam( bs ) == CTF_TEAM_RED ) { goal = &ctf_redflag; } else { goal = &ctf_blueflag;} if ( !maxclients ) { maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" ); } numteammates = 0; for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) { trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) ); //if no config string or no name if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) { continue; } //skip spectators if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) { continue; } // if ( BotSameTeam( bs, i ) ) { // traveltime = BotClientTravelTimeToGoal( i, goal ); // for ( j = 0; j < numteammates; j++ ) { if ( traveltime < traveltimes[j] ) { for ( k = numteammates; k > j; k-- ) { traveltimes[k] = traveltimes[k - 1]; teammates[k] = teammates[k - 1]; } traveltimes[j] = traveltime; teammates[j] = i; break; } } if ( j >= numteammates ) { traveltimes[j] = traveltime; teammates[j] = i; } numteammates++; if ( numteammates >= maxteammates ) { break; } } } return numteammates; }
int BotValidTeamLeader(bot_state_t *bs) { int leadclient; if (!strlen(bs->teamleader)) return qfalse; leadclient = ClientFromName(bs->teamleader); if (leadclient == -1) return qfalse; // client in unserem team ? if(!BotSameTeam(bs, leadclient)) return qfalse; // client bot ? // if this player is not a bot if ( !(g_entities[leadclient].r.svFlags & SVF_BOT) ) return qfalse; return qtrue; }
/* ================== NumPlayersOnSameTeam ================== */ int NumPlayersOnSameTeam(bot_state_t *bs) { int i, num; char buf[MAX_INFO_STRING]; num = 0; for (i = 0; i < level.maxplayers; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, MAX_INFO_STRING); if (strlen(buf)) { if (BotSameTeam(bs, i+1)) num++; } } return num; }
int BotVoiceChatCommand(bot_state_t * bs, int mode, char *voiceChat) { int i, clientNum; //int voiceOnly, color; char *ptr, buf[MAX_MESSAGE_SIZE], *cmd; if (!TeamPlayIsOn()) { return qfalse; } if (mode == SAY_ALL) { return qfalse; // don't do anything with voice chats to everyone } Q_strncpyz(buf, voiceChat, sizeof(buf)); cmd = buf; for (ptr = cmd; *cmd && *cmd > ' '; cmd++); while (*cmd && *cmd <= ' ') *cmd++ = '\0'; //voiceOnly = atoi(ptr); for (ptr = cmd; *cmd && *cmd > ' '; cmd++); while (*cmd && *cmd <= ' ') *cmd++ = '\0'; clientNum = atoi(ptr); for (ptr = cmd; *cmd && *cmd > ' '; cmd++); while (*cmd && *cmd <= ' ') *cmd++ = '\0'; //color = atoi(ptr); if (!BotSameTeam(bs, clientNum)) { return qfalse; } for (i = 0; voiceCommands[i].cmd; i++) { if (!Q_stricmp(cmd, voiceCommands[i].cmd)) { voiceCommands[i].func(bs, clientNum, mode); return qtrue; } } return qfalse; }
/* ================== NumPlayersOnSameTeam ================== */ int NumPlayersOnSameTeam(bot_state_t *bs) { int i, num; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); num = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, MAX_INFO_STRING); if (strlen(buf)) { if (BotSameTeam(bs, i+1)) num++; } } return num; }
int FindHumanTeamLeader(bot_state_t *bs) { int i; for (i = 0; i < MAX_CLIENTS; i++) { if ( g_entities[i].inuse ) { // if this player is not a bot if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { // if this player is ok with being the leader if (!notleader[i]) { // if this player is on the same team if ( BotSameTeam(bs, i) ) { ClientName(i, bs->teamleader, sizeof(bs->teamleader)); return qtrue; } } } } } return qfalse; }
/* ======================================================================================================================================= BotVisibleEnemies ======================================================================================================================================= */ int BotVisibleEnemies(bot_state_t *bs) { int i; aas_entityinfo_t entinfo; for (i = 0; i < level.maxclients; i++) { if (i == bs->client) { continue; } // if on the same team if (BotSameTeam(bs, i)) { continue; } // get the entity information BotEntityInfo(i, &entinfo); // if this player is active if (!entinfo.valid) { continue; } // if the entity isn't the bot self if (entinfo.number == bs->entitynum) { continue; } // if the entity isn't dead if (EntityIsDead(&entinfo)) { continue; } // if the enemy is invisible if (EntityIsInvisible(&entinfo)) { continue; } // check if the enemy is visible if (BotEntityVisible(&bs->cur_ps, 360, i)) { return qtrue; } } return qfalse; }
/* ================== BotChat_Death ================== */ int BotChat_Death(bot_state_t *bs) { char name[32]; float rnd; if (bot_nochat.integer) return qfalse; if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse; rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_DEATH, 0, 1); // don't chat in tournament mode if (gametype == GT_TOURNAMENT) return qfalse; //if fast chatting is off if (!bot_fastchat.integer) { if (random() > rnd) return qfalse; } if (BotNumActivePlayers() <= 1) return qfalse; // if (bs->lastkilledby >= 0 && bs->lastkilledby < MAX_CLIENTS) EasyClientName(bs->lastkilledby, name, 32); else strcpy(name, "[world]"); // if (TeamPlayIsOn() && BotSameTeam(bs, bs->lastkilledby)) { if (bs->lastkilledby == bs->client) return qfalse; BotAI_BotInitialChat(bs, "death_teammate", name, NULL); bs->chatto = CHAT_TEAM; } else { //teamplay if (TeamPlayIsOn()) { #ifdef MISSIONPACK trap_EA_Command(bs->client, "vtaunt"); #endif return qtrue; } // if (bs->botdeathtype == MOD_WATER) BotAI_BotInitialChat(bs, "death_drown", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_SLIME) BotAI_BotInitialChat(bs, "death_slime", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_LAVA) BotAI_BotInitialChat(bs, "death_lava", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_FALLING) BotAI_BotInitialChat(bs, "death_cratered", BotRandomOpponentName(bs), NULL); else if (bs->botsuicide || //all other suicides by own weapon bs->botdeathtype == MOD_CRUSH || bs->botdeathtype == MOD_SUICIDE || bs->botdeathtype == MOD_TARGET_LASER || bs->botdeathtype == MOD_TRIGGER_HURT || bs->botdeathtype == MOD_UNKNOWN) BotAI_BotInitialChat(bs, "death_suicide", BotRandomOpponentName(bs), NULL); else if (bs->botdeathtype == MOD_TELEFRAG) BotAI_BotInitialChat(bs, "death_telefrag", name, NULL); #ifdef MISSIONPACK else if (bs->botdeathtype == MOD_KAMIKAZE && trap_BotNumInitialChats(bs->cs, "death_kamikaze")) BotAI_BotInitialChat(bs, "death_kamikaze", name, NULL); #endif else { if ((bs->botdeathtype == MOD_GAUNTLET || bs->botdeathtype == MOD_RAILGUN || bs->botdeathtype == MOD_BFG || bs->botdeathtype == MOD_BFG_SPLASH) && random() < 0.5) { if (bs->botdeathtype == MOD_GAUNTLET) BotAI_BotInitialChat(bs, "death_gauntlet", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); else if (bs->botdeathtype == MOD_RAILGUN) BotAI_BotInitialChat(bs, "death_rail", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); else BotAI_BotInitialChat(bs, "death_bfg", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); } //choose between insult and praise else if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1)) { BotAI_BotInitialChat(bs, "death_insult", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); } else { BotAI_BotInitialChat(bs, "death_praise", name, // 0 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1 NULL); } } bs->chatto = CHAT_ALL; } bs->lastchat_time = FloatTime(); return qtrue; }
/* ================== BotChat_Death ================== */ int BotChat_Death( bot_state_t *bs ) { char name[32]; float rnd; if ( bot_nochat.integer ) { return qfalse; } if ( bs->lastchat_time > trap_AAS_Time() - 3 ) { return qfalse; } rnd = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_DEATH, 0, 1 ); //if fast chatting is off if ( !bot_fastchat.integer ) { if ( random() > rnd ) { return qfalse; } } if ( BotNumActivePlayers() <= 1 ) { return qfalse; } // if ( bs->lastkilledby >= 0 && bs->lastkilledby < MAX_CLIENTS ) { EasyClientName( bs->lastkilledby, name, 32 ); } else { strcpy( name, "[world]" ); } // if ( TeamPlayIsOn() && BotSameTeam( bs, bs->lastkilledby ) ) { if ( bs->lastkilledby == bs->client ) { return qfalse; } BotAI_BotInitialChat( bs, "death_teammate", name, NULL ); bs->chatto = CHAT_TEAM; } else { //don't chat in teamplay if ( TeamPlayIsOn() ) { return qfalse; } // if ( bs->botdeathtype == MOD_WATER ) { BotAI_BotInitialChat( bs, "death_drown", BotRandomOpponentName( bs ), NULL ); } else if ( bs->botdeathtype == MOD_SLIME ) { BotAI_BotInitialChat( bs, "death_slime", BotRandomOpponentName( bs ), NULL ); } else if ( bs->botdeathtype == MOD_LAVA ) { BotAI_BotInitialChat( bs, "death_lava", BotRandomOpponentName( bs ), NULL ); } else if ( bs->botdeathtype == MOD_FALLING ) { BotAI_BotInitialChat( bs, "death_cratered", BotRandomOpponentName( bs ), NULL ); } else if ( bs->botsuicide || //all other suicides by own weapon bs->botdeathtype == MOD_CRUSH || bs->botdeathtype == MOD_SUICIDE || bs->botdeathtype == MOD_TARGET_LASER || bs->botdeathtype == MOD_TRIGGER_HURT || bs->botdeathtype == MOD_UNKNOWN ) { BotAI_BotInitialChat( bs, "death_suicide", BotRandomOpponentName( bs ), NULL ); } else if ( bs->botdeathtype == MOD_TELEFRAG ) { BotAI_BotInitialChat( bs, "death_telefrag", name, NULL ); } else { if ( ( bs->botdeathtype == MOD_GAUNTLET || bs->botdeathtype == MOD_RAILGUN || bs->botdeathtype == MOD_BFG || bs->botdeathtype == MOD_BFG_SPLASH ) && random() < 0.5 ) { if ( bs->botdeathtype == MOD_GAUNTLET ) { BotAI_BotInitialChat( bs, "death_gauntlet", name, // 0 BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1 NULL ); } else if ( bs->botdeathtype == MOD_RAILGUN ) { BotAI_BotInitialChat( bs, "death_rail", name, // 0 BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1 NULL ); } else { BotAI_BotInitialChat( bs, "death_bfg", name, // 0 BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1 NULL ); } } //choose between insult and praise else if ( random() < trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1 ) ) { BotAI_BotInitialChat( bs, "death_insult", name, // 0 BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1 NULL ); } else { BotAI_BotInitialChat( bs, "death_praise", name, // 0 BotWeaponNameForMeansOfDeath( bs->botdeathtype ), // 1 NULL ); } } bs->chatto = CHAT_ALL; } bs->lastchat_time = trap_AAS_Time(); return qtrue; }
/* ======================================================================================================================================= BotMatch_HelpAccompany ======================================================================================================================================= */ void BotMatch_HelpAccompany(bot_state_t *bs, bot_match_t *match) { int client, other, areanum; char teammate[MAX_MESSAGE_SIZE], netname[MAX_MESSAGE_SIZE]; char itemname[MAX_MESSAGE_SIZE]; bot_match_t teammatematch; aas_entityinfo_t entinfo; if (!TeamPlayIsOn()) { return; } // if not addressed to this bot if (!BotAddressedToBot(bs, match)) { return; } // get the team mate name trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); // get the client to help if (trap_BotFindMatch(teammate, &teammatematch, MTCONTEXT_TEAMMATE) && // if someone asks for him or herself teammatematch.type == MSG_ME) { // get the netname trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); other = qfalse; } else { // asked for someone else client = FindClientByName(teammate); // if this is the bot self if (client == bs->client) { other = qfalse; } else if (!BotSameTeam(bs, client)) { // FIXME: say "I don't help the enemy" return; } else { other = qtrue; } } // if the bot doesn't know who to help (FindClientByName returned -1) if (client < 0) { if (other) { BotAI_BotInitialChat(bs, "whois", teammate, NULL); } else {BotAI_BotInitialChat(bs, "whois", netname, NULL);} trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } // don't help or accompany yourself if (client == bs->client) { return; } bs->teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); // if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum && trap_AAS_AreaReachability(areanum)) { bs->teamgoal.entitynum = client; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } // if no teamgoal yet if (bs->teamgoal.entitynum < 0) { // if near an item if (match->subtype & ST_NEARITEM) { // get the match variable trap_BotMatchVariable(match, ITEM, itemname, sizeof(itemname)); if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { // BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); // trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } } } if (bs->teamgoal.entitynum < 0) { if (other) { BotAI_BotInitialChat(bs, "whereis", teammate, NULL); } else {BotAI_BotInitialChat(bs, "whereareyou", netname, NULL);} trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } // the team mate bs->teammate = client; // last time the team mate was assumed visible bs->teammatevisible_time = trap_AAS_Time(); // set the time to send a message to the team mates bs->teammessage_time = trap_AAS_Time() + 2 * random(); // get the team goal time bs->teamgoal_time = BotGetTime(match); // set the ltg type if (match->type == MSG_HELP) { bs->ltgtype = LTG_TEAMHELP; if (!bs->teamgoal_time) { bs->teamgoal_time = trap_AAS_Time() + TEAM_HELP_TIME; } } else { bs->ltgtype = LTG_TEAMACCOMPANY; if (!bs->teamgoal_time) { bs->teamgoal_time = trap_AAS_Time() + TEAM_ACCOMPANY_TIME; } bs->formation_dist = 3.5 * 32; // 3.5 meter bs->arrive_time = 0; } #ifdef DEBUG BotPrintTeamGoal(bs); #endif // DEBUG }
/* ================== BotTeamOrders FIXME: defend key areas? ================== */ void BotTeamOrders(bot_state_t *bs) { int teammates[MAX_CLIENTS]; int numteammates, i; char buf[MAX_INFO_STRING]; static int maxclients; if (!maxclients) maxclients = Cvar_VariableIntegerValue("sv_maxclients"); numteammates = 0; for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { SV_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // if (BotSameTeam(bs, i)) { teammates[numteammates] = i; numteammates++; } } // switch(numteammates) { case 1: break; case 2: { //nothing special break; } case 3: { //have one follow another and one free roaming BotCreateGroup(bs, teammates, 2); break; } case 4: { BotCreateGroup(bs, teammates, 2); //a group of 2 BotCreateGroup(bs, &teammates[2], 2); //a group of 2 break; } case 5: { BotCreateGroup(bs, teammates, 2); //a group of 2 BotCreateGroup(bs, &teammates[2], 3); //a group of 3 break; } default: { if (numteammates <= 10) { for (i = 0; i < numteammates / 2; i++) { BotCreateGroup(bs, &teammates[i*2], 2); //groups of 2 } } break; } } }
/* ======================================================================================================================================= BotMatch_LeadTheWay ======================================================================================================================================= */ void BotMatch_LeadTheWay(bot_state_t *bs, bot_match_t *match) { aas_entityinfo_t entinfo; char netname[MAX_MESSAGE_SIZE], teammate[MAX_MESSAGE_SIZE]; int client, areanum, other; if (!TeamPlayIsOn()) { return; } // if not addressed to this bot if (!BotAddressedToBot(bs, match)) { return; } // if someone asks for someone else if (match->subtype & ST_SOMEONE) { // get the team mate name trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); client = FindClientByName(teammate); // if this is the bot self if (client == bs->client) { other = qfalse; } else if (!BotSameTeam(bs, client)) { // FIXME: say "I don't help the enemy" return; } else { other = qtrue; } } else { // get the netname trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); other = qfalse; } // if the bot doesn't know who to help (FindClientByName returned -1) if (client < 0) { BotAI_BotInitialChat(bs, "whois", netname, NULL); trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } bs->lead_teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); // if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum && trap_AAS_AreaReachability(areanum)) { bs->lead_teamgoal.entitynum = client; bs->lead_teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->lead_teamgoal.origin); VectorSet(bs->lead_teamgoal.mins, -8, -8, -8); VectorSet(bs->lead_teamgoal.maxs, 8, 8, 8); } } if (bs->teamgoal.entitynum < 0) { if (other) { BotAI_BotInitialChat(bs, "whereis", teammate, NULL); } else {BotAI_BotInitialChat(bs, "whereareyou", netname, NULL);} trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } bs->lead_teammate = client; bs->lead_time = trap_AAS_Time() + TEAM_LEAD_TIME; bs->leadvisible_time = 0; bs->leadmessage_time = -(trap_AAS_Time() + 2 * random()); }
/* ================== BotMatch_HelpAccompany ================== */ void BotMatch_HelpAccompany(bot_state_t *bs, bot_match_t *match) { int playernum, other, areanum; char teammate[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char itemname[MAX_MESSAGE_SIZE]; bot_match_t teammatematch; aas_entityinfo_t entinfo; if (!TeamPlayIsOn()) return; //if not addressed to this bot if (!BotAddressedToBot(bs, match)) return; //get the team mate name BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); //get the player to help if (BotFindMatch(teammate, &teammatematch, MTCONTEXT_TEAMMATE) && //if someone asks for him or herself teammatematch.type == MSG_ME) { //get the netname BotMatchVariable(match, NETNAME, netname, sizeof(netname)); playernum = PlayerFromName(netname); other = qfalse; } else { //asked for someone else playernum = FindPlayerByName(teammate); //if this is the bot self if (playernum == bs->playernum) { other = qfalse; } else if (!BotSameTeam(bs, playernum)) { //FIXME: say "I don't help the enemy" return; } else { other = qtrue; } } //if the bot doesn't know who to help (FindPlayerByName returned -1) if (playernum < 0) { if (other) BotAI_BotInitialChat(bs, "whois", teammate, NULL); else BotAI_BotInitialChat(bs, "whois", netname, NULL); playernum = PlayerFromName(netname); BotEnterChat(bs->cs, playernum, CHAT_TELL); return; } //don't help or accompany yourself if (playernum == bs->playernum) { return; } // bs->teamgoal.entitynum = -1; BotEntityInfo(playernum, &entinfo); //if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum) {// && trap_AAS_AreaReachability(areanum)) { bs->teamgoal.entitynum = playernum; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } //if no teamgoal yet if (bs->teamgoal.entitynum < 0) { //if near an item if (match->subtype & ST_NEARITEM) { //get the match variable BotMatchVariable(match, ITEM, itemname, sizeof(itemname)); // if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); //BotEnterChat(bs->cs, bs->playernum, CHAT_TEAM); return; } } } // if (bs->teamgoal.entitynum < 0) { if (other) BotAI_BotInitialChat(bs, "whereis", teammate, NULL); else BotAI_BotInitialChat(bs, "whereareyou", netname, NULL); playernum = PlayerFromName(netname); BotEnterChat(bs->cs, playernum, CHAT_TEAM); return; } //the team mate bs->teammate = playernum; // BotMatchVariable(match, NETNAME, netname, sizeof(netname)); // playernum = PlayerFromName(netname); //the team mate who ordered bs->decisionmaker = playernum; bs->ordered = qtrue; bs->order_time = FloatTime(); //last time the team mate was assumed visible bs->teammatevisible_time = FloatTime(); //set the time to send a message to the team mates bs->teammessage_time = FloatTime() + 2 * random(); //get the team goal time bs->teamgoal_time = BotGetTime(match); //set the ltg type if (match->type == MSG_HELP) { bs->ltgtype = LTG_TEAMHELP; if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_HELP_TIME; } else { bs->ltgtype = LTG_TEAMACCOMPANY; if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME; bs->formation_dist = 128; bs->arrive_time = 0; // BotSetTeamStatus(bs); // remember last ordered task BotRememberLastOrderedTask(bs); } BotPrintTeamGoal(bs); }
/* ============== BotBlastDamage Determine how much damage a blast from "weapon" detonating at "center" deals, not dealing damage to entity "ignore". The information is stored in the "blast" input argument. ============== */ void BotBlastDamage(bot_state_t *bs, int weapon, vec3_t center, damage_multi_t *blast, gentity_t *ignore) { int i, num_contacted; int contacted[MAX_GENTITIES]; float radius, dist, damage; vec3_t offset, mins, maxs; gentity_t *ent; damage_catagory_t *group; // Reset the damage information memset(blast, 0, sizeof(damage_multi_t)); // Only check for weapons with blast radius radius = weapon_stats[weapon].radius; if (radius <= 0) return; // Compute the bounding box containing all entities that could possibly be // damaged from the blast radius. VectorSet (offset, radius, radius, radius); VectorAdd (center, offset, maxs); VectorSubtract(center, offset, mins); // Get a list of all entities possibly within this bounding box num_contacted = trap_EntitiesInBox(mins, maxs, contacted, MAX_GENTITIES); // Estimate damage dealt to each nearby entity // // NOTE: This duplicates much of the code in G_RadiusDamage() in g_combat.c. for (i = 0; i < num_contacted; i++) { // Do not tracked the ignored entity ent = &g_entities[contacted[i]]; if (ent == ignore) continue; // Check if the entity is on the enemy team // // NOTE: This includes damagable structures on the enemy team, // like the Obelisk in Overload. if (BotEnemyTeam(bs, ent)) group = &blast->enemy; // Also check for players on the same team that the bot can damage. // // NOTE: This function purposely ignores self-damage. It also // ignores damagable team structures, like the Obelisk in Overload // (which players can't damage even when friend fire is turned on). else if (g_friendlyFire.integer && ent->client && BotSameTeam(bs, ent) && bs->ent != ent) group = &blast->team; // Never count damage to neutrally aligned entities, even if they are // damagable (like shot-activated buttons) else continue; // Determine how close the blast shot was to the target's bounding box // (in real world coordinates) EntityWorldBounds(ent, mins, maxs); dist = point_bound_distance(center, mins, maxs); // Compute how much damage the blast would deal to entities at this distance damage = WeaponBlast(weapon, dist); // Update the specific catagory data... group->hits++; group->total += damage; if (group->max < damage) group->max = damage; // ... And the aggregate data blast->all.hits++; blast->all.total += damage; if (blast->all.max < damage) blast->all.max = damage; } }