/* =========================================================================== Return to Castle Wolfenstein single player GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). RTCW SP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW SP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW SP Source Code. If not, see . In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ /***************************************************************************** * name: ai_team.c * * desc: Quake3 bot AI * * *****************************************************************************/ #include "../game/g_local.h" #include "../game/botlib.h" #include "../game/be_aas.h" #include "../game/be_ea.h" #include "../game/be_ai_char.h" #include "../game/be_ai_chat.h" #include "../game/be_ai_gen.h" #include "../game/be_ai_goal.h" #include "../game/be_ai_move.h" #include "../game/be_ai_weap.h" #include "../botai/botai.h" // #include "ai_main.h" #include "ai_dmq3.h" #include "ai_chat.h" #include "ai_cmd.h" #include "ai_dmnet.h" /* ================== BotValidTeamLeader ================== */ int BotValidTeamLeader( bot_state_t *bs ) { if ( !strlen( bs->teamleader ) ) { return qfalse; } if ( ClientFromName( bs->teamleader ) == -1 ) { return qfalse; } return qtrue; } /* ================== 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; } /* ================== BotClientTravelTimeToGoal ================== */ int BotClientTravelTimeToGoal( int client, bot_goal_t *goal ) { playerState_t ps; int areanum; BotAI_GetClientState( client, &ps ); areanum = BotPointAreaNum( ps.origin ); if ( !areanum ) { return 1; } return trap_AAS_AreaTravelTimeToGoalArea( areanum, ps.origin, goal->areanum, TFL_DEFAULT ); } /* ================== 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; } /* ================== BotSayTeamOrders ================== */ void BotSayTeamOrder( bot_state_t *bs, int toclient ) { char teamchat[MAX_MESSAGE_SIZE]; char buf[MAX_MESSAGE_SIZE]; char name[MAX_NETNAME]; //if the bot is talking to itself if ( bs->client == toclient ) { //don't show the message just put it in the console message queue trap_BotGetChatMessage( bs->cs, buf, sizeof( buf ) ); ClientName( bs->client, name, sizeof( name ) ); Com_sprintf( teamchat, sizeof( teamchat ), "(%s): %s", name, buf ); trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, teamchat ); } else { trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); } } /* ================== BotCTFOrders ================== */ void BotCTFOrders_BothFlagsNotAtBase( bot_state_t *bs ) { int numteammates, defenders, attackers, i, other; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME], carriername[MAX_NETNAME]; numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) ); //different orders based on the number of team mates switch ( bs->numteammates ) { case 1: break; case 2: { //tell the one not carrying the flag to attack the enemy base if ( teammates[0] != bs->flagcarrier ) { other = teammates[0]; } else { other = teammates[1];} ClientName( other, name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); BotSayTeamOrder( bs, other ); break; } case 3: { //tell the one closest to the base not carrying the flag to accompany the flag carrier if ( teammates[0] != bs->flagcarrier ) { other = teammates[0]; } else { other = teammates[1];} ClientName( other, name, sizeof( name ) ); ClientName( bs->flagcarrier, carriername, sizeof( carriername ) ); if ( bs->flagcarrier == bs->client ) { BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL ); } else { BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL ); } BotSayTeamOrder( bs, other ); //tell the one furthest from the the base not carrying the flag to get the enemy flag if ( teammates[2] != bs->flagcarrier ) { other = teammates[2]; } else { other = teammates[1];} ClientName( other, name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); BotSayTeamOrder( bs, other ); break; } default: { defenders = (int) ( float ) numteammates * 0.4 + 0.5; attackers = (int) ( float ) numteammates * 0.5 + 0.5; ClientName( bs->flagcarrier, carriername, sizeof( carriername ) ); for ( i = 0; i < defenders; i++ ) { // if ( teammates[i] == bs->flagcarrier ) { continue; } // ClientName( teammates[i], name, sizeof( name ) ); if ( bs->flagcarrier == bs->client ) { BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL ); } else { BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL ); } BotSayTeamOrder( bs, teammates[i] ); } for ( i = 0; i < attackers; i++ ) { // if ( teammates[numteammates - i - 1] == bs->flagcarrier ) { continue; } // ClientName( teammates[numteammates - i - 1], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); BotSayTeamOrder( bs, teammates[numteammates - i - 1] ); } // break; } } } /* ================== BotCTFOrders ================== */ void BotCTFOrders_FlagNotAtBase( bot_state_t *bs ) { int numteammates, defenders, attackers, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) ); //different orders based on the number of team mates switch ( bs->numteammates ) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName( teammates[0], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); BotSayTeamOrder( bs, teammates[0] ); //the other will get the flag ClientName( teammates[1], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); BotSayTeamOrder( bs, teammates[1] ); break; } case 3: { //the one closest to the base will defend the base ClientName( teammates[0], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); BotSayTeamOrder( bs, teammates[0] ); //the other two get the flag ClientName( teammates[1], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); BotSayTeamOrder( bs, teammates[1] ); // ClientName( teammates[2], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); BotSayTeamOrder( bs, teammates[2] ); break; } default: { defenders = (int) ( float ) numteammates * 0.3 + 0.5; attackers = (int) ( float ) numteammates * 0.5 + 0.5; for ( i = 0; i < defenders; i++ ) { // ClientName( teammates[i], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); BotSayTeamOrder( bs, teammates[i] ); } for ( i = 0; i < attackers; i++ ) { // ClientName( teammates[numteammates - i - 1], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); BotSayTeamOrder( bs, teammates[numteammates - i - 1] ); } // break; } } } /* ================== BotCTFOrders ================== */ void BotCTFOrders_EnemyFlagNotAtBase( bot_state_t *bs ) { int numteammates, defenders, attackers, i, other; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME], carriername[MAX_NETNAME]; numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) ); //different orders based on the number of team mates switch ( numteammates ) { case 1: break; case 2: { //tell the one not carrying the flag to defend the base if ( teammates[0] == bs->flagcarrier ) { other = teammates[1]; } else { other = teammates[0];} ClientName( other, name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); BotSayTeamOrder( bs, other ); break; } case 3: { //tell the one closest to the base not carrying the flag to defend the base if ( teammates[0] != bs->flagcarrier ) { other = teammates[0]; } else { other = teammates[1];} ClientName( other, name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); BotSayTeamOrder( bs, other ); //tell the one furthest from the base not carrying the flag to accompany the flag carrier if ( teammates[2] != bs->flagcarrier ) { other = teammates[2]; } else { other = teammates[1];} ClientName( other, name, sizeof( name ) ); ClientName( bs->flagcarrier, carriername, sizeof( carriername ) ); if ( bs->flagcarrier == bs->client ) { BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL ); } else { BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL ); } BotSayTeamOrder( bs, other ); break; } default: { //40% will defend the base defenders = (int) ( float ) numteammates * 0.4 + 0.5; //50% accompanies the flag carrier attackers = (int) ( float ) numteammates * 0.5 + 0.5; for ( i = 0; i < defenders; i++ ) { // if ( teammates[i] == bs->flagcarrier ) { continue; } ClientName( teammates[i], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); BotSayTeamOrder( bs, teammates[i] ); } ClientName( bs->flagcarrier, carriername, sizeof( carriername ) ); for ( i = 0; i < attackers; i++ ) { // if ( teammates[numteammates - i - 1] == bs->flagcarrier ) { continue; } // ClientName( teammates[numteammates - i - 1], name, sizeof( name ) ); if ( bs->flagcarrier == bs->client ) { BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL ); } else { BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL ); } BotSayTeamOrder( bs, teammates[numteammates - i - 1] ); } // break; } } } /* ================== BotCTFOrders ================== */ void BotCTFOrders_BothFlagsAtBase( bot_state_t *bs ) { int numteammates, defenders, attackers, i; int teammates[MAX_CLIENTS]; char name[MAX_NETNAME]; // char buf[MAX_MESSAGE_SIZE]; numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) ); //different orders based on the number of team mates switch ( numteammates ) { case 1: break; case 2: { //the one closest to the base will defend the base ClientName( teammates[0], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); BotSayTeamOrder( bs, teammates[0] ); //the other will get the flag ClientName( teammates[1], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); BotSayTeamOrder( bs, teammates[1] ); break; } case 3: { //the one closest to the base will defend the base ClientName( teammates[0], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); BotSayTeamOrder( bs, teammates[0] ); //the second one closest to the base will defend the base ClientName( teammates[1], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); BotSayTeamOrder( bs, teammates[1] ); //the other will get the flag ClientName( teammates[2], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); BotSayTeamOrder( bs, teammates[2] ); break; } default: { defenders = (int) ( float ) numteammates * 0.5 + 0.5; attackers = (int) ( float ) numteammates * 0.3 + 0.5; for ( i = 0; i < defenders; i++ ) { // ClientName( teammates[i], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); BotSayTeamOrder( bs, teammates[i] ); } for ( i = 0; i < attackers; i++ ) { // ClientName( teammates[numteammates - i - 1], name, sizeof( name ) ); BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); BotSayTeamOrder( bs, teammates[numteammates - i - 1] ); } // break; } } } /* ================== BotTeamOrders ================== */ void BotTeamOrders( bot_state_t *bs ) { //no teamplay orders at this time } /* ================== BotTeamAI ================== */ void BotTeamAI( bot_state_t *bs ) { int numteammates, flagstatus; char netname[MAX_NETNAME]; // if ( gametype != GT_TEAM && gametype != GT_CTF ) { return; } //make sure we've got a valid team leader if ( !BotValidTeamLeader( bs ) ) { // if ( !bs->askteamleader_time && !bs->becometeamleader_time ) { if ( bs->entergame_time + 10 > trap_AAS_Time() ) { bs->askteamleader_time = trap_AAS_Time() + 5 + random() * 10; } else { bs->becometeamleader_time = trap_AAS_Time() + 5 + random() * 10; } } if ( bs->askteamleader_time && bs->askteamleader_time < trap_AAS_Time() ) { //if asked for a team leader and no repsonse BotAI_BotInitialChat( bs, "whoisteamleader", NULL ); trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); bs->askteamleader_time = 0; bs->becometeamleader_time = trap_AAS_Time() + 15 + random() * 10; } if ( bs->becometeamleader_time && bs->becometeamleader_time < trap_AAS_Time() ) { BotAI_BotInitialChat( bs, "iamteamleader", NULL ); trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); ClientName( bs->client, netname, sizeof( netname ) ); strncpy( bs->teamleader, netname, sizeof( bs->teamleader ) ); bs->teamleader[sizeof( bs->teamleader )] = '\0'; bs->becometeamleader_time = 0; } return; } bs->askteamleader_time = 0; bs->becometeamleader_time = 0; //return if this bot is NOT the team leader ClientName( bs->client, netname, sizeof( netname ) ); if ( Q_stricmp( netname, bs->teamleader ) != 0 ) { return; } // //if the game starts OR a new player comes onto the team OR a player leaves the team // numteammates = BotNumTeamMates( bs ); //give orders switch ( gametype ) { case GT_TEAM: { if ( bs->numteammates != numteammates || bs->forceorders ) { bs->teamgiveorders_time = trap_AAS_Time(); bs->numteammates = numteammates; bs->forceorders = qfalse; } //if it's time to give orders if ( bs->teamgiveorders_time < trap_AAS_Time() - 5 ) { BotTeamOrders( bs ); // bs->teamgiveorders_time = 0; } break; } case GT_CTF: { // if ( bs->numteammates != numteammates || bs->flagstatuschanged || bs->forceorders ) { bs->teamgiveorders_time = trap_AAS_Time(); bs->numteammates = numteammates; bs->flagstatuschanged = qfalse; bs->forceorders = qfalse; } //if it's time to give orders if ( bs->teamgiveorders_time && bs->teamgiveorders_time < trap_AAS_Time() - 3 ) { // if ( BotCTFTeam( bs ) == CTF_TEAM_RED ) { flagstatus = bs->redflagstatus * 2 + bs->blueflagstatus; } else { flagstatus = bs->blueflagstatus * 2 + bs->redflagstatus;} // switch ( flagstatus ) { case 0: BotCTFOrders_BothFlagsAtBase( bs ); break; case 1: BotCTFOrders_EnemyFlagNotAtBase( bs ); break; case 2: BotCTFOrders_FlagNotAtBase( bs ); break; case 3: BotCTFOrders_BothFlagsNotAtBase( bs ); break; } // bs->teamgiveorders_time = 0; } break; } } }