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