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