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_cmd.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 #include "chars.h" //characteristics
57 #include "inv.h" //indexes into the inventory
58 #include "syn.h" //synonyms
59 #include "match.h" //string matching types and vars
60
61
62 #ifdef DEBUG
63 /*
64 ==================
65 BotPrintTeamGoal
66 ==================
67 */
BotPrintTeamGoal(bot_state_t * bs)68 void BotPrintTeamGoal( bot_state_t *bs ) {
69 char netname[MAX_NETNAME];
70 float t;
71
72 ClientName( bs->client, netname, sizeof( netname ) );
73 t = bs->teamgoal_time - trap_AAS_Time();
74 switch ( bs->ltgtype ) {
75 case LTG_TEAMHELP:
76 {
77 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna help a team mate for %1.0f secs\n", netname, t );
78 break;
79 }
80 case LTG_TEAMACCOMPANY:
81 {
82 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna accompany a team mate for %1.0f secs\n", netname, t );
83 break;
84 }
85 case LTG_GETFLAG:
86 {
87 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna get the flag for %1.0f secs\n", netname, t );
88 break;
89 }
90 case LTG_RUSHBASE:
91 {
92 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna rush to the base for %1.0f secs\n", netname, t );
93 break;
94 }
95 case LTG_RETURNFLAG:
96 {
97 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna try to return the flag for %1.0f secs\n", netname, t );
98 break;
99 }
100 case LTG_DEFENDKEYAREA:
101 {
102 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna defend a key area for %1.0f secs\n", netname, t );
103 break;
104 }
105 case LTG_GETITEM:
106 {
107 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna get an item for %1.0f secs\n", netname, t );
108 break;
109 }
110 case LTG_KILL:
111 {
112 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna kill someone for %1.0f secs\n", netname, t );
113 break;
114 }
115 case LTG_CAMP:
116 case LTG_CAMPORDER:
117 {
118 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna camp for %1.0f secs\n", netname, t );
119 break;
120 }
121 case LTG_PATROL:
122 {
123 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna patrol for %1.0f secs\n", netname, t );
124 break;
125 }
126 default:
127 {
128 if ( bs->ctfroam_time > trap_AAS_Time() ) {
129 t = bs->ctfroam_time - trap_AAS_Time();
130 BotAI_Print( PRT_MESSAGE, "%s: I'm gonna roam for %1.0f secs\n", netname, t );
131 } else {
132 BotAI_Print( PRT_MESSAGE, "%s: I've got a regular goal\n", netname );
133 }
134 }
135 }
136 }
137 #endif //DEBUG
138
139 /*
140 ==================
141 BotGetItemTeamGoal
142
143 FIXME: add stuff like "upper rocket launcher"
144 "the rl near the railgun", "lower grenade launcher" etc.
145 ==================
146 */
BotGetItemTeamGoal(char * goalname,bot_goal_t * goal)147 int BotGetItemTeamGoal( char *goalname, bot_goal_t *goal ) {
148 int i;
149
150 if ( !strlen( goalname ) ) {
151 return qfalse;
152 }
153 i = -1;
154 do {
155 i = trap_BotGetLevelItemGoal( i, goalname, goal );
156 if ( i > 0 ) { // && !AvoidGoalTime(&bs->gs, goal.number))
157 return qtrue;
158 }
159 } while ( i > 0 );
160 return qfalse;
161 }
162
163 /*
164 ==================
165 BotGetMessageTeamGoal
166 ==================
167 */
BotGetMessageTeamGoal(bot_state_t * bs,char * goalname,bot_goal_t * goal)168 int BotGetMessageTeamGoal( bot_state_t *bs, char *goalname, bot_goal_t *goal ) {
169 bot_waypoint_t *cp;
170
171 if ( BotGetItemTeamGoal( goalname, goal ) ) {
172 return qtrue;
173 }
174
175 cp = BotFindWayPoint( bs->checkpoints, goalname );
176 if ( cp ) {
177 memcpy( goal, &cp->goal, sizeof( bot_goal_t ) );
178 return qtrue;
179 }
180 return qfalse;
181 }
182
183 /*
184 ==================
185 BotGetTime
186 ==================
187 */
BotGetTime(bot_match_t * match)188 float BotGetTime( bot_match_t *match ) {
189 bot_match_t timematch;
190 char timestring[MAX_MESSAGE_SIZE];
191 float t;
192
193 //if the matched string has a time
194 if ( match->subtype & ST_TIME ) {
195 //get the time string
196 trap_BotMatchVariable( match, TIME, timestring, MAX_MESSAGE_SIZE );
197 //match it to find out if the time is in seconds or minutes
198 if ( trap_BotFindMatch( timestring, &timematch, MTCONTEXT_TIME ) ) {
199 if ( timematch.type == MSG_FOREVER ) {
200 t = 99999999;
201 } else {
202 trap_BotMatchVariable( &timematch, TIME, timestring, MAX_MESSAGE_SIZE );
203 if ( timematch.type == MSG_MINUTES ) {
204 t = atof( timestring ) * 60;
205 } else if ( timematch.type == MSG_SECONDS ) {
206 t = atof( timestring );
207 } else { t = 0;}
208 }
209 //if there's a valid time
210 if ( t > 0 ) {
211 return trap_AAS_Time() + t;
212 }
213 }
214 }
215 return 0;
216 }
217
218 /*
219 ==================
220 FindClientByName
221 ==================
222 */
FindClientByName(char * name)223 int FindClientByName( char *name ) {
224 int i;
225 char buf[MAX_INFO_STRING];
226
227 for ( i = 0; i < level.maxclients; i++ ) {
228 ClientName( i, buf, sizeof( buf ) );
229 if ( !Q_stricmp( buf, name ) ) {
230 return i;
231 }
232 }
233 for ( i = 0; i < level.maxclients; i++ ) {
234 ClientName( i, buf, sizeof( buf ) );
235 if ( stristr( buf, name ) ) {
236 return i;
237 }
238 }
239 return -1;
240 }
241
242 /*
243 ==================
244 FindEnemyByName
245 ==================
246 */
FindEnemyByName(bot_state_t * bs,char * name)247 int FindEnemyByName( bot_state_t *bs, char *name ) {
248 int i;
249 char buf[MAX_INFO_STRING];
250
251 for ( i = 0; i < level.maxclients; i++ ) {
252 if ( BotSameTeam( bs, i ) ) {
253 continue;
254 }
255 ClientName( i, buf, sizeof( buf ) );
256 if ( !Q_stricmp( buf, name ) ) {
257 return i;
258 }
259 }
260 for ( i = 0; i < level.maxclients; i++ ) {
261 if ( BotSameTeam( bs, i ) ) {
262 continue;
263 }
264 ClientName( i, buf, sizeof( buf ) );
265 if ( stristr( buf, name ) ) {
266 return i;
267 }
268 }
269 return -1;
270 }
271
272 /*
273 ==================
274 NumPlayersOnSameTeam
275 ==================
276 */
NumPlayersOnSameTeam(bot_state_t * bs)277 int NumPlayersOnSameTeam( bot_state_t *bs ) {
278 int i, num;
279 char buf[MAX_INFO_STRING];
280
281 num = 0;
282 for ( i = 0; i < level.maxclients; i++ ) {
283 trap_GetConfigstring( CS_PLAYERS + i, buf, MAX_INFO_STRING );
284 if ( strlen( buf ) ) {
285 if ( BotSameTeam( bs, i + 1 ) ) {
286 num++;
287 }
288 }
289 }
290 return num;
291 }
292
293 /*
294 ==================
295 TeamPlayIsOn
296 ==================
297 */
BotGetPatrolWaypoints(bot_state_t * bs,bot_match_t * match)298 int BotGetPatrolWaypoints( bot_state_t *bs, bot_match_t *match ) {
299 char keyarea[MAX_MESSAGE_SIZE];
300 int patrolflags;
301 bot_waypoint_t *wp, *newwp, *newpatrolpoints;
302 bot_match_t keyareamatch;
303 bot_goal_t goal;
304
305 newpatrolpoints = NULL;
306 patrolflags = 0;
307 //
308 trap_BotMatchVariable( match, KEYAREA, keyarea, MAX_MESSAGE_SIZE );
309 //
310 while ( 1 ) {
311 if ( !trap_BotFindMatch( keyarea, &keyareamatch, MTCONTEXT_PATROLKEYAREA ) ) {
312 trap_EA_SayTeam( bs->client, "what do you say?" );
313 BotFreeWaypoints( newpatrolpoints );
314 bs->patrolpoints = NULL;
315 return qfalse;
316 }
317 trap_BotMatchVariable( &keyareamatch, KEYAREA, keyarea, MAX_MESSAGE_SIZE );
318 if ( !BotGetMessageTeamGoal( bs, keyarea, &goal ) ) {
319 //BotAI_BotInitialChat(bs, "cannotfind", keyarea, NULL);
320 //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
321 BotFreeWaypoints( newpatrolpoints );
322 bs->patrolpoints = NULL;
323 return qfalse;
324 }
325 //create a new waypoint
326 newwp = BotCreateWayPoint( keyarea, goal.origin, goal.areanum );
327 //add the waypoint to the patrol points
328 newwp->next = NULL;
329 for ( wp = newpatrolpoints; wp && wp->next; wp = wp->next ) ;
330 if ( !wp ) {
331 newpatrolpoints = newwp;
332 newwp->prev = NULL;
333 } else {
334 wp->next = newwp;
335 newwp->prev = wp;
336 }
337 //
338 if ( keyareamatch.subtype & ST_BACK ) {
339 patrolflags = PATROL_LOOP;
340 break;
341 } else if ( keyareamatch.subtype & ST_REVERSE ) {
342 patrolflags = PATROL_REVERSE;
343 break;
344 } else if ( keyareamatch.subtype & ST_MORE ) {
345 trap_BotMatchVariable( &keyareamatch, MORE, keyarea, MAX_MESSAGE_SIZE );
346 } else {
347 break;
348 }
349 }
350 //
351 if ( !newpatrolpoints || !newpatrolpoints->next ) {
352 trap_EA_SayTeam( bs->client, "I need more key points to patrol\n" );
353 BotFreeWaypoints( newpatrolpoints );
354 newpatrolpoints = NULL;
355 return qfalse;
356 }
357 //
358 BotFreeWaypoints( bs->patrolpoints );
359 bs->patrolpoints = newpatrolpoints;
360 //
361 bs->curpatrolpoint = bs->patrolpoints;
362 bs->patrolflags = patrolflags;
363 //
364 return qtrue;
365 }
366
367 /*
368 ==================
369 BotAddressedToBot
370 ==================
371 */
BotAddressedToBot(bot_state_t * bs,bot_match_t * match)372 int BotAddressedToBot( bot_state_t *bs, bot_match_t *match ) {
373 char addressedto[MAX_MESSAGE_SIZE];
374 char netname[MAX_MESSAGE_SIZE];
375 char name[MAX_MESSAGE_SIZE];
376 char botname[128];
377 int client;
378 bot_match_t addresseematch;
379
380 trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
381 client = ClientFromName( netname );
382 if ( client < 0 ) {
383 return qfalse;
384 }
385 if ( !BotSameTeam( bs, client ) ) {
386 return qfalse;
387 }
388 //if the message is addressed to someone
389 if ( match->subtype & ST_ADDRESSED ) {
390 trap_BotMatchVariable( match, ADDRESSEE, addressedto, sizeof( addressedto ) );
391 //the name of this bot
392 ClientName( bs->client, botname, 128 );
393 //
394 while ( trap_BotFindMatch( addressedto, &addresseematch, MTCONTEXT_ADDRESSEE ) ) {
395 if ( addresseematch.type == MSG_EVERYONE ) {
396 return qtrue;
397 } else if ( addresseematch.type == MSG_MULTIPLENAMES ) {
398 trap_BotMatchVariable( &addresseematch, TEAMMATE, name, sizeof( name ) );
399 if ( strlen( name ) ) {
400 if ( stristr( botname, name ) ) {
401 return qtrue;
402 }
403 if ( stristr( bs->subteam, name ) ) {
404 return qtrue;
405 }
406 }
407 trap_BotMatchVariable( &addresseematch, MORE, addressedto, MAX_MESSAGE_SIZE );
408 } else {
409 trap_BotMatchVariable( &addresseematch, TEAMMATE, name, MAX_MESSAGE_SIZE );
410 if ( strlen( name ) ) {
411 if ( stristr( botname, name ) ) {
412 return qtrue;
413 }
414 if ( stristr( bs->subteam, name ) ) {
415 return qtrue;
416 }
417 }
418 break;
419 }
420 }
421 //Com_sprintf(buf, sizeof(buf), "not addressed to me but %s", addressedto);
422 //trap_EA_Say(bs->client, buf);
423 return qfalse;
424 } else {
425 //make sure not everyone reacts to this message
426 if ( random() > (float ) 1.0 / ( NumPlayersOnSameTeam( bs ) - 1 ) ) {
427 return qfalse;
428 }
429 }
430 return qtrue;
431 }
432
433 /*
434 ==================
435 BotGPSToPosition
436 ==================
437 */
BotGPSToPosition(char * buf,vec3_t position)438 int BotGPSToPosition( char *buf, vec3_t position ) {
439 int i, j = 0;
440 int num, sign;
441
442 for ( i = 0; i < 3; i++ ) {
443 num = 0;
444 while ( buf[j] == ' ' ) j++;
445 if ( buf[j] == '-' ) {
446 j++;
447 sign = -1;
448 } else {
449 sign = 1;
450 }
451 while ( buf[j] ) {
452 if ( buf[j] >= '0' && buf[j] <= '9' ) {
453 num = num * 10 + buf[j] - '0';
454 j++;
455 } else {
456 j++;
457 break;
458 }
459 }
460 BotAI_Print( PRT_MESSAGE, "%d\n", sign * num );
461 position[i] = (float) sign * num;
462 }
463 return qtrue;
464 }
465
466 /*
467 ==================
468 BotMatch_HelpAccompany
469 ==================
470 */
BotMatch_HelpAccompany(bot_state_t * bs,bot_match_t * match)471 void BotMatch_HelpAccompany( bot_state_t *bs, bot_match_t *match ) {
472 int client, other, areanum;
473 char teammate[MAX_MESSAGE_SIZE], netname[MAX_MESSAGE_SIZE];
474 char itemname[MAX_MESSAGE_SIZE];
475 bot_match_t teammatematch;
476 aas_entityinfo_t entinfo;
477
478 if ( !TeamPlayIsOn() ) {
479 return;
480 }
481 //if not addressed to this bot
482 if ( !BotAddressedToBot( bs, match ) ) {
483 return;
484 }
485 //get the team mate name
486 trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) );
487 //get the client to help
488 if ( trap_BotFindMatch( teammate, &teammatematch, MTCONTEXT_TEAMMATE ) &&
489 //if someone asks for him or herself
490 teammatematch.type == MSG_ME ) {
491 //get the netname
492 trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
493 client = ClientFromName( netname );
494 other = qfalse;
495 } else {
496 //asked for someone else
497 client = FindClientByName( teammate );
498 //if this is the bot self
499 if ( client == bs->client ) {
500 other = qfalse;
501 } else if ( !BotSameTeam( bs, client ) ) {
502 //FIXME: say "I don't help the enemy"
503 return;
504 } else {
505 other = qtrue;
506 }
507 }
508 //if the bot doesn't know who to help (FindClientByName returned -1)
509 if ( client < 0 ) {
510 if ( other ) {
511 BotAI_BotInitialChat( bs, "whois", teammate, NULL );
512 } else { BotAI_BotInitialChat( bs, "whois", netname, NULL );}
513 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
514 return;
515 }
516 //don't help or accompany yourself
517 if ( client == bs->client ) {
518 return;
519 }
520 //
521 bs->teamgoal.entitynum = -1;
522 BotEntityInfo( client, &entinfo );
523 //if info is valid (in PVS)
524 if ( entinfo.valid ) {
525 areanum = BotPointAreaNum( entinfo.origin );
526 if ( areanum && trap_AAS_AreaReachability( areanum ) ) {
527 bs->teamgoal.entitynum = client;
528 bs->teamgoal.areanum = areanum;
529 VectorCopy( entinfo.origin, bs->teamgoal.origin );
530 VectorSet( bs->teamgoal.mins, -8, -8, -8 );
531 VectorSet( bs->teamgoal.maxs, 8, 8, 8 );
532 }
533 }
534 //if no teamgoal yet
535 if ( bs->teamgoal.entitynum < 0 ) {
536 //if near an item
537 if ( match->subtype & ST_NEARITEM ) {
538 //get the match variable
539 trap_BotMatchVariable( match, ITEM, itemname, sizeof( itemname ) );
540 //
541 if ( !BotGetMessageTeamGoal( bs, itemname, &bs->teamgoal ) ) {
542 //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
543 //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
544 return;
545 }
546 }
547 }
548 //
549 if ( bs->teamgoal.entitynum < 0 ) {
550 if ( other ) {
551 BotAI_BotInitialChat( bs, "whereis", teammate, NULL );
552 } else { BotAI_BotInitialChat( bs, "whereareyou", netname, NULL );}
553 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
554 return;
555 }
556 //the team mate
557 bs->teammate = client;
558 //last time the team mate was assumed visible
559 bs->teammatevisible_time = trap_AAS_Time();
560 //set the time to send a message to the team mates
561 bs->teammessage_time = trap_AAS_Time() + 2 * random();
562 //get the team goal time
563 bs->teamgoal_time = BotGetTime( match );
564 //set the ltg type
565 if ( match->type == MSG_HELP ) {
566 bs->ltgtype = LTG_TEAMHELP;
567 if ( !bs->teamgoal_time ) {
568 bs->teamgoal_time = trap_AAS_Time() + TEAM_HELP_TIME;
569 }
570 } else {
571 bs->ltgtype = LTG_TEAMACCOMPANY;
572 if ( !bs->teamgoal_time ) {
573 bs->teamgoal_time = trap_AAS_Time() + TEAM_ACCOMPANY_TIME;
574 }
575 bs->formation_dist = 3.5 * 32; //3.5 meter
576 bs->arrive_time = 0;
577 }
578 #ifdef DEBUG
579 BotPrintTeamGoal( bs );
580 #endif //DEBUG
581 }
582
583 /*
584 ==================
585 BotMatch_DefendKeyArea
586 ==================
587 */
BotMatch_DefendKeyArea(bot_state_t * bs,bot_match_t * match)588 void BotMatch_DefendKeyArea( bot_state_t *bs, bot_match_t *match ) {
589 char itemname[MAX_MESSAGE_SIZE];
590
591 if ( !TeamPlayIsOn() ) {
592 return;
593 }
594 //if not addressed to this bot
595 if ( !BotAddressedToBot( bs, match ) ) {
596 return;
597 }
598 //get the match variable
599 trap_BotMatchVariable( match, KEYAREA, itemname, sizeof( itemname ) );
600 //
601 if ( !BotGetMessageTeamGoal( bs, itemname, &bs->teamgoal ) ) {
602 //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
603 //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
604 return;
605 }
606 //set the time to send a message to the team mates
607 bs->teammessage_time = trap_AAS_Time() + 2 * random();
608 //set the ltg type
609 bs->ltgtype = LTG_DEFENDKEYAREA;
610 //get the team goal time
611 bs->teamgoal_time = BotGetTime( match );
612 //set the team goal time
613 if ( !bs->teamgoal_time ) {
614 bs->teamgoal_time = trap_AAS_Time() + TEAM_DEFENDKEYAREA_TIME;
615 }
616 //away from defending
617 bs->defendaway_time = 0;
618 #ifdef DEBUG
619 BotPrintTeamGoal( bs );
620 #endif //DEBUG
621 }
622
623 /*
624 ==================
625 BotMatch_GetItem
626 ==================
627 */
BotMatch_GetItem(bot_state_t * bs,bot_match_t * match)628 void BotMatch_GetItem( bot_state_t *bs, bot_match_t *match ) {
629 char itemname[MAX_MESSAGE_SIZE];
630
631 if ( !TeamPlayIsOn() ) {
632 return;
633 }
634 //if not addressed to this bot
635 if ( !BotAddressedToBot( bs, match ) ) {
636 return;
637 }
638 //get the match variable
639 trap_BotMatchVariable( match, ITEM, itemname, sizeof( itemname ) );
640 //
641 if ( !BotGetMessageTeamGoal( bs, itemname, &bs->teamgoal ) ) {
642 //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
643 //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
644 return;
645 }
646 //set the time to send a message to the team mates
647 bs->teammessage_time = trap_AAS_Time() + 2 * random();
648 //set the ltg type
649 bs->ltgtype = LTG_GETITEM;
650 //set the team goal time
651 bs->teamgoal_time = trap_AAS_Time() + TEAM_GETITEM_TIME;
652 #ifdef DEBUG
653 BotPrintTeamGoal( bs );
654 #endif //DEBUG
655 }
656
657 /*
658 ==================
659 BotMatch_Camp
660 ==================
661 */
BotMatch_Camp(bot_state_t * bs,bot_match_t * match)662 void BotMatch_Camp( bot_state_t *bs, bot_match_t *match ) {
663 int client, areanum;
664 char netname[MAX_MESSAGE_SIZE];
665 char itemname[MAX_MESSAGE_SIZE];
666 aas_entityinfo_t entinfo;
667
668 if ( !TeamPlayIsOn() ) {
669 return;
670 }
671 //if not addressed to this bot
672 if ( !BotAddressedToBot( bs, match ) ) {
673 return;
674 }
675 //
676 trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
677 //asked for someone else
678 client = FindClientByName( netname );
679 //if there's no valid client with this name
680 if ( client < 0 ) {
681 BotAI_BotInitialChat( bs, "whois", netname, NULL );
682 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
683 return;
684 }
685 //get the match variable
686 trap_BotMatchVariable( match, KEYAREA, itemname, sizeof( itemname ) );
687 //in CTF it could be the base
688 if ( match->subtype & ST_THERE ) {
689 //camp at the spot the bot is currently standing
690 bs->teamgoal.entitynum = bs->entitynum;
691 bs->teamgoal.areanum = bs->areanum;
692 VectorCopy( bs->origin, bs->teamgoal.origin );
693 VectorSet( bs->teamgoal.mins, -8, -8, -8 );
694 VectorSet( bs->teamgoal.maxs, 8, 8, 8 );
695 } else if ( match->subtype & ST_HERE ) {
696 //if this is the bot self
697 if ( client == bs->client ) {
698 return;
699 }
700 //
701 bs->teamgoal.entitynum = -1;
702 BotEntityInfo( client, &entinfo );
703 //if info is valid (in PVS)
704 if ( entinfo.valid ) {
705 areanum = BotPointAreaNum( entinfo.origin );
706 if ( areanum && trap_AAS_AreaReachability( areanum ) ) {
707 //NOTE: just cheat and assume the bot knows where the person is
708 //if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, client)) {
709 bs->teamgoal.entitynum = client;
710 bs->teamgoal.areanum = areanum;
711 VectorCopy( entinfo.origin, bs->teamgoal.origin );
712 VectorSet( bs->teamgoal.mins, -8, -8, -8 );
713 VectorSet( bs->teamgoal.maxs, 8, 8, 8 );
714 //}
715 }
716 }
717 //if the other is not visible
718 if ( bs->teamgoal.entitynum < 0 ) {
719 BotAI_BotInitialChat( bs, "whereareyou", netname, NULL );
720 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
721 return;
722 }
723 } else if ( !BotGetMessageTeamGoal( bs, itemname, &bs->teamgoal ) ) {
724 //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
725 //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
726 return;
727 }
728 //set the time to send a message to the team mates
729 bs->teammessage_time = trap_AAS_Time() + 2 * random();
730 //set the ltg type
731 bs->ltgtype = LTG_CAMPORDER;
732 //get the team goal time
733 bs->teamgoal_time = BotGetTime( match );
734 //set the team goal time
735 if ( !bs->teamgoal_time ) {
736 bs->teamgoal_time = trap_AAS_Time() + TEAM_CAMP_TIME;
737 }
738 //the teammate that requested the camping
739 bs->teammate = client;
740 //not arrived yet
741 bs->arrive_time = 0;
742 //
743 #ifdef DEBUG
744 BotPrintTeamGoal( bs );
745 #endif //DEBUG
746 }
747
748 /*
749 ==================
750 BotMatch_Patrol
751 ==================
752 */
BotMatch_Patrol(bot_state_t * bs,bot_match_t * match)753 void BotMatch_Patrol( bot_state_t *bs, bot_match_t *match ) {
754 if ( !TeamPlayIsOn() ) {
755 return;
756 }
757 //if not addressed to this bot
758 if ( !BotAddressedToBot( bs, match ) ) {
759 return;
760 }
761 //get the patrol waypoints
762 if ( !BotGetPatrolWaypoints( bs, match ) ) {
763 return;
764 }
765 //set the time to send a message to the team mates
766 bs->teammessage_time = trap_AAS_Time() + 2 * random();
767 //set the ltg type
768 bs->ltgtype = LTG_PATROL;
769 //get the team goal time
770 bs->teamgoal_time = BotGetTime( match );
771 //set the team goal time if not set already
772 if ( !bs->teamgoal_time ) {
773 bs->teamgoal_time = trap_AAS_Time() + TEAM_PATROL_TIME;
774 }
775 //
776 #ifdef DEBUG
777 BotPrintTeamGoal( bs );
778 #endif //DEBUG
779 }
780
781 /*
782 ==================
783 BotMatch_GetFlag
784 ==================
785 */
BotMatch_GetFlag(bot_state_t * bs,bot_match_t * match)786 void BotMatch_GetFlag( bot_state_t *bs, bot_match_t *match ) {
787 //if not in CTF mode
788 if ( gametype != GT_CTF || !ctf_redflag.areanum || !ctf_blueflag.areanum ) {
789 return;
790 }
791 //if not addressed to this bot
792 if ( !BotAddressedToBot( bs, match ) ) {
793 return;
794 }
795 //set the time to send a message to the team mates
796 bs->teammessage_time = trap_AAS_Time() + 2 * random();
797 //set the ltg type
798 bs->ltgtype = LTG_GETFLAG;
799 //set the team goal time
800 bs->teamgoal_time = trap_AAS_Time() + CTF_GETFLAG_TIME;
801 #ifdef DEBUG
802 BotPrintTeamGoal( bs );
803 #endif //DEBUG
804 }
805
806 /*
807 ==================
808 BotMatch_RushBase
809 ==================
810 */
BotMatch_RushBase(bot_state_t * bs,bot_match_t * match)811 void BotMatch_RushBase( bot_state_t *bs, bot_match_t *match ) {
812 //if not in CTF mode
813 if ( gametype != GT_CTF || !ctf_redflag.areanum || !ctf_blueflag.areanum ) {
814 return;
815 }
816 //if not addressed to this bot
817 if ( !BotAddressedToBot( bs, match ) ) {
818 return;
819 }
820 //set the time to send a message to the team mates
821 bs->teammessage_time = trap_AAS_Time() + 2 * random();
822 //set the ltg type
823 bs->ltgtype = LTG_RUSHBASE;
824 //set the team goal time
825 bs->teamgoal_time = trap_AAS_Time() + CTF_RUSHBASE_TIME;
826 bs->rushbaseaway_time = 0;
827 #ifdef DEBUG
828 BotPrintTeamGoal( bs );
829 #endif //DEBUG
830 }
831
832
833 /*
834 ==================
835 BotMatch_ReturnFlag
836 ==================
837 */
BotMatch_ReturnFlag(bot_state_t * bs,bot_match_t * match)838 void BotMatch_ReturnFlag( bot_state_t *bs, bot_match_t *match ) {
839 //if not in CTF mode
840 if ( gametype != GT_CTF ) {
841 return;
842 }
843 //if not addressed to this bot
844 if ( !BotAddressedToBot( bs, match ) ) {
845 return;
846 }
847 //set the time to send a message to the team mates
848 bs->teammessage_time = trap_AAS_Time() + 2 * random();
849 //set the ltg type
850 bs->ltgtype = LTG_RETURNFLAG;
851 //set the team goal time
852 bs->teamgoal_time = trap_AAS_Time() + CTF_RETURNFLAG_TIME;
853 bs->rushbaseaway_time = 0;
854 #ifdef DEBUG
855 BotPrintTeamGoal( bs );
856 #endif //DEBUG
857 }
858
859 /*
860 ==================
861 BotMatch_JoinSubteam
862 ==================
863 */
BotMatch_JoinSubteam(bot_state_t * bs,bot_match_t * match)864 void BotMatch_JoinSubteam( bot_state_t *bs, bot_match_t *match ) {
865 char teammate[MAX_MESSAGE_SIZE];
866
867 if ( !TeamPlayIsOn() ) {
868 return;
869 }
870 //if not addressed to this bot
871 if ( !BotAddressedToBot( bs, match ) ) {
872 return;
873 }
874 //get the sub team name
875 trap_BotMatchVariable( match, TEAMNAME, teammate, MAX_MESSAGE_SIZE );
876 //set the sub team name
877 strncpy( bs->subteam, teammate, 32 );
878 bs->subteam[31] = '\0';
879 //
880 BotAI_BotInitialChat( bs, "joinedteam", teammate, NULL );
881 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
882 }
883
884 /*
885 ==================
886 BotMatch_LeaveSubteam
887 ==================
888 */
BotMatch_LeaveSubteam(bot_state_t * bs,bot_match_t * match)889 void BotMatch_LeaveSubteam( bot_state_t *bs, bot_match_t *match ) {
890 if ( !TeamPlayIsOn() ) {
891 return;
892 }
893 //if not addressed to this bot
894 if ( !BotAddressedToBot( bs, match ) ) {
895 return;
896 }
897 //
898 if ( strlen( bs->subteam ) ) {
899 BotAI_BotInitialChat( bs, "leftteam", bs->subteam, NULL );
900 } //end if
901 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
902 strcpy( bs->subteam, "" );
903 }
904
905 /*
906 ==================
907 BotMatch_LeaveSubteam
908 ==================
909 */
BotMatch_WhichTeam(bot_state_t * bs,bot_match_t * match)910 void BotMatch_WhichTeam( bot_state_t *bs, bot_match_t *match ) {
911 if ( !TeamPlayIsOn() ) {
912 return;
913 }
914 //if not addressed to this bot
915 if ( !BotAddressedToBot( bs, match ) ) {
916 return;
917 }
918 //
919 if ( strlen( bs->subteam ) ) {
920 BotAI_BotInitialChat( bs, "inteam", bs->subteam, NULL );
921 } else {
922 BotAI_BotInitialChat( bs, "noteam", NULL );
923 }
924 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
925 }
926
927 /*
928 ==================
929 BotMatch_CheckPoint
930 ==================
931 */
BotMatch_CheckPoint(bot_state_t * bs,bot_match_t * match)932 void BotMatch_CheckPoint( bot_state_t *bs, bot_match_t *match ) {
933 int areanum;
934 char buf[MAX_MESSAGE_SIZE];
935 vec3_t position;
936 bot_waypoint_t *cp;
937
938 if ( !TeamPlayIsOn() ) {
939 return;
940 }
941 //
942 trap_BotMatchVariable( match, POSITION, buf, MAX_MESSAGE_SIZE );
943 VectorClear( position );
944 //BotGPSToPosition(buf, position);
945 sscanf( buf, "%f %f %f", &position[0], &position[1], &position[2] );
946 position[2] += 0.5;
947 areanum = BotPointAreaNum( position );
948 if ( !areanum ) {
949 if ( BotAddressedToBot( bs, match ) ) {
950 BotAI_BotInitialChat( bs, "checkpoint_invalid", NULL );
951 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
952 }
953 return;
954 }
955 //
956 trap_BotMatchVariable( match, NAME, buf, MAX_MESSAGE_SIZE );
957 //check if there already exists a checkpoint with this name
958 cp = BotFindWayPoint( bs->checkpoints, buf );
959 if ( cp ) {
960 if ( cp->next ) {
961 cp->next->prev = cp->prev;
962 }
963 if ( cp->prev ) {
964 cp->prev->next = cp->next;
965 } else { bs->checkpoints = cp->next;}
966 cp->inuse = qfalse;
967 }
968 //create a new check point
969 cp = BotCreateWayPoint( buf, position, areanum );
970 //add the check point to the bot's known chech points
971 cp->next = bs->checkpoints;
972 if ( bs->checkpoints ) {
973 bs->checkpoints->prev = cp;
974 }
975 bs->checkpoints = cp;
976 //
977 if ( BotAddressedToBot( bs, match ) ) {
978 Com_sprintf( buf, sizeof( buf ), "%1.0f %1.0f %1.0f", cp->goal.origin[0],
979 cp->goal.origin[1],
980 cp->goal.origin[2] );
981
982 BotAI_BotInitialChat( bs, "checkpoint_confirm", cp->name, buf, NULL );
983 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
984 }
985 }
986
987 /*
988 ==================
989 BotMatch_FormationSpace
990 ==================
991 */
BotMatch_FormationSpace(bot_state_t * bs,bot_match_t * match)992 void BotMatch_FormationSpace( bot_state_t *bs, bot_match_t *match ) {
993 char buf[MAX_MESSAGE_SIZE];
994 float space;
995
996 if ( !TeamPlayIsOn() ) {
997 return;
998 }
999 //if not addressed to this bot
1000 if ( !BotAddressedToBot( bs, match ) ) {
1001 return;
1002 }
1003 //
1004 trap_BotMatchVariable( match, NUMBER, buf, MAX_MESSAGE_SIZE );
1005 //if it's the distance in feet
1006 if ( match->subtype & ST_FEET ) {
1007 space = 0.3048 * 32 * atof( buf );
1008 }
1009 //else it's in meters
1010 else {space = 32 * atof( buf );}
1011 //check if the formation intervening space is valid
1012 if ( space < 48 || space > 500 ) {
1013 space = 100;
1014 }
1015 bs->formation_dist = space;
1016 }
1017
1018 /*
1019 ==================
1020 BotMatch_Dismiss
1021 ==================
1022 */
BotMatch_Dismiss(bot_state_t * bs,bot_match_t * match)1023 void BotMatch_Dismiss( bot_state_t *bs, bot_match_t *match ) {
1024 if ( !TeamPlayIsOn() ) {
1025 return;
1026 }
1027 //if not addressed to this bot
1028 if ( !BotAddressedToBot( bs, match ) ) {
1029 return;
1030 }
1031 //
1032 bs->ltgtype = 0;
1033 bs->lead_time = 0;
1034 //
1035 BotAI_BotInitialChat( bs, "dismissed", NULL );
1036 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
1037 }
1038
1039 /*
1040 ==================
1041 BotMatch_StartTeamLeaderShip
1042 ==================
1043 */
BotMatch_StartTeamLeaderShip(bot_state_t * bs,bot_match_t * match)1044 void BotMatch_StartTeamLeaderShip( bot_state_t *bs, bot_match_t *match ) {
1045 int client;
1046 char teammate[MAX_MESSAGE_SIZE];
1047
1048 if ( !TeamPlayIsOn() ) {
1049 return;
1050 }
1051 //if chats for him or herself
1052 if ( match->subtype & ST_I ) {
1053 //get the team mate that will be the team leader
1054 trap_BotMatchVariable( match, NETNAME, teammate, sizeof( teammate ) );
1055 strncpy( bs->teamleader, teammate, sizeof( bs->teamleader ) );
1056 bs->teamleader[sizeof(bs->teamleader)-1] = '\0';
1057 }
1058 //chats for someone else
1059 else {
1060 //get the team mate that will be the team leader
1061 trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) );
1062 client = FindClientByName( teammate );
1063 if ( client >= 0 ) {
1064 ClientName( client, bs->teamleader, sizeof( bs->teamleader ) );
1065 }
1066 }
1067 }
1068
1069 /*
1070 ==================
1071 BotMatch_StopTeamLeaderShip
1072 ==================
1073 */
BotMatch_StopTeamLeaderShip(bot_state_t * bs,bot_match_t * match)1074 void BotMatch_StopTeamLeaderShip( bot_state_t *bs, bot_match_t *match ) {
1075 int client;
1076 char teammate[MAX_MESSAGE_SIZE];
1077 char netname[MAX_MESSAGE_SIZE];
1078
1079 if ( !TeamPlayIsOn() ) {
1080 return;
1081 }
1082 //get the team mate that stops being the team leader
1083 trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) );
1084 //if chats for him or herself
1085 if ( match->subtype & ST_I ) {
1086 trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
1087 client = FindClientByName( netname );
1088 }
1089 //chats for someone else
1090 else {
1091 client = FindClientByName( teammate );
1092 } //end else
1093 if ( client >= 0 ) {
1094 if ( !Q_stricmp( bs->teamleader, ClientName( client, netname, sizeof( netname ) ) ) ) {
1095 bs->teamleader[0] = '\0';
1096 }
1097 }
1098 }
1099
1100 /*
1101 ==================
1102 BotMatch_WhoIsTeamLeader
1103 ==================
1104 */
BotMatch_WhoIsTeamLeader(bot_state_t * bs,bot_match_t * match)1105 void BotMatch_WhoIsTeamLeader( bot_state_t *bs, bot_match_t *match ) {
1106 char netname[MAX_MESSAGE_SIZE];
1107
1108 if ( !TeamPlayIsOn() ) {
1109 return;
1110 }
1111
1112 ClientName( bs->client, netname, sizeof( netname ) );
1113 //if this bot IS the team leader
1114 if ( !Q_stricmp( netname, bs->teamleader ) ) {
1115 trap_EA_SayTeam( bs->client, "I'm the team leader\n" );
1116 }
1117 }
1118
1119 /*
1120 ==================
1121 BotMatch_WhatAreYouDoing
1122 ==================
1123 */
BotMatch_WhatAreYouDoing(bot_state_t * bs,bot_match_t * match)1124 void BotMatch_WhatAreYouDoing( bot_state_t *bs, bot_match_t *match ) {
1125 char netname[MAX_MESSAGE_SIZE];
1126 char goalname[MAX_MESSAGE_SIZE];
1127
1128 //if not addressed to this bot
1129 if ( !BotAddressedToBot( bs, match ) ) {
1130 return;
1131 }
1132 //
1133 switch ( bs->ltgtype ) {
1134 case LTG_TEAMHELP:
1135 {
1136 trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
1137 EasyClientName( bs->teammate, netname, MAX_MESSAGE_SIZE );
1138 BotAI_BotInitialChat( bs, "helping", netname, NULL );
1139 break;
1140 }
1141 case LTG_TEAMACCOMPANY:
1142 {
1143 trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
1144 EasyClientName( bs->teammate, netname, MAX_MESSAGE_SIZE );
1145 BotAI_BotInitialChat( bs, "accompanying", netname, NULL );
1146 break;
1147 }
1148 case LTG_DEFENDKEYAREA:
1149 {
1150 trap_BotGoalName( bs->teamgoal.number, goalname, sizeof( goalname ) );
1151 BotAI_BotInitialChat( bs, "defending", goalname, NULL );
1152 break;
1153 }
1154 case LTG_GETITEM:
1155 {
1156 trap_BotGoalName( bs->teamgoal.number, goalname, sizeof( goalname ) );
1157 BotAI_BotInitialChat( bs, "gettingitem", goalname, NULL );
1158 break;
1159 }
1160 case LTG_KILL:
1161 {
1162 ClientName( bs->teamgoal.entitynum, netname, sizeof( netname ) );
1163 BotAI_BotInitialChat( bs, "killing", netname, NULL );
1164 break;
1165 }
1166 case LTG_CAMP:
1167 case LTG_CAMPORDER:
1168 {
1169 BotAI_BotInitialChat( bs, "camping", NULL );
1170 break;
1171 }
1172 case LTG_PATROL:
1173 {
1174 BotAI_BotInitialChat( bs, "patrolling", NULL );
1175 break;
1176 }
1177 case LTG_GETFLAG:
1178 {
1179 BotAI_BotInitialChat( bs, "capturingflag", NULL );
1180 break;
1181 }
1182 case LTG_RUSHBASE:
1183 {
1184 BotAI_BotInitialChat( bs, "rushingbase", NULL );
1185 break;
1186 }
1187 case LTG_RETURNFLAG:
1188 {
1189 BotAI_BotInitialChat( bs, "returningflag", NULL );
1190 break;
1191 }
1192 default:
1193 {
1194 BotAI_BotInitialChat( bs, "roaming", NULL );
1195 break;
1196 }
1197 }
1198 //chat what the bot is doing
1199 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
1200 }
1201
1202 /*
1203 ==================
1204 BotMatch_WhatIsMyCommand
1205 ==================
1206 */
BotMatch_WhatIsMyCommand(bot_state_t * bs,bot_match_t * match)1207 void BotMatch_WhatIsMyCommand( bot_state_t *bs, bot_match_t *match ) {
1208 char netname[MAX_NETNAME];
1209
1210 ClientName( bs->client, netname, sizeof( netname ) );
1211 if ( Q_stricmp( netname, bs->teamleader ) != 0 ) {
1212 return;
1213 }
1214 bs->forceorders = qtrue;
1215 }
1216
1217 /*
1218 ==================
1219 BotNearestVisibleItem
1220 ==================
1221 */
BotNearestVisibleItem(bot_state_t * bs,char * itemname,bot_goal_t * goal)1222 float BotNearestVisibleItem( bot_state_t *bs, char *itemname, bot_goal_t *goal ) {
1223 int i;
1224 char name[64];
1225 bot_goal_t tmpgoal;
1226 float dist, bestdist;
1227 vec3_t dir;
1228 bsp_trace_t trace;
1229
1230 bestdist = 999999;
1231 i = -1;
1232 do {
1233 i = trap_BotGetLevelItemGoal( i, itemname, &tmpgoal );
1234 trap_BotGoalName( tmpgoal.number, name, sizeof( name ) );
1235 if ( Q_stricmp( itemname, name ) != 0 ) {
1236 continue;
1237 }
1238 VectorSubtract( tmpgoal.origin, bs->origin, dir );
1239 dist = VectorLength( dir );
1240 if ( dist < bestdist ) {
1241 //trace from start to end
1242 BotAI_Trace( &trace, bs->eye, NULL, NULL, tmpgoal.origin, bs->client, CONTENTS_SOLID | CONTENTS_PLAYERCLIP );
1243 if ( trace.fraction >= 1.0 ) {
1244 bestdist = dist;
1245 memcpy( goal, &tmpgoal, sizeof( bot_goal_t ) );
1246 }
1247 }
1248 } while ( i > 0 );
1249 return bestdist;
1250 }
1251
1252 /*
1253 ==================
1254 BotMatch_WhereAreYou
1255 ==================
1256 */
BotMatch_WhereAreYou(bot_state_t * bs,bot_match_t * match)1257 void BotMatch_WhereAreYou( bot_state_t *bs, bot_match_t *match ) {
1258 float dist, bestdist;
1259 int i, bestitem, redflagtt, blueflagtt;
1260 bot_goal_t goal;
1261 char *nearbyitems[] = {
1262 "Shotgun",
1263 "Grenade Launcher",
1264 "Rocket Launcher",
1265 "Plasmagun",
1266 "Railgun",
1267 "Lightning Gun",
1268 "BFG10K",
1269 "Quad Damage",
1270 "Regeneration",
1271 "Battle Suit",
1272 "Speed",
1273 "Invisibility",
1274 "Flight",
1275 "Armor",
1276 "Heavy Armor",
1277 "Red Flag",
1278 "Blue Flag",
1279 NULL
1280 };
1281 //
1282 if ( !TeamPlayIsOn() ) {
1283 return;
1284 }
1285 //if not addressed to this bot
1286 if ( !BotAddressedToBot( bs, match ) ) {
1287 return;
1288 }
1289
1290 bestitem = -1;
1291 bestdist = 999999;
1292 for ( i = 0; nearbyitems[i]; i++ ) {
1293 dist = BotNearestVisibleItem( bs, nearbyitems[i], &goal );
1294 if ( dist < bestdist ) {
1295 bestdist = dist;
1296 bestitem = i;
1297 }
1298 }
1299 if ( bestitem != -1 ) {
1300 if ( gametype == GT_CTF ) {
1301 redflagtt = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, ctf_redflag.areanum, TFL_DEFAULT );
1302 blueflagtt = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, ctf_blueflag.areanum, TFL_DEFAULT );
1303 if ( redflagtt < ( redflagtt + blueflagtt ) * 0.4 ) {
1304 BotAI_BotInitialChat( bs, "ctflocation", nearbyitems[bestitem], "red", NULL );
1305 } else if ( blueflagtt < ( redflagtt + blueflagtt ) * 0.4 ) {
1306 BotAI_BotInitialChat( bs, "ctflocation", nearbyitems[bestitem], "blue", NULL );
1307 } else {
1308 BotAI_BotInitialChat( bs, "location", nearbyitems[bestitem], NULL );
1309 }
1310 } else {
1311 BotAI_BotInitialChat( bs, "location", nearbyitems[bestitem], NULL );
1312 }
1313 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
1314 }
1315 }
1316
1317 /*
1318 ==================
1319 BotMatch_LeadTheWay
1320 ==================
1321 */
BotMatch_LeadTheWay(bot_state_t * bs,bot_match_t * match)1322 void BotMatch_LeadTheWay( bot_state_t *bs, bot_match_t *match ) {
1323 aas_entityinfo_t entinfo;
1324 char netname[MAX_MESSAGE_SIZE], teammate[MAX_MESSAGE_SIZE];
1325 int client, areanum, other;
1326
1327 if ( !TeamPlayIsOn() ) {
1328 return;
1329 }
1330 //if not addressed to this bot
1331 if ( !BotAddressedToBot( bs, match ) ) {
1332 return;
1333 }
1334 //if someone asks for someone else
1335 if ( match->subtype & ST_SOMEONE ) {
1336 //get the team mate name
1337 trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) );
1338 client = FindClientByName( teammate );
1339 //if this is the bot self
1340 if ( client == bs->client ) {
1341 other = qfalse;
1342 } else if ( !BotSameTeam( bs, client ) ) {
1343 //FIXME: say "I don't help the enemy"
1344 return;
1345 } else {
1346 other = qtrue;
1347 }
1348 } else {
1349 //get the netname
1350 trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
1351 client = ClientFromName( netname );
1352 other = qfalse;
1353 }
1354 //if the bot doesn't know who to help (FindClientByName returned -1)
1355 if ( client < 0 ) {
1356 BotAI_BotInitialChat( bs, "whois", netname, NULL );
1357 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
1358 return;
1359 }
1360 //
1361 bs->lead_teamgoal.entitynum = -1;
1362 BotEntityInfo( client, &entinfo );
1363 //if info is valid (in PVS)
1364 if ( entinfo.valid ) {
1365 areanum = BotPointAreaNum( entinfo.origin );
1366 if ( areanum && trap_AAS_AreaReachability( areanum ) ) {
1367 bs->lead_teamgoal.entitynum = client;
1368 bs->lead_teamgoal.areanum = areanum;
1369 VectorCopy( entinfo.origin, bs->lead_teamgoal.origin );
1370 VectorSet( bs->lead_teamgoal.mins, -8, -8, -8 );
1371 VectorSet( bs->lead_teamgoal.maxs, 8, 8, 8 );
1372 }
1373 }
1374
1375 if ( bs->teamgoal.entitynum < 0 ) {
1376 if ( other ) {
1377 BotAI_BotInitialChat( bs, "whereis", teammate, NULL );
1378 } else { BotAI_BotInitialChat( bs, "whereareyou", netname, NULL );}
1379 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
1380 return;
1381 }
1382 bs->lead_teammate = client;
1383 bs->lead_time = trap_AAS_Time() + TEAM_LEAD_TIME;
1384 bs->leadvisible_time = 0;
1385 bs->leadmessage_time = -( trap_AAS_Time() + 2 * random() );
1386 }
1387
1388 /*
1389 ==================
1390 BotMatch_Kill
1391 ==================
1392 */
BotMatch_Kill(bot_state_t * bs,bot_match_t * match)1393 void BotMatch_Kill( bot_state_t *bs, bot_match_t *match ) {
1394 char enemy[MAX_MESSAGE_SIZE];
1395 int client;
1396
1397 if ( !TeamPlayIsOn() ) {
1398 return;
1399 }
1400 //if not addressed to this bot
1401 if ( !BotAddressedToBot( bs, match ) ) {
1402 return;
1403 }
1404
1405 trap_BotMatchVariable( match, ENEMY, enemy, sizeof( enemy ) );
1406 //
1407 client = FindEnemyByName( bs, enemy );
1408 if ( client < 0 ) {
1409 BotAI_BotInitialChat( bs, "whois", enemy, NULL );
1410 trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM );
1411 return;
1412 }
1413 bs->teamgoal.entitynum = client;
1414 //set the time to send a message to the team mates
1415 bs->teammessage_time = trap_AAS_Time() + 2 * random();
1416 //set the ltg type
1417 bs->ltgtype = LTG_KILL;
1418 //set the team goal time
1419 bs->teamgoal_time = trap_AAS_Time() + TEAM_KILL_SOMEONE;
1420 #ifdef DEBUG
1421 BotPrintTeamGoal( bs );
1422 #endif //DEBUG
1423 }
1424
1425 /*
1426 ==================
1427 BotMatch_CTF
1428 ==================
1429 */
BotMatch_CTF(bot_state_t * bs,bot_match_t * match)1430 void BotMatch_CTF( bot_state_t *bs, bot_match_t *match ) {
1431
1432 char flag[128], netname[MAX_NETNAME];
1433
1434 trap_BotMatchVariable( match, FLAG, flag, sizeof( flag ) );
1435 if ( match->subtype & ST_GOTFLAG ) {
1436 if ( !Q_stricmp( flag, "red" ) ) {
1437 bs->redflagstatus = 1;
1438 if ( BotCTFTeam( bs ) == CTF_TEAM_BLUE ) {
1439 trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
1440 bs->flagcarrier = ClientFromName( netname );
1441 }
1442 } else {
1443 bs->blueflagstatus = 1;
1444 if ( BotCTFTeam( bs ) == CTF_TEAM_RED ) {
1445 trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) );
1446 bs->flagcarrier = ClientFromName( netname );
1447 }
1448 }
1449 bs->flagstatuschanged = 1;
1450 } else if ( match->subtype & ST_CAPTUREDFLAG ) {
1451 bs->redflagstatus = 0;
1452 bs->blueflagstatus = 0;
1453 bs->flagcarrier = 0;
1454 bs->flagstatuschanged = 1;
1455 } else if ( match->subtype & ST_RETURNEDFLAG ) {
1456 if ( !Q_stricmp( flag, "red" ) ) {
1457 bs->redflagstatus = 0;
1458 } else { bs->blueflagstatus = 0;}
1459 bs->flagstatuschanged = 1;
1460 }
1461 }
1462
1463 /*
1464 ==================
1465 BotMatchMessage
1466 ==================
1467 */
BotMatchMessage(bot_state_t * bs,char * message)1468 int BotMatchMessage( bot_state_t *bs, char *message ) {
1469 bot_match_t match;
1470
1471 match.type = 0;
1472 //if it is an unknown message
1473 if ( !trap_BotFindMatch( message, &match, MTCONTEXT_ENTERGAME
1474 | MTCONTEXT_INITIALTEAMCHAT
1475 | MTCONTEXT_CTF ) ) {
1476 return qfalse;
1477 }
1478 //react to the found message
1479 switch ( match.type ) {
1480 case MSG_HELP: //someone calling for help
1481 case MSG_ACCOMPANY: //someone calling for company
1482 {
1483 BotMatch_HelpAccompany( bs, &match );
1484 break;
1485 }
1486 case MSG_DEFENDKEYAREA: //teamplay defend a key area
1487 {
1488 BotMatch_DefendKeyArea( bs, &match );
1489 break;
1490 }
1491 case MSG_CAMP: //camp somewhere
1492 {
1493 BotMatch_Camp( bs, &match );
1494 break;
1495 }
1496 case MSG_PATROL: //patrol between several key areas
1497 {
1498 BotMatch_Patrol( bs, &match );
1499 break;
1500 }
1501 #ifdef CTF
1502 case MSG_GETFLAG: //ctf get the enemy flag
1503 {
1504 BotMatch_GetFlag( bs, &match );
1505 break;
1506 }
1507 case MSG_RUSHBASE: //ctf rush to the base
1508 {
1509 BotMatch_RushBase( bs, &match );
1510 break;
1511 }
1512 case MSG_RETURNFLAG:
1513 {
1514 BotMatch_ReturnFlag( bs, &match );
1515 break;
1516 }
1517 #endif //CTF
1518 case MSG_GETITEM:
1519 {
1520 BotMatch_GetItem( bs, &match );
1521 break;
1522 }
1523 case MSG_JOINSUBTEAM: //join a sub team
1524 {
1525 BotMatch_JoinSubteam( bs, &match );
1526 break;
1527 }
1528 case MSG_LEAVESUBTEAM: //leave a sub team
1529 {
1530 BotMatch_LeaveSubteam( bs, &match );
1531 break;
1532 }
1533 case MSG_WHICHTEAM:
1534 {
1535 BotMatch_WhichTeam( bs, &match );
1536 break;
1537 }
1538 case MSG_CHECKPOINT: //remember a check point
1539 {
1540 BotMatch_CheckPoint( bs, &match );
1541 break;
1542 }
1543 case MSG_CREATENEWFORMATION: //start the creation of a new formation
1544 {
1545 trap_EA_SayTeam( bs->client, "the part of my brain to create formations has been damaged" );
1546 break;
1547 }
1548 case MSG_FORMATIONPOSITION: //tell someone his/her position in the formation
1549 {
1550 trap_EA_SayTeam( bs->client, "the part of my brain to create formations has been damaged" );
1551 break;
1552 }
1553 case MSG_FORMATIONSPACE: //set the formation space
1554 {
1555 BotMatch_FormationSpace( bs, &match );
1556 break;
1557 }
1558 case MSG_DOFORMATION: //form a certain formation
1559 {
1560 break;
1561 }
1562 case MSG_DISMISS: //dismiss someone
1563 {
1564 BotMatch_Dismiss( bs, &match );
1565 break;
1566 }
1567 case MSG_STARTTEAMLEADERSHIP: //someone will become the team leader
1568 {
1569 BotMatch_StartTeamLeaderShip( bs, &match );
1570 break;
1571 }
1572 case MSG_STOPTEAMLEADERSHIP: //someone will stop being the team leader
1573 {
1574 BotMatch_StopTeamLeaderShip( bs, &match );
1575 break;
1576 }
1577 case MSG_WHOISTEAMLAEDER:
1578 {
1579 BotMatch_WhoIsTeamLeader( bs, &match );
1580 break;
1581 }
1582 case MSG_WHATAREYOUDOING: //ask a bot what he/she is doing
1583 {
1584 BotMatch_WhatAreYouDoing( bs, &match );
1585 break;
1586 }
1587 case MSG_WHATISMYCOMMAND:
1588 {
1589 BotMatch_WhatIsMyCommand( bs, &match );
1590 break;
1591 }
1592 case MSG_WHEREAREYOU:
1593 {
1594 BotMatch_WhereAreYou( bs, &match );
1595 break;
1596 }
1597 case MSG_LEADTHEWAY:
1598 {
1599 BotMatch_LeadTheWay( bs, &match );
1600 break;
1601 }
1602 case MSG_KILL:
1603 {
1604 BotMatch_Kill( bs, &match );
1605 break;
1606 }
1607 case MSG_ENTERGAME: //someone entered the game
1608 {
1609 //NOTE: eliza chats will catch this
1610 //BotMatchVariable(&match, NETNAME, netname);
1611 //Com_sprintf(buf, sizeof(buf), "heya %s", netname);
1612 //EA_Say(bs->client, buf);
1613 break;
1614 }
1615 case MSG_CTF:
1616 {
1617 BotMatch_CTF( bs, &match );
1618 break;
1619 }
1620 case MSG_WAIT:
1621 {
1622 break;
1623 }
1624 default:
1625 {
1626 BotAI_Print( PRT_MESSAGE, "unknown match type\n" );
1627 break;
1628 }
1629 }
1630 return qtrue;
1631 }
1632