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_chat.c
26 *
27 * desc: Quake3 bot AI
28 *
29 * $Archive: /MissionPack/code/game/ai_chat.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 //
50 #include "chars.h" //characteristics
51 #include "inv.h" //indexes into the inventory
52 #include "syn.h" //synonyms
53 #include "match.h" //string matching types and vars
54
55 // for the voice chats
56 #ifdef MISSIONPACK
57 #include "../../ui/menudef.h"
58 #endif
59
60 #define TIME_BETWEENCHATTING 25
61
62
63 /*
64 ==================
65 BotNumActivePlayers
66 ==================
67 */
BotNumActivePlayers(void)68 int BotNumActivePlayers(void) {
69 int i, num;
70 char buf[MAX_INFO_STRING];
71 static int maxclients;
72
73 if (!maxclients)
74 maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
75
76 num = 0;
77 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
78 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
79 //if no config string or no name
80 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
81 //skip spectators
82 if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
83 //
84 num++;
85 }
86 return num;
87 }
88
89 /*
90 ==================
91 BotIsFirstInRankings
92 ==================
93 */
BotIsFirstInRankings(bot_state_t * bs)94 int BotIsFirstInRankings(bot_state_t *bs) {
95 int i, score;
96 char buf[MAX_INFO_STRING];
97 static int maxclients;
98 playerState_t ps;
99
100 if (!maxclients)
101 maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
102
103 score = bs->cur_ps.persistant[PERS_SCORE];
104 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
105 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
106 //if no config string or no name
107 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
108 //skip spectators
109 if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
110 //
111 BotAI_GetClientState(i, &ps);
112 if (score < ps.persistant[PERS_SCORE]) return qfalse;
113 }
114 return qtrue;
115 }
116
117 /*
118 ==================
119 BotIsLastInRankings
120 ==================
121 */
BotIsLastInRankings(bot_state_t * bs)122 int BotIsLastInRankings(bot_state_t *bs) {
123 int i, score;
124 char buf[MAX_INFO_STRING];
125 static int maxclients;
126 playerState_t ps;
127
128 if (!maxclients)
129 maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
130
131 score = bs->cur_ps.persistant[PERS_SCORE];
132 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
133 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
134 //if no config string or no name
135 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
136 //skip spectators
137 if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
138 //
139 BotAI_GetClientState(i, &ps);
140 if (score > ps.persistant[PERS_SCORE]) return qfalse;
141 }
142 return qtrue;
143 }
144
145 /*
146 ==================
147 BotFirstClientInRankings
148 ==================
149 */
BotFirstClientInRankings(void)150 char *BotFirstClientInRankings(void) {
151 int i, bestscore, bestclient;
152 char buf[MAX_INFO_STRING];
153 static char name[32];
154 static int maxclients;
155 playerState_t ps;
156
157 if (!maxclients)
158 maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
159
160 bestscore = -999999;
161 bestclient = 0;
162 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
163 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
164 //if no config string or no name
165 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
166 //skip spectators
167 if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
168 //
169 BotAI_GetClientState(i, &ps);
170 if (ps.persistant[PERS_SCORE] > bestscore) {
171 bestscore = ps.persistant[PERS_SCORE];
172 bestclient = i;
173 }
174 }
175 EasyClientName(bestclient, name, 32);
176 return name;
177 }
178
179 /*
180 ==================
181 BotLastClientInRankings
182 ==================
183 */
BotLastClientInRankings(void)184 char *BotLastClientInRankings(void) {
185 int i, worstscore, bestclient;
186 char buf[MAX_INFO_STRING];
187 static char name[32];
188 static int maxclients;
189 playerState_t ps;
190
191 if (!maxclients)
192 maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
193
194 worstscore = 999999;
195 bestclient = 0;
196 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
197 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
198 //if no config string or no name
199 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
200 //skip spectators
201 if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
202 //
203 BotAI_GetClientState(i, &ps);
204 if (ps.persistant[PERS_SCORE] < worstscore) {
205 worstscore = ps.persistant[PERS_SCORE];
206 bestclient = i;
207 }
208 }
209 EasyClientName(bestclient, name, 32);
210 return name;
211 }
212
213 /*
214 ==================
215 BotRandomOpponentName
216 ==================
217 */
BotRandomOpponentName(bot_state_t * bs)218 char *BotRandomOpponentName(bot_state_t *bs) {
219 int i, count;
220 char buf[MAX_INFO_STRING];
221 int opponents[MAX_CLIENTS], numopponents;
222 static int maxclients;
223 static char name[32];
224
225 if (!maxclients)
226 maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
227
228 numopponents = 0;
229 opponents[0] = 0;
230 for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
231 if (i == bs->client) continue;
232 //
233 trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
234 //if no config string or no name
235 if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
236 //skip spectators
237 if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue;
238 //skip team mates
239 if (BotSameTeam(bs, i)) continue;
240 //
241 opponents[numopponents] = i;
242 numopponents++;
243 }
244 count = random() * numopponents;
245 for (i = 0; i < numopponents; i++) {
246 count--;
247 if (count <= 0) {
248 EasyClientName(opponents[i], name, sizeof(name));
249 return name;
250 }
251 }
252 EasyClientName(opponents[0], name, sizeof(name));
253 return name;
254 }
255
256 /*
257 ==================
258 BotMapTitle
259 ==================
260 */
261
BotMapTitle(void)262 char *BotMapTitle(void) {
263 char info[1024];
264 static char mapname[128];
265
266 trap_GetServerinfo(info, sizeof(info));
267
268 strncpy(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname)-1);
269 mapname[sizeof(mapname)-1] = '\0';
270
271 return mapname;
272 }
273
274
275 /*
276 ==================
277 BotWeaponNameForMeansOfDeath
278 ==================
279 */
280
BotWeaponNameForMeansOfDeath(int mod)281 char *BotWeaponNameForMeansOfDeath(int mod) {
282 switch(mod) {
283 case MOD_SHOTGUN: return "Shotgun";
284 case MOD_GAUNTLET: return "Gauntlet";
285 case MOD_MACHINEGUN: return "Machinegun";
286 case MOD_GRENADE:
287 case MOD_GRENADE_SPLASH: return "Grenade Launcher";
288 case MOD_ROCKET:
289 case MOD_ROCKET_SPLASH: return "Rocket Launcher";
290 case MOD_PLASMA:
291 case MOD_PLASMA_SPLASH: return "Plasmagun";
292 case MOD_RAILGUN: return "Railgun";
293 case MOD_LIGHTNING: return "Lightning Gun";
294 case MOD_BFG:
295 case MOD_BFG_SPLASH: return "BFG10K";
296 #ifdef MISSIONPACK
297 case MOD_NAIL: return "Nailgun";
298 case MOD_CHAINGUN: return "Chaingun";
299 case MOD_PROXIMITY_MINE: return "Proximity Launcher";
300 case MOD_KAMIKAZE: return "Kamikaze";
301 case MOD_JUICED: return "Prox mine";
302 #endif
303 case MOD_GRAPPLE: return "Grapple";
304 default: return "[unknown weapon]";
305 }
306 }
307
308 /*
309 ==================
310 BotRandomWeaponName
311 ==================
312 */
BotRandomWeaponName(void)313 char *BotRandomWeaponName(void) {
314 int rnd;
315
316 #ifdef MISSIONPACK
317 rnd = random() * 11.9;
318 #else
319 rnd = random() * 8.9;
320 #endif
321 switch(rnd) {
322 case 0: return "Gauntlet";
323 case 1: return "Shotgun";
324 case 2: return "Machinegun";
325 case 3: return "Grenade Launcher";
326 case 4: return "Rocket Launcher";
327 case 5: return "Plasmagun";
328 case 6: return "Railgun";
329 case 7: return "Lightning Gun";
330 #ifdef MISSIONPACK
331 case 8: return "Nailgun";
332 case 9: return "Chaingun";
333 case 10: return "Proximity Launcher";
334 #endif
335 default: return "BFG10K";
336 }
337 }
338
339 /*
340 ==================
341 BotVisibleEnemies
342 ==================
343 */
BotVisibleEnemies(bot_state_t * bs)344 int BotVisibleEnemies(bot_state_t *bs) {
345 float vis;
346 int i;
347 aas_entityinfo_t entinfo;
348
349 for (i = 0; i < MAX_CLIENTS; i++) {
350
351 if (i == bs->client) continue;
352 //
353 BotEntityInfo(i, &entinfo);
354 //
355 if (!entinfo.valid) continue;
356 //if the enemy isn't dead and the enemy isn't the bot self
357 if (EntityIsDead(&entinfo) || entinfo.number == bs->entitynum) continue;
358 //if the enemy is invisible and not shooting
359 if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) {
360 continue;
361 }
362 //if on the same team
363 if (BotSameTeam(bs, i)) continue;
364 //check if the enemy is visible
365 vis = BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, i);
366 if (vis > 0) return qtrue;
367 }
368 return qfalse;
369 }
370
371 /*
372 ==================
373 BotValidChatPosition
374 ==================
375 */
BotValidChatPosition(bot_state_t * bs)376 int BotValidChatPosition(bot_state_t *bs) {
377 vec3_t point, start, end, mins, maxs;
378 bsp_trace_t trace;
379
380 //if the bot is dead all positions are valid
381 if (BotIsDead(bs)) return qtrue;
382 //never start chatting with a powerup
383 if (bs->inventory[INVENTORY_QUAD] ||
384 bs->inventory[INVENTORY_HASTE] ||
385 bs->inventory[INVENTORY_INVISIBILITY] ||
386 bs->inventory[INVENTORY_REGEN] ||
387 bs->inventory[INVENTORY_FLIGHT]) return qfalse;
388 //must be on the ground
389 //if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) return qfalse;
390 //do not chat if in lava or slime
391 VectorCopy(bs->origin, point);
392 point[2] -= 24;
393 if (trap_PointContents(point,bs->entitynum) & (CONTENTS_LAVA|CONTENTS_SLIME)) return qfalse;
394 //do not chat if under water
395 VectorCopy(bs->origin, point);
396 point[2] += 32;
397 if (trap_PointContents(point,bs->entitynum) & MASK_WATER) return qfalse;
398 //must be standing on the world entity
399 VectorCopy(bs->origin, start);
400 VectorCopy(bs->origin, end);
401 start[2] += 1;
402 end[2] -= 10;
403 trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs);
404 BotAI_Trace(&trace, start, mins, maxs, end, bs->client, MASK_SOLID);
405 if (trace.ent != ENTITYNUM_WORLD) return qfalse;
406 //the bot is in a position where it can chat
407 return qtrue;
408 }
409
410 /*
411 ==================
412 BotChat_EnterGame
413 ==================
414 */
BotChat_EnterGame(bot_state_t * bs)415 int BotChat_EnterGame(bot_state_t *bs) {
416 char name[32];
417 float rnd;
418
419 if (bot_nochat.integer) return qfalse;
420 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
421 //don't chat in teamplay
422 if (TeamPlayIsOn()) return qfalse;
423 // don't chat in tournament mode
424 if (gametype == GT_TOURNAMENT) return qfalse;
425 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_ENTEREXITGAME, 0, 1);
426 if (!bot_fastchat.integer) {
427 if (random() > rnd) return qfalse;
428 }
429 if (BotNumActivePlayers() <= 1) return qfalse;
430 if (!BotValidChatPosition(bs)) return qfalse;
431 BotAI_BotInitialChat(bs, "game_enter",
432 EasyClientName(bs->client, name, 32), // 0
433 BotRandomOpponentName(bs), // 1
434 "[invalid var]", // 2
435 "[invalid var]", // 3
436 BotMapTitle(), // 4
437 NULL);
438 bs->lastchat_time = FloatTime();
439 bs->chatto = CHAT_ALL;
440 return qtrue;
441 }
442
443 /*
444 ==================
445 BotChat_ExitGame
446 ==================
447 */
BotChat_ExitGame(bot_state_t * bs)448 int BotChat_ExitGame(bot_state_t *bs) {
449 char name[32];
450 float rnd;
451
452 if (bot_nochat.integer) return qfalse;
453 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
454 //don't chat in teamplay
455 if (TeamPlayIsOn()) return qfalse;
456 // don't chat in tournament mode
457 if (gametype == GT_TOURNAMENT) return qfalse;
458 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_ENTEREXITGAME, 0, 1);
459 if (!bot_fastchat.integer) {
460 if (random() > rnd) return qfalse;
461 }
462 if (BotNumActivePlayers() <= 1) return qfalse;
463 //
464 BotAI_BotInitialChat(bs, "game_exit",
465 EasyClientName(bs->client, name, 32), // 0
466 BotRandomOpponentName(bs), // 1
467 "[invalid var]", // 2
468 "[invalid var]", // 3
469 BotMapTitle(), // 4
470 NULL);
471 bs->lastchat_time = FloatTime();
472 bs->chatto = CHAT_ALL;
473 return qtrue;
474 }
475
476 /*
477 ==================
478 BotChat_StartLevel
479 ==================
480 */
BotChat_StartLevel(bot_state_t * bs)481 int BotChat_StartLevel(bot_state_t *bs) {
482 char name[32];
483 float rnd;
484
485 if (bot_nochat.integer) return qfalse;
486 if (BotIsObserver(bs)) return qfalse;
487 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
488 //don't chat in teamplay
489 if (TeamPlayIsOn()) {
490 trap_EA_Command(bs->client, "vtaunt");
491 return qfalse;
492 }
493 // don't chat in tournament mode
494 if (gametype == GT_TOURNAMENT) return qfalse;
495 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_STARTENDLEVEL, 0, 1);
496 if (!bot_fastchat.integer) {
497 if (random() > rnd) return qfalse;
498 }
499 if (BotNumActivePlayers() <= 1) return qfalse;
500 BotAI_BotInitialChat(bs, "level_start",
501 EasyClientName(bs->client, name, 32), // 0
502 NULL);
503 bs->lastchat_time = FloatTime();
504 bs->chatto = CHAT_ALL;
505 return qtrue;
506 }
507
508 /*
509 ==================
510 BotChat_EndLevel
511 ==================
512 */
BotChat_EndLevel(bot_state_t * bs)513 int BotChat_EndLevel(bot_state_t *bs) {
514 char name[32];
515 float rnd;
516
517 if (bot_nochat.integer) return qfalse;
518 if (BotIsObserver(bs)) return qfalse;
519 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
520 // teamplay
521 if (TeamPlayIsOn())
522 {
523 if (BotIsFirstInRankings(bs)) {
524 trap_EA_Command(bs->client, "vtaunt");
525 }
526 return qtrue;
527 }
528 // don't chat in tournament mode
529 if (gametype == GT_TOURNAMENT) return qfalse;
530 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_STARTENDLEVEL, 0, 1);
531 if (!bot_fastchat.integer) {
532 if (random() > rnd) return qfalse;
533 }
534 if (BotNumActivePlayers() <= 1) return qfalse;
535 //
536 if (BotIsFirstInRankings(bs)) {
537 BotAI_BotInitialChat(bs, "level_end_victory",
538 EasyClientName(bs->client, name, 32), // 0
539 BotRandomOpponentName(bs), // 1
540 "[invalid var]", // 2
541 BotLastClientInRankings(), // 3
542 BotMapTitle(), // 4
543 NULL);
544 }
545 else if (BotIsLastInRankings(bs)) {
546 BotAI_BotInitialChat(bs, "level_end_lose",
547 EasyClientName(bs->client, name, 32), // 0
548 BotRandomOpponentName(bs), // 1
549 BotFirstClientInRankings(), // 2
550 "[invalid var]", // 3
551 BotMapTitle(), // 4
552 NULL);
553 }
554 else {
555 BotAI_BotInitialChat(bs, "level_end",
556 EasyClientName(bs->client, name, 32), // 0
557 BotRandomOpponentName(bs), // 1
558 BotFirstClientInRankings(), // 2
559 BotLastClientInRankings(), // 3
560 BotMapTitle(), // 4
561 NULL);
562 }
563 bs->lastchat_time = FloatTime();
564 bs->chatto = CHAT_ALL;
565 return qtrue;
566 }
567
568 /*
569 ==================
570 BotChat_Death
571 ==================
572 */
BotChat_Death(bot_state_t * bs)573 int BotChat_Death(bot_state_t *bs) {
574 char name[32];
575 float rnd;
576
577 if (bot_nochat.integer) return qfalse;
578 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
579 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_DEATH, 0, 1);
580 // don't chat in tournament mode
581 if (gametype == GT_TOURNAMENT) return qfalse;
582 //if fast chatting is off
583 if (!bot_fastchat.integer) {
584 if (random() > rnd) return qfalse;
585 }
586 if (BotNumActivePlayers() <= 1) return qfalse;
587 //
588 if (bs->lastkilledby >= 0 && bs->lastkilledby < MAX_CLIENTS)
589 EasyClientName(bs->lastkilledby, name, 32);
590 else
591 strcpy(name, "[world]");
592 //
593 if (TeamPlayIsOn() && BotSameTeam(bs, bs->lastkilledby)) {
594 if (bs->lastkilledby == bs->client) return qfalse;
595 BotAI_BotInitialChat(bs, "death_teammate", name, NULL);
596 bs->chatto = CHAT_TEAM;
597 }
598 else
599 {
600 //teamplay
601 if (TeamPlayIsOn()) {
602 trap_EA_Command(bs->client, "vtaunt");
603 return qtrue;
604 }
605 //
606 if (bs->botdeathtype == MOD_WATER)
607 BotAI_BotInitialChat(bs, "death_drown", BotRandomOpponentName(bs), NULL);
608 else if (bs->botdeathtype == MOD_SLIME)
609 BotAI_BotInitialChat(bs, "death_slime", BotRandomOpponentName(bs), NULL);
610 else if (bs->botdeathtype == MOD_LAVA)
611 BotAI_BotInitialChat(bs, "death_lava", BotRandomOpponentName(bs), NULL);
612 else if (bs->botdeathtype == MOD_FALLING)
613 BotAI_BotInitialChat(bs, "death_cratered", BotRandomOpponentName(bs), NULL);
614 else if (bs->botsuicide || //all other suicides by own weapon
615 bs->botdeathtype == MOD_CRUSH ||
616 bs->botdeathtype == MOD_SUICIDE ||
617 bs->botdeathtype == MOD_TARGET_LASER ||
618 bs->botdeathtype == MOD_TRIGGER_HURT ||
619 bs->botdeathtype == MOD_UNKNOWN)
620 BotAI_BotInitialChat(bs, "death_suicide", BotRandomOpponentName(bs), NULL);
621 else if (bs->botdeathtype == MOD_TELEFRAG)
622 BotAI_BotInitialChat(bs, "death_telefrag", name, NULL);
623 #ifdef MISSIONPACK
624 else if (bs->botdeathtype == MOD_KAMIKAZE && trap_BotNumInitialChats(bs->cs, "death_kamikaze"))
625 BotAI_BotInitialChat(bs, "death_kamikaze", name, NULL);
626 #endif
627 else {
628 if ((bs->botdeathtype == MOD_GAUNTLET ||
629 bs->botdeathtype == MOD_RAILGUN ||
630 bs->botdeathtype == MOD_BFG ||
631 bs->botdeathtype == MOD_BFG_SPLASH) && random() < 0.5) {
632
633 if (bs->botdeathtype == MOD_GAUNTLET)
634 BotAI_BotInitialChat(bs, "death_gauntlet",
635 name, // 0
636 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
637 NULL);
638 else if (bs->botdeathtype == MOD_RAILGUN)
639 BotAI_BotInitialChat(bs, "death_rail",
640 name, // 0
641 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
642 NULL);
643 else
644 BotAI_BotInitialChat(bs, "death_bfg",
645 name, // 0
646 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
647 NULL);
648 }
649 //choose between insult and praise
650 else if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1)) {
651 BotAI_BotInitialChat(bs, "death_insult",
652 name, // 0
653 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
654 NULL);
655 }
656 else {
657 BotAI_BotInitialChat(bs, "death_praise",
658 name, // 0
659 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
660 NULL);
661 }
662 }
663 bs->chatto = CHAT_ALL;
664 }
665 bs->lastchat_time = FloatTime();
666 return qtrue;
667 }
668
669 /*
670 ==================
671 BotChat_Kill
672 ==================
673 */
BotChat_Kill(bot_state_t * bs)674 int BotChat_Kill(bot_state_t *bs) {
675 char name[32];
676 float rnd;
677
678 if (bot_nochat.integer) return qfalse;
679 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
680 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1);
681 // don't chat in tournament mode
682 if (gametype == GT_TOURNAMENT) return qfalse;
683 //if fast chat is off
684 if (!bot_fastchat.integer) {
685 if (random() > rnd) return qfalse;
686 }
687 if (bs->lastkilledplayer == bs->client) return qfalse;
688 if (BotNumActivePlayers() <= 1) return qfalse;
689 if (!BotValidChatPosition(bs)) return qfalse;
690 //
691 if (BotVisibleEnemies(bs)) return qfalse;
692 //
693 EasyClientName(bs->lastkilledplayer, name, 32);
694 //
695 bs->chatto = CHAT_ALL;
696 if (TeamPlayIsOn() && BotSameTeam(bs, bs->lastkilledplayer)) {
697 BotAI_BotInitialChat(bs, "kill_teammate", name, NULL);
698 bs->chatto = CHAT_TEAM;
699 }
700 else
701 {
702 //don't chat in teamplay
703 if (TeamPlayIsOn()) {
704 trap_EA_Command(bs->client, "vtaunt");
705 return qfalse; // don't wait
706 }
707 //
708 if (bs->enemydeathtype == MOD_GAUNTLET) {
709 BotAI_BotInitialChat(bs, "kill_gauntlet", name, NULL);
710 }
711 else if (bs->enemydeathtype == MOD_RAILGUN) {
712 BotAI_BotInitialChat(bs, "kill_rail", name, NULL);
713 }
714 else if (bs->enemydeathtype == MOD_TELEFRAG) {
715 BotAI_BotInitialChat(bs, "kill_telefrag", name, NULL);
716 }
717 #ifdef MISSIONPACK
718 else if (bs->botdeathtype == MOD_KAMIKAZE && trap_BotNumInitialChats(bs->cs, "kill_kamikaze"))
719 BotAI_BotInitialChat(bs, "kill_kamikaze", name, NULL);
720 #endif
721 //choose between insult and praise
722 else if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_INSULT, 0, 1)) {
723 BotAI_BotInitialChat(bs, "kill_insult", name, NULL);
724 }
725 else {
726 BotAI_BotInitialChat(bs, "kill_praise", name, NULL);
727 }
728 }
729 bs->lastchat_time = FloatTime();
730 return qtrue;
731 }
732
733 /*
734 ==================
735 BotChat_EnemySuicide
736 ==================
737 */
BotChat_EnemySuicide(bot_state_t * bs)738 int BotChat_EnemySuicide(bot_state_t *bs) {
739 char name[32];
740 float rnd;
741
742 if (bot_nochat.integer) return qfalse;
743 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
744 if (BotNumActivePlayers() <= 1) return qfalse;
745 //
746 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_KILL, 0, 1);
747 //don't chat in teamplay
748 if (TeamPlayIsOn()) return qfalse;
749 // don't chat in tournament mode
750 if (gametype == GT_TOURNAMENT) return qfalse;
751 //if fast chat is off
752 if (!bot_fastchat.integer) {
753 if (random() > rnd) return qfalse;
754 }
755 if (!BotValidChatPosition(bs)) return qfalse;
756 //
757 if (BotVisibleEnemies(bs)) return qfalse;
758 //
759 if (bs->enemy >= 0) EasyClientName(bs->enemy, name, 32);
760 else strcpy(name, "");
761 BotAI_BotInitialChat(bs, "enemy_suicide", name, NULL);
762 bs->lastchat_time = FloatTime();
763 bs->chatto = CHAT_ALL;
764 return qtrue;
765 }
766
767 /*
768 ==================
769 BotChat_HitTalking
770 ==================
771 */
BotChat_HitTalking(bot_state_t * bs)772 int BotChat_HitTalking(bot_state_t *bs) {
773 char name[32], *weap;
774 int lasthurt_client;
775 float rnd;
776
777 if (bot_nochat.integer) return qfalse;
778 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
779 if (BotNumActivePlayers() <= 1) return qfalse;
780 lasthurt_client = g_entities[bs->client].client->lasthurt_client;
781 if (!lasthurt_client) return qfalse;
782 if (lasthurt_client == bs->client) return qfalse;
783 //
784 if (lasthurt_client < 0 || lasthurt_client >= MAX_CLIENTS) return qfalse;
785 //
786 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_HITTALKING, 0, 1);
787 //don't chat in teamplay
788 if (TeamPlayIsOn()) return qfalse;
789 // don't chat in tournament mode
790 if (gametype == GT_TOURNAMENT) return qfalse;
791 //if fast chat is off
792 if (!bot_fastchat.integer) {
793 if (random() > rnd * 0.5) return qfalse;
794 }
795 if (!BotValidChatPosition(bs)) return qfalse;
796 //
797 ClientName(g_entities[bs->client].client->lasthurt_client, name, sizeof(name));
798 weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_client);
799 //
800 BotAI_BotInitialChat(bs, "hit_talking", name, weap, NULL);
801 bs->lastchat_time = FloatTime();
802 bs->chatto = CHAT_ALL;
803 return qtrue;
804 }
805
806 /*
807 ==================
808 BotChat_HitNoDeath
809 ==================
810 */
BotChat_HitNoDeath(bot_state_t * bs)811 int BotChat_HitNoDeath(bot_state_t *bs) {
812 char name[32], *weap;
813 float rnd;
814 int lasthurt_client;
815 aas_entityinfo_t entinfo;
816
817 lasthurt_client = g_entities[bs->client].client->lasthurt_client;
818 if (!lasthurt_client) return qfalse;
819 if (lasthurt_client == bs->client) return qfalse;
820 //
821 if (lasthurt_client < 0 || lasthurt_client >= MAX_CLIENTS) return qfalse;
822 //
823 if (bot_nochat.integer) return qfalse;
824 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
825 if (BotNumActivePlayers() <= 1) return qfalse;
826 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_HITNODEATH, 0, 1);
827 //don't chat in teamplay
828 if (TeamPlayIsOn()) return qfalse;
829 // don't chat in tournament mode
830 if (gametype == GT_TOURNAMENT) return qfalse;
831 //if fast chat is off
832 if (!bot_fastchat.integer) {
833 if (random() > rnd * 0.5) return qfalse;
834 }
835 if (!BotValidChatPosition(bs)) return qfalse;
836 //
837 if (BotVisibleEnemies(bs)) return qfalse;
838 //
839 BotEntityInfo(bs->enemy, &entinfo);
840 if (EntityIsShooting(&entinfo)) return qfalse;
841 //
842 ClientName(lasthurt_client, name, sizeof(name));
843 weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_mod);
844 //
845 BotAI_BotInitialChat(bs, "hit_nodeath", name, weap, NULL);
846 bs->lastchat_time = FloatTime();
847 bs->chatto = CHAT_ALL;
848 return qtrue;
849 }
850
851 /*
852 ==================
853 BotChat_HitNoKill
854 ==================
855 */
BotChat_HitNoKill(bot_state_t * bs)856 int BotChat_HitNoKill(bot_state_t *bs) {
857 char name[32], *weap;
858 float rnd;
859 aas_entityinfo_t entinfo;
860
861 if (bot_nochat.integer) return qfalse;
862 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
863 if (BotNumActivePlayers() <= 1) return qfalse;
864 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_HITNOKILL, 0, 1);
865 //don't chat in teamplay
866 if (TeamPlayIsOn()) return qfalse;
867 // don't chat in tournament mode
868 if (gametype == GT_TOURNAMENT) return qfalse;
869 //if fast chat is off
870 if (!bot_fastchat.integer) {
871 if (random() > rnd * 0.5) return qfalse;
872 }
873 if (!BotValidChatPosition(bs)) return qfalse;
874 //
875 if (BotVisibleEnemies(bs)) return qfalse;
876 //
877 BotEntityInfo(bs->enemy, &entinfo);
878 if (EntityIsShooting(&entinfo)) return qfalse;
879 //
880 ClientName(bs->enemy, name, sizeof(name));
881 weap = BotWeaponNameForMeansOfDeath(g_entities[bs->enemy].client->lasthurt_mod);
882 //
883 BotAI_BotInitialChat(bs, "hit_nokill", name, weap, NULL);
884 bs->lastchat_time = FloatTime();
885 bs->chatto = CHAT_ALL;
886 return qtrue;
887 }
888
889 /*
890 ==================
891 BotChat_Random
892 ==================
893 */
BotChat_Random(bot_state_t * bs)894 int BotChat_Random(bot_state_t *bs) {
895 float rnd;
896 char name[32];
897
898 if (bot_nochat.integer) return qfalse;
899 if (BotIsObserver(bs)) return qfalse;
900 if (bs->lastchat_time > FloatTime() - TIME_BETWEENCHATTING) return qfalse;
901 // don't chat in tournament mode
902 if (gametype == GT_TOURNAMENT) return qfalse;
903 //don't chat when doing something important :)
904 if (bs->ltgtype == LTG_TEAMHELP ||
905 bs->ltgtype == LTG_TEAMACCOMPANY ||
906 bs->ltgtype == LTG_RUSHBASE) return qfalse;
907 //
908 rnd = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_RANDOM, 0, 1);
909 if (random() > bs->thinktime * 0.1) return qfalse;
910 if (!bot_fastchat.integer) {
911 if (random() > rnd) return qfalse;
912 if (random() > 0.25) return qfalse;
913 }
914 if (BotNumActivePlayers() <= 1) return qfalse;
915 //
916 if (!BotValidChatPosition(bs)) return qfalse;
917 //
918 if (BotVisibleEnemies(bs)) return qfalse;
919 //
920 if (bs->lastkilledplayer == bs->client) {
921 strcpy(name, BotRandomOpponentName(bs));
922 }
923 else {
924 EasyClientName(bs->lastkilledplayer, name, sizeof(name));
925 }
926 if (TeamPlayIsOn()) {
927 trap_EA_Command(bs->client, "vtaunt");
928 return qfalse; // don't wait
929 }
930 //
931 if (random() < trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CHAT_MISC, 0, 1)) {
932 BotAI_BotInitialChat(bs, "random_misc",
933 BotRandomOpponentName(bs), // 0
934 name, // 1
935 "[invalid var]", // 2
936 "[invalid var]", // 3
937 BotMapTitle(), // 4
938 BotRandomWeaponName(), // 5
939 NULL);
940 }
941 else {
942 BotAI_BotInitialChat(bs, "random_insult",
943 BotRandomOpponentName(bs), // 0
944 name, // 1
945 "[invalid var]", // 2
946 "[invalid var]", // 3
947 BotMapTitle(), // 4
948 BotRandomWeaponName(), // 5
949 NULL);
950 }
951 bs->lastchat_time = FloatTime();
952 bs->chatto = CHAT_ALL;
953 return qtrue;
954 }
955
956 /*
957 ==================
958 BotChatTime
959 ==================
960 */
BotChatTime(bot_state_t * bs)961 float BotChatTime(bot_state_t *bs) {
962 int cpm;
963
964 cpm = trap_Characteristic_BInteger(bs->character, CHARACTERISTIC_CHAT_CPM, 1, 4000);
965
966 return 2.0; //(float) trap_BotChatLength(bs->cs) * 30 / cpm;
967 }
968
969 /*
970 ==================
971 BotChatTest
972 ==================
973 */
BotChatTest(bot_state_t * bs)974 void BotChatTest(bot_state_t *bs) {
975
976 char name[32];
977 char *weap;
978 int num, i;
979
980 num = trap_BotNumInitialChats(bs->cs, "game_enter");
981 for (i = 0; i < num; i++)
982 {
983 BotAI_BotInitialChat(bs, "game_enter",
984 EasyClientName(bs->client, name, 32), // 0
985 BotRandomOpponentName(bs), // 1
986 "[invalid var]", // 2
987 "[invalid var]", // 3
988 BotMapTitle(), // 4
989 NULL);
990 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
991 }
992 num = trap_BotNumInitialChats(bs->cs, "game_exit");
993 for (i = 0; i < num; i++)
994 {
995 BotAI_BotInitialChat(bs, "game_exit",
996 EasyClientName(bs->client, name, 32), // 0
997 BotRandomOpponentName(bs), // 1
998 "[invalid var]", // 2
999 "[invalid var]", // 3
1000 BotMapTitle(), // 4
1001 NULL);
1002 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1003 }
1004 num = trap_BotNumInitialChats(bs->cs, "level_start");
1005 for (i = 0; i < num; i++)
1006 {
1007 BotAI_BotInitialChat(bs, "level_start",
1008 EasyClientName(bs->client, name, 32), // 0
1009 NULL);
1010 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1011 }
1012 num = trap_BotNumInitialChats(bs->cs, "level_end_victory");
1013 for (i = 0; i < num; i++)
1014 {
1015 BotAI_BotInitialChat(bs, "level_end_victory",
1016 EasyClientName(bs->client, name, 32), // 0
1017 BotRandomOpponentName(bs), // 1
1018 BotFirstClientInRankings(), // 2
1019 BotLastClientInRankings(), // 3
1020 BotMapTitle(), // 4
1021 NULL);
1022 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1023 }
1024 num = trap_BotNumInitialChats(bs->cs, "level_end_lose");
1025 for (i = 0; i < num; i++)
1026 {
1027 BotAI_BotInitialChat(bs, "level_end_lose",
1028 EasyClientName(bs->client, name, 32), // 0
1029 BotRandomOpponentName(bs), // 1
1030 BotFirstClientInRankings(), // 2
1031 BotLastClientInRankings(), // 3
1032 BotMapTitle(), // 4
1033 NULL);
1034 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1035 }
1036 num = trap_BotNumInitialChats(bs->cs, "level_end");
1037 for (i = 0; i < num; i++)
1038 {
1039 BotAI_BotInitialChat(bs, "level_end",
1040 EasyClientName(bs->client, name, 32), // 0
1041 BotRandomOpponentName(bs), // 1
1042 BotFirstClientInRankings(), // 2
1043 BotLastClientInRankings(), // 3
1044 BotMapTitle(), // 4
1045 NULL);
1046 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1047 }
1048 EasyClientName(bs->lastkilledby, name, sizeof(name));
1049 num = trap_BotNumInitialChats(bs->cs, "death_drown");
1050 for (i = 0; i < num; i++)
1051 {
1052 //
1053 BotAI_BotInitialChat(bs, "death_drown", name, NULL);
1054 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1055 }
1056 num = trap_BotNumInitialChats(bs->cs, "death_slime");
1057 for (i = 0; i < num; i++)
1058 {
1059 BotAI_BotInitialChat(bs, "death_slime", name, NULL);
1060 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1061 }
1062 num = trap_BotNumInitialChats(bs->cs, "death_lava");
1063 for (i = 0; i < num; i++)
1064 {
1065 BotAI_BotInitialChat(bs, "death_lava", name, NULL);
1066 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1067 }
1068 num = trap_BotNumInitialChats(bs->cs, "death_cratered");
1069 for (i = 0; i < num; i++)
1070 {
1071 BotAI_BotInitialChat(bs, "death_cratered", name, NULL);
1072 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1073 }
1074 num = trap_BotNumInitialChats(bs->cs, "death_suicide");
1075 for (i = 0; i < num; i++)
1076 {
1077 BotAI_BotInitialChat(bs, "death_suicide", name, NULL);
1078 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1079 }
1080 num = trap_BotNumInitialChats(bs->cs, "death_telefrag");
1081 for (i = 0; i < num; i++)
1082 {
1083 BotAI_BotInitialChat(bs, "death_telefrag", name, NULL);
1084 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1085 }
1086 num = trap_BotNumInitialChats(bs->cs, "death_gauntlet");
1087 for (i = 0; i < num; i++)
1088 {
1089 BotAI_BotInitialChat(bs, "death_gauntlet",
1090 name, // 0
1091 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
1092 NULL);
1093 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1094 }
1095 num = trap_BotNumInitialChats(bs->cs, "death_rail");
1096 for (i = 0; i < num; i++)
1097 {
1098 BotAI_BotInitialChat(bs, "death_rail",
1099 name, // 0
1100 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
1101 NULL);
1102 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1103 }
1104 num = trap_BotNumInitialChats(bs->cs, "death_bfg");
1105 for (i = 0; i < num; i++)
1106 {
1107 BotAI_BotInitialChat(bs, "death_bfg",
1108 name, // 0
1109 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
1110 NULL);
1111 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1112 }
1113 num = trap_BotNumInitialChats(bs->cs, "death_insult");
1114 for (i = 0; i < num; i++)
1115 {
1116 BotAI_BotInitialChat(bs, "death_insult",
1117 name, // 0
1118 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
1119 NULL);
1120 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1121 }
1122 num = trap_BotNumInitialChats(bs->cs, "death_praise");
1123 for (i = 0; i < num; i++)
1124 {
1125 BotAI_BotInitialChat(bs, "death_praise",
1126 name, // 0
1127 BotWeaponNameForMeansOfDeath(bs->botdeathtype), // 1
1128 NULL);
1129 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1130 }
1131 //
1132 EasyClientName(bs->lastkilledplayer, name, 32);
1133 //
1134 num = trap_BotNumInitialChats(bs->cs, "kill_gauntlet");
1135 for (i = 0; i < num; i++)
1136 {
1137 //
1138 BotAI_BotInitialChat(bs, "kill_gauntlet", name, NULL);
1139 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1140 }
1141 num = trap_BotNumInitialChats(bs->cs, "kill_rail");
1142 for (i = 0; i < num; i++)
1143 {
1144 BotAI_BotInitialChat(bs, "kill_rail", name, NULL);
1145 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1146 }
1147 num = trap_BotNumInitialChats(bs->cs, "kill_telefrag");
1148 for (i = 0; i < num; i++)
1149 {
1150 BotAI_BotInitialChat(bs, "kill_telefrag", name, NULL);
1151 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1152 }
1153 num = trap_BotNumInitialChats(bs->cs, "kill_insult");
1154 for (i = 0; i < num; i++)
1155 {
1156 BotAI_BotInitialChat(bs, "kill_insult", name, NULL);
1157 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1158 }
1159 num = trap_BotNumInitialChats(bs->cs, "kill_praise");
1160 for (i = 0; i < num; i++)
1161 {
1162 BotAI_BotInitialChat(bs, "kill_praise", name, NULL);
1163 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1164 }
1165 num = trap_BotNumInitialChats(bs->cs, "enemy_suicide");
1166 for (i = 0; i < num; i++)
1167 {
1168 BotAI_BotInitialChat(bs, "enemy_suicide", name, NULL);
1169 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1170 }
1171 ClientName(g_entities[bs->client].client->lasthurt_client, name, sizeof(name));
1172 weap = BotWeaponNameForMeansOfDeath(g_entities[bs->client].client->lasthurt_client);
1173 num = trap_BotNumInitialChats(bs->cs, "hit_talking");
1174 for (i = 0; i < num; i++)
1175 {
1176 BotAI_BotInitialChat(bs, "hit_talking", name, weap, NULL);
1177 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1178 }
1179 num = trap_BotNumInitialChats(bs->cs, "hit_nodeath");
1180 for (i = 0; i < num; i++)
1181 {
1182 BotAI_BotInitialChat(bs, "hit_nodeath", name, weap, NULL);
1183 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1184 }
1185 num = trap_BotNumInitialChats(bs->cs, "hit_nokill");
1186 for (i = 0; i < num; i++)
1187 {
1188 BotAI_BotInitialChat(bs, "hit_nokill", name, weap, NULL);
1189 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1190 }
1191 //
1192 if (bs->lastkilledplayer == bs->client) {
1193 strcpy(name, BotRandomOpponentName(bs));
1194 }
1195 else {
1196 EasyClientName(bs->lastkilledplayer, name, sizeof(name));
1197 }
1198 //
1199 num = trap_BotNumInitialChats(bs->cs, "random_misc");
1200 for (i = 0; i < num; i++)
1201 {
1202 //
1203 BotAI_BotInitialChat(bs, "random_misc",
1204 BotRandomOpponentName(bs), // 0
1205 name, // 1
1206 "[invalid var]", // 2
1207 "[invalid var]", // 3
1208 BotMapTitle(), // 4
1209 BotRandomWeaponName(), // 5
1210 NULL);
1211 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1212 }
1213 num = trap_BotNumInitialChats(bs->cs, "random_insult");
1214 for (i = 0; i < num; i++)
1215 {
1216 BotAI_BotInitialChat(bs, "random_insult",
1217 BotRandomOpponentName(bs), // 0
1218 name, // 1
1219 "[invalid var]", // 2
1220 "[invalid var]", // 3
1221 BotMapTitle(), // 4
1222 BotRandomWeaponName(), // 5
1223 NULL);
1224 trap_BotEnterChat(bs->cs, 0, CHAT_ALL);
1225 }
1226 }
1227