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