Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

be_ai_chat.c (86556B)


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