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 /*****************************************************************************
25  * name:		be_ai_chat.c
26  *
27  * desc:		bot chat AI
28  *
29  * $Archive: /MissionPack/code/botlib/be_ai_chat.c $
30  * $Author: Ttimo $
31  * $Revision: 12 $
32  * $Modtime: 4/13/01 4:45p $
33  * $Date: 4/13/01 4:45p $
34  *
35  *****************************************************************************/
36 
37 #include "qcommon/q_shared.h"
38 #include "l_memory.h"
39 #include "l_libvar.h"
40 #include "l_script.h"
41 #include "l_precomp.h"
42 #include "l_struct.h"
43 #include "l_utils.h"
44 #include "l_log.h"
45 #include "aasfile.h"
46 #include "botlib.h"
47 #include "be_aas.h"
48 #include "be_aas_funcs.h"
49 #include "be_interface.h"
50 #include "be_ea.h"
51 #include "be_ai_chat.h"
52 
53 
54 //escape character
55 #define ESCAPE_CHAR				0x01	//'_'
56 //
57 // "hi ", people, " ", 0, " entered the game"
58 //becomes:
59 // "hi _rpeople_ _v0_ entered the game"
60 //
61 
62 //match piece types
63 #define MT_VARIABLE					1		//variable match piece
64 #define MT_STRING					2		//string match piece
65 //reply chat key flags
66 #define RCKFL_AND					1		//key must be present
67 #define RCKFL_NOT					2		//key must be absent
68 #define RCKFL_NAME					4		//name of bot must be present
69 #define RCKFL_STRING				8		//key is a string
70 #define RCKFL_VARIABLES				16		//key is a match template
71 #define RCKFL_BOTNAMES				32		//key is a series of botnames
72 #define RCKFL_GENDERFEMALE			64		//bot must be female
73 #define RCKFL_GENDERMALE			128		//bot must be male
74 #define RCKFL_GENDERLESS			256		//bot must be genderless
75 //time to ignore a chat message after using it
76 #define CHATMESSAGE_RECENTTIME	20
77 
78 //the actuall chat messages
79 typedef struct bot_chatmessage_s
80 {
81 	char *chatmessage;					//chat message string
82 	float time;							//last time used
83 	struct bot_chatmessage_s *next;		//next chat message in a list
84 } bot_chatmessage_t;
85 //bot chat type with chat lines
86 typedef struct bot_chattype_s
87 {
88 	char name[MAX_CHATTYPE_NAME];
89 	int numchatmessages;
90 	bot_chatmessage_t *firstchatmessage;
91 	struct bot_chattype_s *next;
92 } bot_chattype_t;
93 //bot chat lines
94 typedef struct bot_chat_s
95 {
96 	bot_chattype_t *types;
97 } bot_chat_t;
98 
99 //random string
100 typedef struct bot_randomstring_s
101 {
102 	char *string;
103 	struct bot_randomstring_s *next;
104 } bot_randomstring_t;
105 //list with random strings
106 typedef struct bot_randomlist_s
107 {
108 	char *string;
109 	int numstrings;
110 	bot_randomstring_t *firstrandomstring;
111 	struct bot_randomlist_s *next;
112 } bot_randomlist_t;
113 
114 //synonym
115 typedef struct bot_synonym_s
116 {
117 	char *string;
118 	float weight;
119 	struct bot_synonym_s *next;
120 } bot_synonym_t;
121 //list with synonyms
122 typedef struct bot_synonymlist_s
123 {
124 	unsigned long int context;
125 	float totalweight;
126 	bot_synonym_t *firstsynonym;
127 	struct bot_synonymlist_s *next;
128 } bot_synonymlist_t;
129 
130 //fixed match string
131 typedef struct bot_matchstring_s
132 {
133 	char *string;
134 	struct bot_matchstring_s *next;
135 } bot_matchstring_t;
136 
137 //piece of a match template
138 typedef struct bot_matchpiece_s
139 {
140 	int type;
141 	bot_matchstring_t *firststring;
142 	int variable;
143 	struct bot_matchpiece_s *next;
144 } bot_matchpiece_t;
145 //match template
146 typedef struct bot_matchtemplate_s
147 {
148 	unsigned long int context;
149 	int type;
150 	int subtype;
151 	bot_matchpiece_t *first;
152 	struct bot_matchtemplate_s *next;
153 } bot_matchtemplate_t;
154 
155 //reply chat key
156 typedef struct bot_replychatkey_s
157 {
158 	int flags;
159 	char *string;
160 	bot_matchpiece_t *match;
161 	struct bot_replychatkey_s *next;
162 } bot_replychatkey_t;
163 //reply chat
164 typedef struct bot_replychat_s
165 {
166 	bot_replychatkey_t *keys;
167 	float priority;
168 	int numchatmessages;
169 	bot_chatmessage_t *firstchatmessage;
170 	struct bot_replychat_s *next;
171 } bot_replychat_t;
172 
173 //string list
174 typedef struct bot_stringlist_s
175 {
176 	char *string;
177 	struct bot_stringlist_s *next;
178 } bot_stringlist_t;
179 
180 //chat state of a bot
181 typedef struct bot_chatstate_s
182 {
183 	int gender;											//0=it, 1=female, 2=male
184 	int client;											//client number
185 	char name[32];										//name of the bot
186 	char chatmessage[MAX_MESSAGE_SIZE];
187 	int handle;
188 	//the console messages visible to the bot
189 	bot_consolemessage_t *firstmessage;			//first message is the first typed message
190 	bot_consolemessage_t *lastmessage;			//last message is the last typed message, bottom of console
191 	//number of console messages stored in the state
192 	int numconsolemessages;
193 	//the bot chat lines
194 	bot_chat_t *chat;
195 } bot_chatstate_t;
196 
197 typedef struct bot_ichatdata_s {
198 	bot_chat_t	*chat;
199 	char		filename[MAX_QPATH];
200 	char		chatname[MAX_QPATH];
201 } bot_ichatdata_t;
202 
203 bot_ichatdata_t	*ichatdata[MAX_CLIENTS];
204 
205 bot_chatstate_t *botchatstates[MAX_CLIENTS+1];
206 //console message heap
207 bot_consolemessage_t *consolemessageheap = NULL;
208 bot_consolemessage_t *freeconsolemessages = NULL;
209 //list with match strings
210 bot_matchtemplate_t *matchtemplates = NULL;
211 //list with synonyms
212 bot_synonymlist_t *synonyms = NULL;
213 //list with random strings
214 bot_randomlist_t *randomstrings = NULL;
215 //reply chats
216 bot_replychat_t *replychats = NULL;
217 
218 //========================================================================
219 //
220 // Parameter:				-
221 // Returns:					-
222 // Changes Globals:		-
223 //========================================================================
BotChatStateFromHandle(int handle)224 bot_chatstate_t *BotChatStateFromHandle(int handle)
225 {
226 	if (handle <= 0 || handle > MAX_CLIENTS)
227 	{
228 		botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle);
229 		return NULL;
230 	} //end if
231 	if (!botchatstates[handle])
232 	{
233 		botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle);
234 		return NULL;
235 	} //end if
236 	return botchatstates[handle];
237 } //end of the function BotChatStateFromHandle
238 //===========================================================================
239 // initialize the heap with unused console messages
240 //
241 // Parameter:				-
242 // Returns:					-
243 // Changes Globals:		-
244 //===========================================================================
InitConsoleMessageHeap(void)245 void InitConsoleMessageHeap(void)
246 {
247 	int i, max_messages;
248 
249 	if (consolemessageheap) FreeMemory(consolemessageheap);
250 	//
251 	max_messages = (int) LibVarValue("max_messages", "1024");
252 	consolemessageheap = (bot_consolemessage_t *) GetClearedHunkMemory(max_messages *
253 												sizeof(bot_consolemessage_t));
254 	consolemessageheap[0].prev = NULL;
255 	consolemessageheap[0].next = &consolemessageheap[1];
256 	for (i = 1; i < max_messages-1; i++)
257 	{
258 		consolemessageheap[i].prev = &consolemessageheap[i - 1];
259 		consolemessageheap[i].next = &consolemessageheap[i + 1];
260 	} //end for
261 	consolemessageheap[max_messages-1].prev = &consolemessageheap[max_messages-2];
262 	consolemessageheap[max_messages-1].next = NULL;
263 	//pointer to the free console messages
264 	freeconsolemessages = consolemessageheap;
265 } //end of the function InitConsoleMessageHeap
266 //===========================================================================
267 // allocate one console message from the heap
268 //
269 // Parameter:				-
270 // Returns:					-
271 // Changes Globals:		-
272 //===========================================================================
AllocConsoleMessage(void)273 bot_consolemessage_t *AllocConsoleMessage(void)
274 {
275 	bot_consolemessage_t *message;
276 	message = freeconsolemessages;
277 	if (freeconsolemessages) freeconsolemessages = freeconsolemessages->next;
278 	if (freeconsolemessages) freeconsolemessages->prev = NULL;
279 	return message;
280 } //end of the function AllocConsoleMessage
281 //===========================================================================
282 // deallocate one console message from the heap
283 //
284 // Parameter:				-
285 // Returns:					-
286 // Changes Globals:		-
287 //===========================================================================
FreeConsoleMessage(bot_consolemessage_t * message)288 void FreeConsoleMessage(bot_consolemessage_t *message)
289 {
290 	if (freeconsolemessages) freeconsolemessages->prev = message;
291 	message->prev = NULL;
292 	message->next = freeconsolemessages;
293 	freeconsolemessages = message;
294 } //end of the function FreeConsoleMessage
295 //===========================================================================
296 //
297 // Parameter:				-
298 // Returns:					-
299 // Changes Globals:		-
300 //===========================================================================
BotRemoveConsoleMessage(int chatstate,int handle)301 void BotRemoveConsoleMessage(int chatstate, int handle)
302 {
303 	bot_consolemessage_t *m, *nextm;
304 	bot_chatstate_t *cs;
305 
306 	cs = BotChatStateFromHandle(chatstate);
307 	if (!cs) return;
308 
309 	for (m = cs->firstmessage; m; m = nextm)
310 	{
311 		nextm = m->next;
312 		if (m->handle == handle)
313 		{
314 			if (m->next) m->next->prev = m->prev;
315 			else cs->lastmessage = m->prev;
316 			if (m->prev) m->prev->next = m->next;
317 			else cs->firstmessage = m->next;
318 
319 			FreeConsoleMessage(m);
320 			cs->numconsolemessages--;
321 			break;
322 		} //end if
323 	} //end for
324 } //end of the function BotRemoveConsoleMessage
325 //===========================================================================
326 //
327 // Parameter:				-
328 // Returns:					-
329 // Changes Globals:		-
330 //===========================================================================
BotQueueConsoleMessage(int chatstate,int type,char * message)331 void BotQueueConsoleMessage(int chatstate, int type, char *message)
332 {
333 	bot_consolemessage_t *m;
334 	bot_chatstate_t *cs;
335 
336 	cs = BotChatStateFromHandle(chatstate);
337 	if (!cs) return;
338 
339 	m = AllocConsoleMessage();
340 	if (!m)
341 	{
342 		botimport.Print(PRT_ERROR, "empty console message heap\n");
343 		return;
344 	} //end if
345 	cs->handle++;
346 	if (cs->handle <= 0 || cs->handle > 8192) cs->handle = 1;
347 	m->handle = cs->handle;
348 	m->time = AAS_Time();
349 	m->type = type;
350 	strncpy(m->message, message, MAX_MESSAGE_SIZE);
351 	m->next = NULL;
352 	if (cs->lastmessage)
353 	{
354 		cs->lastmessage->next = m;
355 		m->prev = cs->lastmessage;
356 		cs->lastmessage = m;
357 	} //end if
358 	else
359 	{
360 		cs->lastmessage = m;
361 		cs->firstmessage = m;
362 		m->prev = NULL;
363 	} //end if
364 	cs->numconsolemessages++;
365 } //end of the function BotQueueConsoleMessage
366 //===========================================================================
367 //
368 // Parameter:				-
369 // Returns:					-
370 // Changes Globals:		-
371 //===========================================================================
BotNextConsoleMessage(int chatstate,bot_consolemessage_t * cm)372 int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm)
373 {
374 	bot_chatstate_t *cs;
375 	bot_consolemessage_t *firstmsg;
376 
377 	cs = BotChatStateFromHandle(chatstate);
378 	if (!cs) return 0;
379 	if ((firstmsg = cs->firstmessage))
380 	{
381 		cm->handle = firstmsg->handle;
382 		cm->time = firstmsg->time;
383 		cm->type = firstmsg->type;
384 		Q_strncpyz(cm->message, firstmsg->message,
385 			   sizeof(cm->message));
386 
387 		/* We omit setting the two pointers in cm because pointer
388 		 * size in the VM differs between the size in the engine on
389 		 * 64 bit machines, which would lead to a buffer overflow if
390 		 * this functions is called from the VM. The pointers are
391 		 * of no interest to functions calling
392 		 * BotNextConsoleMessage anyways.
393 		 */
394 
395 		return cm->handle;
396 	} //end if
397 	return 0;
398 } //end of the function BotConsoleMessage
399 //===========================================================================
400 //
401 // Parameter:				-
402 // Returns:					-
403 // Changes Globals:		-
404 //===========================================================================
BotNumConsoleMessages(int chatstate)405 int BotNumConsoleMessages(int chatstate)
406 {
407 	bot_chatstate_t *cs;
408 
409 	cs = BotChatStateFromHandle(chatstate);
410 	if (!cs) return 0;
411 	return cs->numconsolemessages;
412 } //end of the function BotNumConsoleMessages
413 //===========================================================================
414 //
415 // Parameter:				-
416 // Returns:					-
417 // Changes Globals:		-
418 //===========================================================================
IsWhiteSpace(char c)419 int IsWhiteSpace(char c)
420 {
421 	if ((c >= 'a' && c <= 'z')
422 		|| (c >= 'A' && c <= 'Z')
423 		|| (c >= '0' && c <= '9')
424 		|| c == '(' || c == ')'
425 		|| c == '?' || c == ':'
426 		|| c == '\''|| c == '/'
427 		|| c == ',' || c == '.'
428 		|| c == '['	|| c == ']'
429 		|| c == '-' || c == '_'
430 		|| c == '+' || c == '=') return qfalse;
431 	return qtrue;
432 } //end of the function IsWhiteSpace
433 //===========================================================================
434 //
435 // Parameter:			-
436 // Returns:				-
437 // Changes Globals:		-
438 //===========================================================================
BotRemoveTildes(char * message)439 void BotRemoveTildes(char *message)
440 {
441 	int i;
442 
443 	//remove all tildes from the chat message
444 	for (i = 0; message[i]; i++)
445 	{
446 		if (message[i] == '~')
447 		{
448 			memmove(&message[i], &message[i+1], strlen(&message[i+1])+1);
449 		} //end if
450 	} //end for
451 } //end of the function BotRemoveTildes
452 //===========================================================================
453 //
454 // Parameter:				-
455 // Returns:					-
456 // Changes Globals:		-
457 //===========================================================================
UnifyWhiteSpaces(char * string)458 void UnifyWhiteSpaces(char *string)
459 {
460 	char *ptr, *oldptr;
461 
462 	for (ptr = oldptr = string; *ptr; oldptr = ptr)
463 	{
464 		while(*ptr && IsWhiteSpace(*ptr)) ptr++;
465 		if (ptr > oldptr)
466 		{
467 			//if not at the start and not at the end of the string
468 			//write only one space
469 			if (oldptr > string && *ptr) *oldptr++ = ' ';
470 			//remove all other white spaces
471 			if (ptr > oldptr) memmove(oldptr, ptr, strlen(ptr)+1);
472 		} //end if
473 		while(*ptr && !IsWhiteSpace(*ptr)) ptr++;
474 	} //end while
475 } //end of the function UnifyWhiteSpaces
476 //===========================================================================
477 //
478 // Parameter:				-
479 // Returns:					-
480 // Changes Globals:		-
481 //===========================================================================
StringContains(char * str1,char * str2,int casesensitive)482 int StringContains(char *str1, char *str2, int casesensitive)
483 {
484 	int len, i, j, index;
485 
486 	if (str1 == NULL || str2 == NULL) return -1;
487 
488 	len = strlen(str1) - strlen(str2);
489 	index = 0;
490 	for (i = 0; i <= len; i++, str1++, index++)
491 	{
492 		for (j = 0; str2[j]; j++)
493 		{
494 			if (casesensitive)
495 			{
496 				if (str1[j] != str2[j]) break;
497 			} //end if
498 			else
499 			{
500 				if (toupper(str1[j]) != toupper(str2[j])) break;
501 			} //end else
502 		} //end for
503 		if (!str2[j]) return index;
504 	} //end for
505 	return -1;
506 } //end of the function StringContains
507 //===========================================================================
508 //
509 // Parameter:				-
510 // Returns:					-
511 // Changes Globals:		-
512 //===========================================================================
StringContainsWord(char * str1,char * str2,int casesensitive)513 char *StringContainsWord(char *str1, char *str2, int casesensitive)
514 {
515 	int len, i, j;
516 
517 	len = strlen(str1) - strlen(str2);
518 	for (i = 0; i <= len; i++, str1++)
519 	{
520 		//if not at the start of the string
521 		if (i)
522 		{
523 			//skip to the start of the next word
524 			while(*str1 && *str1 != ' ' && *str1 != '.' && *str1 != ',' && *str1 != '!') str1++;
525 			if (!*str1) break;
526 			str1++;
527 		} //end for
528 		//compare the word
529 		for (j = 0; str2[j]; j++)
530 		{
531 			if (casesensitive)
532 			{
533 				if (str1[j] != str2[j]) break;
534 			} //end if
535 			else
536 			{
537 				if (toupper(str1[j]) != toupper(str2[j])) break;
538 			} //end else
539 		} //end for
540 		//if there was a word match
541 		if (!str2[j])
542 		{
543 			//if the first string has an end of word
544 			if (!str1[j] || str1[j] == ' ' || str1[j] == '.' || str1[j] == ',' || str1[j] == '!') return str1;
545 		} //end if
546 	} //end for
547 	return NULL;
548 } //end of the function StringContainsWord
549 //===========================================================================
550 //
551 // Parameter:				-
552 // Returns:					-
553 // Changes Globals:		-
554 //===========================================================================
StringReplaceWords(char * string,char * synonym,char * replacement)555 void StringReplaceWords(char *string, char *synonym, char *replacement)
556 {
557 	char *str, *str2;
558 
559 	//find the synonym in the string
560 	str = StringContainsWord(string, synonym, qfalse);
561 	//if the synonym occured in the string
562 	while(str)
563 	{
564 		//if the synonym isn't part of the replacement which is already in the string
565 		//usefull for abreviations
566 		str2 = StringContainsWord(string, replacement, qfalse);
567 		while(str2)
568 		{
569 			if (str2 <= str && str < str2 + strlen(replacement)) break;
570 			str2 = StringContainsWord(str2+1, replacement, qfalse);
571 		} //end while
572 		if (!str2)
573 		{
574 			memmove(str + strlen(replacement), str+strlen(synonym), strlen(str+strlen(synonym))+1);
575 			//append the synonum replacement
576 			Com_Memcpy(str, replacement, strlen(replacement));
577 		} //end if
578 		//find the next synonym in the string
579 		str = StringContainsWord(str+strlen(replacement), synonym, qfalse);
580 	} //end if
581 } //end of the function StringReplaceWords
582 //===========================================================================
583 //
584 // Parameter:				-
585 // Returns:					-
586 // Changes Globals:		-
587 //===========================================================================
BotDumpSynonymList(bot_synonymlist_t * synlist)588 void BotDumpSynonymList(bot_synonymlist_t *synlist)
589 {
590 	FILE *fp;
591 	bot_synonymlist_t *syn;
592 	bot_synonym_t *synonym;
593 
594 	fp = Log_FilePointer();
595 	if (!fp) return;
596 	for (syn = synlist; syn; syn = syn->next)
597 	{
598 	        fprintf(fp, "%ld : [", syn->context);
599 		for (synonym = syn->firstsynonym; synonym; synonym = synonym->next)
600 		{
601 			fprintf(fp, "(\"%s\", %1.2f)", synonym->string, synonym->weight);
602 			if (synonym->next) fprintf(fp, ", ");
603 		} //end for
604 		fprintf(fp, "]\n");
605 	} //end for
606 } //end of the function BotDumpSynonymList
607 //===========================================================================
608 //
609 // Parameter:				-
610 // Returns:					-
611 // Changes Globals:		-
612 //===========================================================================
BotLoadSynonyms(char * filename)613 bot_synonymlist_t *BotLoadSynonyms(char *filename)
614 {
615 	int pass, size, contextlevel, numsynonyms;
616 	unsigned long int context, contextstack[32];
617 	char *ptr = NULL;
618 	source_t *source;
619 	token_t token;
620 	bot_synonymlist_t *synlist, *lastsyn, *syn;
621 	bot_synonym_t *synonym, *lastsynonym;
622 
623 	size = 0;
624 	synlist = NULL; //make compiler happy
625 	syn = NULL; //make compiler happy
626 	synonym = NULL; //make compiler happy
627 	//the synonyms are parsed in two phases
628 	for (pass = 0; pass < 2; pass++)
629 	{
630 		//
631 		if (pass && size) ptr = (char *) GetClearedHunkMemory(size);
632 		//
633 		PC_SetBaseFolder(BOTFILESBASEFOLDER);
634 		source = LoadSourceFile(filename);
635 		if (!source)
636 		{
637 			botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
638 			return NULL;
639 		} //end if
640 		//
641 		context = 0;
642 		contextlevel = 0;
643 		synlist = NULL; //list synonyms
644 		lastsyn = NULL; //last synonym in the list
645 		//
646 		while(PC_ReadToken(source, &token))
647 		{
648 			if (token.type == TT_NUMBER)
649 			{
650 				context |= token.intvalue;
651 				contextstack[contextlevel] = token.intvalue;
652 				contextlevel++;
653 				if (contextlevel >= 32)
654 				{
655 					SourceError(source, "more than 32 context levels");
656 					FreeSource(source);
657 					return NULL;
658 				} //end if
659 				if (!PC_ExpectTokenString(source, "{"))
660 				{
661 					FreeSource(source);
662 					return NULL;
663 				} //end if
664 			} //end if
665 			else if (token.type == TT_PUNCTUATION)
666 			{
667 				if (!strcmp(token.string, "}"))
668 				{
669 					contextlevel--;
670 					if (contextlevel < 0)
671 					{
672 						SourceError(source, "too many }");
673 						FreeSource(source);
674 						return NULL;
675 					} //end if
676 					context &= ~contextstack[contextlevel];
677 				} //end if
678 				else if (!strcmp(token.string, "["))
679 				{
680 					size += sizeof(bot_synonymlist_t);
681 					if (pass)
682 					{
683 						syn = (bot_synonymlist_t *) ptr;
684 						ptr += sizeof(bot_synonymlist_t);
685 						syn->context = context;
686 						syn->firstsynonym = NULL;
687 						syn->next = NULL;
688 						if (lastsyn) lastsyn->next = syn;
689 						else synlist = syn;
690 						lastsyn = syn;
691 					} //end if
692 					numsynonyms = 0;
693 					lastsynonym = NULL;
694 					while(1)
695 					{
696 						size_t len;
697 						if (!PC_ExpectTokenString(source, "(") ||
698 							!PC_ExpectTokenType(source, TT_STRING, 0, &token))
699 						{
700 							FreeSource(source);
701 							return NULL;
702 						} //end if
703 						StripDoubleQuotes(token.string);
704 						if (strlen(token.string) <= 0)
705 						{
706 							SourceError(source, "empty string");
707 							FreeSource(source);
708 							return NULL;
709 						} //end if
710 						len = strlen(token.string) + 1;
711 						len = PAD(len, sizeof(long));
712 						size += sizeof(bot_synonym_t) + len;
713 						if (pass)
714 						{
715 							synonym = (bot_synonym_t *) ptr;
716 							ptr += sizeof(bot_synonym_t);
717 							synonym->string = ptr;
718 							ptr += len;
719 							strcpy(synonym->string, token.string);
720 							//
721 							if (lastsynonym) lastsynonym->next = synonym;
722 							else syn->firstsynonym = synonym;
723 							lastsynonym = synonym;
724 						} //end if
725 						numsynonyms++;
726 						if (!PC_ExpectTokenString(source, ",") ||
727 							!PC_ExpectTokenType(source, TT_NUMBER, 0, &token) ||
728 							!PC_ExpectTokenString(source, ")"))
729 						{
730 							FreeSource(source);
731 							return NULL;
732 						} //end if
733 						if (pass)
734 						{
735 							synonym->weight = token.floatvalue;
736 							syn->totalweight += synonym->weight;
737 						} //end if
738 						if (PC_CheckTokenString(source, "]")) break;
739 						if (!PC_ExpectTokenString(source, ","))
740 						{
741 							FreeSource(source);
742 							return NULL;
743 						} //end if
744 					} //end while
745 					if (numsynonyms < 2)
746 					{
747 						SourceError(source, "synonym must have at least two entries");
748 						FreeSource(source);
749 						return NULL;
750 					} //end if
751 				} //end else
752 				else
753 				{
754 					SourceError(source, "unexpected %s", token.string);
755 					FreeSource(source);
756 					return NULL;
757 				} //end if
758 			} //end else if
759 		} //end while
760 		//
761 		FreeSource(source);
762 		//
763 		if (contextlevel > 0)
764 		{
765 			SourceError(source, "missing }");
766 			return NULL;
767 		} //end if
768 	} //end for
769 	botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
770 	//
771 	//BotDumpSynonymList(synlist);
772 	//
773 	return synlist;
774 } //end of the function BotLoadSynonyms
775 //===========================================================================
776 // replace all the synonyms in the string
777 //
778 // Parameter:				-
779 // Returns:					-
780 // Changes Globals:		-
781 //===========================================================================
BotReplaceSynonyms(char * string,unsigned long int context)782 void BotReplaceSynonyms(char *string, unsigned long int context)
783 {
784 	bot_synonymlist_t *syn;
785 	bot_synonym_t *synonym;
786 
787 	for (syn = synonyms; syn; syn = syn->next)
788 	{
789 		if (!(syn->context & context)) continue;
790 		for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next)
791 		{
792 			StringReplaceWords(string, synonym->string, syn->firstsynonym->string);
793 		} //end for
794 	} //end for
795 } //end of the function BotReplaceSynonyms
796 //===========================================================================
797 //
798 // Parameter:				-
799 // Returns:					-
800 // Changes Globals:		-
801 //===========================================================================
BotReplaceWeightedSynonyms(char * string,unsigned long int context)802 void BotReplaceWeightedSynonyms(char *string, unsigned long int context)
803 {
804 	bot_synonymlist_t *syn;
805 	bot_synonym_t *synonym, *replacement;
806 	float weight, curweight;
807 
808 	for (syn = synonyms; syn; syn = syn->next)
809 	{
810 		if (!(syn->context & context)) continue;
811 		//choose a weighted random replacement synonym
812 		weight = Q_flrand(0.0f, 1.0f) * syn->totalweight;
813 		if (!weight) continue;
814 		curweight = 0;
815 		for (replacement = syn->firstsynonym; replacement; replacement = replacement->next)
816 		{
817 			curweight += replacement->weight;
818 			if (weight < curweight) break;
819 		} //end for
820 		if (!replacement) continue;
821 		//replace all synonyms with the replacement
822 		for (synonym = syn->firstsynonym; synonym; synonym = synonym->next)
823 		{
824 			if (synonym == replacement) continue;
825 			StringReplaceWords(string, synonym->string, replacement->string);
826 		} //end for
827 	} //end for
828 } //end of the function BotReplaceWeightedSynonyms
829 //===========================================================================
830 //
831 // Parameter:				-
832 // Returns:					-
833 // Changes Globals:		-
834 //===========================================================================
BotReplaceReplySynonyms(char * string,unsigned long int context)835 void BotReplaceReplySynonyms(char *string, unsigned long int context)
836 {
837 	char *str1, *str2, *replacement;
838 	bot_synonymlist_t *syn;
839 	bot_synonym_t *synonym;
840 
841 	for (str1 = string; *str1; )
842 	{
843 		//go to the start of the next word
844 		while(*str1 && *str1 <= ' ') str1++;
845 		if (!*str1) break;
846 		//
847 		for (syn = synonyms; syn; syn = syn->next)
848 		{
849 			if (!(syn->context & context)) continue;
850 			for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next)
851 			{
852 				//if the synonym is not at the front of the string continue
853 				str2 = StringContainsWord(str1, synonym->string, qfalse);
854 				if (!str2 || str2 != str1) continue;
855 				//
856 				replacement = syn->firstsynonym->string;
857 				//if the replacement IS in front of the string continue
858 				str2 = StringContainsWord(str1, replacement, qfalse);
859 				if (str2 && str2 == str1) continue;
860 				//
861 				memmove(str1 + strlen(replacement), str1+strlen(synonym->string),
862 							strlen(str1+strlen(synonym->string)) + 1);
863 				//append the synonum replacement
864 				Com_Memcpy(str1, replacement, strlen(replacement));
865 				//
866 				break;
867 			} //end for
868 			//if a synonym has been replaced
869 			if (synonym) break;
870 		} //end for
871 		//skip over this word
872 		while(*str1 && *str1 > ' ') str1++;
873 		if (!*str1) break;
874 	} //end while
875 } //end of the function BotReplaceReplySynonyms
876 //===========================================================================
877 //
878 // Parameter:			-
879 // Returns:				-
880 // Changes Globals:		-
881 //===========================================================================
BotLoadChatMessage(source_t * source,char * chatmessagestring)882 int BotLoadChatMessage(source_t *source, char *chatmessagestring)
883 {
884 	char *ptr;
885 	token_t token;
886 
887 	ptr = chatmessagestring;
888 	*ptr = 0;
889 	//
890 	while(1)
891 	{
892 		if (!PC_ExpectAnyToken(source, &token)) return qfalse;
893 		//fixed string
894 		if (token.type == TT_STRING)
895 		{
896 			StripDoubleQuotes(token.string);
897 			if (strlen(ptr) + strlen(token.string) + 1 > MAX_MESSAGE_SIZE)
898 			{
899 				SourceError(source, "chat message too long");
900 				return qfalse;
901 			} //end if
902 			strcat(ptr, token.string);
903 		} //end else if
904 		//variable string
905 		else if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER))
906 		{
907 			if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE)
908 			{
909 				SourceError(source, "chat message too long");
910 				return qfalse;
911 			} //end if
912 			sprintf(&ptr[strlen(ptr)], "%cv%ld%c", ESCAPE_CHAR, token.intvalue, ESCAPE_CHAR);
913 		} //end if
914 		//random string
915 		else if (token.type == TT_NAME)
916 		{
917 			if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE)
918 			{
919 				SourceError(source, "chat message too long");
920 				return qfalse;
921 			} //end if
922 			sprintf(&ptr[strlen(ptr)], "%cr%s%c", ESCAPE_CHAR, token.string, ESCAPE_CHAR);
923 		} //end else if
924 		else
925 		{
926 			SourceError(source, "unknown message component %s", token.string);
927 			return qfalse;
928 		} //end else
929 		if (PC_CheckTokenString(source, ";")) break;
930 		if (!PC_ExpectTokenString(source, ",")) return qfalse;
931 	} //end while
932 	//
933 	return qtrue;
934 } //end of the function BotLoadChatMessage
935 //===========================================================================
936 //
937 // Parameter:				-
938 // Returns:					-
939 // Changes Globals:		-
940 //===========================================================================
BotDumpRandomStringList(bot_randomlist_t * randomlist)941 void BotDumpRandomStringList(bot_randomlist_t *randomlist)
942 {
943 	FILE *fp;
944 	bot_randomlist_t *random;
945 	bot_randomstring_t *rs;
946 
947 	fp = Log_FilePointer();
948 	if (!fp) return;
949 	for (random = randomlist; random; random = random->next)
950 	{
951 		fprintf(fp, "%s = {", random->string);
952 		for (rs = random->firstrandomstring; rs; rs = rs->next)
953 		{
954 			fprintf(fp, "\"%s\"", rs->string);
955 			if (rs->next) fprintf(fp, ", ");
956 			else fprintf(fp, "}\n");
957 		} //end for
958 	} //end for
959 } //end of the function BotDumpRandomStringList
960 //===========================================================================
961 //
962 // Parameter:				-
963 // Returns:					-
964 // Changes Globals:		-
965 //===========================================================================
BotLoadRandomStrings(char * filename)966 bot_randomlist_t *BotLoadRandomStrings(char *filename)
967 {
968 	int pass, size;
969 	char *ptr = NULL, chatmessagestring[MAX_MESSAGE_SIZE];
970 	source_t *source;
971 	token_t token;
972 	bot_randomlist_t *randomlist, *lastrandom, *random;
973 	bot_randomstring_t *randomstring;
974 
975 #ifdef DEBUG
976 	int starttime = Sys_MilliSeconds();
977 #endif //DEBUG
978 
979 	size = 0;
980 	randomlist = NULL;
981 	random = NULL;
982 	//the synonyms are parsed in two phases
983 	for (pass = 0; pass < 2; pass++)
984 	{
985 		//
986 		if (pass && size) ptr = (char *) GetClearedHunkMemory(size);
987 		//
988 		PC_SetBaseFolder(BOTFILESBASEFOLDER);
989 		source = LoadSourceFile(filename);
990 		if (!source)
991 		{
992 			botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
993 			return NULL;
994 		} //end if
995 		//
996 		randomlist = NULL; //list
997 		lastrandom = NULL; //last
998 		//
999 		while(PC_ReadToken(source, &token))
1000 		{
1001 			size_t len;
1002 			if (token.type != TT_NAME)
1003 			{
1004 				SourceError(source, "unknown random %s", token.string);
1005 				FreeSource(source);
1006 				return NULL;
1007 			} //end if
1008 			len = strlen(token.string) + 1;
1009 			len = PAD(len, sizeof(long));
1010 			size += sizeof(bot_randomlist_t) + len;
1011 			if (pass)
1012 			{
1013 				random = (bot_randomlist_t *) ptr;
1014 				ptr += sizeof(bot_randomlist_t);
1015 				random->string = ptr;
1016 				ptr += len;
1017 				strcpy(random->string, token.string);
1018 				random->firstrandomstring = NULL;
1019 				random->numstrings = 0;
1020 				//
1021 				if (lastrandom) lastrandom->next = random;
1022 				else randomlist = random;
1023 				lastrandom = random;
1024 			} //end if
1025 			if (!PC_ExpectTokenString(source, "=") ||
1026 				!PC_ExpectTokenString(source, "{"))
1027 			{
1028 				FreeSource(source);
1029 				return NULL;
1030 			} //end if
1031 			while(!PC_CheckTokenString(source, "}"))
1032 			{
1033 				size_t len;
1034 				if (!BotLoadChatMessage(source, chatmessagestring))
1035 				{
1036 					FreeSource(source);
1037 					return NULL;
1038 				} //end if
1039 				len = strlen(chatmessagestring) + 1;
1040 				len = PAD(len, sizeof(long));
1041 				size += sizeof(bot_randomstring_t) + len;
1042 				if (pass)
1043 				{
1044 					randomstring = (bot_randomstring_t *) ptr;
1045 					ptr += sizeof(bot_randomstring_t);
1046 					randomstring->string = ptr;
1047 					ptr += len;
1048 					strcpy(randomstring->string, chatmessagestring);
1049 					//
1050 					random->numstrings++;
1051 					randomstring->next = random->firstrandomstring;
1052 					random->firstrandomstring = randomstring;
1053 				} //end if
1054 			} //end while
1055 		} //end while
1056 		//free the source after one pass
1057 		FreeSource(source);
1058 	} //end for
1059 	botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
1060 	//
1061 #ifdef DEBUG
1062 	botimport.Print(PRT_MESSAGE, "random strings %d msec\n", Sys_MilliSeconds() - starttime);
1063 	//BotDumpRandomStringList(randomlist);
1064 #endif //DEBUG
1065 	//
1066 	return randomlist;
1067 } //end of the function BotLoadRandomStrings
1068 //===========================================================================
1069 //
1070 // Parameter:				-
1071 // Returns:					-
1072 // Changes Globals:		-
1073 //===========================================================================
RandomString(char * name)1074 char *RandomString(char *name)
1075 {
1076 	bot_randomlist_t *random;
1077 	bot_randomstring_t *rs;
1078 	int i;
1079 
1080 	for (random = randomstrings; random; random = random->next)
1081 	{
1082 		if (!strcmp(random->string, name))
1083 		{
1084 			i = Q_flrand(0.0f, 1.0f) * random->numstrings;
1085 			for (rs = random->firstrandomstring; rs; rs = rs->next)
1086 			{
1087 				if (--i < 0) break;
1088 			} //end for
1089 			if (rs)
1090 			{
1091 				return rs->string;
1092 			} //end if
1093 		} //end for
1094 	} //end for
1095 	return NULL;
1096 } //end of the function RandomString
1097 //===========================================================================
1098 //
1099 // Parameter:				-
1100 // Returns:					-
1101 // Changes Globals:		-
1102 //===========================================================================
BotDumpMatchTemplates(bot_matchtemplate_t * matches)1103 void BotDumpMatchTemplates(bot_matchtemplate_t *matches)
1104 {
1105 	FILE *fp;
1106 	bot_matchtemplate_t *mt;
1107 	bot_matchpiece_t *mp;
1108 	bot_matchstring_t *ms;
1109 
1110 	fp = Log_FilePointer();
1111 	if (!fp) return;
1112 	for (mt = matches; mt; mt = mt->next)
1113 	{
1114 	        fprintf(fp, "{ " );
1115 		for (mp = mt->first; mp; mp = mp->next)
1116 		{
1117 			if (mp->type == MT_STRING)
1118 			{
1119 				for (ms = mp->firststring; ms; ms = ms->next)
1120 				{
1121 					fprintf(fp, "\"%s\"", ms->string);
1122 					if (ms->next) fprintf(fp, "|");
1123 				} //end for
1124 			} //end if
1125 			else if (mp->type == MT_VARIABLE)
1126 			{
1127 				fprintf(fp, "%d", mp->variable);
1128 			} //end else if
1129 			if (mp->next) fprintf(fp, ", ");
1130 		} //end for
1131 		fprintf(fp, " = (%d, %d);}\n", mt->type, mt->subtype);
1132 	} //end for
1133 } //end of the function BotDumpMatchTemplates
1134 //===========================================================================
1135 //
1136 // Parameter:				-
1137 // Returns:					-
1138 // Changes Globals:		-
1139 //===========================================================================
BotFreeMatchPieces(bot_matchpiece_t * matchpieces)1140 void BotFreeMatchPieces(bot_matchpiece_t *matchpieces)
1141 {
1142 	bot_matchpiece_t *mp, *nextmp;
1143 	bot_matchstring_t *ms, *nextms;
1144 
1145 	for (mp = matchpieces; mp; mp = nextmp)
1146 	{
1147 		nextmp = mp->next;
1148 		if (mp->type == MT_STRING)
1149 		{
1150 			for (ms = mp->firststring; ms; ms = nextms)
1151 			{
1152 				nextms = ms->next;
1153 				FreeMemory(ms);
1154 			} //end for
1155 		} //end if
1156 		FreeMemory(mp);
1157 	} //end for
1158 } //end of the function BotFreeMatchPieces
1159 //===========================================================================
1160 //
1161 // Parameter:				-
1162 // Returns:					-
1163 // Changes Globals:		-
1164 //===========================================================================
BotLoadMatchPieces(source_t * source,char * endtoken)1165 bot_matchpiece_t *BotLoadMatchPieces(source_t *source, char *endtoken)
1166 {
1167 	int lastwasvariable, emptystring;
1168 	token_t token;
1169 	bot_matchpiece_t *matchpiece, *firstpiece, *lastpiece;
1170 	bot_matchstring_t *matchstring, *lastmatchstring;
1171 
1172 	firstpiece = NULL;
1173 	lastpiece = NULL;
1174 	//
1175 	lastwasvariable = qfalse;
1176 	//
1177 	while(PC_ReadToken(source, &token))
1178 	{
1179 		if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER))
1180 		{
1181 			if (token.intvalue >= MAX_MATCHVARIABLES)
1182 			{
1183 				SourceError(source, "can't have more than %d match variables", MAX_MATCHVARIABLES);
1184 				FreeSource(source);
1185 				BotFreeMatchPieces(firstpiece);
1186 				return NULL;
1187 			} //end if
1188 			if (lastwasvariable)
1189 			{
1190 				SourceError(source, "not allowed to have adjacent variables");
1191 				FreeSource(source);
1192 				BotFreeMatchPieces(firstpiece);
1193 				return NULL;
1194 			} //end if
1195 			lastwasvariable = qtrue;
1196 			//
1197 			matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t));
1198 			matchpiece->type = MT_VARIABLE;
1199 			matchpiece->variable = token.intvalue;
1200 			matchpiece->next = NULL;
1201 			if (lastpiece) lastpiece->next = matchpiece;
1202 			else firstpiece = matchpiece;
1203 			lastpiece = matchpiece;
1204 		} //end if
1205 		else if (token.type == TT_STRING)
1206 		{
1207 			//
1208 			matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t));
1209 			matchpiece->firststring = NULL;
1210 			matchpiece->type = MT_STRING;
1211 			matchpiece->variable = 0;
1212 			matchpiece->next = NULL;
1213 			if (lastpiece) lastpiece->next = matchpiece;
1214 			else firstpiece = matchpiece;
1215 			lastpiece = matchpiece;
1216 			//
1217 			lastmatchstring = NULL;
1218 			emptystring = qfalse;
1219 			//
1220 			do
1221 			{
1222 				if (matchpiece->firststring)
1223 				{
1224 					if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
1225 					{
1226 						FreeSource(source);
1227 						BotFreeMatchPieces(firstpiece);
1228 						return NULL;
1229 					} //end if
1230 				} //end if
1231 				StripDoubleQuotes(token.string);
1232 				matchstring = (bot_matchstring_t *) GetClearedHunkMemory(sizeof(bot_matchstring_t) + strlen(token.string) + 1);
1233 				matchstring->string = (char *) matchstring + sizeof(bot_matchstring_t);
1234 				strcpy(matchstring->string, token.string);
1235 				if (!strlen(token.string)) emptystring = qtrue;
1236 				matchstring->next = NULL;
1237 				if (lastmatchstring) lastmatchstring->next = matchstring;
1238 				else matchpiece->firststring = matchstring;
1239 				lastmatchstring = matchstring;
1240 			} while(PC_CheckTokenString(source, "|"));
1241 			//if there was no empty string found
1242 			if (!emptystring) lastwasvariable = qfalse;
1243 		} //end if
1244 		else
1245 		{
1246 			SourceError(source, "invalid token %s", token.string);
1247 			FreeSource(source);
1248 			BotFreeMatchPieces(firstpiece);
1249 			return NULL;
1250 		} //end else
1251 		if (PC_CheckTokenString(source, endtoken)) break;
1252 		if (!PC_ExpectTokenString(source, ","))
1253 		{
1254 			FreeSource(source);
1255 			BotFreeMatchPieces(firstpiece);
1256 			return NULL;
1257 		} //end if
1258 	} //end while
1259 	return firstpiece;
1260 } //end of the function BotLoadMatchPieces
1261 //===========================================================================
1262 //
1263 // Parameter:				-
1264 // Returns:					-
1265 // Changes Globals:		-
1266 //===========================================================================
BotFreeMatchTemplates(bot_matchtemplate_t * mt)1267 void BotFreeMatchTemplates(bot_matchtemplate_t *mt)
1268 {
1269 	bot_matchtemplate_t *nextmt;
1270 
1271 	for (; mt; mt = nextmt)
1272 	{
1273 		nextmt = mt->next;
1274 		BotFreeMatchPieces(mt->first);
1275 		FreeMemory(mt);
1276 	} //end for
1277 } //end of the function BotFreeMatchTemplates
1278 //===========================================================================
1279 //
1280 // Parameter:				-
1281 // Returns:					-
1282 // Changes Globals:		-
1283 //===========================================================================
BotLoadMatchTemplates(char * matchfile)1284 bot_matchtemplate_t *BotLoadMatchTemplates(char *matchfile)
1285 {
1286 	source_t *source;
1287 	token_t token;
1288 	bot_matchtemplate_t *matchtemplate, *matches, *lastmatch;
1289 	unsigned long int context;
1290 
1291 	PC_SetBaseFolder(BOTFILESBASEFOLDER);
1292 	source = LoadSourceFile(matchfile);
1293 	if (!source)
1294 	{
1295 		botimport.Print(PRT_ERROR, "counldn't load %s\n", matchfile);
1296 		return NULL;
1297 	} //end if
1298 	//
1299 	matches = NULL; //list with matches
1300 	lastmatch = NULL; //last match in the list
1301 
1302 	while(PC_ReadToken(source, &token))
1303 	{
1304 		if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER))
1305 		{
1306 			SourceError(source, "expected integer, found %s", token.string);
1307 			BotFreeMatchTemplates(matches);
1308 			FreeSource(source);
1309 			return NULL;
1310 		} //end if
1311 		//the context
1312 		context = token.intvalue;
1313 		//
1314 		if (!PC_ExpectTokenString(source, "{"))
1315 		{
1316 			BotFreeMatchTemplates(matches);
1317 			FreeSource(source);
1318 			return NULL;
1319 		} //end if
1320 		//
1321 		while(PC_ReadToken(source, &token))
1322 		{
1323 			if (!strcmp(token.string, "}")) break;
1324 			//
1325 			PC_UnreadLastToken(source);
1326 			//
1327 			matchtemplate = (bot_matchtemplate_t *) GetClearedHunkMemory(sizeof(bot_matchtemplate_t));
1328 			matchtemplate->context = context;
1329 			matchtemplate->next = NULL;
1330 			//add the match template to the list
1331 			if (lastmatch) lastmatch->next = matchtemplate;
1332 			else matches = matchtemplate;
1333 			lastmatch = matchtemplate;
1334 			//load the match template
1335 			matchtemplate->first = BotLoadMatchPieces(source, "=");
1336 			if (!matchtemplate->first)
1337 			{
1338 				BotFreeMatchTemplates(matches);
1339 				return NULL;
1340 			} //end if
1341 			//read the match type
1342 			if (!PC_ExpectTokenString(source, "(") ||
1343 				!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))
1344 			{
1345 				BotFreeMatchTemplates(matches);
1346 				FreeSource(source);
1347 				return NULL;
1348 			} //end if
1349 			matchtemplate->type = token.intvalue;
1350 			//read the match subtype
1351 			if (!PC_ExpectTokenString(source, ",") ||
1352 				!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))
1353 			{
1354 				BotFreeMatchTemplates(matches);
1355 				FreeSource(source);
1356 				return NULL;
1357 			} //end if
1358 			matchtemplate->subtype = token.intvalue;
1359 			//read trailing punctuations
1360 			if (!PC_ExpectTokenString(source, ")") ||
1361 				!PC_ExpectTokenString(source, ";"))
1362 			{
1363 				BotFreeMatchTemplates(matches);
1364 				FreeSource(source);
1365 				return NULL;
1366 			} //end if
1367 		} //end while
1368 	} //end while
1369 	//free the source
1370 	FreeSource(source);
1371 	botimport.Print(PRT_MESSAGE, "loaded %s\n", matchfile);
1372 	//
1373 	//BotDumpMatchTemplates(matches);
1374 	//
1375 	return matches;
1376 } //end of the function BotLoadMatchTemplates
1377 //===========================================================================
1378 //
1379 // Parameter:				-
1380 // Returns:					-
1381 // Changes Globals:		-
1382 //===========================================================================
StringsMatch(bot_matchpiece_t * pieces,bot_match_t * match)1383 int StringsMatch(bot_matchpiece_t *pieces, bot_match_t *match)
1384 {
1385 	int lastvariable, index;
1386 	char *strptr, *newstrptr;
1387 	bot_matchpiece_t *mp;
1388 	bot_matchstring_t *ms;
1389 
1390 	//no last variable
1391 	lastvariable = -1;
1392 	//pointer to the string to compare the match string with
1393 	strptr = match->string;
1394 	//Log_Write("match: %s", strptr);
1395 	//compare the string with the current match string
1396 	for (mp = pieces; mp; mp = mp->next)
1397 	{
1398 		//if it is a piece of string
1399 		if (mp->type == MT_STRING)
1400 		{
1401 			newstrptr = NULL;
1402 			for (ms = mp->firststring; ms; ms = ms->next)
1403 			{
1404 				if (!strlen(ms->string))
1405 				{
1406 					newstrptr = strptr;
1407 					break;
1408 				} //end if
1409 				//Log_Write("MT_STRING: %s", mp->string);
1410 				index = StringContains(strptr, ms->string, qfalse);
1411 				if (index >= 0)
1412 				{
1413 					newstrptr = strptr + index;
1414 					if (lastvariable >= 0)
1415 					{
1416 						match->variables[lastvariable].length =
1417 								(newstrptr - match->string) - match->variables[lastvariable].offset;
1418 								//newstrptr - match->variables[lastvariable].ptr;
1419 						lastvariable = -1;
1420 						break;
1421 					} //end if
1422 					else if (index == 0)
1423 					{
1424 						break;
1425 					} //end else
1426 					newstrptr = NULL;
1427 				} //end if
1428 			} //end for
1429 			if (!newstrptr) return qfalse;
1430 			strptr = newstrptr + strlen(ms->string);
1431 		} //end if
1432 		//if it is a variable piece of string
1433 		else if (mp->type == MT_VARIABLE)
1434 		{
1435 			//Log_Write("MT_VARIABLE");
1436 			match->variables[mp->variable].offset = strptr - match->string;
1437 			lastvariable = mp->variable;
1438 		} //end else if
1439 	} //end for
1440 	//if a match was found
1441 	if (!mp && (lastvariable >= 0 || !strlen(strptr)))
1442 	{
1443 		//if the last piece was a variable string
1444 		if (lastvariable >= 0)
1445 		{
1446         		assert( match->variables[lastvariable].offset >= 0 ); // bk001204
1447 			match->variables[lastvariable].length =
1448 				strlen(&match->string[ (int) match->variables[lastvariable].offset]);
1449 		} //end if
1450 		return qtrue;
1451 	} //end if
1452 	return qfalse;
1453 } //end of the function StringsMatch
1454 //===========================================================================
1455 //
1456 // Parameter:				-
1457 // Returns:					-
1458 // Changes Globals:		-
1459 //===========================================================================
BotFindMatch(char * str,bot_match_t * match,unsigned long int context)1460 int BotFindMatch(char *str, bot_match_t *match, unsigned long int context)
1461 {
1462 	int i;
1463 	bot_matchtemplate_t *ms;
1464 
1465 	strncpy(match->string, str, MAX_MESSAGE_SIZE);
1466 	//remove any trailing enters
1467 	while(strlen(match->string) &&
1468 			match->string[strlen(match->string)-1] == '\n')
1469 	{
1470 		match->string[strlen(match->string)-1] = '\0';
1471 	} //end while
1472 	//compare the string with all the match strings
1473 	for (ms = matchtemplates; ms; ms = ms->next)
1474 	{
1475 		if (!(ms->context & context)) continue;
1476 		//reset the match variable offsets
1477 		for (i = 0; i < MAX_MATCHVARIABLES; i++) match->variables[i].offset = -1;
1478 		//
1479 		if (StringsMatch(ms->first, match))
1480 		{
1481 			match->type = ms->type;
1482 			match->subtype = ms->subtype;
1483 			return qtrue;
1484 		} //end if
1485 	} //end for
1486 	return qfalse;
1487 } //end of the function BotFindMatch
1488 //===========================================================================
1489 //
1490 // Parameter:				-
1491 // Returns:					-
1492 // Changes Globals:		-
1493 //===========================================================================
BotMatchVariable(bot_match_t * match,int variable,char * buf,int size)1494 void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size)
1495 {
1496 	if (variable < 0 || variable >= MAX_MATCHVARIABLES)
1497 	{
1498 		botimport.Print(PRT_FATAL, "BotMatchVariable: variable out of range\n");
1499 		strcpy(buf, "");
1500 		return;
1501 	} //end if
1502 
1503 	if (match->variables[variable].offset >= 0)
1504 	{
1505 		if (match->variables[variable].length < size)
1506 			size = match->variables[variable].length+1;
1507 		assert( match->variables[variable].offset >= 0 ); // bk001204
1508 		strncpy(buf, &match->string[ (int) match->variables[variable].offset], size-1);
1509 		buf[size-1] = '\0';
1510 	} //end if
1511 	else
1512 	{
1513 		strcpy(buf, "");
1514 	} //end else
1515 	return;
1516 } //end of the function BotMatchVariable
1517 //===========================================================================
1518 //
1519 // Parameter:				-
1520 // Returns:					-
1521 // Changes Globals:		-
1522 //===========================================================================
BotFindStringInList(bot_stringlist_t * list,char * string)1523 bot_stringlist_t *BotFindStringInList(bot_stringlist_t *list, char *string)
1524 {
1525 	bot_stringlist_t *s;
1526 
1527 	for (s = list; s; s = s->next)
1528 	{
1529 		if (!strcmp(s->string, string)) return s;
1530 	} //end for
1531 	return NULL;
1532 } //end of the function BotFindStringInList
1533 //===========================================================================
1534 //
1535 // Parameter:				-
1536 // Returns:					-
1537 // Changes Globals:		-
1538 //===========================================================================
BotCheckChatMessageIntegrety(char * message,bot_stringlist_t * stringlist)1539 bot_stringlist_t *BotCheckChatMessageIntegrety(char *message, bot_stringlist_t *stringlist)
1540 {
1541 	int i;
1542 	char *msgptr;
1543 	char temp[MAX_MESSAGE_SIZE];
1544 	bot_stringlist_t *s;
1545 
1546 	msgptr = message;
1547 	//
1548 	while(*msgptr)
1549 	{
1550 		if (*msgptr == ESCAPE_CHAR)
1551 		{
1552 			msgptr++;
1553 			switch(*msgptr)
1554 			{
1555 				case 'v': //variable
1556 				{
1557 					//step over the 'v'
1558 					msgptr++;
1559 					while(*msgptr && *msgptr != ESCAPE_CHAR) msgptr++;
1560 					//step over the trailing escape char
1561 					if (*msgptr) msgptr++;
1562 					break;
1563 				} //end case
1564 				case 'r': //random
1565 				{
1566 					//step over the 'r'
1567 					msgptr++;
1568 					for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++)
1569 					{
1570 						temp[i] = *msgptr++;
1571 					} //end while
1572 					temp[i] = '\0';
1573 					//step over the trailing escape char
1574 					if (*msgptr) msgptr++;
1575 					//find the random keyword
1576 					if (!RandomString(temp))
1577 					{
1578 						if (!BotFindStringInList(stringlist, temp))
1579 						{
1580 							Log_Write("%s = {\"%s\"} //MISSING RANDOM\r\n", temp, temp);
1581 							s = (struct bot_stringlist_s *)GetClearedMemory(sizeof(bot_stringlist_t) + strlen(temp) + 1);
1582 							s->string = (char *) s + sizeof(bot_stringlist_t);
1583 							strcpy(s->string, temp);
1584 							s->next = stringlist;
1585 							stringlist = s;
1586 						} //end if
1587 					} //end if
1588 					break;
1589 				} //end case
1590 				default:
1591 				{
1592 					botimport.Print(PRT_FATAL, "BotCheckChatMessageIntegrety: message \"%s\" invalid escape char\n", message);
1593 					break;
1594 				} //end default
1595 			} //end switch
1596 		} //end if
1597 		else
1598 		{
1599 			msgptr++;
1600 		} //end else
1601 	} //end while
1602 	return stringlist;
1603 } //end of the function BotCheckChatMessageIntegrety
1604 //===========================================================================
1605 //
1606 // Parameter:				-
1607 // Returns:					-
1608 // Changes Globals:		-
1609 //===========================================================================
BotCheckInitialChatIntegrety(bot_chat_t * chat)1610 void BotCheckInitialChatIntegrety(bot_chat_t *chat)
1611 {
1612 	bot_chattype_t *t;
1613 	bot_chatmessage_t *cm;
1614 	bot_stringlist_t *stringlist, *s, *nexts;
1615 
1616 	stringlist = NULL;
1617 	for (t = chat->types; t; t = t->next)
1618 	{
1619 		for (cm = t->firstchatmessage; cm; cm = cm->next)
1620 		{
1621 			stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist);
1622 		} //end for
1623 	} //end for
1624 	for (s = stringlist; s; s = nexts)
1625 	{
1626 		nexts = s->next;
1627 		FreeMemory(s);
1628 	} //end for
1629 } //end of the function BotCheckInitialChatIntegrety
1630 //===========================================================================
1631 //
1632 // Parameter:				-
1633 // Returns:					-
1634 // Changes Globals:		-
1635 //===========================================================================
BotCheckReplyChatIntegrety(bot_replychat_t * replychat)1636 void BotCheckReplyChatIntegrety(bot_replychat_t *replychat)
1637 {
1638 	bot_replychat_t *rp;
1639 	bot_chatmessage_t *cm;
1640 	bot_stringlist_t *stringlist, *s, *nexts;
1641 
1642 	stringlist = NULL;
1643 	for (rp = replychat; rp; rp = rp->next)
1644 	{
1645 		for (cm = rp->firstchatmessage; cm; cm = cm->next)
1646 		{
1647 			stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist);
1648 		} //end for
1649 	} //end for
1650 	for (s = stringlist; s; s = nexts)
1651 	{
1652 		nexts = s->next;
1653 		FreeMemory(s);
1654 	} //end for
1655 } //end of the function BotCheckReplyChatIntegrety
1656 //===========================================================================
1657 //
1658 // Parameter:				-
1659 // Returns:					-
1660 // Changes Globals:		-
1661 //===========================================================================
BotDumpReplyChat(bot_replychat_t * replychat)1662 void BotDumpReplyChat(bot_replychat_t *replychat)
1663 {
1664 	FILE *fp;
1665 	bot_replychat_t *rp;
1666 	bot_replychatkey_t *key;
1667 	bot_chatmessage_t *cm;
1668 	bot_matchpiece_t *mp;
1669 
1670 	fp = Log_FilePointer();
1671 	if (!fp) return;
1672 	fprintf(fp, "BotDumpReplyChat:\n");
1673 	for (rp = replychat; rp; rp = rp->next)
1674 	{
1675 		fprintf(fp, "[");
1676 		for (key = rp->keys; key; key = key->next)
1677 		{
1678 			if (key->flags & RCKFL_AND) fprintf(fp, "&");
1679 			else if (key->flags & RCKFL_NOT) fprintf(fp, "!");
1680 			//
1681 			if (key->flags & RCKFL_NAME) fprintf(fp, "name");
1682 			else if (key->flags & RCKFL_GENDERFEMALE) fprintf(fp, "female");
1683 			else if (key->flags & RCKFL_GENDERMALE) fprintf(fp, "male");
1684 			else if (key->flags & RCKFL_GENDERLESS) fprintf(fp, "it");
1685 			else if (key->flags & RCKFL_VARIABLES)
1686 			{
1687 				fprintf(fp, "(");
1688 				for (mp = key->match; mp; mp = mp->next)
1689 				{
1690 					if (mp->type == MT_STRING) fprintf(fp, "\"%s\"", mp->firststring->string);
1691 					else fprintf(fp, "%d", mp->variable);
1692 					if (mp->next) fprintf(fp, ", ");
1693 				} //end for
1694 				fprintf(fp, ")");
1695 			} //end if
1696 			else if (key->flags & RCKFL_STRING)
1697 			{
1698 				fprintf(fp, "\"%s\"", key->string);
1699 			} //end if
1700 			if (key->next) fprintf(fp, ", ");
1701 			else fprintf(fp, "] = %1.0f\n", rp->priority);
1702 		} //end for
1703 		fprintf(fp, "{\n");
1704 		for (cm = rp->firstchatmessage; cm; cm = cm->next)
1705 		{
1706 			fprintf(fp, "\t\"%s\";\n", cm->chatmessage);
1707 		} //end for
1708 		fprintf(fp, "}\n");
1709 	} //end for
1710 } //end of the function BotDumpReplyChat
1711 //===========================================================================
1712 //
1713 // Parameter:				-
1714 // Returns:					-
1715 // Changes Globals:		-
1716 //===========================================================================
BotFreeReplyChat(bot_replychat_t * replychat)1717 void BotFreeReplyChat(bot_replychat_t *replychat)
1718 {
1719 	bot_replychat_t *rp, *nextrp;
1720 	bot_replychatkey_t *key, *nextkey;
1721 	bot_chatmessage_t *cm, *nextcm;
1722 
1723 	for (rp = replychat; rp; rp = nextrp)
1724 	{
1725 		nextrp = rp->next;
1726 		for (key = rp->keys; key; key = nextkey)
1727 		{
1728 			nextkey = key->next;
1729 			if (key->match) BotFreeMatchPieces(key->match);
1730 			if (key->string) FreeMemory(key->string);
1731 			FreeMemory(key);
1732 		} //end for
1733 		for (cm = rp->firstchatmessage; cm; cm = nextcm)
1734 		{
1735 			nextcm = cm->next;
1736 			FreeMemory(cm);
1737 		} //end for
1738 		FreeMemory(rp);
1739 	} //end for
1740 } //end of the function BotFreeReplyChat
1741 //===========================================================================
1742 //
1743 // Parameter:			-
1744 // Returns:				-
1745 // Changes Globals:		-
1746 //===========================================================================
BotCheckValidReplyChatKeySet(source_t * source,bot_replychatkey_t * keys)1747 void BotCheckValidReplyChatKeySet(source_t *source, bot_replychatkey_t *keys)
1748 {
1749 	int allprefixed, hasvariableskey, hasstringkey;
1750 	bot_matchpiece_t *m;
1751 	bot_matchstring_t *ms;
1752 	bot_replychatkey_t *key, *key2;
1753 
1754 	//
1755 	allprefixed = qtrue;
1756 	hasvariableskey = hasstringkey = qfalse;
1757 	for (key = keys; key; key = key->next)
1758 	{
1759 		if (!(key->flags & (RCKFL_AND|RCKFL_NOT)))
1760 		{
1761 			allprefixed = qfalse;
1762 			if (key->flags & RCKFL_VARIABLES)
1763 			{
1764 				for (m = key->match; m; m = m->next)
1765 				{
1766 					if (m->type == MT_VARIABLE) hasvariableskey = qtrue;
1767 				} //end for
1768 			} //end if
1769 			else if (key->flags & RCKFL_STRING)
1770 			{
1771 				hasstringkey = qtrue;
1772 			} //end else if
1773 		} //end if
1774 		else if ((key->flags & RCKFL_AND) && (key->flags & RCKFL_STRING))
1775 		{
1776 			for (key2 = keys; key2; key2 = key2->next)
1777 			{
1778 				if (key2 == key) continue;
1779 				if (key2->flags & RCKFL_NOT) continue;
1780 				if (key2->flags & RCKFL_VARIABLES)
1781 				{
1782 					for (m = key2->match; m; m = m->next)
1783 					{
1784 						if (m->type == MT_STRING)
1785 						{
1786 							for (ms = m->firststring; ms; ms = ms->next)
1787 							{
1788 								if (StringContains(ms->string, key->string, qfalse) != -1)
1789 								{
1790 									break;
1791 								} //end if
1792 							} //end for
1793 							if (ms) break;
1794 						} //end if
1795 						else if (m->type == MT_VARIABLE)
1796 						{
1797 							break;
1798 						} //end if
1799 					} //end for
1800 					if (!m)
1801 					{
1802 						SourceWarning(source, "one of the match templates does not "
1803 										"leave space for the key %s with the & prefix", key->string);
1804 					} //end if
1805 				} //end if
1806 			} //end for
1807 		} //end else
1808 		if ((key->flags & RCKFL_NOT) && (key->flags & RCKFL_STRING))
1809 		{
1810 			for (key2 = keys; key2; key2 = key2->next)
1811 			{
1812 				if (key2 == key) continue;
1813 				if (key2->flags & RCKFL_NOT) continue;
1814 				if (key2->flags & RCKFL_STRING)
1815 				{
1816 					if (StringContains(key2->string, key->string, qfalse) != -1)
1817 					{
1818 						SourceWarning(source, "the key %s with prefix ! is inside the key %s", key->string, key2->string);
1819 					} //end if
1820 				} //end if
1821 				else if (key2->flags & RCKFL_VARIABLES)
1822 				{
1823 					for (m = key2->match; m; m = m->next)
1824 					{
1825 						if (m->type == MT_STRING)
1826 						{
1827 							for (ms = m->firststring; ms; ms = ms->next)
1828 							{
1829 								if (StringContains(ms->string, key->string, qfalse) != -1)
1830 								{
1831 									SourceWarning(source, "the key %s with prefix ! is inside "
1832 												"the match template string %s", key->string, ms->string);
1833 								} //end if
1834 							} //end for
1835 						} //end if
1836 					} //end for
1837 				} //end else if
1838 			} //end for
1839 		} //end if
1840 	} //end for
1841 	if (allprefixed) SourceWarning(source, "all keys have a & or ! prefix");
1842 	if (hasvariableskey && hasstringkey)
1843 	{
1844 		SourceWarning(source, "variables from the match template(s) could be "
1845 								"invalid when outputting one of the chat messages");
1846 	} //end if
1847 } //end of the function BotCheckValidReplyChatKeySet
1848 //===========================================================================
1849 //
1850 // Parameter:			-
1851 // Returns:				-
1852 // Changes Globals:		-
1853 //===========================================================================
BotLoadReplyChat(char * filename)1854 bot_replychat_t *BotLoadReplyChat(char *filename)
1855 {
1856 	char chatmessagestring[MAX_MESSAGE_SIZE];
1857 	char namebuffer[MAX_MESSAGE_SIZE];
1858 	source_t *source;
1859 	token_t token;
1860 	bot_chatmessage_t *chatmessage = NULL;
1861 	bot_replychat_t *replychat, *replychatlist;
1862 	bot_replychatkey_t *key;
1863 
1864 	PC_SetBaseFolder(BOTFILESBASEFOLDER);
1865 	source = LoadSourceFile(filename);
1866 	if (!source)
1867 	{
1868 		botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
1869 		return NULL;
1870 	} //end if
1871 	//
1872 	replychatlist = NULL;
1873 	//
1874 	while(PC_ReadToken(source, &token))
1875 	{
1876 		if (strcmp(token.string, "["))
1877 		{
1878 			SourceError(source, "expected [, found %s", token.string);
1879 			BotFreeReplyChat(replychatlist);
1880 			FreeSource(source);
1881 			return NULL;
1882 		} //end if
1883 		//
1884 		replychat = (struct bot_replychat_s *)GetClearedHunkMemory(sizeof(bot_replychat_t));
1885 		replychat->keys = NULL;
1886 		replychat->next = replychatlist;
1887 		replychatlist = replychat;
1888 		//read the keys, there must be at least one key
1889 		do
1890 		{
1891 			//allocate a key
1892 			key = (bot_replychatkey_t *) GetClearedHunkMemory(sizeof(bot_replychatkey_t));
1893 			key->flags = 0;
1894 			key->string = NULL;
1895 			key->match = NULL;
1896 			key->next = replychat->keys;
1897 			replychat->keys = key;
1898 			//check for MUST BE PRESENT and MUST BE ABSENT keys
1899 			if (PC_CheckTokenString(source, "&")) key->flags |= RCKFL_AND;
1900 			else if (PC_CheckTokenString(source, "!")) key->flags |= RCKFL_NOT;
1901 			//special keys
1902 			if (PC_CheckTokenString(source, "name")) key->flags |= RCKFL_NAME;
1903 			else if (PC_CheckTokenString(source, "female")) key->flags |= RCKFL_GENDERFEMALE;
1904 			else if (PC_CheckTokenString(source, "male")) key->flags |= RCKFL_GENDERMALE;
1905 			else if (PC_CheckTokenString(source, "it")) key->flags |= RCKFL_GENDERLESS;
1906 			else if (PC_CheckTokenString(source, "(")) //match key
1907 			{
1908 				key->flags |= RCKFL_VARIABLES;
1909 				key->match = BotLoadMatchPieces(source, ")");
1910 				if (!key->match)
1911 				{
1912 					BotFreeReplyChat(replychatlist);
1913 					return NULL;
1914 				} //end if
1915 			} //end else if
1916 			else if (PC_CheckTokenString(source, "<")) //bot names
1917 			{
1918 				key->flags |= RCKFL_BOTNAMES;
1919 				strcpy(namebuffer, "");
1920 				do
1921 				{
1922 					if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
1923 					{
1924 						BotFreeReplyChat(replychatlist);
1925 						FreeSource(source);
1926 						return NULL;
1927 					} //end if
1928 					StripDoubleQuotes(token.string);
1929 					if (strlen(namebuffer)) strcat(namebuffer, "\\");
1930 					strcat(namebuffer, token.string);
1931 				} while(PC_CheckTokenString(source, ","));
1932 				if (!PC_ExpectTokenString(source, ">"))
1933 				{
1934 					BotFreeReplyChat(replychatlist);
1935 					FreeSource(source);
1936 					return NULL;
1937 				} //end if
1938 				key->string = (char *) GetClearedHunkMemory(strlen(namebuffer) + 1);
1939 				strcpy(key->string, namebuffer);
1940 			} //end else if
1941 			else //normal string key
1942 			{
1943 				key->flags |= RCKFL_STRING;
1944 				if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
1945 				{
1946 					BotFreeReplyChat(replychatlist);
1947 					FreeSource(source);
1948 					return NULL;
1949 				} //end if
1950 				StripDoubleQuotes(token.string);
1951 				key->string = (char *) GetClearedHunkMemory(strlen(token.string) + 1);
1952 				strcpy(key->string, token.string);
1953 			} //end else
1954 			//
1955 			PC_CheckTokenString(source, ",");
1956 		} while(!PC_CheckTokenString(source, "]"));
1957 		//
1958 		BotCheckValidReplyChatKeySet(source, replychat->keys);
1959 		//read the = sign and the priority
1960 		if (!PC_ExpectTokenString(source, "=") ||
1961 			!PC_ExpectTokenType(source, TT_NUMBER, 0, &token))
1962 		{
1963 			BotFreeReplyChat(replychatlist);
1964 			FreeSource(source);
1965 			return NULL;
1966 		} //end if
1967 		replychat->priority = token.floatvalue;
1968 		//read the leading {
1969 		if (!PC_ExpectTokenString(source, "{"))
1970 		{
1971 			BotFreeReplyChat(replychatlist);
1972 			FreeSource(source);
1973 			return NULL;
1974 		} //end if
1975 		replychat->numchatmessages = 0;
1976 		//while the trailing } is not found
1977 		while(!PC_CheckTokenString(source, "}"))
1978 		{
1979 			if (!BotLoadChatMessage(source, chatmessagestring))
1980 			{
1981 				BotFreeReplyChat(replychatlist);
1982 				FreeSource(source);
1983 				return NULL;
1984 			} //end if
1985 			chatmessage = (bot_chatmessage_t *) GetClearedHunkMemory(sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1);
1986 			chatmessage->chatmessage = (char *) chatmessage + sizeof(bot_chatmessage_t);
1987 			strcpy(chatmessage->chatmessage, chatmessagestring);
1988 			chatmessage->time = -2*CHATMESSAGE_RECENTTIME;
1989 			chatmessage->next = replychat->firstchatmessage;
1990 			//add the chat message to the reply chat
1991 			replychat->firstchatmessage = chatmessage;
1992 			replychat->numchatmessages++;
1993 		} //end while
1994 	} //end while
1995 	FreeSource(source);
1996 	botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
1997 	//
1998 	//BotDumpReplyChat(replychatlist);
1999 	if (botDeveloper)
2000 	{
2001 		BotCheckReplyChatIntegrety(replychatlist);
2002 	} //end if
2003 	//
2004 	if (!replychatlist) botimport.Print(PRT_MESSAGE, "no rchats\n");
2005 	//
2006 	return replychatlist;
2007 } //end of the function BotLoadReplyChat
2008 //===========================================================================
2009 //
2010 // Parameter:				-
2011 // Returns:					-
2012 // Changes Globals:		-
2013 //===========================================================================
BotDumpInitialChat(bot_chat_t * chat)2014 void BotDumpInitialChat(bot_chat_t *chat)
2015 {
2016 	bot_chattype_t *t;
2017 	bot_chatmessage_t *m;
2018 
2019 	Log_Write("{");
2020 	for (t = chat->types; t; t = t->next)
2021 	{
2022 		Log_Write(" type \"%s\"", t->name);
2023 		Log_Write(" {");
2024 		Log_Write("  numchatmessages = %d", t->numchatmessages);
2025 		for (m = t->firstchatmessage; m; m = m->next)
2026 		{
2027 			Log_Write("  \"%s\"", m->chatmessage);
2028 		} //end for
2029 		Log_Write(" }");
2030 	} //end for
2031 	Log_Write("}");
2032 } //end of the function BotDumpInitialChat
2033 //===========================================================================
2034 //
2035 // Parameter:				-
2036 // Returns:					-
2037 // Changes Globals:		-
2038 //===========================================================================
BotLoadInitialChat(char * chatfile,char * chatname)2039 bot_chat_t *BotLoadInitialChat(char *chatfile, char *chatname)
2040 {
2041 	int pass, foundchat, indent, size;
2042 	char *ptr = NULL;
2043 	char chatmessagestring[MAX_MESSAGE_SIZE];
2044 	source_t *source;
2045 	token_t token;
2046 	bot_chat_t *chat = NULL;
2047 	bot_chattype_t *chattype = NULL;
2048 	bot_chatmessage_t *chatmessage = NULL;
2049 #ifdef DEBUG
2050 	int starttime;
2051 
2052 	starttime = Sys_MilliSeconds();
2053 #endif //DEBUG
2054 	//
2055 	size = 0;
2056 	foundchat = qfalse;
2057 	//a bot chat is parsed in two phases
2058 	for (pass = 0; pass < 2; pass++)
2059 	{
2060 		//allocate memory
2061 		if (pass && size) ptr = (char *) GetClearedMemory(size);
2062 		//load the source file
2063 		PC_SetBaseFolder(BOTFILESBASEFOLDER);
2064 		source = LoadSourceFile(chatfile);
2065 		if (!source)
2066 		{
2067 			botimport.Print(PRT_ERROR, "counldn't load %s\n", chatfile);
2068 			return NULL;
2069 		} //end if
2070 		//chat structure
2071 		if (pass)
2072 		{
2073 			chat = (bot_chat_t *) ptr;
2074 			ptr += sizeof(bot_chat_t);
2075 		} //end if
2076 		size = sizeof(bot_chat_t);
2077 		//
2078 		while(PC_ReadToken(source, &token))
2079 		{
2080 			if (!strcmp(token.string, "chat"))
2081 			{
2082 				if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
2083 				{
2084 					FreeSource(source);
2085 					return NULL;
2086 				} //end if
2087 				StripDoubleQuotes(token.string);
2088 				//after the chat name we expect an opening brace
2089 				if (!PC_ExpectTokenString(source, "{"))
2090 				{
2091 					FreeSource(source);
2092 					return NULL;
2093 				} //end if
2094 				//if the chat name is found
2095 				if (!Q_stricmp(token.string, chatname))
2096 				{
2097 					foundchat = qtrue;
2098 					//read the chat types
2099 					while(1)
2100 					{
2101 						if (!PC_ExpectAnyToken(source, &token))
2102 						{
2103 							FreeSource(source);
2104 							return NULL;
2105 						} //end if
2106 						if (!strcmp(token.string, "}")) break;
2107 						if (strcmp(token.string, "type"))
2108 						{
2109 							SourceError(source, "expected type found %s", token.string);
2110 							FreeSource(source);
2111 							return NULL;
2112 						} //end if
2113 						//expect the chat type name
2114 						if (!PC_ExpectTokenType(source, TT_STRING, 0, &token) ||
2115 							!PC_ExpectTokenString(source, "{"))
2116 						{
2117 							FreeSource(source);
2118 							return NULL;
2119 						} //end if
2120 						StripDoubleQuotes(token.string);
2121 						if (pass)
2122 						{
2123 							chattype = (bot_chattype_t *) ptr;
2124 							strncpy(chattype->name, token.string, MAX_CHATTYPE_NAME);
2125 							chattype->firstchatmessage = NULL;
2126 							//add the chat type to the chat
2127 							chattype->next = chat->types;
2128 							chat->types = chattype;
2129 							//
2130 							ptr += sizeof(bot_chattype_t);
2131 						} //end if
2132 						size += sizeof(bot_chattype_t);
2133 						//read the chat messages
2134 						while(!PC_CheckTokenString(source, "}"))
2135 						{
2136 							size_t len;
2137 							if (!BotLoadChatMessage(source, chatmessagestring))
2138 							{
2139 								FreeSource(source);
2140 								return NULL;
2141 							} //end if
2142 							len = strlen(chatmessagestring) + 1;
2143 							len = PAD(len, sizeof(long));
2144 							if (pass)
2145 							{
2146 								chatmessage = (bot_chatmessage_t *) ptr;
2147 								chatmessage->time = -2*CHATMESSAGE_RECENTTIME;
2148 								//put the chat message in the list
2149 								chatmessage->next = chattype->firstchatmessage;
2150 								chattype->firstchatmessage = chatmessage;
2151 								//store the chat message
2152 								ptr += sizeof(bot_chatmessage_t);
2153 								chatmessage->chatmessage = ptr;
2154 								strcpy(chatmessage->chatmessage, chatmessagestring);
2155 								ptr += len;
2156 								//the number of chat messages increased
2157 								chattype->numchatmessages++;
2158 							} //end if
2159 							size += sizeof(bot_chatmessage_t) + len;
2160 						} //end if
2161 					} //end while
2162 				} //end if
2163 				else //skip the bot chat
2164 				{
2165 					indent = 1;
2166 					while(indent)
2167 					{
2168 						if (!PC_ExpectAnyToken(source, &token))
2169 						{
2170 							FreeSource(source);
2171 							return NULL;
2172 						} //end if
2173 						if (!strcmp(token.string, "{")) indent++;
2174 						else if (!strcmp(token.string, "}")) indent--;
2175 					} //end while
2176 				} //end else
2177 			} //end if
2178 			else
2179 			{
2180 				SourceError(source, "unknown definition %s", token.string);
2181 				FreeSource(source);
2182 				return NULL;
2183 			} //end else
2184 		} //end while
2185 		//free the source
2186 		FreeSource(source);
2187 		//if the requested character is not found
2188 		if (!foundchat)
2189 		{
2190 			botimport.Print(PRT_ERROR, "couldn't find chat %s in %s\n", chatname, chatfile);
2191 			return NULL;
2192 		} //end if
2193 	} //end for
2194 	//
2195 	botimport.Print(PRT_MESSAGE, "loaded %s from %s\n", chatname, chatfile);
2196 	//
2197 	//BotDumpInitialChat(chat);
2198 	if (botDeveloper)
2199 	{
2200 		BotCheckInitialChatIntegrety(chat);
2201 	} //end if
2202 #ifdef DEBUG
2203 	botimport.Print(PRT_MESSAGE, "initial chats loaded in %d msec\n", Sys_MilliSeconds() - starttime);
2204 #endif //DEBUG
2205 	//character was read successfully
2206 	return chat;
2207 } //end of the function BotLoadInitialChat
2208 //===========================================================================
2209 //
2210 // Parameter:			-
2211 // Returns:				-
2212 // Changes Globals:		-
2213 //===========================================================================
BotFreeChatFile(int chatstate)2214 void BotFreeChatFile(int chatstate)
2215 {
2216 	bot_chatstate_t *cs;
2217 
2218 	cs = BotChatStateFromHandle(chatstate);
2219 	if (!cs) return;
2220 	if (cs->chat) FreeMemory(cs->chat);
2221 	cs->chat = NULL;
2222 } //end of the function BotFreeChatFile
2223 //===========================================================================
2224 //
2225 // Parameter:				-
2226 // Returns:					-
2227 // Changes Globals:		-
2228 //===========================================================================
BotLoadChatFile(int chatstate,char * chatfile,char * chatname)2229 int BotLoadChatFile(int chatstate, char *chatfile, char *chatname)
2230 {
2231 	bot_chatstate_t *cs;
2232 	int n, avail = 0;
2233 
2234 	cs = BotChatStateFromHandle(chatstate);
2235 	if (!cs) return BLERR_CANNOTLOADICHAT;
2236 	BotFreeChatFile(chatstate);
2237 
2238 	if (!LibVarGetValue("bot_reloadcharacters"))
2239 	{
2240 		avail = -1;
2241 		for( n = 0; n < MAX_CLIENTS; n++ ) {
2242 			if( !ichatdata[n] ) {
2243 				if( avail == -1 ) {
2244 					avail = n;
2245 				}
2246 				continue;
2247 			}
2248 			if( strcmp( chatfile, ichatdata[n]->filename ) != 0 ) {
2249 				continue;
2250 			}
2251 			if( strcmp( chatname, ichatdata[n]->chatname ) != 0 ) {
2252 				continue;
2253 			}
2254 			cs->chat = ichatdata[n]->chat;
2255 		//		botimport.Print( PRT_MESSAGE, "retained %s from %s\n", chatname, chatfile );
2256 			return BLERR_NOERROR;
2257 		}
2258 
2259 		if( avail == -1 ) {
2260 			botimport.Print(PRT_FATAL, "ichatdata table full; couldn't load chat %s from %s\n", chatname, chatfile);
2261 			return BLERR_CANNOTLOADICHAT;
2262 		}
2263 	}
2264 
2265 	cs->chat = BotLoadInitialChat(chatfile, chatname);
2266 	if (!cs->chat)
2267 	{
2268 		botimport.Print(PRT_FATAL, "couldn't load chat %s from %s\n", chatname, chatfile);
2269 		return BLERR_CANNOTLOADICHAT;
2270 	} //end if
2271 	if (!LibVarGetValue("bot_reloadcharacters"))
2272 	{
2273 		ichatdata[avail] = (bot_ichatdata_t *)GetClearedMemory( sizeof(bot_ichatdata_t) );
2274 		ichatdata[avail]->chat = cs->chat;
2275 		Q_strncpyz( ichatdata[avail]->chatname, chatname, sizeof(ichatdata[avail]->chatname) );
2276 		Q_strncpyz( ichatdata[avail]->filename, chatfile, sizeof(ichatdata[avail]->filename) );
2277 	} //end if
2278 
2279 	return BLERR_NOERROR;
2280 } //end of the function BotLoadChatFile
2281 //===========================================================================
2282 //
2283 // Parameter:			-
2284 // Returns:				-
2285 // Changes Globals:		-
2286 //===========================================================================
BotExpandChatMessage(char * outmessage,char * message,unsigned long mcontext,bot_match_t * match,unsigned long vcontext,int reply)2287 int BotExpandChatMessage(char *outmessage, char *message, unsigned long mcontext,
2288 							 bot_match_t *match, unsigned long vcontext, int reply)
2289 {
2290 	int num, len, i, expansion;
2291 	char *outputbuf, *ptr, *msgptr;
2292 	char temp[MAX_MESSAGE_SIZE];
2293 
2294 	expansion = qfalse;
2295 	msgptr = message;
2296 	outputbuf = outmessage;
2297 	len = 0;
2298 	//
2299 	while(*msgptr)
2300 	{
2301 		if (*msgptr == ESCAPE_CHAR)
2302 		{
2303 			msgptr++;
2304 			switch(*msgptr)
2305 			{
2306 				case 'v': //variable
2307 				{
2308 					msgptr++;
2309 					num = 0;
2310 					while(*msgptr && *msgptr != ESCAPE_CHAR)
2311 					{
2312 						num = num * 10 + (*msgptr++) - '0';
2313 					} //end while
2314 					//step over the trailing escape char
2315 					if (*msgptr) msgptr++;
2316 					if (num > MAX_MATCHVARIABLES)
2317 					{
2318 						botimport.Print(PRT_ERROR, "BotConstructChat: message %s variable %d out of range\n", message, num);
2319 						return qfalse;
2320 					} //end if
2321 					if (match->variables[num].offset >= 0)
2322 					{
2323 					        assert( match->variables[num].offset >= 0 ); // bk001204
2324 						ptr = &match->string[ (int) match->variables[num].offset];
2325 						for (i = 0; i < match->variables[num].length; i++)
2326 						{
2327 							temp[i] = ptr[i];
2328 						} //end for
2329 						temp[i] = 0;
2330 						//if it's a reply message
2331 						if (reply)
2332 						{
2333 							//replace the reply synonyms in the variables
2334 							BotReplaceReplySynonyms(temp, vcontext);
2335 						} //end if
2336 						else
2337 						{
2338 							//replace synonyms in the variable context
2339 							BotReplaceSynonyms(temp, vcontext);
2340 						} //end else
2341 						//
2342 						if (len + strlen(temp) >= MAX_MESSAGE_SIZE)
2343 						{
2344 							botimport.Print(PRT_ERROR, "BotConstructChat: message %s too long\n", message);
2345 							return qfalse;
2346 						} //end if
2347 						strcpy(&outputbuf[len], temp);
2348 						len += strlen(temp);
2349 					} //end if
2350 					break;
2351 				} //end case
2352 				case 'r': //random
2353 				{
2354 					msgptr++;
2355 					for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++)
2356 					{
2357 						temp[i] = *msgptr++;
2358 					} //end while
2359 					temp[i] = '\0';
2360 					//step over the trailing escape char
2361 					if (*msgptr) msgptr++;
2362 					//find the random keyword
2363 					ptr = RandomString(temp);
2364 					if (!ptr)
2365 					{
2366 						botimport.Print(PRT_ERROR, "BotConstructChat: unknown random string %s\n", temp);
2367 						return qfalse;
2368 					} //end if
2369 					if (len + strlen(ptr) >= MAX_MESSAGE_SIZE)
2370 					{
2371 						botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message);
2372 						return qfalse;
2373 					} //end if
2374 					strcpy(&outputbuf[len], ptr);
2375 					len += strlen(ptr);
2376 					expansion = qtrue;
2377 					break;
2378 				} //end case
2379 				default:
2380 				{
2381 					botimport.Print(PRT_FATAL, "BotConstructChat: message \"%s\" invalid escape char\n", message);
2382 					break;
2383 				} //end default
2384 			} //end switch
2385 		} //end if
2386 		else
2387 		{
2388 			outputbuf[len++] = *msgptr++;
2389 			if (len >= MAX_MESSAGE_SIZE)
2390 			{
2391 				botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message);
2392 				break;
2393 			} //end if
2394 		} //end else
2395 	} //end while
2396 	outputbuf[len] = '\0';
2397 	//replace synonyms weighted in the message context
2398 	BotReplaceWeightedSynonyms(outputbuf, mcontext);
2399 	//return true if a random was expanded
2400 	return expansion;
2401 } //end of the function BotExpandChatMessage
2402 //===========================================================================
2403 //
2404 // Parameter:			-
2405 // Returns:				-
2406 // Changes Globals:		-
2407 //===========================================================================
BotConstructChatMessage(bot_chatstate_t * chatstate,char * message,unsigned long mcontext,bot_match_t * match,unsigned long vcontext,int reply)2408 void BotConstructChatMessage(bot_chatstate_t *chatstate, char *message, unsigned long mcontext,
2409 							 bot_match_t *match, unsigned long vcontext, int reply)
2410 {
2411 	int i;
2412 	char srcmessage[MAX_MESSAGE_SIZE];
2413 
2414 	strcpy(srcmessage, message);
2415 	for (i = 0; i < 10; i++)
2416 	{
2417 		if (!BotExpandChatMessage(chatstate->chatmessage, srcmessage, mcontext, match, vcontext, reply))
2418 		{
2419 			break;
2420 		} //end if
2421 		strcpy(srcmessage, chatstate->chatmessage);
2422 	} //end for
2423 	if (i >= 10)
2424 	{
2425 		botimport.Print(PRT_WARNING, "too many expansions in chat message\n");
2426 		botimport.Print(PRT_WARNING, "%s\n", chatstate->chatmessage);
2427 	} //end if
2428 } //end of the function BotConstructChatMessage
2429 //===========================================================================
2430 // randomly chooses one of the chat message of the given type
2431 //
2432 // Parameter:				-
2433 // Returns:					-
2434 // Changes Globals:		-
2435 //===========================================================================
BotChooseInitialChatMessage(bot_chatstate_t * cs,char * type)2436 char *BotChooseInitialChatMessage(bot_chatstate_t *cs, char *type)
2437 {
2438 	int n, numchatmessages;
2439 	float besttime;
2440 	bot_chattype_t *t;
2441 	bot_chatmessage_t *m, *bestchatmessage;
2442 	bot_chat_t *chat;
2443 
2444 	chat = cs->chat;
2445 	for (t = chat->types; t; t = t->next)
2446 	{
2447 		if (!Q_stricmp(t->name, type))
2448 		{
2449 			numchatmessages = 0;
2450 			for (m = t->firstchatmessage; m; m = m->next)
2451 			{
2452 				if (m->time > AAS_Time()) continue;
2453 				numchatmessages++;
2454 			} //end if
2455 			//if all chat messages have been used recently
2456 			if (numchatmessages <= 0)
2457 			{
2458 				besttime = 0;
2459 				bestchatmessage = NULL;
2460 				for (m = t->firstchatmessage; m; m = m->next)
2461 				{
2462 					if (!besttime || m->time < besttime)
2463 					{
2464 						bestchatmessage = m;
2465 						besttime = m->time;
2466 					} //end if
2467 				} //end for
2468 				if (bestchatmessage) return bestchatmessage->chatmessage;
2469 			} //end if
2470 			else //choose a chat message randomly
2471 			{
2472 				n = Q_flrand(0.0f, 1.0f) * numchatmessages;
2473 				for (m = t->firstchatmessage; m; m = m->next)
2474 				{
2475 					if (m->time > AAS_Time()) continue;
2476 					if (--n < 0)
2477 					{
2478 						m->time = AAS_Time() + CHATMESSAGE_RECENTTIME;
2479 						return m->chatmessage;
2480 					} //end if
2481 				} //end for
2482 			} //end else
2483 			return NULL;
2484 		} //end if
2485 	} //end for
2486 	return NULL;
2487 } //end of the function BotChooseInitialChatMessage
2488 //===========================================================================
2489 //
2490 // Parameter:				-
2491 // Returns:					-
2492 // Changes Globals:		-
2493 //===========================================================================
BotNumInitialChats(int chatstate,char * type)2494 int BotNumInitialChats(int chatstate, char *type)
2495 {
2496 	bot_chatstate_t *cs;
2497 	bot_chattype_t *t;
2498 
2499 	cs = BotChatStateFromHandle(chatstate);
2500 	if (!cs) return 0;
2501 
2502 	for (t = cs->chat->types; t; t = t->next)
2503 	{
2504 		if (!Q_stricmp(t->name, type))
2505 		{
2506 			if (LibVarGetValue("bot_testichat")) {
2507 				botimport.Print(PRT_MESSAGE, "%s has %d chat lines\n", type, t->numchatmessages);
2508 				botimport.Print(PRT_MESSAGE, "-------------------\n");
2509 			}
2510 			return t->numchatmessages;
2511 		} //end if
2512 	} //end for
2513 	return 0;
2514 } //end of the function BotNumInitialChats
2515 //===========================================================================
2516 //
2517 // Parameter:				-
2518 // Returns:					-
2519 // Changes Globals:		-
2520 //===========================================================================
BotInitialChat(int chatstate,char * type,int mcontext,char * var0,char * var1,char * var2,char * var3,char * var4,char * var5,char * var6,char * var7)2521 void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7)
2522 {
2523 	char *message;
2524 	int index;
2525 	bot_match_t match;
2526 	bot_chatstate_t *cs;
2527 
2528 	cs = BotChatStateFromHandle(chatstate);
2529 	if (!cs) return;
2530 	//if no chat file is loaded
2531 	if (!cs->chat) return;
2532 	//choose a chat message randomly of the given type
2533 	message = BotChooseInitialChatMessage(cs, type);
2534 	//if there's no message of the given type
2535 	if (!message)
2536 	{
2537 #ifdef DEBUG
2538 		botimport.Print(PRT_MESSAGE, "no chat messages of type %s\n", type);
2539 #endif //DEBUG
2540 		return;
2541 	} //end if
2542 	//
2543 	Com_Memset(&match, 0, sizeof(match));
2544 	index = 0;
2545 	if( var0 ) {
2546 		strcat(match.string, var0);
2547 		match.variables[0].offset = index;
2548 		match.variables[0].length = strlen(var0);
2549 		index += strlen(var0);
2550 	}
2551 	if( var1 ) {
2552 		strcat(match.string, var1);
2553 		match.variables[1].offset = index;
2554 		match.variables[1].length = strlen(var1);
2555 		index += strlen(var1);
2556 	}
2557 	if( var2 ) {
2558 		strcat(match.string, var2);
2559 		match.variables[2].offset = index;
2560 		match.variables[2].length = strlen(var2);
2561 		index += strlen(var2);
2562 	}
2563 	if( var3 ) {
2564 		strcat(match.string, var3);
2565 		match.variables[3].offset = index;
2566 		match.variables[3].length = strlen(var3);
2567 		index += strlen(var3);
2568 	}
2569 	if( var4 ) {
2570 		strcat(match.string, var4);
2571 		match.variables[4].offset = index;
2572 		match.variables[4].length = strlen(var4);
2573 		index += strlen(var4);
2574 	}
2575 	if( var5 ) {
2576 		strcat(match.string, var5);
2577 		match.variables[5].offset = index;
2578 		match.variables[5].length = strlen(var5);
2579 		index += strlen(var5);
2580 	}
2581 	if( var6 ) {
2582 		strcat(match.string, var6);
2583 		match.variables[6].offset = index;
2584 		match.variables[6].length = strlen(var6);
2585 		index += strlen(var6);
2586 	}
2587 	if( var7 ) {
2588 		strcat(match.string, var7);
2589 		match.variables[7].offset = index;
2590 		match.variables[7].length = strlen(var7);
2591 		index += strlen(var7);
2592 	}
2593  	//
2594 	BotConstructChatMessage(cs, message, mcontext, &match, 0, qfalse);
2595 } //end of the function BotInitialChat
2596 //===========================================================================
2597 //
2598 // Parameter:				-
2599 // Returns:					-
2600 // Changes Globals:		-
2601 //===========================================================================
BotPrintReplyChatKeys(bot_replychat_t * replychat)2602 void BotPrintReplyChatKeys(bot_replychat_t *replychat)
2603 {
2604 	bot_replychatkey_t *key;
2605 	bot_matchpiece_t *mp;
2606 
2607 	botimport.Print(PRT_MESSAGE, "[");
2608 	for (key = replychat->keys; key; key = key->next)
2609 	{
2610 		if (key->flags & RCKFL_AND) botimport.Print(PRT_MESSAGE, "&");
2611 		else if (key->flags & RCKFL_NOT) botimport.Print(PRT_MESSAGE, "!");
2612 		//
2613 		if (key->flags & RCKFL_NAME) botimport.Print(PRT_MESSAGE, "name");
2614 		else if (key->flags & RCKFL_GENDERFEMALE) botimport.Print(PRT_MESSAGE, "female");
2615 		else if (key->flags & RCKFL_GENDERMALE) botimport.Print(PRT_MESSAGE, "male");
2616 		else if (key->flags & RCKFL_GENDERLESS) botimport.Print(PRT_MESSAGE, "it");
2617 		else if (key->flags & RCKFL_VARIABLES)
2618 		{
2619 			botimport.Print(PRT_MESSAGE, "(");
2620 			for (mp = key->match; mp; mp = mp->next)
2621 			{
2622 				if (mp->type == MT_STRING) botimport.Print(PRT_MESSAGE, "\"%s\"", mp->firststring->string);
2623 				else botimport.Print(PRT_MESSAGE, "%d", mp->variable);
2624 				if (mp->next) botimport.Print(PRT_MESSAGE, ", ");
2625 			} //end for
2626 			botimport.Print(PRT_MESSAGE, ")");
2627 		} //end if
2628 		else if (key->flags & RCKFL_STRING)
2629 		{
2630 			botimport.Print(PRT_MESSAGE, "\"%s\"", key->string);
2631 		} //end if
2632 		if (key->next) botimport.Print(PRT_MESSAGE, ", ");
2633 		else botimport.Print(PRT_MESSAGE, "] = %1.0f\n", replychat->priority);
2634 	} //end for
2635 	botimport.Print(PRT_MESSAGE, "{\n");
2636 } //end of the function BotPrintReplyChatKeys
2637 //===========================================================================
2638 //
2639 // Parameter:				-
2640 // Returns:					-
2641 // Changes Globals:		-
2642 //===========================================================================
BotReplyChat(int chatstate,char * message,int mcontext,int vcontext,char * var0,char * var1,char * var2,char * var3,char * var4,char * var5,char * var6,char * var7)2643 int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7)
2644 {
2645 	bot_replychat_t *rchat, *bestrchat;
2646 	bot_replychatkey_t *key;
2647 	bot_chatmessage_t *m, *bestchatmessage;
2648 	bot_match_t match, bestmatch;
2649 	int bestpriority, num, found, res, numchatmessages, index;
2650 	bot_chatstate_t *cs;
2651 
2652 	cs = BotChatStateFromHandle(chatstate);
2653 	if (!cs) return qfalse;
2654 	Com_Memset(&match, 0, sizeof(bot_match_t));
2655 	strcpy(match.string, message);
2656 	bestpriority = -1;
2657 	bestchatmessage = NULL;
2658 	bestrchat = NULL;
2659 	//go through all the reply chats
2660 	for (rchat = replychats; rchat; rchat = rchat->next)
2661 	{
2662 		found = qfalse;
2663 		for (key = rchat->keys; key; key = key->next)
2664 		{
2665 			res = qfalse;
2666 			//get the match result
2667 			if (key->flags & RCKFL_NAME) res = (StringContains(message, cs->name, qfalse) != -1);
2668 			else if (key->flags & RCKFL_BOTNAMES) res = (StringContains(key->string, cs->name, qfalse) != -1);
2669 			else if (key->flags & RCKFL_GENDERFEMALE) res = (cs->gender == CHAT_GENDERFEMALE);
2670 			else if (key->flags & RCKFL_GENDERMALE) res = (cs->gender == CHAT_GENDERMALE);
2671 			else if (key->flags & RCKFL_GENDERLESS) res = (cs->gender == CHAT_GENDERLESS);
2672 			else if (key->flags & RCKFL_VARIABLES) res = StringsMatch(key->match, &match);
2673 			else if (key->flags & RCKFL_STRING) res = (StringContainsWord(message, key->string, qfalse) != NULL);
2674 			//if the key must be present
2675 			if (key->flags & RCKFL_AND)
2676 			{
2677 				if (!res)
2678 				{
2679 					found = qfalse;
2680 					break;
2681 				} //end if
2682 			} //end else if
2683 			//if the key must be absent
2684 			else if (key->flags & RCKFL_NOT)
2685 			{
2686 				if (res)
2687 				{
2688 					found = qfalse;
2689 					break;
2690 				} //end if
2691 			} //end if
2692 			else if (res)
2693 			{
2694 				found = qtrue;
2695 			} //end else
2696 		} //end for
2697 		//
2698 		if (found)
2699 		{
2700 			if (rchat->priority > bestpriority)
2701 			{
2702 				numchatmessages = 0;
2703 				for (m = rchat->firstchatmessage; m; m = m->next)
2704 				{
2705 					if (m->time > AAS_Time()) continue;
2706 					numchatmessages++;
2707 				} //end if
2708 				num = Q_flrand(0.0f, 1.0f) * numchatmessages;
2709 				for (m = rchat->firstchatmessage; m; m = m->next)
2710 				{
2711 					if (--num < 0) break;
2712 					if (m->time > AAS_Time()) continue;
2713 				} //end for
2714 				//if the reply chat has a message
2715 				if (m)
2716 				{
2717 					Com_Memcpy(&bestmatch, &match, sizeof(bot_match_t));
2718 					bestchatmessage = m;
2719 					bestrchat = rchat;
2720 					bestpriority = rchat->priority;
2721 				} //end if
2722 			} //end if
2723 		} //end if
2724 	} //end for
2725 	if (bestchatmessage)
2726 	{
2727 		index = strlen(bestmatch.string);
2728 		if( var0 ) {
2729 			strcat(bestmatch.string, var0);
2730 			bestmatch.variables[0].offset = index;
2731 			bestmatch.variables[0].length = strlen(var0);
2732 			index += strlen(var0);
2733 		}
2734 		if( var1 ) {
2735 			strcat(bestmatch.string, var1);
2736 			bestmatch.variables[1].offset = index;
2737 			bestmatch.variables[1].length = strlen(var1);
2738 			index += strlen(var1);
2739 		}
2740 		if( var2 ) {
2741 			strcat(bestmatch.string, var2);
2742 			bestmatch.variables[2].offset = index;
2743 			bestmatch.variables[2].length = strlen(var2);
2744 			index += strlen(var2);
2745 		}
2746 		if( var3 ) {
2747 			strcat(bestmatch.string, var3);
2748 			bestmatch.variables[3].offset = index;
2749 			bestmatch.variables[3].length = strlen(var3);
2750 			index += strlen(var3);
2751 		}
2752 		if( var4 ) {
2753 			strcat(bestmatch.string, var4);
2754 			bestmatch.variables[4].offset = index;
2755 			bestmatch.variables[4].length = strlen(var4);
2756 			index += strlen(var4);
2757 		}
2758 		if( var5 ) {
2759 			strcat(bestmatch.string, var5);
2760 			bestmatch.variables[5].offset = index;
2761 			bestmatch.variables[5].length = strlen(var5);
2762 			index += strlen(var5);
2763 		}
2764 		if( var6 ) {
2765 			strcat(bestmatch.string, var6);
2766 			bestmatch.variables[6].offset = index;
2767 			bestmatch.variables[6].length = strlen(var6);
2768 			index += strlen(var6);
2769 		}
2770 		if( var7 ) {
2771 			strcat(bestmatch.string, var7);
2772 			bestmatch.variables[7].offset = index;
2773 			bestmatch.variables[7].length = strlen(var7);
2774 			index += strlen(var7);
2775 		}
2776 		if (LibVarGetValue("bot_testrchat"))
2777 		{
2778 			for (m = bestrchat->firstchatmessage; m; m = m->next)
2779 			{
2780 				BotConstructChatMessage(cs, m->chatmessage, mcontext, &bestmatch, vcontext, qtrue);
2781 				BotRemoveTildes(cs->chatmessage);
2782 				botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage);
2783 			} //end if
2784 		} //end if
2785 		else
2786 		{
2787 			bestchatmessage->time = AAS_Time() + CHATMESSAGE_RECENTTIME;
2788 			BotConstructChatMessage(cs, bestchatmessage->chatmessage, mcontext, &bestmatch, vcontext, qtrue);
2789 		} //end else
2790 		return qtrue;
2791 	} //end if
2792 	return qfalse;
2793 } //end of the function BotReplyChat
2794 //===========================================================================
2795 //
2796 // Parameter:				-
2797 // Returns:					-
2798 // Changes Globals:		-
2799 //===========================================================================
BotChatLength(int chatstate)2800 int BotChatLength(int chatstate)
2801 {
2802 	bot_chatstate_t *cs;
2803 
2804 	cs = BotChatStateFromHandle(chatstate);
2805 	if (!cs) return 0;
2806 	return strlen(cs->chatmessage);
2807 } //end of the function BotChatLength
2808 //===========================================================================
2809 //
2810 // Parameter:			-
2811 // Returns:				-
2812 // Changes Globals:		-
2813 //===========================================================================
BotEnterChat(int chatstate,int clientto,int sendto)2814 void BotEnterChat(int chatstate, int clientto, int sendto)
2815 {
2816 	bot_chatstate_t *cs;
2817 
2818 	cs = BotChatStateFromHandle(chatstate);
2819 	if (!cs) return;
2820 
2821 	if (strlen(cs->chatmessage))
2822 	{
2823 		BotRemoveTildes(cs->chatmessage);
2824 		if (LibVarGetValue("bot_testichat")) {
2825 			botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage);
2826 		}
2827 		else {
2828 			switch(sendto) {
2829 				case CHAT_TEAM:
2830 					EA_Command(cs->client, va("say_team %s", cs->chatmessage));
2831 					break;
2832 				case CHAT_TELL:
2833 					EA_Command(cs->client, va("tell %d %s", clientto, cs->chatmessage));
2834 					break;
2835 				default: //CHAT_ALL
2836 					EA_Command(cs->client, va("say %s", cs->chatmessage));
2837 					break;
2838 			}
2839 		}
2840 		//clear the chat message from the state
2841 		strcpy(cs->chatmessage, "");
2842 	} //end if
2843 } //end of the function BotEnterChat
2844 //===========================================================================
2845 //
2846 // Parameter:			-
2847 // Returns:				-
2848 // Changes Globals:		-
2849 //===========================================================================
BotGetChatMessage(int chatstate,char * buf,int size)2850 void BotGetChatMessage(int chatstate, char *buf, int size)
2851 {
2852 	bot_chatstate_t *cs;
2853 
2854 	cs = BotChatStateFromHandle(chatstate);
2855 	if (!cs) return;
2856 
2857 	BotRemoveTildes(cs->chatmessage);
2858 	strncpy(buf, cs->chatmessage, size-1);
2859 	buf[size-1] = '\0';
2860 	//clear the chat message from the state
2861 	strcpy(cs->chatmessage, "");
2862 } //end of the function BotGetChatMessage
2863 //===========================================================================
2864 //
2865 // Parameter:			-
2866 // Returns:				-
2867 // Changes Globals:		-
2868 //===========================================================================
BotSetChatGender(int chatstate,int gender)2869 void BotSetChatGender(int chatstate, int gender)
2870 {
2871 	bot_chatstate_t *cs;
2872 
2873 	cs = BotChatStateFromHandle(chatstate);
2874 	if (!cs) return;
2875 	switch(gender)
2876 	{
2877 		case CHAT_GENDERFEMALE: cs->gender = CHAT_GENDERFEMALE; break;
2878 		case CHAT_GENDERMALE: cs->gender = CHAT_GENDERMALE; break;
2879 		default: cs->gender = CHAT_GENDERLESS; break;
2880 	} //end switch
2881 } //end of the function BotSetChatGender
2882 //===========================================================================
2883 //
2884 // Parameter:				-
2885 // Returns:					-
2886 // Changes Globals:		-
2887 //===========================================================================
BotSetChatName(int chatstate,char * name,int client)2888 void BotSetChatName(int chatstate, char *name, int client)
2889 {
2890 	bot_chatstate_t *cs;
2891 
2892 	cs = BotChatStateFromHandle(chatstate);
2893 	if (!cs) return;
2894 	cs->client = client;
2895 	Com_Memset(cs->name, 0, sizeof(cs->name));
2896 	strncpy(cs->name, name, sizeof(cs->name));
2897 	cs->name[sizeof(cs->name)-1] = '\0';
2898 } //end of the function BotSetChatName
2899 //===========================================================================
2900 //
2901 // Parameter:				-
2902 // Returns:					-
2903 // Changes Globals:		-
2904 //===========================================================================
BotResetChatAI(void)2905 void BotResetChatAI(void)
2906 {
2907 	bot_replychat_t *rchat;
2908 	bot_chatmessage_t *m;
2909 
2910 	for (rchat = replychats; rchat; rchat = rchat->next)
2911 	{
2912 		for (m = rchat->firstchatmessage; m; m = m->next)
2913 		{
2914 			m->time = 0;
2915 		} //end for
2916 	} //end for
2917 } //end of the function BotResetChatAI
2918 //========================================================================
2919 //
2920 // Parameter:				-
2921 // Returns:					-
2922 // Changes Globals:		-
2923 //========================================================================
BotAllocChatState(void)2924 int BotAllocChatState(void)
2925 {
2926 	int i;
2927 
2928 	for (i = 1; i <= MAX_CLIENTS; i++)
2929 	{
2930 		if (!botchatstates[i])
2931 		{
2932 			botchatstates[i] = (struct bot_chatstate_s *)GetClearedMemory(sizeof(bot_chatstate_t));
2933 			return i;
2934 		} //end if
2935 	} //end for
2936 	return 0;
2937 } //end of the function BotAllocChatState
2938 //========================================================================
2939 //
2940 // Parameter:				-
2941 // Returns:					-
2942 // Changes Globals:		-
2943 //========================================================================
BotFreeChatState(int handle)2944 void BotFreeChatState(int handle)
2945 {
2946 	bot_consolemessage_t m;
2947 	int h;
2948 
2949 	if (handle <= 0 || handle > MAX_CLIENTS)
2950 	{
2951 		botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle);
2952 		return;
2953 	} //end if
2954 	if (!botchatstates[handle])
2955 	{
2956 		botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle);
2957 		return;
2958 	} //end if
2959 	if (LibVarGetValue("bot_reloadcharacters"))
2960 	{
2961 		BotFreeChatFile(handle);
2962 	} //end if
2963 	//free all the console messages left in the chat state
2964 	for (h = BotNextConsoleMessage(handle, &m); h; h = BotNextConsoleMessage(handle, &m))
2965 	{
2966 		//remove the console message
2967 		BotRemoveConsoleMessage(handle, h);
2968 	} //end for
2969 	FreeMemory(botchatstates[handle]);
2970 	botchatstates[handle] = NULL;
2971 } //end of the function BotFreeChatState
2972 //===========================================================================
2973 //
2974 // Parameter:				-
2975 // Returns:					-
2976 // Changes Globals:		-
2977 //===========================================================================
BotSetupChatAI(void)2978 int BotSetupChatAI(void)
2979 {
2980 	char *file;
2981 
2982 #ifdef DEBUG
2983 	int starttime = Sys_MilliSeconds();
2984 #endif //DEBUG
2985 
2986 	file = LibVarString("synfile", "syn.c");
2987 	synonyms = BotLoadSynonyms(file);
2988 	file = LibVarString("rndfile", "rnd.c");
2989 	randomstrings = BotLoadRandomStrings(file);
2990 	file = LibVarString("matchfile", "match.c");
2991 	matchtemplates = BotLoadMatchTemplates(file);
2992 	//
2993 	if (!LibVarValue("nochat", "0"))
2994 	{
2995 		file = LibVarString("rchatfile", "rchat.c");
2996 		replychats = BotLoadReplyChat(file);
2997 	} //end if
2998 
2999 	InitConsoleMessageHeap();
3000 
3001 #ifdef DEBUG
3002 	botimport.Print(PRT_MESSAGE, "setup chat AI %d msec\n", Sys_MilliSeconds() - starttime);
3003 #endif //DEBUG
3004 	return BLERR_NOERROR;
3005 } //end of the function BotSetupChatAI
3006 //===========================================================================
3007 //
3008 // Parameter:				-
3009 // Returns:					-
3010 // Changes Globals:		-
3011 //===========================================================================
BotShutdownChatAI(void)3012 void BotShutdownChatAI(void)
3013 {
3014 	int i;
3015 
3016 	//free all remaining chat states
3017 	for(i = 0; i < MAX_CLIENTS; i++)
3018 	{
3019 		if (botchatstates[i])
3020 		{
3021 			BotFreeChatState(i);
3022 		} //end if
3023 	} //end for
3024 	//free all cached chats
3025 	for(i = 0; i < MAX_CLIENTS; i++)
3026 	{
3027 		if (ichatdata[i])
3028 		{
3029 			FreeMemory(ichatdata[i]->chat);
3030 			FreeMemory(ichatdata[i]);
3031 			ichatdata[i] = NULL;
3032 		} //end if
3033 	} //end for
3034 	if (consolemessageheap) FreeMemory(consolemessageheap);
3035 	consolemessageheap = NULL;
3036 	if (matchtemplates) BotFreeMatchTemplates(matchtemplates);
3037 	matchtemplates = NULL;
3038 	if (randomstrings) FreeMemory(randomstrings);
3039 	randomstrings = NULL;
3040 	if (synonyms) FreeMemory(synonyms);
3041 	synonyms = NULL;
3042 	if (replychats) BotFreeReplyChat(replychats);
3043 	replychats = NULL;
3044 } //end of the function BotShutdownChatAI
3045