1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7
8 This file is part of the OpenJK source code.
9
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13
14 This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23
24 #include "g_local.h"
25 #include "qcommon/q_shared.h"
26 #include "botlib/botlib.h"
27 #include "ai_main.h"
28
29 #ifdef BOT_ZMALLOC
30 #define MAX_BALLOC 8192
31
32 void *BAllocList[MAX_BALLOC];
33 #endif
34
35 char gBotChatBuffer[MAX_CLIENTS][MAX_CHAT_BUFFER_SIZE];
36
B_TempAlloc(int size)37 void *B_TempAlloc(int size)
38 {
39 return BG_TempAlloc(size);
40 }
41
B_TempFree(int size)42 void B_TempFree(int size)
43 {
44 BG_TempFree(size);
45 }
46
47
B_Alloc(int size)48 void *B_Alloc(int size)
49 {
50 #ifdef BOT_ZMALLOC
51 void *ptr = NULL;
52 int i = 0;
53
54 #ifdef BOTMEMTRACK
55 int free = 0;
56 int used = 0;
57
58 while (i < MAX_BALLOC)
59 {
60 if (!BAllocList[i])
61 {
62 free++;
63 }
64 else
65 {
66 used++;
67 }
68
69 i++;
70 }
71
72 trap->Print("Allocations used: %i\nFree allocation slots: %i\n", used, free);
73
74 i = 0;
75 #endif
76
77 ptr = trap->BotGetMemoryGame(size);
78
79 while (i < MAX_BALLOC)
80 {
81 if (!BAllocList[i])
82 {
83 BAllocList[i] = ptr;
84 break;
85 }
86 i++;
87 }
88
89 if (i == MAX_BALLOC)
90 {
91 //If this happens we'll have to rely on this chunk being freed manually with B_Free, which it hopefully will be
92 #ifdef DEBUG
93 trap->Print("WARNING: MAXIMUM B_ALLOC ALLOCATIONS EXCEEDED\n");
94 #endif
95 }
96
97 return ptr;
98 #else
99
100 return BG_Alloc(size);
101
102 #endif
103 }
104
B_Free(void * ptr)105 void B_Free(void *ptr)
106 {
107 #ifdef BOT_ZMALLOC
108 int i = 0;
109
110 #ifdef BOTMEMTRACK
111 int free = 0;
112 int used = 0;
113
114 while (i < MAX_BALLOC)
115 {
116 if (!BAllocList[i])
117 {
118 free++;
119 }
120 else
121 {
122 used++;
123 }
124
125 i++;
126 }
127
128 trap->Print("Allocations used: %i\nFree allocation slots: %i\n", used, free);
129
130 i = 0;
131 #endif
132
133 while (i < MAX_BALLOC)
134 {
135 if (BAllocList[i] == ptr)
136 {
137 BAllocList[i] = NULL;
138 break;
139 }
140
141 i++;
142 }
143
144 if (i == MAX_BALLOC)
145 {
146 //Likely because the limit was exceeded and we're now freeing the chunk manually as we hoped would happen
147 #ifdef DEBUG
148 trap->Print("WARNING: Freeing allocation which is not in the allocation structure\n");
149 #endif
150 }
151
152 trap->BotFreeMemoryGame(ptr);
153 #endif
154 }
155
B_InitAlloc(void)156 void B_InitAlloc(void)
157 {
158 #ifdef BOT_ZMALLOC
159 memset(BAllocList, 0, sizeof(BAllocList));
160 #endif
161
162 memset(gWPArray, 0, sizeof(gWPArray));
163 }
164
B_CleanupAlloc(void)165 void B_CleanupAlloc(void)
166 {
167 #ifdef BOT_ZMALLOC
168 int i = 0;
169
170 while (i < MAX_BALLOC)
171 {
172 if (BAllocList[i])
173 {
174 trap->BotFreeMemoryGame(BAllocList[i]);
175 BAllocList[i] = NULL;
176 }
177
178 i++;
179 }
180 #endif
181 }
182
GetValueGroup(char * buf,char * group,char * outbuf)183 int GetValueGroup(char *buf, char *group, char *outbuf)
184 {
185 char *place, *placesecond;
186 int failure;
187 int i;
188 int startpoint, startletter;
189 int subg = 0;
190
191 i = 0;
192
193 place = strstr(buf, group);
194
195 if (!place)
196 {
197 return 0;
198 }
199
200 startpoint = place - buf + strlen(group) + 1;
201 startletter = (place - buf) - 1;
202
203 failure = 0;
204
205 while (buf[startpoint+1] != '{' || buf[startletter] != '\n')
206 {
207 placesecond = strstr(place+1, group);
208
209 if (placesecond)
210 {
211 startpoint += (placesecond - place);
212 startletter += (placesecond - place);
213 place = placesecond;
214 }
215 else
216 {
217 failure = 1;
218 break;
219 }
220 }
221
222 if (failure)
223 {
224 return 0;
225 }
226
227 //we have found the proper group name if we made it here, so find the opening brace and read into the outbuf
228 //until hitting the end brace
229
230 while (buf[startpoint] != '{')
231 {
232 startpoint++;
233 }
234
235 startpoint++;
236
237 while (buf[startpoint] != '}' || subg)
238 {
239 if (buf[startpoint] == '{')
240 {
241 subg++;
242 }
243 else if (buf[startpoint] == '}')
244 {
245 subg--;
246 }
247 outbuf[i] = buf[startpoint];
248 i++;
249 startpoint++;
250 }
251 outbuf[i] = '\0';
252
253 return 1;
254 }
255
GetPairedValue(char * buf,char * key,char * outbuf)256 int GetPairedValue(char *buf, char *key, char *outbuf)
257 {
258 char *place, *placesecond;
259 int startpoint, startletter;
260 int i, found;
261
262 if (!buf || !key || !outbuf)
263 {
264 return 0;
265 }
266
267 i = 0;
268
269 while (buf[i] && buf[i] != '\0')
270 {
271 if (buf[i] == '/')
272 {
273 if (buf[i+1] && buf[i+1] != '\0' && buf[i+1] == '/')
274 {
275 while (buf[i] != '\n')
276 {
277 buf[i] = '/';
278 i++;
279 }
280 }
281 }
282 i++;
283 }
284
285 place = strstr(buf, key);
286
287 if (!place)
288 {
289 return 0;
290 }
291 //tab == 9
292 startpoint = place - buf + strlen(key);
293 startletter = (place - buf) - 1;
294
295 found = 0;
296
297 while (!found)
298 {
299 if (startletter == 0 || !buf[startletter] || buf[startletter] == '\0' || buf[startletter] == 9 || buf[startletter] == ' ' || buf[startletter] == '\n')
300 {
301 if (buf[startpoint] == '\0' || buf[startpoint] == 9 || buf[startpoint] == ' ' || buf[startpoint] == '\n')
302 {
303 found = 1;
304 break;
305 }
306 }
307
308 placesecond = strstr(place+1, key);
309
310 if (placesecond)
311 {
312 startpoint += placesecond - place;
313 startletter += placesecond - place;
314 place = placesecond;
315 }
316 else
317 {
318 place = NULL;
319 break;
320 }
321
322 }
323
324 if (!found || !place || !buf[startpoint] || buf[startpoint] == '\0')
325 {
326 return 0;
327 }
328
329 while (buf[startpoint] == ' ' || buf[startpoint] == 9 || buf[startpoint] == '\n')
330 {
331 startpoint++;
332 }
333
334 i = 0;
335
336 while (buf[startpoint] && buf[startpoint] != '\0' && buf[startpoint] != '\n')
337 {
338 outbuf[i] = buf[startpoint];
339 i++;
340 startpoint++;
341 }
342
343 outbuf[i] = '\0';
344
345 return 1;
346 }
347
BotDoChat(bot_state_t * bs,char * section,int always)348 int BotDoChat(bot_state_t *bs, char *section, int always)
349 {
350 char *chatgroup;
351 int rVal;
352 int inc_1;
353 int inc_2;
354 int inc_n;
355 int lines;
356 int checkedline;
357 int getthisline;
358 gentity_t *cobject;
359
360 if (!bs->canChat)
361 {
362 return 0;
363 }
364
365 if (bs->doChat)
366 { //already have a chat scheduled
367 return 0;
368 }
369
370 if (trap->Cvar_VariableIntegerValue("se_language"))
371 { //no chatting unless English.
372 return 0;
373 }
374
375 if (Q_irand(1, 10) > bs->chatFrequency && !always)
376 {
377 return 0;
378 }
379
380 bs->chatTeam = 0;
381
382 chatgroup = (char *)B_TempAlloc(MAX_CHAT_BUFFER_SIZE);
383
384 rVal = GetValueGroup(gBotChatBuffer[bs->client], section, chatgroup);
385
386 if (!rVal) //the bot has no group defined for the specified chat event
387 {
388 B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup
389 return 0;
390 }
391
392 inc_1 = 0;
393 inc_2 = 2;
394
395 while (chatgroup[inc_2] && chatgroup[inc_2] != '\0')
396 {
397 if (chatgroup[inc_2] != 13 && chatgroup[inc_2] != 9)
398 {
399 chatgroup[inc_1] = chatgroup[inc_2];
400 inc_1++;
401 }
402 inc_2++;
403 }
404 chatgroup[inc_1] = '\0';
405
406 inc_1 = 0;
407
408 lines = 0;
409
410 while (chatgroup[inc_1] && chatgroup[inc_1] != '\0')
411 {
412 if (chatgroup[inc_1] == '\n')
413 {
414 lines++;
415 }
416 inc_1++;
417 }
418
419 if (!lines)
420 {
421 B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup
422 return 0;
423 }
424
425 getthisline = Q_irand(0, (lines+1));
426
427 if (getthisline < 1)
428 {
429 getthisline = 1;
430 }
431 if (getthisline > lines)
432 {
433 getthisline = lines;
434 }
435
436 checkedline = 1;
437
438 inc_1 = 0;
439
440 while (checkedline != getthisline)
441 {
442 if (chatgroup[inc_1] && chatgroup[inc_1] != '\0')
443 {
444 if (chatgroup[inc_1] == '\n')
445 {
446 inc_1++;
447 checkedline++;
448 }
449 }
450
451 if (checkedline == getthisline)
452 {
453 break;
454 }
455
456 inc_1++;
457 }
458
459 //we're at the starting position of the desired line here
460 inc_2 = 0;
461
462 while (chatgroup[inc_1] != '\n')
463 {
464 chatgroup[inc_2] = chatgroup[inc_1];
465 inc_2++;
466 inc_1++;
467 }
468 chatgroup[inc_2] = '\0';
469
470 //trap->EA_Say(bs->client, chatgroup);
471 inc_1 = 0;
472 inc_2 = 0;
473
474 if (strlen(chatgroup) > MAX_CHAT_LINE_SIZE)
475 {
476 B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup
477 return 0;
478 }
479
480 while (chatgroup[inc_1])
481 {
482 if (chatgroup[inc_1] == '%' && chatgroup[inc_1+1] != '%')
483 {
484 inc_1++;
485
486 if (chatgroup[inc_1] == 's' && bs->chatObject)
487 {
488 cobject = bs->chatObject;
489 }
490 else if (chatgroup[inc_1] == 'a' && bs->chatAltObject)
491 {
492 cobject = bs->chatAltObject;
493 }
494 else
495 {
496 cobject = NULL;
497 }
498
499 if (cobject && cobject->client)
500 {
501 inc_n = 0;
502
503 while (cobject->client->pers.netname[inc_n])
504 {
505 bs->currentChat[inc_2] = cobject->client->pers.netname[inc_n];
506 inc_2++;
507 inc_n++;
508 }
509 inc_2--; //to make up for the auto-increment below
510 }
511 }
512 else
513 {
514 bs->currentChat[inc_2] = chatgroup[inc_1];
515 }
516 inc_2++;
517 inc_1++;
518 }
519 bs->currentChat[inc_2] = '\0';
520
521 if (strcmp(section, "GeneralGreetings") == 0)
522 {
523 bs->doChat = 2;
524 }
525 else
526 {
527 bs->doChat = 1;
528 }
529 bs->chatTime_stored = (strlen(bs->currentChat)*45)+Q_irand(1300, 1500);
530 bs->chatTime = level.time + bs->chatTime_stored;
531
532 B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup
533
534 return 1;
535 }
536
ParseEmotionalAttachments(bot_state_t * bs,char * buf)537 void ParseEmotionalAttachments(bot_state_t *bs, char *buf)
538 {
539 int i = 0;
540 int i_c = 0;
541 char tbuf[16];
542
543 while (buf[i] && buf[i] != '}')
544 {
545 while (buf[i] == ' ' || buf[i] == '{' || buf[i] == 9 || buf[i] == 13 || buf[i] == '\n')
546 {
547 i++;
548 }
549
550 if (buf[i] && buf[i] != '}')
551 {
552 i_c = 0;
553 while (buf[i] != '{' && buf[i] != 9 && buf[i] != 13 && buf[i] != '\n')
554 {
555 bs->loved[bs->lovednum].name[i_c] = buf[i];
556 i_c++;
557 i++;
558 }
559 bs->loved[bs->lovednum].name[i_c] = '\0';
560
561 while (buf[i] == ' ' || buf[i] == '{' || buf[i] == 9 || buf[i] == 13 || buf[i] == '\n')
562 {
563 i++;
564 }
565
566 i_c = 0;
567
568 while (buf[i] != '{' && buf[i] != 9 && buf[i] != 13 && buf[i] != '\n')
569 {
570 tbuf[i_c] = buf[i];
571 i_c++;
572 i++;
573 }
574 tbuf[i_c] = '\0';
575
576 bs->loved[bs->lovednum].level = atoi(tbuf);
577
578 bs->lovednum++;
579 }
580 else
581 {
582 break;
583 }
584
585 if (bs->lovednum >= MAX_LOVED_ONES)
586 {
587 return;
588 }
589
590 i++;
591 }
592 }
593
ReadChatGroups(bot_state_t * bs,char * buf)594 int ReadChatGroups(bot_state_t *bs, char *buf)
595 {
596 char *cgroupbegin;
597 int cgbplace;
598 int i;
599
600 cgroupbegin = strstr(buf, "BEGIN_CHAT_GROUPS");
601
602 if (!cgroupbegin)
603 {
604 return 0;
605 }
606
607 if (strlen(cgroupbegin) >= MAX_CHAT_BUFFER_SIZE)
608 {
609 trap->Print(S_COLOR_RED "Error: Personality chat section exceeds max size\n");
610 return 0;
611 }
612
613 cgbplace = cgroupbegin - buf+1;
614
615 while (buf[cgbplace] != '\n')
616 {
617 cgbplace++;
618 }
619
620 i = 0;
621
622 while (buf[cgbplace] && buf[cgbplace] != '\0')
623 {
624 gBotChatBuffer[bs->client][i] = buf[cgbplace];
625 i++;
626 cgbplace++;
627 }
628
629 gBotChatBuffer[bs->client][i] = '\0';
630
631 return 1;
632 }
633
BotUtilizePersonality(bot_state_t * bs)634 void BotUtilizePersonality(bot_state_t *bs)
635 {
636 fileHandle_t f;
637 int len, rlen;
638 int failed;
639 int i;
640 //char buf[131072];
641 char *buf = (char *)B_TempAlloc(131072);
642 char *readbuf, *group;
643
644 len = trap->FS_Open(bs->settings.personalityfile, &f, FS_READ);
645
646 failed = 0;
647
648 if (!f)
649 {
650 trap->Print(S_COLOR_RED "Error: Specified personality not found\n");
651 B_TempFree(131072); //buf
652 return;
653 }
654
655 if (len >= 131072)
656 {
657 trap->Print(S_COLOR_RED "Personality file exceeds maximum length\n");
658 B_TempFree(131072); //buf
659 trap->FS_Close( f );
660 return;
661 }
662
663 trap->FS_Read(buf, len, f);
664
665 rlen = len;
666
667 while (len < 131072)
668 { //kill all characters after the file length, since sometimes FS_Read doesn't do that entirely (or so it seems)
669 buf[len] = '\0';
670 len++;
671 }
672
673 len = rlen;
674
675 readbuf = (char *)B_TempAlloc(1024);
676 group = (char *)B_TempAlloc(65536);
677
678 if (!GetValueGroup(buf, "GeneralBotInfo", group))
679 {
680 trap->Print(S_COLOR_RED "Personality file contains no GeneralBotInfo group\n");
681 failed = 1; //set failed so we know to set everything to default values
682 }
683
684 if (!failed && GetPairedValue(group, "reflex", readbuf))
685 {
686 bs->skills.reflex = atoi(readbuf);
687 }
688 else
689 {
690 bs->skills.reflex = 100; //default
691 }
692
693 if (!failed && GetPairedValue(group, "accuracy", readbuf))
694 {
695 bs->skills.accuracy = atof(readbuf);
696 }
697 else
698 {
699 bs->skills.accuracy = 10; //default
700 }
701
702 if (!failed && GetPairedValue(group, "turnspeed", readbuf))
703 {
704 bs->skills.turnspeed = atof(readbuf);
705 }
706 else
707 {
708 bs->skills.turnspeed = 0.01f; //default
709 }
710
711 if (!failed && GetPairedValue(group, "turnspeed_combat", readbuf))
712 {
713 bs->skills.turnspeed_combat = atof(readbuf);
714 }
715 else
716 {
717 bs->skills.turnspeed_combat = 0.05f; //default
718 }
719
720 if (!failed && GetPairedValue(group, "maxturn", readbuf))
721 {
722 bs->skills.maxturn = atof(readbuf);
723 }
724 else
725 {
726 bs->skills.maxturn = 360; //default
727 }
728
729 if (!failed && GetPairedValue(group, "perfectaim", readbuf))
730 {
731 bs->skills.perfectaim = atoi(readbuf);
732 }
733 else
734 {
735 bs->skills.perfectaim = 0; //default
736 }
737
738 if (!failed && GetPairedValue(group, "chatability", readbuf))
739 {
740 bs->canChat = atoi(readbuf);
741 }
742 else
743 {
744 bs->canChat = 0; //default
745 }
746
747 if (!failed && GetPairedValue(group, "chatfrequency", readbuf))
748 {
749 bs->chatFrequency = atoi(readbuf);
750 }
751 else
752 {
753 bs->chatFrequency = 5; //default
754 }
755
756 if (!failed && GetPairedValue(group, "hatelevel", readbuf))
757 {
758 bs->loved_death_thresh = atoi(readbuf);
759 }
760 else
761 {
762 bs->loved_death_thresh = 3; //default
763 }
764
765 if (!failed && GetPairedValue(group, "camper", readbuf))
766 {
767 bs->isCamper = atoi(readbuf);
768 }
769 else
770 {
771 bs->isCamper = 0; //default
772 }
773
774 if (!failed && GetPairedValue(group, "saberspecialist", readbuf))
775 {
776 bs->saberSpecialist = atoi(readbuf);
777 }
778 else
779 {
780 bs->saberSpecialist = 0; //default
781 }
782
783 if (!failed && GetPairedValue(group, "forceinfo", readbuf))
784 {
785 Com_sprintf(bs->forceinfo, sizeof(bs->forceinfo), "%s\0", readbuf);
786 }
787 else
788 {
789 Com_sprintf(bs->forceinfo, sizeof(bs->forceinfo), "%s\0", DEFAULT_FORCEPOWERS);
790 }
791
792 i = 0;
793
794 while (i < MAX_CHAT_BUFFER_SIZE)
795 { //clear out the chat buffer for this bot
796 gBotChatBuffer[bs->client][i] = '\0';
797 i++;
798 }
799
800 if (bs->canChat)
801 {
802 if (!ReadChatGroups(bs, buf))
803 {
804 bs->canChat = 0;
805 }
806 }
807
808 if (GetValueGroup(buf, "BotWeaponWeights", group))
809 {
810 if (GetPairedValue(group, "WP_STUN_BATON", readbuf))
811 {
812 bs->botWeaponWeights[WP_STUN_BATON] = atoi(readbuf);
813 bs->botWeaponWeights[WP_MELEE] = bs->botWeaponWeights[WP_STUN_BATON];
814 }
815
816 if (GetPairedValue(group, "WP_SABER", readbuf))
817 {
818 bs->botWeaponWeights[WP_SABER] = atoi(readbuf);
819 }
820
821 if (GetPairedValue(group, "WP_BRYAR_PISTOL", readbuf))
822 {
823 bs->botWeaponWeights[WP_BRYAR_PISTOL] = atoi(readbuf);
824 }
825
826 if (GetPairedValue(group, "WP_BLASTER", readbuf))
827 {
828 bs->botWeaponWeights[WP_BLASTER] = atoi(readbuf);
829 }
830
831 if (GetPairedValue(group, "WP_DISRUPTOR", readbuf))
832 {
833 bs->botWeaponWeights[WP_DISRUPTOR] = atoi(readbuf);
834 }
835
836 if (GetPairedValue(group, "WP_BOWCASTER", readbuf))
837 {
838 bs->botWeaponWeights[WP_BOWCASTER] = atoi(readbuf);
839 }
840
841 if (GetPairedValue(group, "WP_REPEATER", readbuf))
842 {
843 bs->botWeaponWeights[WP_REPEATER] = atoi(readbuf);
844 }
845
846 if (GetPairedValue(group, "WP_DEMP2", readbuf))
847 {
848 bs->botWeaponWeights[WP_DEMP2] = atoi(readbuf);
849 }
850
851 if (GetPairedValue(group, "WP_FLECHETTE", readbuf))
852 {
853 bs->botWeaponWeights[WP_FLECHETTE] = atoi(readbuf);
854 }
855
856 if (GetPairedValue(group, "WP_ROCKET_LAUNCHER", readbuf))
857 {
858 bs->botWeaponWeights[WP_ROCKET_LAUNCHER] = atoi(readbuf);
859 }
860
861 if (GetPairedValue(group, "WP_THERMAL", readbuf))
862 {
863 bs->botWeaponWeights[WP_THERMAL] = atoi(readbuf);
864 }
865
866 if (GetPairedValue(group, "WP_TRIP_MINE", readbuf))
867 {
868 bs->botWeaponWeights[WP_TRIP_MINE] = atoi(readbuf);
869 }
870
871 if (GetPairedValue(group, "WP_DET_PACK", readbuf))
872 {
873 bs->botWeaponWeights[WP_DET_PACK] = atoi(readbuf);
874 }
875 }
876
877 bs->lovednum = 0;
878
879 if (GetValueGroup(buf, "EmotionalAttachments", group))
880 {
881 ParseEmotionalAttachments(bs, group);
882 }
883
884 B_TempFree(131072); //buf
885 B_TempFree(1024); //readbuf
886 B_TempFree(65536); //group
887 trap->FS_Close(f);
888 }
889