Esempio n. 1
0
/*
=======================================================================================================================================
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;
}
Esempio n. 2
0
/*
==================
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);
	}
}
Esempio n. 4
0
/*
==================
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;
}
Esempio n. 5
0
/*
==================
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;
}
Esempio n. 6
0
/*
==================
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;
}
Esempio n. 7
0
/*
==================
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;
}
Esempio n. 8
0
/*
==================
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;
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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;
}
Esempio n. 11
0
/*
==================
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;
}
Esempio n. 13
0
/*
==================
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;
}
Esempio n. 14
0
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));
}
Esempio n. 15
0
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));
}
Esempio n. 16
0
/*
==================
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;
}
Esempio n. 18
0
/*
==================
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;
}
Esempio n. 19
0
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;
}
Esempio n. 20
0
/*
==================
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;

}
Esempio n. 22
0
/*
=======================================================================================================================================
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;
}
Esempio n. 23
0
/*
==================
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;
}
Esempio n. 24
0
/*
==================
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;
}
Esempio n. 25
0
/*
=======================================================================================================================================
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
}
Esempio n. 26
0
/*
==================
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;
        }
    }
}
Esempio n. 27
0
/*
=======================================================================================================================================
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());
}
Esempio n. 28
0
/*
==================
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);
}
Esempio n. 29
0
/*
==============
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;
	}
}