1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (RTCW SP Source Code).
8
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW SP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW SP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 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.
23
24 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.
25
26 ===========================================================================
27 */
28
29
30 /*****************************************************************************
31 * name: ai_team.c
32 *
33 * desc: Quake3 bot AI
34 *
35 *
36 *****************************************************************************/
37
38 #include "g_local.h"
39 #include "../botlib/botlib.h"
40 #include "../botlib/be_aas.h"
41 #include "../botlib/be_ea.h"
42 #include "../botlib/be_ai_char.h"
43 #include "../botlib/be_ai_chat.h"
44 #include "../botlib/be_ai_gen.h"
45 #include "../botlib/be_ai_goal.h"
46 #include "../botlib/be_ai_move.h"
47 #include "../botlib/be_ai_weap.h"
48 #include "../botlib/botai.h"
49 //
50 #include "ai_main.h"
51 #include "ai_dmq3.h"
52 #include "ai_chat.h"
53 #include "ai_cmd.h"
54 #include "ai_dmnet.h"
55
56
57 /*
58 ==================
59 BotValidTeamLeader
60 ==================
61 */
BotValidTeamLeader(bot_state_t * bs)62 int BotValidTeamLeader( bot_state_t *bs ) {
63 if ( !strlen( bs->teamleader ) ) {
64 return qfalse;
65 }
66 if ( ClientFromName( bs->teamleader ) == -1 ) {
67 return qfalse;
68 }
69 return qtrue;
70 }
71
72 /*
73 ==================
74 BotNumTeamMates
75 ==================
76 */
BotNumTeamMates(bot_state_t * bs)77 int BotNumTeamMates( bot_state_t *bs ) {
78 int i, numplayers;
79 char buf[MAX_INFO_STRING];
80
81 numplayers = 0;
82 for ( i = 0; i < level.maxclients; i++ ) {
83 trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) );
84 //if no config string or no name
85 if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) {
86 continue;
87 }
88 //skip spectators
89 if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) {
90 continue;
91 }
92 //
93 if ( BotSameTeam( bs, i ) ) {
94 numplayers++;
95 }
96 }
97 return numplayers;
98 }
99
100 /*
101 ==================
102 BotClientTravelTimeToGoal
103 ==================
104 */
BotClientTravelTimeToGoal(int client,bot_goal_t * goal)105 int BotClientTravelTimeToGoal( int client, bot_goal_t *goal ) {
106 playerState_t ps;
107 int areanum;
108
109 if ( BotAI_GetClientState( client, &ps ) ) {
110 areanum = BotPointAreaNum( ps.origin );
111 } else {
112 areanum = 0;
113 }
114
115 if ( !areanum ) {
116 return 1;
117 }
118 return trap_AAS_AreaTravelTimeToGoalArea( areanum, ps.origin, goal->areanum, TFL_DEFAULT );
119 }
120
121 /*
122 ==================
123 BotSortTeamMatesByBaseTravelTime
124 ==================
125 */
BotSortTeamMatesByBaseTravelTime(bot_state_t * bs,int * teammates,int maxteammates)126 int BotSortTeamMatesByBaseTravelTime( bot_state_t *bs, int *teammates, int maxteammates ) {
127
128 int i, j, k, numteammates, traveltime;
129 char buf[MAX_INFO_STRING];
130 int traveltimes[MAX_CLIENTS];
131 bot_goal_t *goal;
132
133 if ( BotCTFTeam( bs ) == CTF_TEAM_RED ) {
134 goal = &ctf_redflag;
135 } else { goal = &ctf_blueflag;}
136
137 numteammates = 0;
138 for ( i = 0; i < level.maxclients; i++ ) {
139 trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) );
140 //if no config string or no name
141 if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) {
142 continue;
143 }
144 //skip spectators
145 if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) {
146 continue;
147 }
148 //
149 if ( BotSameTeam( bs, i ) && goal ) {
150 //
151 traveltime = BotClientTravelTimeToGoal( i, goal );
152 //
153 for ( j = 0; j < numteammates; j++ ) {
154 if ( traveltime < traveltimes[j] ) {
155 for ( k = numteammates; k > j; k-- ) {
156 traveltimes[k] = traveltimes[k - 1];
157 teammates[k] = teammates[k - 1];
158 }
159 traveltimes[j] = traveltime;
160 teammates[j] = i;
161 break;
162 }
163 }
164 if ( j >= numteammates ) {
165 traveltimes[j] = traveltime;
166 teammates[j] = i;
167 }
168 numteammates++;
169 if ( numteammates >= maxteammates ) {
170 break;
171 }
172 }
173 }
174 return numteammates;
175 }
176
177 /*
178 ==================
179 BotSayTeamOrders
180 ==================
181 */
BotSayTeamOrder(bot_state_t * bs,int toclient)182 void BotSayTeamOrder( bot_state_t *bs, int toclient ) {
183 char teamchat[MAX_MESSAGE_SIZE];
184 char buf[MAX_MESSAGE_SIZE];
185 char name[MAX_NETNAME];
186
187 //if the bot is talking to itself
188 if ( bs->client == toclient ) {
189 //don't show the message just put it in the console message queue
190 trap_BotGetChatMessage( bs->cs, buf, sizeof( buf ) );
191 ClientName( bs->client, name, sizeof( name ) );
192 Com_sprintf( teamchat, sizeof( teamchat ), "(%s): %s", name, buf );
193 trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, teamchat );
194 } else {
195 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
196 }
197 }
198
199 /*
200 ==================
201 BotCTFOrders
202 ==================
203 */
BotCTFOrders_BothFlagsNotAtBase(bot_state_t * bs)204 void BotCTFOrders_BothFlagsNotAtBase( bot_state_t *bs ) {
205 int numteammates, defenders, attackers, i, other;
206 int teammates[MAX_CLIENTS] = {0};
207 char name[MAX_NETNAME], carriername[MAX_NETNAME];
208
209 numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) );
210 //different orders based on the number of team mates
211 switch ( bs->numteammates ) {
212 case 1: break;
213 case 2:
214 {
215 //tell the one not carrying the flag to attack the enemy base
216 if ( teammates[0] != bs->flagcarrier ) {
217 other = teammates[0];
218 } else { other = teammates[1];}
219 ClientName( other, name, sizeof( name ) );
220 BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL );
221 BotSayTeamOrder( bs, other );
222 break;
223 }
224 case 3:
225 {
226 //tell the one closest to the base not carrying the flag to accompany the flag carrier
227 if ( teammates[0] != bs->flagcarrier ) {
228 other = teammates[0];
229 } else { other = teammates[1];}
230 ClientName( other, name, sizeof( name ) );
231 ClientName( bs->flagcarrier, carriername, sizeof( carriername ) );
232 if ( bs->flagcarrier == bs->client ) {
233 BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL );
234 } else {
235 BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL );
236 }
237 BotSayTeamOrder( bs, other );
238 //tell the one furthest from the the base not carrying the flag to get the enemy flag
239 if ( teammates[2] != bs->flagcarrier ) {
240 other = teammates[2];
241 } else { other = teammates[1];}
242 ClientName( other, name, sizeof( name ) );
243 BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL );
244 BotSayTeamOrder( bs, other );
245 break;
246 }
247 default:
248 {
249 defenders = (int) ( float ) numteammates * 0.4 + 0.5;
250 attackers = (int) ( float ) numteammates * 0.5 + 0.5;
251 ClientName( bs->flagcarrier, carriername, sizeof( carriername ) );
252 for ( i = 0; i < defenders; i++ ) {
253 //
254 if ( teammates[i] == bs->flagcarrier ) {
255 continue;
256 }
257 //
258 ClientName( teammates[i], name, sizeof( name ) );
259 if ( bs->flagcarrier == bs->client ) {
260 BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL );
261 } else {
262 BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL );
263 }
264 BotSayTeamOrder( bs, teammates[i] );
265 }
266 for ( i = 0; i < attackers; i++ ) {
267 //
268 if ( teammates[numteammates - i - 1] == bs->flagcarrier ) {
269 continue;
270 }
271 //
272 ClientName( teammates[numteammates - i - 1], name, sizeof( name ) );
273 BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL );
274 BotSayTeamOrder( bs, teammates[numteammates - i - 1] );
275 }
276 //
277 break;
278 }
279 }
280 }
281
282 /*
283 ==================
284 BotCTFOrders
285 ==================
286 */
BotCTFOrders_FlagNotAtBase(bot_state_t * bs)287 void BotCTFOrders_FlagNotAtBase( bot_state_t *bs ) {
288 int numteammates, defenders, attackers, i;
289 int teammates[MAX_CLIENTS];
290 char name[MAX_NETNAME];
291
292 numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) );
293 //different orders based on the number of team mates
294 switch ( bs->numteammates ) {
295 case 1: break;
296 case 2:
297 {
298 //the one closest to the base will defend the base
299 ClientName( teammates[0], name, sizeof( name ) );
300 BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL );
301 BotSayTeamOrder( bs, teammates[0] );
302 //the other will get the flag
303 ClientName( teammates[1], name, sizeof( name ) );
304 BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL );
305 BotSayTeamOrder( bs, teammates[1] );
306 break;
307 }
308 case 3:
309 {
310 //the one closest to the base will defend the base
311 ClientName( teammates[0], name, sizeof( name ) );
312 BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL );
313 BotSayTeamOrder( bs, teammates[0] );
314 //the other two get the flag
315 ClientName( teammates[1], name, sizeof( name ) );
316 BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL );
317 BotSayTeamOrder( bs, teammates[1] );
318 //
319 ClientName( teammates[2], name, sizeof( name ) );
320 BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL );
321 BotSayTeamOrder( bs, teammates[2] );
322 break;
323 }
324 default:
325 {
326 defenders = (int) ( float ) numteammates * 0.3 + 0.5;
327 attackers = (int) ( float ) numteammates * 0.5 + 0.5;
328 for ( i = 0; i < defenders; i++ ) {
329 //
330 ClientName( teammates[i], name, sizeof( name ) );
331 BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL );
332 BotSayTeamOrder( bs, teammates[i] );
333 }
334 for ( i = 0; i < attackers; i++ ) {
335 //
336 ClientName( teammates[numteammates - i - 1], name, sizeof( name ) );
337 BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL );
338 BotSayTeamOrder( bs, teammates[numteammates - i - 1] );
339 }
340 //
341 break;
342 }
343 }
344 }
345
346 /*
347 ==================
348 BotCTFOrders
349 ==================
350 */
BotCTFOrders_EnemyFlagNotAtBase(bot_state_t * bs)351 void BotCTFOrders_EnemyFlagNotAtBase( bot_state_t *bs ) {
352 int numteammates, defenders, attackers, i, other;
353 int teammates[MAX_CLIENTS];
354 char name[MAX_NETNAME], carriername[MAX_NETNAME];
355
356 numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) );
357 //different orders based on the number of team mates
358 switch ( numteammates ) {
359 case 1: break;
360 case 2:
361 {
362 //tell the one not carrying the flag to defend the base
363 if ( teammates[0] == bs->flagcarrier ) {
364 other = teammates[1];
365 } else { other = teammates[0];}
366 ClientName( other, name, sizeof( name ) );
367 BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL );
368 BotSayTeamOrder( bs, other );
369 break;
370 }
371 case 3:
372 {
373 //tell the one closest to the base not carrying the flag to defend the base
374 if ( teammates[0] != bs->flagcarrier ) {
375 other = teammates[0];
376 } else { other = teammates[1];}
377 ClientName( other, name, sizeof( name ) );
378 BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL );
379 BotSayTeamOrder( bs, other );
380 //tell the one furthest from the base not carrying the flag to accompany the flag carrier
381 if ( teammates[2] != bs->flagcarrier ) {
382 other = teammates[2];
383 } else { other = teammates[1];}
384 ClientName( other, name, sizeof( name ) );
385 ClientName( bs->flagcarrier, carriername, sizeof( carriername ) );
386 if ( bs->flagcarrier == bs->client ) {
387 BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL );
388 } else {
389 BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL );
390 }
391 BotSayTeamOrder( bs, other );
392 break;
393 }
394 default:
395 {
396 //40% will defend the base
397 defenders = (int) ( float ) numteammates * 0.4 + 0.5;
398 //50% accompanies the flag carrier
399 attackers = (int) ( float ) numteammates * 0.5 + 0.5;
400 for ( i = 0; i < defenders; i++ ) {
401 //
402 if ( teammates[i] == bs->flagcarrier ) {
403 continue;
404 }
405 ClientName( teammates[i], name, sizeof( name ) );
406 BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL );
407 BotSayTeamOrder( bs, teammates[i] );
408 }
409 ClientName( bs->flagcarrier, carriername, sizeof( carriername ) );
410 for ( i = 0; i < attackers; i++ ) {
411 //
412 if ( teammates[numteammates - i - 1] == bs->flagcarrier ) {
413 continue;
414 }
415 //
416 ClientName( teammates[numteammates - i - 1], name, sizeof( name ) );
417 if ( bs->flagcarrier == bs->client ) {
418 BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL );
419 } else {
420 BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL );
421 }
422 BotSayTeamOrder( bs, teammates[numteammates - i - 1] );
423 }
424 //
425 break;
426 }
427 }
428 }
429
430
431 /*
432 ==================
433 BotCTFOrders
434 ==================
435 */
BotCTFOrders_BothFlagsAtBase(bot_state_t * bs)436 void BotCTFOrders_BothFlagsAtBase( bot_state_t *bs ) {
437 int numteammates, defenders, attackers, i;
438 int teammates[MAX_CLIENTS] = {0};
439 char name[MAX_NETNAME];
440 // char buf[MAX_MESSAGE_SIZE];
441
442 numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) );
443 //different orders based on the number of team mates
444 switch ( numteammates ) {
445 case 1: break;
446 case 2:
447 {
448 //the one closest to the base will defend the base
449 ClientName( teammates[0], name, sizeof( name ) );
450 BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL );
451 BotSayTeamOrder( bs, teammates[0] );
452 //the other will get the flag
453 ClientName( teammates[1], name, sizeof( name ) );
454 BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL );
455 BotSayTeamOrder( bs, teammates[1] );
456 break;
457 }
458 case 3:
459 {
460 //the one closest to the base will defend the base
461 ClientName( teammates[0], name, sizeof( name ) );
462 BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL );
463 BotSayTeamOrder( bs, teammates[0] );
464 //the second one closest to the base will defend the base
465 ClientName( teammates[1], name, sizeof( name ) );
466 BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL );
467 BotSayTeamOrder( bs, teammates[1] );
468 //the other will get the flag
469 ClientName( teammates[2], name, sizeof( name ) );
470 BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL );
471 BotSayTeamOrder( bs, teammates[2] );
472 break;
473 }
474 default:
475 {
476 defenders = (int) ( float ) numteammates * 0.5 + 0.5;
477 attackers = (int) ( float ) numteammates * 0.3 + 0.5;
478 for ( i = 0; i < defenders; i++ ) {
479 //
480 ClientName( teammates[i], name, sizeof( name ) );
481 BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL );
482 BotSayTeamOrder( bs, teammates[i] );
483 }
484 for ( i = 0; i < attackers; i++ ) {
485 //
486 ClientName( teammates[numteammates - i - 1], name, sizeof( name ) );
487 BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL );
488 BotSayTeamOrder( bs, teammates[numteammates - i - 1] );
489 }
490 //
491 break;
492 }
493 }
494 }
495
496
497 /*
498 ==================
499 BotTeamOrders
500 ==================
501 */
BotTeamOrders(bot_state_t * bs)502 void BotTeamOrders( bot_state_t *bs ) {
503 //no teamplay orders at this time
504 }
505
506
507 /*
508 ==================
509 BotTeamAI
510 ==================
511 */
BotTeamAI(bot_state_t * bs)512 void BotTeamAI( bot_state_t *bs ) {
513 int numteammates, flagstatus;
514 char netname[MAX_NETNAME];
515
516 //
517 if ( gametype != GT_TEAM && gametype != GT_CTF ) {
518 return;
519 }
520 //make sure we've got a valid team leader
521 if ( !BotValidTeamLeader( bs ) ) {
522 //
523 if ( !bs->askteamleader_time && !bs->becometeamleader_time ) {
524 if ( bs->entergame_time + 10 > trap_AAS_Time() ) {
525 bs->askteamleader_time = trap_AAS_Time() + 5 + random() * 10;
526 } else {
527 bs->becometeamleader_time = trap_AAS_Time() + 5 + random() * 10;
528 }
529 }
530 if ( bs->askteamleader_time && bs->askteamleader_time < trap_AAS_Time() ) {
531 //if asked for a team leader and no repsonse
532 BotAI_BotInitialChat( bs, "whoisteamleader", NULL );
533 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
534 bs->askteamleader_time = 0;
535 bs->becometeamleader_time = trap_AAS_Time() + 15 + random() * 10;
536 }
537 if ( bs->becometeamleader_time && bs->becometeamleader_time < trap_AAS_Time() ) {
538 BotAI_BotInitialChat( bs, "iamteamleader", NULL );
539 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
540 ClientName( bs->client, netname, sizeof( netname ) );
541 strncpy( bs->teamleader, netname, sizeof( bs->teamleader ) );
542 bs->teamleader[sizeof( bs->teamleader )-1] = '\0';
543 bs->becometeamleader_time = 0;
544 }
545 return;
546 }
547 bs->askteamleader_time = 0;
548 bs->becometeamleader_time = 0;
549
550 //return if this bot is NOT the team leader
551 ClientName( bs->client, netname, sizeof( netname ) );
552 if ( Q_stricmp( netname, bs->teamleader ) != 0 ) {
553 return;
554 }
555 //
556 //if the game starts OR a new player comes onto the team OR a player leaves the team
557 //
558 numteammates = BotNumTeamMates( bs );
559 //give orders
560 switch ( gametype ) {
561 case GT_TEAM:
562 {
563 if ( bs->numteammates != numteammates || bs->forceorders ) {
564 bs->teamgiveorders_time = trap_AAS_Time();
565 bs->numteammates = numteammates;
566 bs->forceorders = qfalse;
567 }
568 //if it's time to give orders
569 if ( bs->teamgiveorders_time < trap_AAS_Time() - 5 ) {
570 BotTeamOrders( bs );
571 //
572 bs->teamgiveorders_time = 0;
573 }
574 break;
575 }
576 case GT_CTF:
577 {
578 //
579 if ( bs->numteammates != numteammates || bs->flagstatuschanged || bs->forceorders ) {
580 bs->teamgiveorders_time = trap_AAS_Time();
581 bs->numteammates = numteammates;
582 bs->flagstatuschanged = qfalse;
583 bs->forceorders = qfalse;
584 }
585 //if it's time to give orders
586 if ( bs->teamgiveorders_time && bs->teamgiveorders_time < trap_AAS_Time() - 3 ) {
587 //
588 if ( BotCTFTeam( bs ) == CTF_TEAM_RED ) {
589 flagstatus = bs->redflagstatus * 2 + bs->blueflagstatus;
590 } else { flagstatus = bs->blueflagstatus * 2 + bs->redflagstatus;}
591 //
592 switch ( flagstatus ) {
593 case 0: BotCTFOrders_BothFlagsAtBase( bs ); break;
594 case 1: BotCTFOrders_EnemyFlagNotAtBase( bs ); break;
595 case 2: BotCTFOrders_FlagNotAtBase( bs ); break;
596 case 3: BotCTFOrders_BothFlagsNotAtBase( bs ); break;
597 }
598 //
599 bs->teamgiveorders_time = 0;
600 }
601 break;
602 }
603 }
604 }
605
606
607