1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 //
23 
24 /*****************************************************************************
25  * name:		ai_vcmd.c
26  *
27  * desc:		Quake3 bot AI
28  *
29  * $Archive: /MissionPack/code/game/ai_vcmd.c $
30  *
31  *****************************************************************************/
32 
33 #include "g_local.h"
34 #include "../botlib/botlib.h"
35 #include "../botlib/be_aas.h"
36 #include "../botlib/be_ea.h"
37 #include "../botlib/be_ai_char.h"
38 #include "../botlib/be_ai_chat.h"
39 #include "../botlib/be_ai_gen.h"
40 #include "../botlib/be_ai_goal.h"
41 #include "../botlib/be_ai_move.h"
42 #include "../botlib/be_ai_weap.h"
43 //
44 #include "ai_main.h"
45 #include "ai_dmq3.h"
46 #include "ai_chat.h"
47 #include "ai_cmd.h"
48 #include "ai_dmnet.h"
49 #include "ai_team.h"
50 #include "ai_vcmd.h"
51 //
52 #include "chars.h"				//characteristics
53 #include "inv.h"				//indexes into the inventory
54 #include "syn.h"				//synonyms
55 #include "match.h"				//string matching types and vars
56 
57 // for the voice chats
58 #include "../../ui/menudef.h"
59 
60 
61 typedef struct voiceCommand_s
62 {
63 	char *cmd;
64 	void (*func)(bot_state_t *bs, int client, int mode);
65 } voiceCommand_t;
66 
67 /*
68 ==================
69 BotVoiceChat_GetFlag
70 ==================
71 */
BotVoiceChat_GetFlag(bot_state_t * bs,int client,int mode)72 void BotVoiceChat_GetFlag(bot_state_t *bs, int client, int mode) {
73 	//
74 	if (gametype == GT_CTF) {
75 		if (!ctf_redflag.areanum || !ctf_blueflag.areanum)
76 			return;
77 	}
78 #ifdef MISSIONPACK
79 	else if (gametype == GT_1FCTF) {
80 		if (!ctf_neutralflag.areanum || !ctf_redflag.areanum || !ctf_blueflag.areanum)
81 			return;
82 	}
83 #endif
84 	else {
85 		return;
86 	}
87 	//
88 	bs->decisionmaker = client;
89 	bs->ordered = qtrue;
90 	bs->order_time = FloatTime();
91 	//set the time to send a message to the team mates
92 	bs->teammessage_time = FloatTime() + 2 * random();
93 	//set the ltg type
94 	bs->ltgtype = LTG_GETFLAG;
95 	//set the team goal time
96 	bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME;
97 	// get an alternate route in ctf
98 	if (gametype == GT_CTF) {
99 		//get an alternative route goal towards the enemy base
100 		BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs));
101 	}
102 	//
103 	BotSetTeamStatus(bs);
104 	// remember last ordered task
105 	BotRememberLastOrderedTask(bs);
106 #ifdef DEBUG
107 	BotPrintTeamGoal(bs);
108 #endif //DEBUG
109 }
110 
111 /*
112 ==================
113 BotVoiceChat_Offense
114 ==================
115 */
BotVoiceChat_Offense(bot_state_t * bs,int client,int mode)116 void BotVoiceChat_Offense(bot_state_t *bs, int client, int mode) {
117 	if ( gametype == GT_CTF
118 #ifdef MISSIONPACK
119 		|| gametype == GT_1FCTF
120 #endif
121 		) {
122 		BotVoiceChat_GetFlag(bs, client, mode);
123 		return;
124 	}
125 #ifdef MISSIONPACK
126 	if (gametype == GT_HARVESTER) {
127 		//
128 		bs->decisionmaker = client;
129 		bs->ordered = qtrue;
130 		bs->order_time = FloatTime();
131 		//set the time to send a message to the team mates
132 		bs->teammessage_time = FloatTime() + 2 * random();
133 		//set the ltg type
134 		bs->ltgtype = LTG_HARVEST;
135 		//set the team goal time
136 		bs->teamgoal_time = FloatTime() + TEAM_HARVEST_TIME;
137 		bs->harvestaway_time = 0;
138 		//
139 		BotSetTeamStatus(bs);
140 		// remember last ordered task
141 		BotRememberLastOrderedTask(bs);
142 	}
143 	else
144 #endif
145 	{
146 		//
147 		bs->decisionmaker = client;
148 		bs->ordered = qtrue;
149 		bs->order_time = FloatTime();
150 		//set the time to send a message to the team mates
151 		bs->teammessage_time = FloatTime() + 2 * random();
152 		//set the ltg type
153 		bs->ltgtype = LTG_ATTACKENEMYBASE;
154 		//set the team goal time
155 		bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME;
156 		bs->attackaway_time = 0;
157 		//
158 		BotSetTeamStatus(bs);
159 		// remember last ordered task
160 		BotRememberLastOrderedTask(bs);
161 	}
162 #ifdef DEBUG
163 	BotPrintTeamGoal(bs);
164 #endif //DEBUG
165 }
166 
167 /*
168 ==================
169 BotVoiceChat_Defend
170 ==================
171 */
BotVoiceChat_Defend(bot_state_t * bs,int client,int mode)172 void BotVoiceChat_Defend(bot_state_t *bs, int client, int mode) {
173 #ifdef MISSIONPACK
174 	if ( gametype == GT_OBELISK || gametype == GT_HARVESTER) {
175 		//
176 		switch(BotTeam(bs)) {
177 			case TEAM_RED: memcpy(&bs->teamgoal, &redobelisk, sizeof(bot_goal_t)); break;
178 			case TEAM_BLUE: memcpy(&bs->teamgoal, &blueobelisk, sizeof(bot_goal_t)); break;
179 			default: return;
180 		}
181 	}
182 	else
183 #endif
184 		if (gametype == GT_CTF
185 #ifdef MISSIONPACK
186 			|| gametype == GT_1FCTF
187 #endif
188 			) {
189 		//
190 		switch(BotTeam(bs)) {
191 			case TEAM_RED: memcpy(&bs->teamgoal, &ctf_redflag, sizeof(bot_goal_t)); break;
192 			case TEAM_BLUE: memcpy(&bs->teamgoal, &ctf_blueflag, sizeof(bot_goal_t)); break;
193 			default: return;
194 		}
195 	}
196 	else {
197 		return;
198 	}
199 	//
200 	bs->decisionmaker = client;
201 	bs->ordered = qtrue;
202 	bs->order_time = FloatTime();
203 	//set the time to send a message to the team mates
204 	bs->teammessage_time = FloatTime() + 2 * random();
205 	//set the ltg type
206 	bs->ltgtype = LTG_DEFENDKEYAREA;
207 	//get the team goal time
208 	bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME;
209 	//away from defending
210 	bs->defendaway_time = 0;
211 	//
212 	BotSetTeamStatus(bs);
213 	// remember last ordered task
214 	BotRememberLastOrderedTask(bs);
215 #ifdef DEBUG
216 	BotPrintTeamGoal(bs);
217 #endif //DEBUG
218 }
219 
220 /*
221 ==================
222 BotVoiceChat_DefendFlag
223 ==================
224 */
BotVoiceChat_DefendFlag(bot_state_t * bs,int client,int mode)225 void BotVoiceChat_DefendFlag(bot_state_t *bs, int client, int mode) {
226 	BotVoiceChat_Defend(bs, client, mode);
227 }
228 
229 /*
230 ==================
231 BotVoiceChat_Patrol
232 ==================
233 */
BotVoiceChat_Patrol(bot_state_t * bs,int client,int mode)234 void BotVoiceChat_Patrol(bot_state_t *bs, int client, int mode) {
235 	//
236 	bs->decisionmaker = client;
237 	//
238 	bs->ltgtype = 0;
239 	bs->lead_time = 0;
240 	bs->lastgoal_ltgtype = 0;
241 	//
242 	BotAI_BotInitialChat(bs, "dismissed", NULL);
243 	trap_BotEnterChat(bs->cs, client, CHAT_TELL);
244 	BotVoiceChatOnly(bs, -1, VOICECHAT_ONPATROL);
245 	//
246 	BotSetTeamStatus(bs);
247 #ifdef DEBUG
248 	BotPrintTeamGoal(bs);
249 #endif //DEBUG
250 }
251 
252 /*
253 ==================
254 BotVoiceChat_Camp
255 ==================
256 */
BotVoiceChat_Camp(bot_state_t * bs,int client,int mode)257 void BotVoiceChat_Camp(bot_state_t *bs, int client, int mode) {
258 	int areanum;
259 	aas_entityinfo_t entinfo;
260 	char netname[MAX_NETNAME];
261 
262 	//
263 	bs->teamgoal.entitynum = -1;
264 	BotEntityInfo(client, &entinfo);
265 	//if info is valid (in PVS)
266 	if (entinfo.valid) {
267 		areanum = BotPointAreaNum(entinfo.origin);
268 		if (areanum) { // && trap_AAS_AreaReachability(areanum)) {
269 			//NOTE: just assume the bot knows where the person is
270 			//if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, client)) {
271 				bs->teamgoal.entitynum = client;
272 				bs->teamgoal.areanum = areanum;
273 				VectorCopy(entinfo.origin, bs->teamgoal.origin);
274 				VectorSet(bs->teamgoal.mins, -8, -8, -8);
275 				VectorSet(bs->teamgoal.maxs, 8, 8, 8);
276 			//}
277 		}
278 	}
279 	//if the other is not visible
280 	if (bs->teamgoal.entitynum < 0) {
281 		BotAI_BotInitialChat(bs, "whereareyou", EasyClientName(client, netname, sizeof(netname)), NULL);
282 		trap_BotEnterChat(bs->cs, client, CHAT_TELL);
283 		return;
284 	}
285 	//
286 	bs->decisionmaker = client;
287 	bs->ordered = qtrue;
288 	bs->order_time = FloatTime();
289 	//set the time to send a message to the team mates
290 	bs->teammessage_time = FloatTime() + 2 * random();
291 	//set the ltg type
292 	bs->ltgtype = LTG_CAMPORDER;
293 	//get the team goal time
294 	bs->teamgoal_time = FloatTime() + TEAM_CAMP_TIME;
295 	//the teammate that requested the camping
296 	bs->teammate = client;
297 	//not arrived yet
298 	bs->arrive_time = 0;
299 	//
300 	BotSetTeamStatus(bs);
301 	// remember last ordered task
302 	BotRememberLastOrderedTask(bs);
303 #ifdef DEBUG
304 	BotPrintTeamGoal(bs);
305 #endif //DEBUG
306 }
307 
308 /*
309 ==================
310 BotVoiceChat_FollowMe
311 ==================
312 */
BotVoiceChat_FollowMe(bot_state_t * bs,int client,int mode)313 void BotVoiceChat_FollowMe(bot_state_t *bs, int client, int mode) {
314 	int areanum;
315 	aas_entityinfo_t entinfo;
316 	char netname[MAX_NETNAME];
317 
318 	bs->teamgoal.entitynum = -1;
319 	BotEntityInfo(client, &entinfo);
320 	//if info is valid (in PVS)
321 	if (entinfo.valid) {
322 		areanum = BotPointAreaNum(entinfo.origin);
323 		if (areanum) { // && trap_AAS_AreaReachability(areanum)) {
324 			bs->teamgoal.entitynum = client;
325 			bs->teamgoal.areanum = areanum;
326 			VectorCopy(entinfo.origin, bs->teamgoal.origin);
327 			VectorSet(bs->teamgoal.mins, -8, -8, -8);
328 			VectorSet(bs->teamgoal.maxs, 8, 8, 8);
329 		}
330 	}
331 	//if the other is not visible
332 	if (bs->teamgoal.entitynum < 0) {
333 		BotAI_BotInitialChat(bs, "whereareyou", EasyClientName(client, netname, sizeof(netname)), NULL);
334 		trap_BotEnterChat(bs->cs, client, CHAT_TELL);
335 		return;
336 	}
337 	//
338 	bs->decisionmaker = client;
339 	bs->ordered = qtrue;
340 	bs->order_time = FloatTime();
341 	//the team mate
342 	bs->teammate = client;
343 	//last time the team mate was assumed visible
344 	bs->teammatevisible_time = FloatTime();
345 	//set the time to send a message to the team mates
346 	bs->teammessage_time = FloatTime() + 2 * random();
347 	//get the team goal time
348 	bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME;
349 	//set the ltg type
350 	bs->ltgtype = LTG_TEAMACCOMPANY;
351 	bs->formation_dist = 3.5 * 32;		//3.5 meter
352 	bs->arrive_time = 0;
353 	//
354 	BotSetTeamStatus(bs);
355 	// remember last ordered task
356 	BotRememberLastOrderedTask(bs);
357 #ifdef DEBUG
358 	BotPrintTeamGoal(bs);
359 #endif //DEBUG
360 }
361 
362 /*
363 ==================
364 BotVoiceChat_FollowFlagCarrier
365 ==================
366 */
BotVoiceChat_FollowFlagCarrier(bot_state_t * bs,int client,int mode)367 void BotVoiceChat_FollowFlagCarrier(bot_state_t *bs, int client, int mode) {
368 	int carrier;
369 
370 	carrier = BotTeamFlagCarrier(bs);
371 	if (carrier >= 0)
372 		BotVoiceChat_FollowMe(bs, carrier, mode);
373 #ifdef DEBUG
374 	BotPrintTeamGoal(bs);
375 #endif //DEBUG
376 }
377 
378 /*
379 ==================
380 BotVoiceChat_ReturnFlag
381 ==================
382 */
BotVoiceChat_ReturnFlag(bot_state_t * bs,int client,int mode)383 void BotVoiceChat_ReturnFlag(bot_state_t *bs, int client, int mode) {
384 	//if not in CTF mode
385 	if (
386 		gametype != GT_CTF
387 #ifdef MISSIONPACK
388 		&& gametype != GT_1FCTF
389 #endif
390 		) {
391 		return;
392 	}
393 	//
394 	bs->decisionmaker = client;
395 	bs->ordered = qtrue;
396 	bs->order_time = FloatTime();
397 	//set the time to send a message to the team mates
398 	bs->teammessage_time = FloatTime() + 2 * random();
399 	//set the ltg type
400 	bs->ltgtype = LTG_RETURNFLAG;
401 	//set the team goal time
402 	bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME;
403 	bs->rushbaseaway_time = 0;
404 	BotSetTeamStatus(bs);
405 #ifdef DEBUG
406 	BotPrintTeamGoal(bs);
407 #endif //DEBUG
408 }
409 
410 /*
411 ==================
412 BotVoiceChat_StartLeader
413 ==================
414 */
BotVoiceChat_StartLeader(bot_state_t * bs,int client,int mode)415 void BotVoiceChat_StartLeader(bot_state_t *bs, int client, int mode) {
416 	ClientName(client, bs->teamleader, sizeof(bs->teamleader));
417 }
418 
419 /*
420 ==================
421 BotVoiceChat_StopLeader
422 ==================
423 */
BotVoiceChat_StopLeader(bot_state_t * bs,int client,int mode)424 void BotVoiceChat_StopLeader(bot_state_t *bs, int client, int mode) {
425 	char netname[MAX_MESSAGE_SIZE];
426 
427 	if (!Q_stricmp(bs->teamleader, ClientName(client, netname, sizeof(netname)))) {
428 		bs->teamleader[0] = '\0';
429 		notleader[client] = qtrue;
430 	}
431 }
432 
433 /*
434 ==================
435 BotVoiceChat_WhoIsLeader
436 ==================
437 */
BotVoiceChat_WhoIsLeader(bot_state_t * bs,int client,int mode)438 void BotVoiceChat_WhoIsLeader(bot_state_t *bs, int client, int mode) {
439 	char netname[MAX_MESSAGE_SIZE];
440 
441 	if (!TeamPlayIsOn()) return;
442 
443 	ClientName(bs->client, netname, sizeof(netname));
444 	//if this bot IS the team leader
445 	if (!Q_stricmp(netname, bs->teamleader)) {
446 		BotAI_BotInitialChat(bs, "iamteamleader", NULL);
447 		trap_BotEnterChat(bs->cs, 0, CHAT_TEAM);
448 		BotVoiceChatOnly(bs, -1, VOICECHAT_STARTLEADER);
449 	}
450 }
451 
452 /*
453 ==================
454 BotVoiceChat_WantOnDefense
455 ==================
456 */
BotVoiceChat_WantOnDefense(bot_state_t * bs,int client,int mode)457 void BotVoiceChat_WantOnDefense(bot_state_t *bs, int client, int mode) {
458 	char netname[MAX_NETNAME];
459 	int preference;
460 
461 	preference = BotGetTeamMateTaskPreference(bs, client);
462 	preference &= ~TEAMTP_ATTACKER;
463 	preference |= TEAMTP_DEFENDER;
464 	BotSetTeamMateTaskPreference(bs, client, preference);
465 	//
466 	EasyClientName(client, netname, sizeof(netname));
467 	BotAI_BotInitialChat(bs, "keepinmind", netname, NULL);
468 	trap_BotEnterChat(bs->cs, client, CHAT_TELL);
469 	BotVoiceChatOnly(bs, client, VOICECHAT_YES);
470 	trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
471 }
472 
473 /*
474 ==================
475 BotVoiceChat_WantOnOffense
476 ==================
477 */
BotVoiceChat_WantOnOffense(bot_state_t * bs,int client,int mode)478 void BotVoiceChat_WantOnOffense(bot_state_t *bs, int client, int mode) {
479 	char netname[MAX_NETNAME];
480 	int preference;
481 
482 	preference = BotGetTeamMateTaskPreference(bs, client);
483 	preference &= ~TEAMTP_DEFENDER;
484 	preference |= TEAMTP_ATTACKER;
485 	BotSetTeamMateTaskPreference(bs, client, preference);
486 	//
487 	EasyClientName(client, netname, sizeof(netname));
488 	BotAI_BotInitialChat(bs, "keepinmind", netname, NULL);
489 	trap_BotEnterChat(bs->cs, client, CHAT_TELL);
490 	BotVoiceChatOnly(bs, client, VOICECHAT_YES);
491 	trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
492 }
493 
BotVoiceChat_Dummy(bot_state_t * bs,int client,int mode)494 void BotVoiceChat_Dummy(bot_state_t *bs, int client, int mode) {
495 }
496 
497 voiceCommand_t voiceCommands[] = {
498 	{VOICECHAT_GETFLAG, BotVoiceChat_GetFlag},
499 	{VOICECHAT_OFFENSE, BotVoiceChat_Offense },
500 	{VOICECHAT_DEFEND, BotVoiceChat_Defend },
501 	{VOICECHAT_DEFENDFLAG, BotVoiceChat_DefendFlag },
502 	{VOICECHAT_PATROL, BotVoiceChat_Patrol },
503 	{VOICECHAT_CAMP, BotVoiceChat_Camp },
504 	{VOICECHAT_FOLLOWME, BotVoiceChat_FollowMe },
505 	{VOICECHAT_FOLLOWFLAGCARRIER, BotVoiceChat_FollowFlagCarrier },
506 	{VOICECHAT_RETURNFLAG, BotVoiceChat_ReturnFlag },
507 	{VOICECHAT_STARTLEADER, BotVoiceChat_StartLeader },
508 	{VOICECHAT_STOPLEADER, BotVoiceChat_StopLeader },
509 	{VOICECHAT_WHOISLEADER, BotVoiceChat_WhoIsLeader },
510 	{VOICECHAT_WANTONDEFENSE, BotVoiceChat_WantOnDefense },
511 	{VOICECHAT_WANTONOFFENSE, BotVoiceChat_WantOnOffense },
512 	{NULL, BotVoiceChat_Dummy}
513 };
514 
BotVoiceChatCommand(bot_state_t * bs,int mode,char * voiceChat)515 int BotVoiceChatCommand(bot_state_t *bs, int mode, char *voiceChat) {
516 	int i, voiceOnly, clientNum, color;
517 	char *ptr, buf[MAX_MESSAGE_SIZE], *cmd;
518 
519 	if (!TeamPlayIsOn()) {
520 		return qfalse;
521 	}
522 
523 	if ( mode == SAY_ALL ) {
524 		return qfalse;	// don't do anything with voice chats to everyone
525 	}
526 
527 	Q_strncpyz(buf, voiceChat, sizeof(buf));
528 	cmd = buf;
529 	for (ptr = cmd; *cmd && *cmd > ' '; cmd++);
530 	while (*cmd && *cmd <= ' ') *cmd++ = '\0';
531 	voiceOnly = atoi(ptr);
532 	for (ptr = cmd; *cmd && *cmd > ' '; cmd++);
533 	while (*cmd && *cmd <= ' ') *cmd++ = '\0';
534 	clientNum = atoi(ptr);
535 	for (ptr = cmd; *cmd && *cmd > ' '; cmd++);
536 	while (*cmd && *cmd <= ' ') *cmd++ = '\0';
537 	color = atoi(ptr);
538 
539 	if (!BotSameTeam(bs, clientNum)) {
540 		return qfalse;
541 	}
542 
543 	for (i = 0; voiceCommands[i].cmd; i++) {
544 		if (!Q_stricmp(cmd, voiceCommands[i].cmd)) {
545 			voiceCommands[i].func(bs, clientNum, mode);
546 			return qtrue;
547 		}
548 	}
549 	return qfalse;
550 }
551