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