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