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