Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

be_ai_chat.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 
00023 /*****************************************************************************
00024  * name:        be_ai_chat.c
00025  *
00026  * desc:        bot chat AI
00027  *
00028  * $Archive: /MissionPack/code/botlib/be_ai_chat.c $
00029  *
00030  *****************************************************************************/
00031 
00032 #include "../game/q_shared.h"
00033 #include "l_memory.h"
00034 #include "l_libvar.h"
00035 #include "l_script.h"
00036 #include "l_precomp.h"
00037 #include "l_struct.h"
00038 #include "l_utils.h"
00039 #include "l_log.h"
00040 #include "aasfile.h"
00041 #include "../game/botlib.h"
00042 #include "../game/be_aas.h"
00043 #include "be_aas_funcs.h"
00044 #include "be_interface.h"
00045 #include "../game/be_ea.h"
00046 #include "../game/be_ai_chat.h"
00047 
00048 
00049 //escape character
00050 #define ESCAPE_CHAR             0x01    //'_'
00051 //
00052 // "hi ", people, " ", 0, " entered the game"
00053 //becomes:
00054 // "hi _rpeople_ _v0_ entered the game"
00055 //
00056 
00057 //match piece types
00058 #define MT_VARIABLE                 1       //variable match piece
00059 #define MT_STRING                   2       //string match piece
00060 //reply chat key flags
00061 #define RCKFL_AND                   1       //key must be present
00062 #define RCKFL_NOT                   2       //key must be absent
00063 #define RCKFL_NAME                  4       //name of bot must be present
00064 #define RCKFL_STRING                8       //key is a string
00065 #define RCKFL_VARIABLES             16      //key is a match template
00066 #define RCKFL_BOTNAMES              32      //key is a series of botnames
00067 #define RCKFL_GENDERFEMALE          64      //bot must be female
00068 #define RCKFL_GENDERMALE            128     //bot must be male
00069 #define RCKFL_GENDERLESS            256     //bot must be genderless
00070 //time to ignore a chat message after using it
00071 #define CHATMESSAGE_RECENTTIME  20
00072 
00073 //the actuall chat messages
00074 typedef struct bot_chatmessage_s
00075 {
00076     char *chatmessage;                  //chat message string
00077     float time;                         //last time used
00078     struct bot_chatmessage_s *next;     //next chat message in a list
00079 } bot_chatmessage_t;
00080 //bot chat type with chat lines
00081 typedef struct bot_chattype_s
00082 {
00083     char name[MAX_CHATTYPE_NAME];
00084     int numchatmessages;
00085     bot_chatmessage_t *firstchatmessage;
00086     struct bot_chattype_s *next;
00087 } bot_chattype_t;
00088 //bot chat lines
00089 typedef struct bot_chat_s
00090 {
00091     bot_chattype_t *types;
00092 } bot_chat_t;
00093 
00094 //random string
00095 typedef struct bot_randomstring_s
00096 {
00097     char *string;
00098     struct bot_randomstring_s *next;
00099 } bot_randomstring_t;
00100 //list with random strings
00101 typedef struct bot_randomlist_s
00102 {
00103     char *string;
00104     int numstrings;
00105     bot_randomstring_t *firstrandomstring;
00106     struct bot_randomlist_s *next;
00107 } bot_randomlist_t;
00108 
00109 //synonym
00110 typedef struct bot_synonym_s
00111 {
00112     char *string;
00113     float weight;
00114     struct bot_synonym_s *next;
00115 } bot_synonym_t;
00116 //list with synonyms
00117 typedef struct bot_synonymlist_s
00118 {
00119     unsigned long int context;
00120     float totalweight;
00121     bot_synonym_t *firstsynonym;
00122     struct bot_synonymlist_s *next;
00123 } bot_synonymlist_t;
00124 
00125 //fixed match string
00126 typedef struct bot_matchstring_s
00127 {
00128     char *string;
00129     struct bot_matchstring_s *next;
00130 } bot_matchstring_t;
00131 
00132 //piece of a match template
00133 typedef struct bot_matchpiece_s
00134 {
00135     int type;
00136     bot_matchstring_t *firststring;
00137     int variable;
00138     struct bot_matchpiece_s *next;
00139 } bot_matchpiece_t;
00140 //match template
00141 typedef struct bot_matchtemplate_s
00142 {
00143     unsigned long int context;
00144     int type;
00145     int subtype;
00146     bot_matchpiece_t *first;
00147     struct bot_matchtemplate_s *next;
00148 } bot_matchtemplate_t;
00149 
00150 //reply chat key
00151 typedef struct bot_replychatkey_s
00152 {
00153     int flags;
00154     char *string;
00155     bot_matchpiece_t *match;
00156     struct bot_replychatkey_s *next;
00157 } bot_replychatkey_t;
00158 //reply chat
00159 typedef struct bot_replychat_s
00160 {
00161     bot_replychatkey_t *keys;
00162     float priority;
00163     int numchatmessages;
00164     bot_chatmessage_t *firstchatmessage;
00165     struct bot_replychat_s *next;
00166 } bot_replychat_t;
00167 
00168 //string list
00169 typedef struct bot_stringlist_s
00170 {
00171     char *string;
00172     struct bot_stringlist_s *next;
00173 } bot_stringlist_t;
00174 
00175 //chat state of a bot
00176 typedef struct bot_chatstate_s
00177 {
00178     int gender;                                         //0=it, 1=female, 2=male
00179     int client;                                         //client number
00180     char name[32];                                      //name of the bot
00181     char chatmessage[MAX_MESSAGE_SIZE];
00182     int handle;
00183     //the console messages visible to the bot
00184     bot_consolemessage_t *firstmessage;         //first message is the first typed message
00185     bot_consolemessage_t *lastmessage;          //last message is the last typed message, bottom of console
00186     //number of console messages stored in the state
00187     int numconsolemessages;
00188     //the bot chat lines
00189     bot_chat_t *chat;
00190 } bot_chatstate_t;
00191 
00192 typedef struct {
00193     bot_chat_t  *chat;
00194     char        filename[MAX_QPATH];
00195     char        chatname[MAX_QPATH];
00196 } bot_ichatdata_t;
00197 
00198 bot_ichatdata_t *ichatdata[MAX_CLIENTS];
00199 
00200 bot_chatstate_t *botchatstates[MAX_CLIENTS+1];
00201 //console message heap
00202 bot_consolemessage_t *consolemessageheap = NULL;
00203 bot_consolemessage_t *freeconsolemessages = NULL;
00204 //list with match strings
00205 bot_matchtemplate_t *matchtemplates = NULL;
00206 //list with synonyms
00207 bot_synonymlist_t *synonyms = NULL;
00208 //list with random strings
00209 bot_randomlist_t *randomstrings = NULL;
00210 //reply chats
00211 bot_replychat_t *replychats = NULL;
00212 
00213 //========================================================================
00214 //
00215 // Parameter:               -
00216 // Returns:                 -
00217 // Changes Globals:     -
00218 //========================================================================
00219 bot_chatstate_t *BotChatStateFromHandle(int handle)
00220 {
00221     if (handle <= 0 || handle > MAX_CLIENTS)
00222     {
00223         botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle);
00224         return NULL;
00225     } //end if
00226     if (!botchatstates[handle])
00227     {
00228         botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle);
00229         return NULL;
00230     } //end if
00231     return botchatstates[handle];
00232 } //end of the function BotChatStateFromHandle
00233 //===========================================================================
00234 // initialize the heap with unused console messages
00235 //
00236 // Parameter:               -
00237 // Returns:                 -
00238 // Changes Globals:     -
00239 //===========================================================================
00240 void InitConsoleMessageHeap(void)
00241 {
00242     int i, max_messages;
00243 
00244     if (consolemessageheap) FreeMemory(consolemessageheap);
00245     //
00246     max_messages = (int) LibVarValue("max_messages", "1024");
00247     consolemessageheap = (bot_consolemessage_t *) GetClearedHunkMemory(max_messages *
00248                                                 sizeof(bot_consolemessage_t));
00249     consolemessageheap[0].prev = NULL;
00250     consolemessageheap[0].next = &consolemessageheap[1];
00251     for (i = 1; i < max_messages-1; i++)
00252     {
00253         consolemessageheap[i].prev = &consolemessageheap[i - 1];
00254         consolemessageheap[i].next = &consolemessageheap[i + 1];
00255     } //end for
00256     consolemessageheap[max_messages-1].prev = &consolemessageheap[max_messages-2];
00257     consolemessageheap[max_messages-1].next = NULL;
00258     //pointer to the free console messages
00259     freeconsolemessages = consolemessageheap;
00260 } //end of the function InitConsoleMessageHeap
00261 //===========================================================================
00262 // allocate one console message from the heap
00263 //
00264 // Parameter:               -
00265 // Returns:                 -
00266 // Changes Globals:     -
00267 //===========================================================================
00268 bot_consolemessage_t *AllocConsoleMessage(void)
00269 {
00270     bot_consolemessage_t *message;
00271     message = freeconsolemessages;
00272     if (freeconsolemessages) freeconsolemessages = freeconsolemessages->next;
00273     if (freeconsolemessages) freeconsolemessages->prev = NULL;
00274     return message;
00275 } //end of the function AllocConsoleMessage
00276 //===========================================================================
00277 // deallocate one console message from the heap
00278 //
00279 // Parameter:               -
00280 // Returns:                 -
00281 // Changes Globals:     -
00282 //===========================================================================
00283 void FreeConsoleMessage(bot_consolemessage_t *message)
00284 {
00285     if (freeconsolemessages) freeconsolemessages->prev = message;
00286     message->prev = NULL;
00287     message->next = freeconsolemessages;
00288     freeconsolemessages = message;
00289 } //end of the function FreeConsoleMessage
00290 //===========================================================================
00291 //
00292 // Parameter:               -
00293 // Returns:                 -
00294 // Changes Globals:     -
00295 //===========================================================================
00296 void BotRemoveConsoleMessage(int chatstate, int handle)
00297 {
00298     bot_consolemessage_t *m, *nextm;
00299     bot_chatstate_t *cs;
00300 
00301     cs = BotChatStateFromHandle(chatstate);
00302     if (!cs) return;
00303 
00304     for (m = cs->firstmessage; m; m = nextm)
00305     {
00306         nextm = m->next;
00307         if (m->handle == handle)
00308         {
00309             if (m->next) m->next->prev = m->prev;
00310             else cs->lastmessage = m->prev;
00311             if (m->prev) m->prev->next = m->next;
00312             else cs->firstmessage = m->next;
00313 
00314             FreeConsoleMessage(m);
00315             cs->numconsolemessages--;
00316             break;
00317         } //end if
00318     } //end for
00319 } //end of the function BotRemoveConsoleMessage
00320 //===========================================================================
00321 //
00322 // Parameter:               -
00323 // Returns:                 -
00324 // Changes Globals:     -
00325 //===========================================================================
00326 void BotQueueConsoleMessage(int chatstate, int type, char *message)
00327 {
00328     bot_consolemessage_t *m;
00329     bot_chatstate_t *cs;
00330 
00331     cs = BotChatStateFromHandle(chatstate);
00332     if (!cs) return;
00333 
00334     m = AllocConsoleMessage();
00335     if (!m)
00336     {
00337         botimport.Print(PRT_ERROR, "empty console message heap\n");
00338         return;
00339     } //end if
00340     cs->handle++;
00341     if (cs->handle <= 0 || cs->handle > 8192) cs->handle = 1;
00342     m->handle = cs->handle;
00343     m->time = AAS_Time();
00344     m->type = type;
00345     strncpy(m->message, message, MAX_MESSAGE_SIZE);
00346     m->next = NULL;
00347     if (cs->lastmessage)
00348     {
00349         cs->lastmessage->next = m;
00350         m->prev = cs->lastmessage;
00351         cs->lastmessage = m;
00352     } //end if
00353     else
00354     {
00355         cs->lastmessage = m;
00356         cs->firstmessage = m;
00357         m->prev = NULL;
00358     } //end if
00359     cs->numconsolemessages++;
00360 } //end of the function BotQueueConsoleMessage
00361 //===========================================================================
00362 //
00363 // Parameter:               -
00364 // Returns:                 -
00365 // Changes Globals:     -
00366 //===========================================================================
00367 int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm)
00368 {
00369     bot_chatstate_t *cs;
00370 
00371     cs = BotChatStateFromHandle(chatstate);
00372     if (!cs) return 0;
00373     if (cs->firstmessage)
00374     {
00375         Com_Memcpy(cm, cs->firstmessage, sizeof(bot_consolemessage_t));
00376         cm->next = cm->prev = NULL;
00377         return cm->handle;
00378     } //end if
00379     return 0;
00380 } //end of the function BotConsoleMessage
00381 //===========================================================================
00382 //
00383 // Parameter:               -
00384 // Returns:                 -
00385 // Changes Globals:     -
00386 //===========================================================================
00387 int BotNumConsoleMessages(int chatstate)
00388 {
00389     bot_chatstate_t *cs;
00390 
00391     cs = BotChatStateFromHandle(chatstate);
00392     if (!cs) return 0;
00393     return cs->numconsolemessages;
00394 } //end of the function BotNumConsoleMessages
00395 //===========================================================================
00396 //
00397 // Parameter:               -
00398 // Returns:                 -
00399 // Changes Globals:     -
00400 //===========================================================================
00401 int IsWhiteSpace(char c)
00402 {
00403     if ((c >= 'a' && c <= 'z')
00404         || (c >= 'A' && c <= 'Z')
00405         || (c >= '0' && c <= '9')
00406         || c == '(' || c == ')'
00407         || c == '?' || c == ':'
00408         || c == '\''|| c == '/'
00409         || c == ',' || c == '.'
00410         || c == '[' || c == ']'
00411         || c == '-' || c == '_'
00412         || c == '+' || c == '=') return qfalse;
00413     return qtrue;
00414 } //end of the function IsWhiteSpace
00415 //===========================================================================
00416 //
00417 // Parameter:           -
00418 // Returns:             -
00419 // Changes Globals:     -
00420 //===========================================================================
00421 void BotRemoveTildes(char *message)
00422 {
00423     int i;
00424 
00425     //remove all tildes from the chat message
00426     for (i = 0; message[i]; i++)
00427     {
00428         if (message[i] == '~')
00429         {
00430             memmove(&message[i], &message[i+1], strlen(&message[i+1])+1);
00431         } //end if
00432     } //end for
00433 } //end of the function BotRemoveTildes
00434 //===========================================================================
00435 //
00436 // Parameter:               -
00437 // Returns:                 -
00438 // Changes Globals:     -
00439 //===========================================================================
00440 void UnifyWhiteSpaces(char *string)
00441 {
00442     char *ptr, *oldptr;
00443 
00444     for (ptr = oldptr = string; *ptr; oldptr = ptr)
00445     {
00446         while(*ptr && IsWhiteSpace(*ptr)) ptr++;
00447         if (ptr > oldptr)
00448         {
00449             //if not at the start and not at the end of the string
00450             //write only one space
00451             if (oldptr > string && *ptr) *oldptr++ = ' ';
00452             //remove all other white spaces
00453             if (ptr > oldptr) memmove(oldptr, ptr, strlen(ptr)+1);
00454         } //end if
00455         while(*ptr && !IsWhiteSpace(*ptr)) ptr++;
00456     } //end while
00457 } //end of the function UnifyWhiteSpaces
00458 //===========================================================================
00459 //
00460 // Parameter:               -
00461 // Returns:                 -
00462 // Changes Globals:     -
00463 //===========================================================================
00464 int StringContains(char *str1, char *str2, int casesensitive)
00465 {
00466     int len, i, j, index;
00467 
00468     if (str1 == NULL || str2 == NULL) return -1;
00469 
00470     len = strlen(str1) - strlen(str2);
00471     index = 0;
00472     for (i = 0; i <= len; i++, str1++, index++)
00473     {
00474         for (j = 0; str2[j]; j++)
00475         {
00476             if (casesensitive)
00477             {
00478                 if (str1[j] != str2[j]) break;
00479             } //end if
00480             else
00481             {
00482                 if (toupper(str1[j]) != toupper(str2[j])) break;
00483             } //end else
00484         } //end for
00485         if (!str2[j]) return index;
00486     } //end for
00487     return -1;
00488 } //end of the function StringContains
00489 //===========================================================================
00490 //
00491 // Parameter:               -
00492 // Returns:                 -
00493 // Changes Globals:     -
00494 //===========================================================================
00495 char *StringContainsWord(char *str1, char *str2, int casesensitive)
00496 {
00497     int len, i, j;
00498 
00499     len = strlen(str1) - strlen(str2);
00500     for (i = 0; i <= len; i++, str1++)
00501     {
00502         //if not at the start of the string
00503         if (i)
00504         {
00505             //skip to the start of the next word
00506             while(*str1 && *str1 != ' ' && *str1 != '.' && *str1 != ',' && *str1 != '!') str1++;
00507             if (!*str1) break;
00508             str1++;
00509         } //end for
00510         //compare the word
00511         for (j = 0; str2[j]; j++)
00512         {
00513             if (casesensitive)
00514             {
00515                 if (str1[j] != str2[j]) break;
00516             } //end if
00517             else
00518             {
00519                 if (toupper(str1[j]) != toupper(str2[j])) break;
00520             } //end else
00521         } //end for
00522         //if there was a word match
00523         if (!str2[j])
00524         {
00525             //if the first string has an end of word
00526             if (!str1[j] || str1[j] == ' ' || str1[j] == '.' || str1[j] == ',' || str1[j] == '!') return str1;
00527         } //end if
00528     } //end for
00529     return NULL;
00530 } //end of the function StringContainsWord
00531 //===========================================================================
00532 //
00533 // Parameter:               -
00534 // Returns:                 -
00535 // Changes Globals:     -
00536 //===========================================================================
00537 void StringReplaceWords(char *string, char *synonym, char *replacement)
00538 {
00539     char *str, *str2;
00540 
00541     //find the synonym in the string
00542     str = StringContainsWord(string, synonym, qfalse);
00543     //if the synonym occured in the string
00544     while(str)
00545     {
00546         //if the synonym isn't part of the replacement which is already in the string
00547         //usefull for abreviations
00548         str2 = StringContainsWord(string, replacement, qfalse);
00549         while(str2)
00550         {
00551             if (str2 <= str && str < str2 + strlen(replacement)) break;
00552             str2 = StringContainsWord(str2+1, replacement, qfalse);
00553         } //end while
00554         if (!str2)
00555         {
00556             memmove(str + strlen(replacement), str+strlen(synonym), strlen(str+strlen(synonym))+1);
00557             //append the synonum replacement
00558             Com_Memcpy(str, replacement, strlen(replacement));
00559         } //end if
00560         //find the next synonym in the string
00561         str = StringContainsWord(str+strlen(replacement), synonym, qfalse);
00562     } //end if
00563 } //end of the function StringReplaceWords
00564 //===========================================================================
00565 //
00566 // Parameter:               -
00567 // Returns:                 -
00568 // Changes Globals:     -
00569 //===========================================================================
00570 void BotDumpSynonymList(bot_synonymlist_t *synlist)
00571 {
00572     FILE *fp;
00573     bot_synonymlist_t *syn;
00574     bot_synonym_t *synonym;
00575 
00576     fp = Log_FilePointer();
00577     if (!fp) return;
00578     for (syn = synlist; syn; syn = syn->next)
00579     {
00580             fprintf(fp, "%ld : [", syn->context);
00581         for (synonym = syn->firstsynonym; synonym; synonym = synonym->next)
00582         {
00583             fprintf(fp, "(\"%s\", %1.2f)", synonym->string, synonym->weight);
00584             if (synonym->next) fprintf(fp, ", ");
00585         } //end for
00586         fprintf(fp, "]\n");
00587     } //end for
00588 } //end of the function BotDumpSynonymList
00589 //===========================================================================
00590 //
00591 // Parameter:               -
00592 // Returns:                 -
00593 // Changes Globals:     -
00594 //===========================================================================
00595 bot_synonymlist_t *BotLoadSynonyms(char *filename)
00596 {
00597     int pass, size, contextlevel, numsynonyms;
00598     unsigned long int context, contextstack[32];
00599     char *ptr = NULL;
00600     source_t *source;
00601     token_t token;
00602     bot_synonymlist_t *synlist, *lastsyn, *syn;
00603     bot_synonym_t *synonym, *lastsynonym;
00604 
00605     size = 0;
00606     synlist = NULL; //make compiler happy
00607     syn = NULL; //make compiler happy
00608     synonym = NULL; //make compiler happy
00609     //the synonyms are parsed in two phases
00610     for (pass = 0; pass < 2; pass++)
00611     {
00612         //
00613         if (pass && size) ptr = (char *) GetClearedHunkMemory(size);
00614         //
00615         PC_SetBaseFolder(BOTFILESBASEFOLDER);
00616         source = LoadSourceFile(filename);
00617         if (!source)
00618         {
00619             botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
00620             return NULL;
00621         } //end if
00622         //
00623         context = 0;
00624         contextlevel = 0;
00625         synlist = NULL; //list synonyms
00626         lastsyn = NULL; //last synonym in the list
00627         //
00628         while(PC_ReadToken(source, &token))
00629         {
00630             if (token.type == TT_NUMBER)
00631             {
00632                 context |= token.intvalue;
00633                 contextstack[contextlevel] = token.intvalue;
00634                 contextlevel++;
00635                 if (contextlevel >= 32)
00636                 {
00637                     SourceError(source, "more than 32 context levels");
00638                     FreeSource(source);
00639                     return NULL;
00640                 } //end if
00641                 if (!PC_ExpectTokenString(source, "{"))
00642                 {
00643                     FreeSource(source);
00644                     return NULL;
00645                 } //end if
00646             } //end if
00647             else if (token.type == TT_PUNCTUATION)
00648             {
00649                 if (!strcmp(token.string, "}"))
00650                 {
00651                     contextlevel--;
00652                     if (contextlevel < 0)
00653                     {
00654                         SourceError(source, "too many }");
00655                         FreeSource(source);
00656                         return NULL;
00657                     } //end if
00658                     context &= ~contextstack[contextlevel];
00659                 } //end if
00660                 else if (!strcmp(token.string, "["))
00661                 {
00662                     size += sizeof(bot_synonymlist_t);
00663                     if (pass)
00664                     {
00665                         syn = (bot_synonymlist_t *) ptr;
00666                         ptr += sizeof(bot_synonymlist_t);
00667                         syn->context = context;
00668                         syn->firstsynonym = NULL;
00669                         syn->next = NULL;
00670                         if (lastsyn) lastsyn->next = syn;
00671                         else synlist = syn;
00672                         lastsyn = syn;
00673                     } //end if
00674                     numsynonyms = 0;
00675                     lastsynonym = NULL;
00676                     while(1)
00677                     {
00678                         if (!PC_ExpectTokenString(source, "(") ||
00679                             !PC_ExpectTokenType(source, TT_STRING, 0, &token))
00680                         {
00681                             FreeSource(source);
00682                             return NULL;
00683                         } //end if
00684                         StripDoubleQuotes(token.string);
00685                         if (strlen(token.string) <= 0)
00686                         {
00687                             SourceError(source, "empty string", token.string);
00688                             FreeSource(source);
00689                             return NULL;
00690                         } //end if
00691                         size += sizeof(bot_synonym_t) + strlen(token.string) + 1;
00692                         if (pass)
00693                         {
00694                             synonym = (bot_synonym_t *) ptr;
00695                             ptr += sizeof(bot_synonym_t);
00696                             synonym->string = ptr;
00697                             ptr += strlen(token.string) + 1;
00698                             strcpy(synonym->string, token.string);
00699                             //
00700                             if (lastsynonym) lastsynonym->next = synonym;
00701                             else syn->firstsynonym = synonym;
00702                             lastsynonym = synonym;
00703                         } //end if
00704                         numsynonyms++;
00705                         if (!PC_ExpectTokenString(source, ",") ||
00706                             !PC_ExpectTokenType(source, TT_NUMBER, 0, &token) ||
00707                             !PC_ExpectTokenString(source, ")"))
00708                         {
00709                             FreeSource(source);
00710                             return NULL;
00711                         } //end if
00712                         if (pass)
00713                         {
00714                             synonym->weight = token.floatvalue;
00715                             syn->totalweight += synonym->weight;
00716                         } //end if
00717                         if (PC_CheckTokenString(source, "]")) break;
00718                         if (!PC_ExpectTokenString(source, ","))
00719                         {
00720                             FreeSource(source);
00721                             return NULL;
00722                         } //end if
00723                     } //end while
00724                     if (numsynonyms < 2)
00725                     {
00726                         SourceError(source, "synonym must have at least two entries\n");
00727                         FreeSource(source);
00728                         return NULL;
00729                     } //end if
00730                 } //end else
00731                 else
00732                 {
00733                     SourceError(source, "unexpected %s", token.string);
00734                     FreeSource(source);
00735                     return NULL;
00736                 } //end if
00737             } //end else if
00738         } //end while
00739         //
00740         FreeSource(source);
00741         //
00742         if (contextlevel > 0)
00743         {
00744             SourceError(source, "missing }");
00745             return NULL;
00746         } //end if
00747     } //end for
00748     botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
00749     //
00750     //BotDumpSynonymList(synlist);
00751     //
00752     return synlist;
00753 } //end of the function BotLoadSynonyms
00754 //===========================================================================
00755 // replace all the synonyms in the string
00756 //
00757 // Parameter:               -
00758 // Returns:                 -
00759 // Changes Globals:     -
00760 //===========================================================================
00761 void BotReplaceSynonyms(char *string, unsigned long int context)
00762 {
00763     bot_synonymlist_t *syn;
00764     bot_synonym_t *synonym;
00765 
00766     for (syn = synonyms; syn; syn = syn->next)
00767     {
00768         if (!(syn->context & context)) continue;
00769         for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next)
00770         {
00771             StringReplaceWords(string, synonym->string, syn->firstsynonym->string);
00772         } //end for
00773     } //end for
00774 } //end of the function BotReplaceSynonyms
00775 //===========================================================================
00776 //
00777 // Parameter:               -
00778 // Returns:                 -
00779 // Changes Globals:     -
00780 //===========================================================================
00781 void BotReplaceWeightedSynonyms(char *string, unsigned long int context)
00782 {
00783     bot_synonymlist_t *syn;
00784     bot_synonym_t *synonym, *replacement;
00785     float weight, curweight;
00786 
00787     for (syn = synonyms; syn; syn = syn->next)
00788     {
00789         if (!(syn->context & context)) continue;
00790         //choose a weighted random replacement synonym
00791         weight = random() * syn->totalweight;
00792         if (!weight) continue;
00793         curweight = 0;
00794         for (replacement = syn->firstsynonym; replacement; replacement = replacement->next)
00795         {
00796             curweight += replacement->weight;
00797             if (weight < curweight) break;
00798         } //end for
00799         if (!replacement) continue;
00800         //replace all synonyms with the replacement
00801         for (synonym = syn->firstsynonym; synonym; synonym = synonym->next)
00802         {
00803             if (synonym == replacement) continue;
00804             StringReplaceWords(string, synonym->string, replacement->string);
00805         } //end for
00806     } //end for
00807 } //end of the function BotReplaceWeightedSynonyms
00808 //===========================================================================
00809 //
00810 // Parameter:               -
00811 // Returns:                 -
00812 // Changes Globals:     -
00813 //===========================================================================
00814 void BotReplaceReplySynonyms(char *string, unsigned long int context)
00815 {
00816     char *str1, *str2, *replacement;
00817     bot_synonymlist_t *syn;
00818     bot_synonym_t *synonym;
00819 
00820     for (str1 = string; *str1; )
00821     {
00822         //go to the start of the next word
00823         while(*str1 && *str1 <= ' ') str1++;
00824         if (!*str1) break;
00825         //
00826         for (syn = synonyms; syn; syn = syn->next)
00827         {
00828             if (!(syn->context & context)) continue;
00829             for (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next)
00830             {
00831                 str2 = synonym->string;
00832                 //if the synonym is not at the front of the string continue
00833                 str2 = StringContainsWord(str1, synonym->string, qfalse);
00834                 if (!str2 || str2 != str1) continue;
00835                 //
00836                 replacement = syn->firstsynonym->string;
00837                 //if the replacement IS in front of the string continue
00838                 str2 = StringContainsWord(str1, replacement, qfalse);
00839                 if (str2 && str2 == str1) continue;
00840                 //
00841                 memmove(str1 + strlen(replacement), str1+strlen(synonym->string),
00842                             strlen(str1+strlen(synonym->string)) + 1);
00843                 //append the synonum replacement
00844                 Com_Memcpy(str1, replacement, strlen(replacement));
00845                 //
00846                 break;
00847             } //end for
00848             //if a synonym has been replaced
00849             if (synonym) break;
00850         } //end for
00851         //skip over this word
00852         while(*str1 && *str1 > ' ') str1++;
00853         if (!*str1) break;
00854     } //end while
00855 } //end of the function BotReplaceReplySynonyms
00856 //===========================================================================
00857 //
00858 // Parameter:           -
00859 // Returns:             -
00860 // Changes Globals:     -
00861 //===========================================================================
00862 int BotLoadChatMessage(source_t *source, char *chatmessagestring)
00863 {
00864     char *ptr;
00865     token_t token;
00866 
00867     ptr = chatmessagestring;
00868     *ptr = 0;
00869     //
00870     while(1)
00871     {
00872         if (!PC_ExpectAnyToken(source, &token)) return qfalse;
00873         //fixed string
00874         if (token.type == TT_STRING)
00875         {
00876             StripDoubleQuotes(token.string);
00877             if (strlen(ptr) + strlen(token.string) + 1 > MAX_MESSAGE_SIZE)
00878             {
00879                 SourceError(source, "chat message too long\n");
00880                 return qfalse;
00881             } //end if
00882             strcat(ptr, token.string);
00883         } //end else if
00884         //variable string
00885         else if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER))
00886         {
00887             if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE)
00888             {
00889                 SourceError(source, "chat message too long\n");
00890                 return qfalse;
00891             } //end if
00892             sprintf(&ptr[strlen(ptr)], "%cv%ld%c", ESCAPE_CHAR, token.intvalue, ESCAPE_CHAR);
00893         } //end if
00894         //random string
00895         else if (token.type == TT_NAME)
00896         {
00897             if (strlen(ptr) + 7 > MAX_MESSAGE_SIZE)
00898             {
00899                 SourceError(source, "chat message too long\n");
00900                 return qfalse;
00901             } //end if
00902             sprintf(&ptr[strlen(ptr)], "%cr%s%c", ESCAPE_CHAR, token.string, ESCAPE_CHAR);
00903         } //end else if
00904         else
00905         {
00906             SourceError(source, "unknown message component %s\n", token.string);
00907             return qfalse;
00908         } //end else
00909         if (PC_CheckTokenString(source, ";")) break;
00910         if (!PC_ExpectTokenString(source, ",")) return qfalse;
00911     } //end while
00912     //
00913     return qtrue;
00914 } //end of the function BotLoadChatMessage
00915 //===========================================================================
00916 //
00917 // Parameter:               -
00918 // Returns:                 -
00919 // Changes Globals:     -
00920 //===========================================================================
00921 void BotDumpRandomStringList(bot_randomlist_t *randomlist)
00922 {
00923     FILE *fp;
00924     bot_randomlist_t *random;
00925     bot_randomstring_t *rs;
00926 
00927     fp = Log_FilePointer();
00928     if (!fp) return;
00929     for (random = randomlist; random; random = random->next)
00930     {
00931         fprintf(fp, "%s = {", random->string);
00932         for (rs = random->firstrandomstring; rs; rs = rs->next)
00933         {
00934             fprintf(fp, "\"%s\"", rs->string);
00935             if (rs->next) fprintf(fp, ", ");
00936             else fprintf(fp, "}\n");
00937         } //end for
00938     } //end for
00939 } //end of the function BotDumpRandomStringList
00940 //===========================================================================
00941 //
00942 // Parameter:               -
00943 // Returns:                 -
00944 // Changes Globals:     -
00945 //===========================================================================
00946 bot_randomlist_t *BotLoadRandomStrings(char *filename)
00947 {
00948     int pass, size;
00949     char *ptr = NULL, chatmessagestring[MAX_MESSAGE_SIZE];
00950     source_t *source;
00951     token_t token;
00952     bot_randomlist_t *randomlist, *lastrandom, *random;
00953     bot_randomstring_t *randomstring;
00954 
00955 #ifdef DEBUG
00956     int starttime = Sys_MilliSeconds();
00957 #endif //DEBUG
00958 
00959     size = 0;
00960     randomlist = NULL;
00961     random = NULL;
00962     //the synonyms are parsed in two phases
00963     for (pass = 0; pass < 2; pass++)
00964     {
00965         //
00966         if (pass && size) ptr = (char *) GetClearedHunkMemory(size);
00967         //
00968         PC_SetBaseFolder(BOTFILESBASEFOLDER);
00969         source = LoadSourceFile(filename);
00970         if (!source)
00971         {
00972             botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
00973             return NULL;
00974         } //end if
00975         //
00976         randomlist = NULL; //list
00977         lastrandom = NULL; //last
00978         //
00979         while(PC_ReadToken(source, &token))
00980         {
00981             if (token.type != TT_NAME)
00982             {
00983                 SourceError(source, "unknown random %s", token.string);
00984                 FreeSource(source);
00985                 return NULL;
00986             } //end if
00987             size += sizeof(bot_randomlist_t) + strlen(token.string) + 1;
00988             if (pass)
00989             {
00990                 random = (bot_randomlist_t *) ptr;
00991                 ptr += sizeof(bot_randomlist_t);
00992                 random->string = ptr;
00993                 ptr += strlen(token.string) + 1;
00994                 strcpy(random->string, token.string);
00995                 random->firstrandomstring = NULL;
00996                 random->numstrings = 0;
00997                 //
00998                 if (lastrandom) lastrandom->next = random;
00999                 else randomlist = random;
01000                 lastrandom = random;
01001             } //end if
01002             if (!PC_ExpectTokenString(source, "=") ||
01003                 !PC_ExpectTokenString(source, "{"))
01004             {
01005                 FreeSource(source);
01006                 return NULL;
01007             } //end if
01008             while(!PC_CheckTokenString(source, "}"))
01009             {
01010                 if (!BotLoadChatMessage(source, chatmessagestring))
01011                 {
01012                     FreeSource(source);
01013                     return NULL;
01014                 } //end if
01015                 size += sizeof(bot_randomstring_t) + strlen(chatmessagestring) + 1;
01016                 if (pass)
01017                 {
01018                     randomstring = (bot_randomstring_t *) ptr;
01019                     ptr += sizeof(bot_randomstring_t);
01020                     randomstring->string = ptr;
01021                     ptr += strlen(chatmessagestring) + 1;
01022                     strcpy(randomstring->string, chatmessagestring);
01023                     //
01024                     random->numstrings++;
01025                     randomstring->next = random->firstrandomstring;
01026                     random->firstrandomstring = randomstring;
01027                 } //end if
01028             } //end while
01029         } //end while
01030         //free the source after one pass
01031         FreeSource(source);
01032     } //end for
01033     botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
01034     //
01035 #ifdef DEBUG
01036     botimport.Print(PRT_MESSAGE, "random strings %d msec\n", Sys_MilliSeconds() - starttime);
01037     //BotDumpRandomStringList(randomlist);
01038 #endif //DEBUG
01039     //
01040     return randomlist;
01041 } //end of the function BotLoadRandomStrings
01042 //===========================================================================
01043 //
01044 // Parameter:               -
01045 // Returns:                 -
01046 // Changes Globals:     -
01047 //===========================================================================
01048 char *RandomString(char *name)
01049 {
01050     bot_randomlist_t *random;
01051     bot_randomstring_t *rs;
01052     int i;
01053 
01054     for (random = randomstrings; random; random = random->next)
01055     {
01056         if (!strcmp(random->string, name))
01057         {
01058             i = random() * random->numstrings;
01059             for (rs = random->firstrandomstring; rs; rs = rs->next)
01060             {
01061                 if (--i < 0) break;
01062             } //end for
01063             if (rs)
01064             {
01065                 return rs->string;
01066             } //end if
01067         } //end for
01068     } //end for
01069     return NULL;
01070 } //end of the function RandomString
01071 //===========================================================================
01072 //
01073 // Parameter:               -
01074 // Returns:                 -
01075 // Changes Globals:     -
01076 //===========================================================================
01077 void BotDumpMatchTemplates(bot_matchtemplate_t *matches)
01078 {
01079     FILE *fp;
01080     bot_matchtemplate_t *mt;
01081     bot_matchpiece_t *mp;
01082     bot_matchstring_t *ms;
01083 
01084     fp = Log_FilePointer();
01085     if (!fp) return;
01086     for (mt = matches; mt; mt = mt->next)
01087     {
01088             fprintf(fp, "{ " );
01089         for (mp = mt->first; mp; mp = mp->next)
01090         {
01091             if (mp->type == MT_STRING)
01092             {
01093                 for (ms = mp->firststring; ms; ms = ms->next)
01094                 {
01095                     fprintf(fp, "\"%s\"", ms->string);
01096                     if (ms->next) fprintf(fp, "|");
01097                 } //end for
01098             } //end if
01099             else if (mp->type == MT_VARIABLE)
01100             {
01101                 fprintf(fp, "%d", mp->variable);
01102             } //end else if
01103             if (mp->next) fprintf(fp, ", ");
01104         } //end for
01105         fprintf(fp, " = (%d, %d);}\n", mt->type, mt->subtype);
01106     } //end for
01107 } //end of the function BotDumpMatchTemplates
01108 //===========================================================================
01109 //
01110 // Parameter:               -
01111 // Returns:                 -
01112 // Changes Globals:     -
01113 //===========================================================================
01114 void BotFreeMatchPieces(bot_matchpiece_t *matchpieces)
01115 {
01116     bot_matchpiece_t *mp, *nextmp;
01117     bot_matchstring_t *ms, *nextms;
01118 
01119     for (mp = matchpieces; mp; mp = nextmp)
01120     {
01121         nextmp = mp->next;
01122         if (mp->type == MT_STRING)
01123         {
01124             for (ms = mp->firststring; ms; ms = nextms)
01125             {
01126                 nextms = ms->next;
01127                 FreeMemory(ms);
01128             } //end for
01129         } //end if
01130         FreeMemory(mp);
01131     } //end for
01132 } //end of the function BotFreeMatchPieces
01133 //===========================================================================
01134 //
01135 // Parameter:               -
01136 // Returns:                 -
01137 // Changes Globals:     -
01138 //===========================================================================
01139 bot_matchpiece_t *BotLoadMatchPieces(source_t *source, char *endtoken)
01140 {
01141     int lastwasvariable, emptystring;
01142     token_t token;
01143     bot_matchpiece_t *matchpiece, *firstpiece, *lastpiece;
01144     bot_matchstring_t *matchstring, *lastmatchstring;
01145 
01146     firstpiece = NULL;
01147     lastpiece = NULL;
01148     //
01149     lastwasvariable = qfalse;
01150     //
01151     while(PC_ReadToken(source, &token))
01152     {
01153         if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER))
01154         {
01155             if (token.intvalue < 0 || token.intvalue >= MAX_MATCHVARIABLES)
01156             {
01157                 SourceError(source, "can't have more than %d match variables\n", MAX_MATCHVARIABLES);
01158                 FreeSource(source);
01159                 BotFreeMatchPieces(firstpiece);
01160                 return NULL;
01161             } //end if
01162             if (lastwasvariable)
01163             {
01164                 SourceError(source, "not allowed to have adjacent variables\n");
01165                 FreeSource(source);
01166                 BotFreeMatchPieces(firstpiece);
01167                 return NULL;
01168             } //end if
01169             lastwasvariable = qtrue;
01170             //
01171             matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t));
01172             matchpiece->type = MT_VARIABLE;
01173             matchpiece->variable = token.intvalue;
01174             matchpiece->next = NULL;
01175             if (lastpiece) lastpiece->next = matchpiece;
01176             else firstpiece = matchpiece;
01177             lastpiece = matchpiece;
01178         } //end if
01179         else if (token.type == TT_STRING)
01180         {
01181             //
01182             matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t));
01183             matchpiece->firststring = NULL;
01184             matchpiece->type = MT_STRING;
01185             matchpiece->variable = 0;
01186             matchpiece->next = NULL;
01187             if (lastpiece) lastpiece->next = matchpiece;
01188             else firstpiece = matchpiece;
01189             lastpiece = matchpiece;
01190             //
01191             lastmatchstring = NULL;
01192             emptystring = qfalse;
01193             //
01194             do
01195             {
01196                 if (matchpiece->firststring)
01197                 {
01198                     if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
01199                     {
01200                         FreeSource(source);
01201                         BotFreeMatchPieces(firstpiece);
01202                         return NULL;
01203                     } //end if
01204                 } //end if
01205                 StripDoubleQuotes(token.string);
01206                 matchstring = (bot_matchstring_t *) GetClearedHunkMemory(sizeof(bot_matchstring_t) + strlen(token.string) + 1);
01207                 matchstring->string = (char *) matchstring + sizeof(bot_matchstring_t);
01208                 strcpy(matchstring->string, token.string);
01209                 if (!strlen(token.string)) emptystring = qtrue;
01210                 matchstring->next = NULL;
01211                 if (lastmatchstring) lastmatchstring->next = matchstring;
01212                 else matchpiece->firststring = matchstring;
01213                 lastmatchstring = matchstring;
01214             } while(PC_CheckTokenString(source, "|"));
01215             //if there was no empty string found
01216             if (!emptystring) lastwasvariable = qfalse;
01217         } //end if
01218         else
01219         {
01220             SourceError(source, "invalid token %s\n", token.string);
01221             FreeSource(source);
01222             BotFreeMatchPieces(firstpiece);
01223             return NULL;
01224         } //end else
01225         if (PC_CheckTokenString(source, endtoken)) break;
01226         if (!PC_ExpectTokenString(source, ","))
01227         {
01228             FreeSource(source);
01229             BotFreeMatchPieces(firstpiece);
01230             return NULL;
01231         } //end if
01232     } //end while
01233     return firstpiece;
01234 } //end of the function BotLoadMatchPieces
01235 //===========================================================================
01236 //
01237 // Parameter:               -
01238 // Returns:                 -
01239 // Changes Globals:     -
01240 //===========================================================================
01241 void BotFreeMatchTemplates(bot_matchtemplate_t *mt)
01242 {
01243     bot_matchtemplate_t *nextmt;
01244 
01245     for (; mt; mt = nextmt)
01246     {
01247         nextmt = mt->next;
01248         BotFreeMatchPieces(mt->first);
01249         FreeMemory(mt);
01250     } //end for
01251 } //end of the function BotFreeMatchTemplates
01252 //===========================================================================
01253 //
01254 // Parameter:               -
01255 // Returns:                 -
01256 // Changes Globals:     -
01257 //===========================================================================
01258 bot_matchtemplate_t *BotLoadMatchTemplates(char *matchfile)
01259 {
01260     source_t *source;
01261     token_t token;
01262     bot_matchtemplate_t *matchtemplate, *matches, *lastmatch;
01263     unsigned long int context;
01264 
01265     PC_SetBaseFolder(BOTFILESBASEFOLDER);
01266     source = LoadSourceFile(matchfile);
01267     if (!source)
01268     {
01269         botimport.Print(PRT_ERROR, "counldn't load %s\n", matchfile);
01270         return NULL;
01271     } //end if
01272     //
01273     matches = NULL; //list with matches
01274     lastmatch = NULL; //last match in the list
01275 
01276     while(PC_ReadToken(source, &token))
01277     {
01278         if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER))
01279         {
01280             SourceError(source, "expected integer, found %s\n", token.string);
01281             BotFreeMatchTemplates(matches);
01282             FreeSource(source);
01283             return NULL;
01284         } //end if
01285         //the context
01286         context = token.intvalue;
01287         //
01288         if (!PC_ExpectTokenString(source, "{"))
01289         {
01290             BotFreeMatchTemplates(matches);
01291             FreeSource(source);
01292             return NULL;
01293         } //end if
01294         //
01295         while(PC_ReadToken(source, &token))
01296         {
01297             if (!strcmp(token.string, "}")) break;
01298             //
01299             PC_UnreadLastToken(source);
01300             //
01301             matchtemplate = (bot_matchtemplate_t *) GetClearedHunkMemory(sizeof(bot_matchtemplate_t));
01302             matchtemplate->context = context;
01303             matchtemplate->next = NULL;
01304             //add the match template to the list
01305             if (lastmatch) lastmatch->next = matchtemplate;
01306             else matches = matchtemplate;
01307             lastmatch = matchtemplate;
01308             //load the match template
01309             matchtemplate->first = BotLoadMatchPieces(source, "=");
01310             if (!matchtemplate->first)
01311             {
01312                 BotFreeMatchTemplates(matches);
01313                 return NULL;
01314             } //end if
01315             //read the match type
01316             if (!PC_ExpectTokenString(source, "(") ||
01317                 !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))
01318             {
01319                 BotFreeMatchTemplates(matches);
01320                 FreeSource(source);
01321                 return NULL;
01322             } //end if
01323             matchtemplate->type = token.intvalue;
01324             //read the match subtype
01325             if (!PC_ExpectTokenString(source, ",") ||
01326                 !PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))
01327             {
01328                 BotFreeMatchTemplates(matches);
01329                 FreeSource(source);
01330                 return NULL;
01331             } //end if
01332             matchtemplate->subtype = token.intvalue;
01333             //read trailing punctuations
01334             if (!PC_ExpectTokenString(source, ")") ||
01335                 !PC_ExpectTokenString(source, ";"))
01336             {
01337                 BotFreeMatchTemplates(matches);
01338                 FreeSource(source);
01339                 return NULL;
01340             } //end if
01341         } //end while
01342     } //end while
01343     //free the source
01344     FreeSource(source);
01345     botimport.Print(PRT_MESSAGE, "loaded %s\n", matchfile);
01346     //
01347     //BotDumpMatchTemplates(matches);
01348     //
01349     return matches;
01350 } //end of the function BotLoadMatchTemplates
01351 //===========================================================================
01352 //
01353 // Parameter:               -
01354 // Returns:                 -
01355 // Changes Globals:     -
01356 //===========================================================================
01357 int StringsMatch(bot_matchpiece_t *pieces, bot_match_t *match)
01358 {
01359     int lastvariable, index;
01360     char *strptr, *newstrptr;
01361     bot_matchpiece_t *mp;
01362     bot_matchstring_t *ms;
01363 
01364     //no last variable
01365     lastvariable = -1;
01366     //pointer to the string to compare the match string with
01367     strptr = match->string;
01368     //Log_Write("match: %s", strptr);
01369     //compare the string with the current match string
01370     for (mp = pieces; mp; mp = mp->next)
01371     {
01372         //if it is a piece of string
01373         if (mp->type == MT_STRING)
01374         {
01375             newstrptr = NULL;
01376             for (ms = mp->firststring; ms; ms = ms->next)
01377             {
01378                 if (!strlen(ms->string))
01379                 {
01380                     newstrptr = strptr;
01381                     break;
01382                 } //end if
01383                 //Log_Write("MT_STRING: %s", mp->string);
01384                 index = StringContains(strptr, ms->string, qfalse);
01385                 if (index >= 0)
01386                 {
01387                     newstrptr = strptr + index;
01388                     if (lastvariable >= 0)
01389                     {
01390                         match->variables[lastvariable].length =
01391                                 (newstrptr - match->string) - match->variables[lastvariable].offset;
01392                                 //newstrptr - match->variables[lastvariable].ptr;
01393                         lastvariable = -1;
01394                         break;
01395                     } //end if
01396                     else if (index == 0)
01397                     {
01398                         break;
01399                     } //end else
01400                     newstrptr = NULL;
01401                 } //end if
01402             } //end for
01403             if (!newstrptr) return qfalse;
01404             strptr = newstrptr + strlen(ms->string);
01405         } //end if
01406         //if it is a variable piece of string
01407         else if (mp->type == MT_VARIABLE)
01408         {
01409             //Log_Write("MT_VARIABLE");
01410             match->variables[mp->variable].offset = strptr - match->string;
01411             lastvariable = mp->variable;
01412         } //end else if
01413     } //end for
01414     //if a match was found
01415     if (!mp && (lastvariable >= 0 || !strlen(strptr)))
01416     {
01417         //if the last piece was a variable string
01418         if (lastvariable >= 0)
01419         {
01420                 assert( match->variables[lastvariable].offset >= 0 ); // bk001204
01421             match->variables[lastvariable].length =
01422                 strlen(&match->string[ (int) match->variables[lastvariable].offset]);
01423         } //end if
01424         return qtrue;
01425     } //end if
01426     return qfalse;
01427 } //end of the function StringsMatch
01428 //===========================================================================
01429 //
01430 // Parameter:               -
01431 // Returns:                 -
01432 // Changes Globals:     -
01433 //===========================================================================
01434 int BotFindMatch(char *str, bot_match_t *match, unsigned long int context)
01435 {
01436     int i;
01437     bot_matchtemplate_t *ms;
01438 
01439     strncpy(match->string, str, MAX_MESSAGE_SIZE);
01440     //remove any trailing enters
01441     while(strlen(match->string) &&
01442             match->string[strlen(match->string)-1] == '\n')
01443     {
01444         match->string[strlen(match->string)-1] = '\0';
01445     } //end while
01446     //compare the string with all the match strings
01447     for (ms = matchtemplates; ms; ms = ms->next)
01448     {
01449         if (!(ms->context & context)) continue;
01450         //reset the match variable offsets
01451         for (i = 0; i < MAX_MATCHVARIABLES; i++) match->variables[i].offset = -1;
01452         //
01453         if (StringsMatch(ms->first, match))
01454         {
01455             match->type = ms->type;
01456             match->subtype = ms->subtype;
01457             return qtrue;
01458         } //end if
01459     } //end for
01460     return qfalse;
01461 } //end of the function BotFindMatch
01462 //===========================================================================
01463 //
01464 // Parameter:               -
01465 // Returns:                 -
01466 // Changes Globals:     -
01467 //===========================================================================
01468 void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size)
01469 {
01470     if (variable < 0 || variable >= MAX_MATCHVARIABLES)
01471     {
01472         botimport.Print(PRT_FATAL, "BotMatchVariable: variable out of range\n");
01473         strcpy(buf, "");
01474         return;
01475     } //end if
01476 
01477     if (match->variables[variable].offset >= 0)
01478     {
01479         if (match->variables[variable].length < size)
01480             size = match->variables[variable].length+1;
01481         assert( match->variables[variable].offset >= 0 ); // bk001204
01482         strncpy(buf, &match->string[ (int) match->variables[variable].offset], size-1);
01483         buf[size-1] = '\0';
01484     } //end if
01485     else
01486     {
01487         strcpy(buf, "");
01488     } //end else
01489     return;
01490 } //end of the function BotMatchVariable
01491 //===========================================================================
01492 //
01493 // Parameter:               -
01494 // Returns:                 -
01495 // Changes Globals:     -
01496 //===========================================================================
01497 bot_stringlist_t *BotFindStringInList(bot_stringlist_t *list, char *string)
01498 {
01499     bot_stringlist_t *s;
01500 
01501     for (s = list; s; s = s->next)
01502     {
01503         if (!strcmp(s->string, string)) return s;
01504     } //end for
01505     return NULL;
01506 } //end of the function BotFindStringInList
01507 //===========================================================================
01508 //
01509 // Parameter:               -
01510 // Returns:                 -
01511 // Changes Globals:     -
01512 //===========================================================================
01513 bot_stringlist_t *BotCheckChatMessageIntegrety(char *message, bot_stringlist_t *stringlist)
01514 {
01515     int i;
01516     char *msgptr;
01517     char temp[MAX_MESSAGE_SIZE];
01518     bot_stringlist_t *s;
01519 
01520     msgptr = message;
01521     //
01522     while(*msgptr)
01523     {
01524         if (*msgptr == ESCAPE_CHAR)
01525         {
01526             msgptr++;
01527             switch(*msgptr)
01528             {
01529                 case 'v': //variable
01530                 {
01531                     //step over the 'v'
01532                     msgptr++;
01533                     while(*msgptr && *msgptr != ESCAPE_CHAR) msgptr++;
01534                     //step over the trailing escape char
01535                     if (*msgptr) msgptr++;
01536                     break;
01537                 } //end case
01538                 case 'r': //random
01539                 {
01540                     //step over the 'r'
01541                     msgptr++;
01542                     for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++)
01543                     {
01544                         temp[i] = *msgptr++;
01545                     } //end while
01546                     temp[i] = '\0';
01547                     //step over the trailing escape char
01548                     if (*msgptr) msgptr++;
01549                     //find the random keyword
01550                     if (!RandomString(temp))
01551                     {
01552                         if (!BotFindStringInList(stringlist, temp))
01553                         {
01554                             Log_Write("%s = {\"%s\"} //MISSING RANDOM\r\n", temp, temp);
01555                             s = GetClearedMemory(sizeof(bot_stringlist_t) + strlen(temp) + 1);
01556                             s->string = (char *) s + sizeof(bot_stringlist_t);
01557                             strcpy(s->string, temp);
01558                             s->next = stringlist;
01559                             stringlist = s;
01560                         } //end if
01561                     } //end if
01562                     break;
01563                 } //end case
01564                 default:
01565                 {
01566                     botimport.Print(PRT_FATAL, "BotCheckChatMessageIntegrety: message \"%s\" invalid escape char\n", message);
01567                     break;
01568                 } //end default
01569             } //end switch
01570         } //end if
01571         else
01572         {
01573             msgptr++;
01574         } //end else
01575     } //end while
01576     return stringlist;
01577 } //end of the function BotCheckChatMessageIntegrety
01578 //===========================================================================
01579 //
01580 // Parameter:               -
01581 // Returns:                 -
01582 // Changes Globals:     -
01583 //===========================================================================
01584 void BotCheckInitialChatIntegrety(bot_chat_t *chat)
01585 {
01586     bot_chattype_t *t;
01587     bot_chatmessage_t *cm;
01588     bot_stringlist_t *stringlist, *s, *nexts;
01589 
01590     stringlist = NULL;
01591     for (t = chat->types; t; t = t->next)
01592     {
01593         for (cm = t->firstchatmessage; cm; cm = cm->next)
01594         {
01595             stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist);
01596         } //end for
01597     } //end for
01598     for (s = stringlist; s; s = nexts)
01599     {
01600         nexts = s->next;
01601         FreeMemory(s);
01602     } //end for
01603 } //end of the function BotCheckInitialChatIntegrety
01604 //===========================================================================
01605 //
01606 // Parameter:               -
01607 // Returns:                 -
01608 // Changes Globals:     -
01609 //===========================================================================
01610 void BotCheckReplyChatIntegrety(bot_replychat_t *replychat)
01611 {
01612     bot_replychat_t *rp;
01613     bot_chatmessage_t *cm;
01614     bot_stringlist_t *stringlist, *s, *nexts;
01615 
01616     stringlist = NULL;
01617     for (rp = replychat; rp; rp = rp->next)
01618     {
01619         for (cm = rp->firstchatmessage; cm; cm = cm->next)
01620         {
01621             stringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist);
01622         } //end for
01623     } //end for
01624     for (s = stringlist; s; s = nexts)
01625     {
01626         nexts = s->next;
01627         FreeMemory(s);
01628     } //end for
01629 } //end of the function BotCheckReplyChatIntegrety
01630 //===========================================================================
01631 //
01632 // Parameter:               -
01633 // Returns:                 -
01634 // Changes Globals:     -
01635 //===========================================================================
01636 void BotDumpReplyChat(bot_replychat_t *replychat)
01637 {
01638     FILE *fp;
01639     bot_replychat_t *rp;
01640     bot_replychatkey_t *key;
01641     bot_chatmessage_t *cm;
01642     bot_matchpiece_t *mp;
01643 
01644     fp = Log_FilePointer();
01645     if (!fp) return;
01646     fprintf(fp, "BotDumpReplyChat:\n");
01647     for (rp = replychat; rp; rp = rp->next)
01648     {
01649         fprintf(fp, "[");
01650         for (key = rp->keys; key; key = key->next)
01651         {
01652             if (key->flags & RCKFL_AND) fprintf(fp, "&");
01653             else if (key->flags & RCKFL_NOT) fprintf(fp, "!");
01654             //
01655             if (key->flags & RCKFL_NAME) fprintf(fp, "name");
01656             else if (key->flags & RCKFL_GENDERFEMALE) fprintf(fp, "female");
01657             else if (key->flags & RCKFL_GENDERMALE) fprintf(fp, "male");
01658             else if (key->flags & RCKFL_GENDERLESS) fprintf(fp, "it");
01659             else if (key->flags & RCKFL_VARIABLES)
01660             {
01661                 fprintf(fp, "(");
01662                 for (mp = key->match; mp; mp = mp->next)
01663                 {
01664                     if (mp->type == MT_STRING) fprintf(fp, "\"%s\"", mp->firststring->string);
01665                     else fprintf(fp, "%d", mp->variable);
01666                     if (mp->next) fprintf(fp, ", ");
01667                 } //end for
01668                 fprintf(fp, ")");
01669             } //end if
01670             else if (key->flags & RCKFL_STRING)
01671             {
01672                 fprintf(fp, "\"%s\"", key->string);
01673             } //end if
01674             if (key->next) fprintf(fp, ", ");
01675             else fprintf(fp, "] = %1.0f\n", rp->priority);
01676         } //end for
01677         fprintf(fp, "{\n");
01678         for (cm = rp->firstchatmessage; cm; cm = cm->next)
01679         {
01680             fprintf(fp, "\t\"%s\";\n", cm->chatmessage);
01681         } //end for
01682         fprintf(fp, "}\n");
01683     } //end for
01684 } //end of the function BotDumpReplyChat
01685 //===========================================================================
01686 //
01687 // Parameter:               -
01688 // Returns:                 -
01689 // Changes Globals:     -
01690 //===========================================================================
01691 void BotFreeReplyChat(bot_replychat_t *replychat)
01692 {
01693     bot_replychat_t *rp, *nextrp;
01694     bot_replychatkey_t *key, *nextkey;
01695     bot_chatmessage_t *cm, *nextcm;
01696 
01697     for (rp = replychat; rp; rp = nextrp)
01698     {
01699         nextrp = rp->next;
01700         for (key = rp->keys; key; key = nextkey)
01701         {
01702             nextkey = key->next;
01703             if (key->match) BotFreeMatchPieces(key->match);
01704             if (key->string) FreeMemory(key->string);
01705             FreeMemory(key);
01706         } //end for
01707         for (cm = rp->firstchatmessage; cm; cm = nextcm)
01708         {
01709             nextcm = cm->next;
01710             FreeMemory(cm);
01711         } //end for
01712         FreeMemory(rp);
01713     } //end for
01714 } //end of the function BotFreeReplyChat
01715 //===========================================================================
01716 //
01717 // Parameter:           -
01718 // Returns:             -
01719 // Changes Globals:     -
01720 //===========================================================================
01721 void BotCheckValidReplyChatKeySet(source_t *source, bot_replychatkey_t *keys)
01722 {
01723     int allprefixed, hasvariableskey, hasstringkey;
01724     bot_matchpiece_t *m;
01725     bot_matchstring_t *ms;
01726     bot_replychatkey_t *key, *key2;
01727 
01728     //
01729     allprefixed = qtrue;
01730     hasvariableskey = hasstringkey = qfalse;
01731     for (key = keys; key; key = key->next)
01732     {
01733         if (!(key->flags & (RCKFL_AND|RCKFL_NOT)))
01734         {
01735             allprefixed = qfalse;
01736             if (key->flags & RCKFL_VARIABLES)
01737             {
01738                 for (m = key->match; m; m = m->next)
01739                 {
01740                     if (m->type == MT_VARIABLE) hasvariableskey = qtrue;
01741                 } //end for
01742             } //end if
01743             else if (key->flags & RCKFL_STRING)
01744             {
01745                 hasstringkey = qtrue;
01746             } //end else if
01747         } //end if
01748         else if ((key->flags & RCKFL_AND) && (key->flags & RCKFL_STRING))
01749         {
01750             for (key2 = keys; key2; key2 = key2->next)
01751             {
01752                 if (key2 == key) continue;
01753                 if (key2->flags & RCKFL_NOT) continue;
01754                 if (key2->flags & RCKFL_VARIABLES)
01755                 {
01756                     for (m = key2->match; m; m = m->next)
01757                     {
01758                         if (m->type == MT_STRING)
01759                         {
01760                             for (ms = m->firststring; ms; ms = ms->next)
01761                             {
01762                                 if (StringContains(ms->string, key->string, qfalse) != -1)
01763                                 {
01764                                     break;
01765                                 } //end if
01766                             } //end for
01767                             if (ms) break;
01768                         } //end if
01769                         else if (m->type == MT_VARIABLE)
01770                         {
01771                             break;
01772                         } //end if
01773                     } //end for
01774                     if (!m)
01775                     {
01776                         SourceWarning(source, "one of the match templates does not "
01777                                         "leave space for the key %s with the & prefix", key->string);
01778                     } //end if
01779                 } //end if
01780             } //end for
01781         } //end else
01782         if ((key->flags & RCKFL_NOT) && (key->flags & RCKFL_STRING))
01783         {
01784             for (key2 = keys; key2; key2 = key2->next)
01785             {
01786                 if (key2 == key) continue;
01787                 if (key2->flags & RCKFL_NOT) continue;
01788                 if (key2->flags & RCKFL_STRING)
01789                 {
01790                     if (StringContains(key2->string, key->string, qfalse) != -1)
01791                     {
01792                         SourceWarning(source, "the key %s with prefix ! is inside the key %s", key->string, key2->string);
01793                     } //end if
01794                 } //end if
01795                 else if (key2->flags & RCKFL_VARIABLES)
01796                 {
01797                     for (m = key2->match; m; m = m->next)
01798                     {
01799                         if (m->type == MT_STRING)
01800                         {
01801                             for (ms = m->firststring; ms; ms = ms->next)
01802                             {
01803                                 if (StringContains(ms->string, key->string, qfalse) != -1)
01804                                 {
01805                                     SourceWarning(source, "the key %s with prefix ! is inside "
01806                                                 "the match template string %s", key->string, ms->string);
01807                                 } //end if
01808                             } //end for
01809                         } //end if
01810                     } //end for
01811                 } //end else if
01812             } //end for
01813         } //end if
01814     } //end for
01815     if (allprefixed) SourceWarning(source, "all keys have a & or ! prefix");
01816     if (hasvariableskey && hasstringkey)
01817     {
01818         SourceWarning(source, "variables from the match template(s) could be "
01819                                 "invalid when outputting one of the chat messages");
01820     } //end if
01821 } //end of the function BotCheckValidReplyChatKeySet
01822 //===========================================================================
01823 //
01824 // Parameter:           -
01825 // Returns:             -
01826 // Changes Globals:     -
01827 //===========================================================================
01828 bot_replychat_t *BotLoadReplyChat(char *filename)
01829 {
01830     char chatmessagestring[MAX_MESSAGE_SIZE];
01831     char namebuffer[MAX_MESSAGE_SIZE];
01832     source_t *source;
01833     token_t token;
01834     bot_chatmessage_t *chatmessage = NULL;
01835     bot_replychat_t *replychat, *replychatlist;
01836     bot_replychatkey_t *key;
01837 
01838     PC_SetBaseFolder(BOTFILESBASEFOLDER);
01839     source = LoadSourceFile(filename);
01840     if (!source)
01841     {
01842         botimport.Print(PRT_ERROR, "counldn't load %s\n", filename);
01843         return NULL;
01844     } //end if
01845     //
01846     replychatlist = NULL;
01847     //
01848     while(PC_ReadToken(source, &token))
01849     {
01850         if (strcmp(token.string, "["))
01851         {
01852             SourceError(source, "expected [, found %s", token.string);
01853             BotFreeReplyChat(replychatlist);
01854             FreeSource(source);
01855             return NULL;
01856         } //end if
01857         //
01858         replychat = GetClearedHunkMemory(sizeof(bot_replychat_t));
01859         replychat->keys = NULL;
01860         replychat->next = replychatlist;
01861         replychatlist = replychat;
01862         //read the keys, there must be at least one key
01863         do
01864         {
01865             //allocate a key
01866             key = (bot_replychatkey_t *) GetClearedHunkMemory(sizeof(bot_replychatkey_t));
01867             key->flags = 0;
01868             key->string = NULL;
01869             key->match = NULL;
01870             key->next = replychat->keys;
01871             replychat->keys = key;
01872             //check for MUST BE PRESENT and MUST BE ABSENT keys
01873             if (PC_CheckTokenString(source, "&")) key->flags |= RCKFL_AND;
01874             else if (PC_CheckTokenString(source, "!")) key->flags |= RCKFL_NOT;
01875             //special keys
01876             if (PC_CheckTokenString(source, "name")) key->flags |= RCKFL_NAME;
01877             else if (PC_CheckTokenString(source, "female")) key->flags |= RCKFL_GENDERFEMALE;
01878             else if (PC_CheckTokenString(source, "male")) key->flags |= RCKFL_GENDERMALE;
01879             else if (PC_CheckTokenString(source, "it")) key->flags |= RCKFL_GENDERLESS;
01880             else if (PC_CheckTokenString(source, "(")) //match key
01881             {
01882                 key->flags |= RCKFL_VARIABLES;
01883                 key->match = BotLoadMatchPieces(source, ")");
01884                 if (!key->match)
01885                 {
01886                     BotFreeReplyChat(replychatlist);
01887                     return NULL;
01888                 } //end if
01889             } //end else if
01890             else if (PC_CheckTokenString(source, "<")) //bot names
01891             {
01892                 key->flags |= RCKFL_BOTNAMES;
01893                 strcpy(namebuffer, "");
01894                 do
01895                 {
01896                     if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
01897                     {
01898                         BotFreeReplyChat(replychatlist);
01899                         FreeSource(source);
01900                         return NULL;
01901                     } //end if
01902                     StripDoubleQuotes(token.string);
01903                     if (strlen(namebuffer)) strcat(namebuffer, "\\");
01904                     strcat(namebuffer, token.string);
01905                 } while(PC_CheckTokenString(source, ","));
01906                 if (!PC_ExpectTokenString(source, ">"))
01907                 {
01908                     BotFreeReplyChat(replychatlist);
01909                     FreeSource(source);
01910                     return NULL;
01911                 } //end if
01912                 key->string = (char *) GetClearedHunkMemory(strlen(namebuffer) + 1);
01913                 strcpy(key->string, namebuffer);
01914             } //end else if
01915             else //normal string key
01916             {
01917                 key->flags |= RCKFL_STRING;
01918                 if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
01919                 {
01920                     BotFreeReplyChat(replychatlist);
01921                     FreeSource(source);
01922                     return NULL;
01923                 } //end if
01924                 StripDoubleQuotes(token.string);
01925                 key->string = (char *) GetClearedHunkMemory(strlen(token.string) + 1);
01926                 strcpy(key->string, token.string);
01927             } //end else
01928             //
01929             PC_CheckTokenString(source, ",");
01930         } while(!PC_CheckTokenString(source, "]"));
01931         //
01932         BotCheckValidReplyChatKeySet(source, replychat->keys);
01933         //read the = sign and the priority
01934         if (!PC_ExpectTokenString(source, "=") ||
01935             !PC_ExpectTokenType(source, TT_NUMBER, 0, &token))
01936         {
01937             BotFreeReplyChat(replychatlist);
01938             FreeSource(source);
01939             return NULL;
01940         } //end if
01941         replychat->priority = token.floatvalue;
01942         //read the leading {
01943         if (!PC_ExpectTokenString(source, "{"))
01944         {
01945             BotFreeReplyChat(replychatlist);
01946             FreeSource(source);
01947             return NULL;
01948         } //end if
01949         replychat->numchatmessages = 0;
01950         //while the trailing } is not found
01951         while(!PC_CheckTokenString(source, "}"))
01952         {
01953             if (!BotLoadChatMessage(source, chatmessagestring))
01954             {
01955                 BotFreeReplyChat(replychatlist);
01956                 FreeSource(source);
01957                 return NULL;
01958             } //end if
01959             chatmessage = (bot_chatmessage_t *) GetClearedHunkMemory(sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1);
01960             chatmessage->chatmessage = (char *) chatmessage + sizeof(bot_chatmessage_t);
01961             strcpy(chatmessage->chatmessage, chatmessagestring);
01962             chatmessage->time = -2*CHATMESSAGE_RECENTTIME;
01963             chatmessage->next = replychat->firstchatmessage;
01964             //add the chat message to the reply chat
01965             replychat->firstchatmessage = chatmessage;
01966             replychat->numchatmessages++;
01967         } //end while
01968     } //end while
01969     FreeSource(source);
01970     botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
01971     //
01972     //BotDumpReplyChat(replychatlist);
01973     if (bot_developer)
01974     {
01975         BotCheckReplyChatIntegrety(replychatlist);
01976     } //end if
01977     //
01978     if (!replychatlist) botimport.Print(PRT_MESSAGE, "no rchats\n");
01979     //
01980     return replychatlist;
01981 } //end of the function BotLoadReplyChat
01982 //===========================================================================
01983 //
01984 // Parameter:               -
01985 // Returns:                 -
01986 // Changes Globals:     -
01987 //===========================================================================
01988 void BotDumpInitialChat(bot_chat_t *chat)
01989 {
01990     bot_chattype_t *t;
01991     bot_chatmessage_t *m;
01992 
01993     Log_Write("{");
01994     for (t = chat->types; t; t = t->next)
01995     {
01996         Log_Write(" type \"%s\"", t->name);
01997         Log_Write(" {");
01998         Log_Write("  numchatmessages = %d", t->numchatmessages);
01999         for (m = t->firstchatmessage; m; m = m->next)
02000         {
02001             Log_Write("  \"%s\"", m->chatmessage);
02002         } //end for
02003         Log_Write(" }");
02004     } //end for
02005     Log_Write("}");
02006 } //end of the function BotDumpInitialChat
02007 //===========================================================================
02008 //
02009 // Parameter:               -
02010 // Returns:                 -
02011 // Changes Globals:     -
02012 //===========================================================================
02013 bot_chat_t *BotLoadInitialChat(char *chatfile, char *chatname)
02014 {
02015     int pass, foundchat, indent, size;
02016     char *ptr = NULL;
02017     char chatmessagestring[MAX_MESSAGE_SIZE];
02018     source_t *source;
02019     token_t token;
02020     bot_chat_t *chat = NULL;
02021     bot_chattype_t *chattype = NULL;
02022     bot_chatmessage_t *chatmessage = NULL;
02023 #ifdef DEBUG
02024     int starttime;
02025 
02026     starttime = Sys_MilliSeconds();
02027 #endif //DEBUG
02028     //
02029     size = 0;
02030     foundchat = qfalse;
02031     //a bot chat is parsed in two phases
02032     for (pass = 0; pass < 2; pass++)
02033     {
02034         //allocate memory
02035         if (pass && size) ptr = (char *) GetClearedMemory(size);
02036         //load the source file
02037         PC_SetBaseFolder(BOTFILESBASEFOLDER);
02038         source = LoadSourceFile(chatfile);
02039         if (!source)
02040         {
02041             botimport.Print(PRT_ERROR, "counldn't load %s\n", chatfile);
02042             return NULL;
02043         } //end if
02044         //chat structure
02045         if (pass)
02046         {
02047             chat = (bot_chat_t *) ptr;
02048             ptr += sizeof(bot_chat_t);
02049         } //end if
02050         size = sizeof(bot_chat_t);
02051         //
02052         while(PC_ReadToken(source, &token))
02053         {
02054             if (!strcmp(token.string, "chat"))
02055             {
02056                 if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
02057                 {
02058                     FreeSource(source);
02059                     return NULL;
02060                 } //end if
02061                 StripDoubleQuotes(token.string);
02062                 //after the chat name we expect a opening brace
02063                 if (!PC_ExpectTokenString(source, "{"))
02064                 {
02065                     FreeSource(source);
02066                     return NULL;
02067                 } //end if
02068                 //if the chat name is found
02069                 if (!Q_stricmp(token.string, chatname))
02070                 {
02071                     foundchat = qtrue;
02072                     //read the chat types
02073                     while(1)
02074                     {
02075                         if (!PC_ExpectAnyToken(source, &token))
02076                         {
02077                             FreeSource(source);
02078                             return NULL;
02079                         } //end if
02080                         if (!strcmp(token.string, "}")) break;
02081                         if (strcmp(token.string, "type"))
02082                         {
02083                             SourceError(source, "expected type found %s\n", token.string);
02084                             FreeSource(source);
02085                             return NULL;
02086                         } //end if
02087                         //expect the chat type name
02088                         if (!PC_ExpectTokenType(source, TT_STRING, 0, &token) ||
02089                             !PC_ExpectTokenString(source, "{"))
02090                         {
02091                             FreeSource(source);
02092                             return NULL;
02093                         } //end if
02094                         StripDoubleQuotes(token.string);
02095                         if (pass)
02096                         {
02097                             chattype = (bot_chattype_t *) ptr;
02098                             strncpy(chattype->name, token.string, MAX_CHATTYPE_NAME);
02099                             chattype->firstchatmessage = NULL;
02100                             //add the chat type to the chat
02101                             chattype->next = chat->types;
02102                             chat->types = chattype;
02103                             //
02104                             ptr += sizeof(bot_chattype_t);
02105                         } //end if
02106                         size += sizeof(bot_chattype_t);
02107                         //read the chat messages
02108                         while(!PC_CheckTokenString(source, "}"))
02109                         {
02110                             if (!BotLoadChatMessage(source, chatmessagestring))
02111                             {
02112                                 FreeSource(source);
02113                                 return NULL;
02114                             } //end if
02115                             if (pass)
02116                             {
02117                                 chatmessage = (bot_chatmessage_t *) ptr;
02118                                 chatmessage->time = -2*CHATMESSAGE_RECENTTIME;
02119                                 //put the chat message in the list
02120                                 chatmessage->next = chattype->firstchatmessage;
02121                                 chattype->firstchatmessage = chatmessage;
02122                                 //store the chat message
02123                                 ptr += sizeof(bot_chatmessage_t);
02124                                 chatmessage->chatmessage = ptr;
02125                                 strcpy(chatmessage->chatmessage, chatmessagestring);
02126                                 ptr += strlen(chatmessagestring) + 1;
02127                                 //the number of chat messages increased
02128                                 chattype->numchatmessages++;
02129                             } //end if
02130                             size += sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1;
02131                         } //end if
02132                     } //end while
02133                 } //end if
02134                 else //skip the bot chat
02135                 {
02136                     indent = 1;
02137                     while(indent)
02138                     {
02139                         if (!PC_ExpectAnyToken(source, &token))
02140                         {
02141                             FreeSource(source);
02142                             return NULL;
02143                         } //end if
02144                         if (!strcmp(token.string, "{")) indent++;
02145                         else if (!strcmp(token.string, "}")) indent--;
02146                     } //end while
02147                 } //end else
02148             } //end if
02149             else
02150             {
02151                 SourceError(source, "unknown definition %s\n", token.string);
02152                 FreeSource(source);
02153                 return NULL;
02154             } //end else
02155         } //end while
02156         //free the source
02157         FreeSource(source);
02158         //if the requested character is not found
02159         if (!foundchat)
02160         {
02161             botimport.Print(PRT_ERROR, "couldn't find chat %s in %s\n", chatname, chatfile);
02162             return NULL;
02163         } //end if
02164     } //end for
02165     //
02166     botimport.Print(PRT_MESSAGE, "loaded %s from %s\n", chatname, chatfile);
02167     //
02168     //BotDumpInitialChat(chat);
02169     if (bot_developer)
02170     {
02171         BotCheckInitialChatIntegrety(chat);
02172     } //end if
02173 #ifdef DEBUG
02174     botimport.Print(PRT_MESSAGE, "initial chats loaded in %d msec\n", Sys_MilliSeconds() - starttime);
02175 #endif //DEBUG
02176     //character was read succesfully
02177     return chat;
02178 } //end of the function BotLoadInitialChat
02179 //===========================================================================
02180 //
02181 // Parameter:           -
02182 // Returns:             -
02183 // Changes Globals:     -
02184 //===========================================================================
02185 void BotFreeChatFile(int chatstate)
02186 {
02187     bot_chatstate_t *cs;
02188 
02189     cs = BotChatStateFromHandle(chatstate);
02190     if (!cs) return;
02191     if (cs->chat) FreeMemory(cs->chat);
02192     cs->chat = NULL;
02193 } //end of the function BotFreeChatFile
02194 //===========================================================================
02195 //
02196 // Parameter:               -
02197 // Returns:                 -
02198 // Changes Globals:     -
02199 //===========================================================================
02200 int BotLoadChatFile(int chatstate, char *chatfile, char *chatname)
02201 {
02202     bot_chatstate_t *cs;
02203     int n, avail = 0;
02204 
02205     cs = BotChatStateFromHandle(chatstate);
02206     if (!cs) return BLERR_CANNOTLOADICHAT;
02207     BotFreeChatFile(chatstate);
02208 
02209     if (!LibVarGetValue("bot_reloadcharacters"))
02210     {
02211         avail = -1;
02212         for( n = 0; n < MAX_CLIENTS; n++ ) {
02213             if( !ichatdata[n] ) {
02214                 if( avail == -1 ) {
02215                     avail = n;
02216                 }
02217                 continue;
02218             }
02219             if( strcmp( chatfile, ichatdata[n]->filename ) != 0 ) { 
02220                 continue;
02221             }
02222             if( strcmp( chatname, ichatdata[n]->chatname ) != 0 ) { 
02223                 continue;
02224             }
02225             cs->chat = ichatdata[n]->chat;
02226         //      botimport.Print( PRT_MESSAGE, "retained %s from %s\n", chatname, chatfile );
02227             return BLERR_NOERROR;
02228         }
02229 
02230         if( avail == -1 ) {
02231             botimport.Print(PRT_FATAL, "ichatdata table full; couldn't load chat %s from %s\n", chatname, chatfile);
02232             return BLERR_CANNOTLOADICHAT;
02233         }
02234     }
02235 
02236     cs->chat = BotLoadInitialChat(chatfile, chatname);
02237     if (!cs->chat)
02238     {
02239         botimport.Print(PRT_FATAL, "couldn't load chat %s from %s\n", chatname, chatfile);
02240         return BLERR_CANNOTLOADICHAT;
02241     } //end if
02242     if (!LibVarGetValue("bot_reloadcharacters"))
02243     {
02244         ichatdata[avail] = GetClearedMemory( sizeof(bot_ichatdata_t) );
02245         ichatdata[avail]->chat = cs->chat;
02246         Q_strncpyz( ichatdata[avail]->chatname, chatname, sizeof(ichatdata[avail]->chatname) );
02247         Q_strncpyz( ichatdata[avail]->filename, chatfile, sizeof(ichatdata[avail]->filename) );
02248     } //end if
02249 
02250     return BLERR_NOERROR;
02251 } //end of the function BotLoadChatFile
02252 //===========================================================================
02253 //
02254 // Parameter:           -
02255 // Returns:             -
02256 // Changes Globals:     -
02257 //===========================================================================
02258 int BotExpandChatMessage(char *outmessage, char *message, unsigned long mcontext,
02259                              bot_match_t *match, unsigned long vcontext, int reply)
02260 {
02261     int num, len, i, expansion;
02262     char *outputbuf, *ptr, *msgptr;
02263     char temp[MAX_MESSAGE_SIZE];
02264 
02265     expansion = qfalse;
02266     msgptr = message;
02267     outputbuf = outmessage;
02268     len = 0;
02269     //
02270     while(*msgptr)
02271     {
02272         if (*msgptr == ESCAPE_CHAR)
02273         {
02274             msgptr++;
02275             switch(*msgptr)
02276             {
02277                 case 'v': //variable
02278                 {
02279                     msgptr++;
02280                     num = 0;
02281                     while(*msgptr && *msgptr != ESCAPE_CHAR)
02282                     {
02283                         num = num * 10 + (*msgptr++) - '0';
02284                     } //end while
02285                     //step over the trailing escape char
02286                     if (*msgptr) msgptr++;
02287                     if (num > MAX_MATCHVARIABLES)
02288                     {
02289                         botimport.Print(PRT_ERROR, "BotConstructChat: message %s variable %d out of range\n", message, num);
02290                         return qfalse;
02291                     } //end if
02292                     if (match->variables[num].offset >= 0)
02293                     {
02294                             assert( match->variables[num].offset >= 0 ); // bk001204
02295                         ptr = &match->string[ (int) match->variables[num].offset];
02296                         for (i = 0; i < match->variables[num].length; i++)
02297                         {
02298                             temp[i] = ptr[i];
02299                         } //end for
02300                         temp[i] = 0;
02301                         //if it's a reply message
02302                         if (reply)
02303                         {
02304                             //replace the reply synonyms in the variables
02305                             BotReplaceReplySynonyms(temp, vcontext);
02306                         } //end if
02307                         else 
02308                         {
02309                             //replace synonyms in the variable context
02310                             BotReplaceSynonyms(temp, vcontext);
02311                         } //end else
02312                         //
02313                         if (len + strlen(temp) >= MAX_MESSAGE_SIZE)
02314                         {
02315                             botimport.Print(PRT_ERROR, "BotConstructChat: message %s too long\n", message);
02316                             return qfalse;
02317                         } //end if
02318                         strcpy(&outputbuf[len], temp);
02319                         len += strlen(temp);
02320                     } //end if
02321                     break;
02322                 } //end case
02323                 case 'r': //random
02324                 {
02325                     msgptr++;
02326                     for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++)
02327                     {
02328                         temp[i] = *msgptr++;
02329                     } //end while
02330                     temp[i] = '\0';
02331                     //step over the trailing escape char
02332                     if (*msgptr) msgptr++;
02333                     //find the random keyword
02334                     ptr = RandomString(temp);
02335                     if (!ptr)
02336                     {
02337                         botimport.Print(PRT_ERROR, "BotConstructChat: unknown random string %s\n", temp);
02338                         return qfalse;
02339                     } //end if
02340                     if (len + strlen(ptr) >= MAX_MESSAGE_SIZE)
02341                     {
02342                         botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message);
02343                         return qfalse;
02344                     } //end if
02345                     strcpy(&outputbuf[len], ptr);
02346                     len += strlen(ptr);
02347                     expansion = qtrue;
02348                     break;
02349                 } //end case
02350                 default:
02351                 {
02352                     botimport.Print(PRT_FATAL, "BotConstructChat: message \"%s\" invalid escape char\n", message);
02353                     break;
02354                 } //end default
02355             } //end switch
02356         } //end if
02357         else
02358         {
02359             outputbuf[len++] = *msgptr++;
02360             if (len >= MAX_MESSAGE_SIZE)
02361             {
02362                 botimport.Print(PRT_ERROR, "BotConstructChat: message \"%s\" too long\n", message);
02363                 break;
02364             } //end if
02365         } //end else
02366     } //end while
02367     outputbuf[len] = '\0';
02368     //replace synonyms weighted in the message context
02369     BotReplaceWeightedSynonyms(outputbuf, mcontext);
02370     //return true if a random was expanded
02371     return expansion;
02372 } //end of the function BotExpandChatMessage
02373 //===========================================================================
02374 //
02375 // Parameter:           -
02376 // Returns:             -
02377 // Changes Globals:     -
02378 //===========================================================================
02379 void BotConstructChatMessage(bot_chatstate_t *chatstate, char *message, unsigned long mcontext,
02380                              bot_match_t *match, unsigned long vcontext, int reply)
02381 {
02382     int i;
02383     char srcmessage[MAX_MESSAGE_SIZE];
02384 
02385     strcpy(srcmessage, message);
02386     for (i = 0; i < 10; i++)
02387     {
02388         if (!BotExpandChatMessage(chatstate->chatmessage, srcmessage, mcontext, match, vcontext, reply))
02389         {
02390             break;
02391         } //end if
02392         strcpy(srcmessage, chatstate->chatmessage);
02393     } //end for
02394     if (i >= 10)
02395     {
02396         botimport.Print(PRT_WARNING, "too many expansions in chat message\n");
02397         botimport.Print(PRT_WARNING, "%s\n", chatstate->chatmessage);
02398     } //end if
02399 } //end of the function BotConstructChatMessage
02400 //===========================================================================
02401 // randomly chooses one of the chat message of the given type
02402 //
02403 // Parameter:               -
02404 // Returns:                 -
02405 // Changes Globals:     -
02406 //===========================================================================
02407 char *BotChooseInitialChatMessage(bot_chatstate_t *cs, char *type)
02408 {
02409     int n, numchatmessages;
02410     float besttime;
02411     bot_chattype_t *t;
02412     bot_chatmessage_t *m, *bestchatmessage;
02413     bot_chat_t *chat;
02414 
02415     chat = cs->chat;
02416     for (t = chat->types; t; t = t->next)
02417     {
02418         if (!Q_stricmp(t->name, type))
02419         {
02420             numchatmessages = 0;
02421             for (m = t->firstchatmessage; m; m = m->next)
02422             {
02423                 if (m->time > AAS_Time()) continue;
02424                 numchatmessages++;
02425             } //end if
02426             //if all chat messages have been used recently
02427             if (numchatmessages <= 0)
02428             {
02429                 besttime = 0;
02430                 bestchatmessage = NULL;
02431                 for (m = t->firstchatmessage; m; m = m->next)
02432                 {
02433                     if (!besttime || m->time < besttime)
02434                     {
02435                         bestchatmessage = m;
02436                         besttime = m->time;
02437                     } //end if
02438                 } //end for
02439                 if (bestchatmessage) return bestchatmessage->chatmessage;
02440             } //end if
02441             else //choose a chat message randomly
02442             {
02443                 n = random() * numchatmessages;
02444                 for (m = t->firstchatmessage; m; m = m->next)
02445                 {
02446                     if (m->time > AAS_Time()) continue;
02447                     if (--n < 0)
02448                     {
02449                         m->time = AAS_Time() + CHATMESSAGE_RECENTTIME;
02450                         return m->chatmessage;
02451                     } //end if
02452                 } //end for
02453             } //end else
02454             return NULL;
02455         } //end if
02456     } //end for
02457     return NULL;
02458 } //end of the function BotChooseInitialChatMessage
02459 //===========================================================================
02460 //
02461 // Parameter:               -
02462 // Returns:                 -
02463 // Changes Globals:     -
02464 //===========================================================================
02465 int BotNumInitialChats(int chatstate, char *type)
02466 {
02467     bot_chatstate_t *cs;
02468     bot_chattype_t *t;
02469 
02470     cs = BotChatStateFromHandle(chatstate);
02471     if (!cs) return 0;
02472 
02473     for (t = cs->chat->types; t; t = t->next)
02474     {
02475         if (!Q_stricmp(t->name, type))
02476         {
02477             if (LibVarGetValue("bot_testichat")) {
02478                 botimport.Print(PRT_MESSAGE, "%s has %d chat lines\n", type, t->numchatmessages);
02479                 botimport.Print(PRT_MESSAGE, "-------------------\n");
02480             }
02481             return t->numchatmessages;
02482         } //end if
02483     } //end for
02484     return 0;
02485 } //end of the function BotNumInitialChats
02486 //===========================================================================
02487 //
02488 // Parameter:               -
02489 // Returns:                 -
02490 // Changes Globals:     -
02491 //===========================================================================
02492 void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7)
02493 {
02494     char *message;
02495     int index;
02496     bot_match_t match;
02497     bot_chatstate_t *cs;
02498 
02499     cs = BotChatStateFromHandle(chatstate);
02500     if (!cs) return;
02501     //if no chat file is loaded
02502     if (!cs->chat) return;
02503     //choose a chat message randomly of the given type
02504     message = BotChooseInitialChatMessage(cs, type);
02505     //if there's no message of the given type
02506     if (!message)
02507     {
02508 #ifdef DEBUG
02509         botimport.Print(PRT_MESSAGE, "no chat messages of type %s\n", type);
02510 #endif //DEBUG
02511         return;
02512     } //end if
02513     //
02514     Com_Memset(&match, 0, sizeof(match));
02515     index = 0;
02516     if( var0 ) {
02517         strcat(match.string, var0);
02518         match.variables[0].offset = index;
02519         match.variables[0].length = strlen(var0);
02520         index += strlen(var0);
02521     }
02522     if( var1 ) {
02523         strcat(match.string, var1);
02524         match.variables[1].offset = index;
02525         match.variables[1].length = strlen(var1);
02526         index += strlen(var1);
02527     }
02528     if( var2 ) {
02529         strcat(match.string, var2);
02530         match.variables[2].offset = index;
02531         match.variables[2].length = strlen(var2);
02532         index += strlen(var2);
02533     }
02534     if( var3 ) {
02535         strcat(match.string, var3);
02536         match.variables[3].offset = index;
02537         match.variables[3].length = strlen(var3);
02538         index += strlen(var3);
02539     }
02540     if( var4 ) {
02541         strcat(match.string, var4);
02542         match.variables[4].offset = index;
02543         match.variables[4].length = strlen(var4);
02544         index += strlen(var4);
02545     }
02546     if( var5 ) {
02547         strcat(match.string, var5);
02548         match.variables[5].offset = index;
02549         match.variables[5].length = strlen(var5);
02550         index += strlen(var5);
02551     }
02552     if( var6 ) {
02553         strcat(match.string, var6);
02554         match.variables[6].offset = index;
02555         match.variables[6].length = strlen(var6);
02556         index += strlen(var6);
02557     }
02558     if( var7 ) {
02559         strcat(match.string, var7);
02560         match.variables[7].offset = index;
02561         match.variables[7].length = strlen(var7);
02562         index += strlen(var7);
02563     }
02564     //
02565     BotConstructChatMessage(cs, message, mcontext, &match, 0, qfalse);
02566 } //end of the function BotInitialChat
02567 //===========================================================================
02568 //
02569 // Parameter:               -
02570 // Returns:                 -
02571 // Changes Globals:     -
02572 //===========================================================================
02573 void BotPrintReplyChatKeys(bot_replychat_t *replychat)
02574 {
02575     bot_replychatkey_t *key;
02576     bot_matchpiece_t *mp;
02577 
02578     botimport.Print(PRT_MESSAGE, "[");
02579     for (key = replychat->keys; key; key = key->next)
02580     {
02581         if (key->flags & RCKFL_AND) botimport.Print(PRT_MESSAGE, "&");
02582         else if (key->flags & RCKFL_NOT) botimport.Print(PRT_MESSAGE, "!");
02583         //
02584         if (key->flags & RCKFL_NAME) botimport.Print(PRT_MESSAGE, "name");
02585         else if (key->flags & RCKFL_GENDERFEMALE) botimport.Print(PRT_MESSAGE, "female");
02586         else if (key->flags & RCKFL_GENDERMALE) botimport.Print(PRT_MESSAGE, "male");
02587         else if (key->flags & RCKFL_GENDERLESS) botimport.Print(PRT_MESSAGE, "it");
02588         else if (key->flags & RCKFL_VARIABLES)
02589         {
02590             botimport.Print(PRT_MESSAGE, "(");
02591             for (mp = key->match; mp; mp = mp->next)
02592             {
02593                 if (mp->type == MT_STRING) botimport.Print(PRT_MESSAGE, "\"%s\"", mp->firststring->string);
02594                 else botimport.Print(PRT_MESSAGE, "%d", mp->variable);
02595                 if (mp->next) botimport.Print(PRT_MESSAGE, ", ");
02596             } //end for
02597             botimport.Print(PRT_MESSAGE, ")");
02598         } //end if
02599         else if (key->flags & RCKFL_STRING)
02600         {
02601             botimport.Print(PRT_MESSAGE, "\"%s\"", key->string);
02602         } //end if
02603         if (key->next) botimport.Print(PRT_MESSAGE, ", ");
02604         else botimport.Print(PRT_MESSAGE, "] = %1.0f\n", replychat->priority);
02605     } //end for
02606     botimport.Print(PRT_MESSAGE, "{\n");
02607 } //end of the function BotPrintReplyChatKeys
02608 //===========================================================================
02609 //
02610 // Parameter:               -
02611 // Returns:                 -
02612 // Changes Globals:     -
02613 //===========================================================================
02614 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)
02615 {
02616     bot_replychat_t *rchat, *bestrchat;
02617     bot_replychatkey_t *key;
02618     bot_chatmessage_t *m, *bestchatmessage;
02619     bot_match_t match, bestmatch;
02620     int bestpriority, num, found, res, numchatmessages, index;
02621     bot_chatstate_t *cs;
02622 
02623     cs = BotChatStateFromHandle(chatstate);
02624     if (!cs) return qfalse;
02625     Com_Memset(&match, 0, sizeof(bot_match_t));
02626     strcpy(match.string, message);
02627     bestpriority = -1;
02628     bestchatmessage = NULL;
02629     bestrchat = NULL;
02630     //go through all the reply chats
02631     for (rchat = replychats; rchat; rchat = rchat->next)
02632     {
02633         found = qfalse;
02634         for (key = rchat->keys; key; key = key->next)
02635         {
02636             res = qfalse;
02637             //get the match result
02638             if (key->flags & RCKFL_NAME) res = (StringContains(message, cs->name, qfalse) != -1);
02639             else if (key->flags & RCKFL_BOTNAMES) res = (StringContains(key->string, cs->name, qfalse) != -1);
02640             else if (key->flags & RCKFL_GENDERFEMALE) res = (cs->gender == CHAT_GENDERFEMALE);
02641             else if (key->flags & RCKFL_GENDERMALE) res = (cs->gender == CHAT_GENDERMALE);
02642             else if (key->flags & RCKFL_GENDERLESS) res = (cs->gender == CHAT_GENDERLESS);
02643             else if (key->flags & RCKFL_VARIABLES) res = StringsMatch(key->match, &match);
02644             else if (key->flags & RCKFL_STRING) res = (StringContainsWord(message, key->string, qfalse) != NULL);
02645             //if the key must be present
02646             if (key->flags & RCKFL_AND)
02647             {
02648                 if (!res)
02649                 {
02650                     found = qfalse;
02651                     break;
02652                 } //end if
02653             } //end else if
02654             //if the key must be absent
02655             else if (key->flags & RCKFL_NOT)
02656             {
02657                 if (res)
02658                 {
02659                     found = qfalse;
02660                     break;
02661                 } //end if
02662             } //end if
02663             else if (res)
02664             {
02665                 found = qtrue;
02666             } //end else
02667         } //end for
02668         //
02669         if (found)
02670         {
02671             if (rchat->priority > bestpriority)
02672             {
02673                 numchatmessages = 0;
02674                 for (m = rchat->firstchatmessage; m; m = m->next)
02675                 {
02676                     if (m->time > AAS_Time()) continue;
02677                     numchatmessages++;
02678                 } //end if
02679                 num = random() * numchatmessages;
02680                 for (m = rchat->firstchatmessage; m; m = m->next)
02681                 {
02682                     if (--num < 0) break;
02683                     if (m->time > AAS_Time()) continue;
02684                 } //end for
02685                 //if the reply chat has a message
02686                 if (m)
02687                 {
02688                     Com_Memcpy(&bestmatch, &match, sizeof(bot_match_t));
02689                     bestchatmessage = m;
02690                     bestrchat = rchat;
02691                     bestpriority = rchat->priority;
02692                 } //end if
02693             } //end if
02694         } //end if
02695     } //end for
02696     if (bestchatmessage)
02697     {
02698         index = strlen(bestmatch.string);
02699         if( var0 ) {
02700             strcat(bestmatch.string, var0);
02701             bestmatch.variables[0].offset = index;
02702             bestmatch.variables[0].length = strlen(var0);
02703             index += strlen(var0);
02704         }
02705         if( var1 ) {
02706             strcat(bestmatch.string, var1);
02707             bestmatch.variables[1].offset = index;
02708             bestmatch.variables[1].length = strlen(var1);
02709             index += strlen(var1);
02710         }
02711         if( var2 ) {
02712             strcat(bestmatch.string, var2);
02713             bestmatch.variables[2].offset = index;
02714             bestmatch.variables[2].length = strlen(var2);
02715             index += strlen(var2);
02716         }
02717         if( var3 ) {
02718             strcat(bestmatch.string, var3);
02719             bestmatch.variables[3].offset = index;
02720             bestmatch.variables[3].length = strlen(var3);
02721             index += strlen(var3);
02722         }
02723         if( var4 ) {
02724             strcat(bestmatch.string, var4);
02725             bestmatch.variables[4].offset = index;
02726             bestmatch.variables[4].length = strlen(var4);
02727             index += strlen(var4);
02728         }
02729         if( var5 ) {
02730             strcat(bestmatch.string, var5);
02731             bestmatch.variables[5].offset = index;
02732             bestmatch.variables[5].length = strlen(var5);
02733             index += strlen(var5);
02734         }
02735         if( var6 ) {
02736             strcat(bestmatch.string, var6);
02737             bestmatch.variables[6].offset = index;
02738             bestmatch.variables[6].length = strlen(var6);
02739             index += strlen(var6);
02740         }
02741         if( var7 ) {
02742             strcat(bestmatch.string, var7);
02743             bestmatch.variables[7].offset = index;
02744             bestmatch.variables[7].length = strlen(var7);
02745             index += strlen(var7);
02746         }
02747         if (LibVarGetValue("bot_testrchat"))
02748         {
02749             for (m = bestrchat->firstchatmessage; m; m = m->next)
02750             {
02751                 BotConstructChatMessage(cs, m->chatmessage, mcontext, &bestmatch, vcontext, qtrue);
02752                 BotRemoveTildes(cs->chatmessage);
02753                 botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage);
02754             } //end if
02755         } //end if
02756         else
02757         {
02758             bestchatmessage->time = AAS_Time() + CHATMESSAGE_RECENTTIME;
02759             BotConstructChatMessage(cs, bestchatmessage->chatmessage, mcontext, &bestmatch, vcontext, qtrue);
02760         } //end else
02761         return qtrue;
02762     } //end if
02763     return qfalse;
02764 } //end of the function BotReplyChat
02765 //===========================================================================
02766 //
02767 // Parameter:               -
02768 // Returns:                 -
02769 // Changes Globals:     -
02770 //===========================================================================
02771 int BotChatLength(int chatstate)
02772 {
02773     bot_chatstate_t *cs;
02774 
02775     cs = BotChatStateFromHandle(chatstate);
02776     if (!cs) return 0;
02777     return strlen(cs->chatmessage);
02778 } //end of the function BotChatLength
02779 //===========================================================================
02780 //
02781 // Parameter:           -
02782 // Returns:             -
02783 // Changes Globals:     -
02784 //===========================================================================
02785 void BotEnterChat(int chatstate, int clientto, int sendto)
02786 {
02787     bot_chatstate_t *cs;
02788 
02789     cs = BotChatStateFromHandle(chatstate);
02790     if (!cs) return;
02791 
02792     if (strlen(cs->chatmessage))
02793     {
02794         BotRemoveTildes(cs->chatmessage);
02795         if (LibVarGetValue("bot_testichat")) {
02796             botimport.Print(PRT_MESSAGE, "%s\n", cs->chatmessage);
02797         }
02798         else {
02799             switch(sendto) {
02800                 case CHAT_TEAM:
02801                     EA_Command(cs->client, va("say_team %s", cs->chatmessage));
02802                     break;
02803                 case CHAT_TELL:
02804                     EA_Command(cs->client, va("tell %d %s", clientto, cs->chatmessage));
02805                     break;
02806                 default: //CHAT_ALL
02807                     EA_Command(cs->client, va("say %s", cs->chatmessage));
02808                     break;
02809             }
02810         }
02811         //clear the chat message from the state
02812         strcpy(cs->chatmessage, "");
02813     } //end if
02814 } //end of the function BotEnterChat
02815 //===========================================================================
02816 //
02817 // Parameter:           -
02818 // Returns:             -
02819 // Changes Globals:     -
02820 //===========================================================================
02821 void BotGetChatMessage(int chatstate, char *buf, int size)
02822 {
02823     bot_chatstate_t *cs;
02824 
02825     cs = BotChatStateFromHandle(chatstate);
02826     if (!cs) return;
02827 
02828     BotRemoveTildes(cs->chatmessage);
02829     strncpy(buf, cs->chatmessage, size-1);
02830     buf[size-1] = '\0';
02831     //clear the chat message from the state
02832     strcpy(cs->chatmessage, "");
02833 } //end of the function BotGetChatMessage
02834 //===========================================================================
02835 //
02836 // Parameter:           -
02837 // Returns:             -
02838 // Changes Globals:     -
02839 //===========================================================================
02840 void BotSetChatGender(int chatstate, int gender)
02841 {
02842     bot_chatstate_t *cs;
02843 
02844     cs = BotChatStateFromHandle(chatstate);
02845     if (!cs) return;
02846     switch(gender)
02847     {
02848         case CHAT_GENDERFEMALE: cs->gender = CHAT_GENDERFEMALE; break;
02849         case CHAT_GENDERMALE: cs->gender = CHAT_GENDERMALE; break;
02850         default: cs->gender = CHAT_GENDERLESS; break;
02851     } //end switch
02852 } //end of the function BotSetChatGender
02853 //===========================================================================
02854 //
02855 // Parameter:               -
02856 // Returns:                 -
02857 // Changes Globals:     -
02858 //===========================================================================
02859 void BotSetChatName(int chatstate, char *name, int client)
02860 {
02861     bot_chatstate_t *cs;
02862 
02863     cs = BotChatStateFromHandle(chatstate);
02864     if (!cs) return;
02865     cs->client = client;
02866     Com_Memset(cs->name, 0, sizeof(cs->name));
02867     strncpy(cs->name, name, sizeof(cs->name));
02868     cs->name[sizeof(cs->name)-1] = '\0';
02869 } //end of the function BotSetChatName
02870 //===========================================================================
02871 //
02872 // Parameter:               -
02873 // Returns:                 -
02874 // Changes Globals:     -
02875 //===========================================================================
02876 void BotResetChatAI(void)
02877 {
02878     bot_replychat_t *rchat;
02879     bot_chatmessage_t *m;
02880 
02881     for (rchat = replychats; rchat; rchat = rchat->next)
02882     {
02883         for (m = rchat->firstchatmessage; m; m = m->next)
02884         {
02885             m->time = 0;
02886         } //end for
02887     } //end for
02888 } //end of the function BotResetChatAI
02889 //========================================================================
02890 //
02891 // Parameter:               -
02892 // Returns:                 -
02893 // Changes Globals:     -
02894 //========================================================================
02895 int BotAllocChatState(void)
02896 {
02897     int i;
02898 
02899     for (i = 1; i <= MAX_CLIENTS; i++)
02900     {
02901         if (!botchatstates[i])
02902         {
02903             botchatstates[i] = GetClearedMemory(sizeof(bot_chatstate_t));
02904             return i;
02905         } //end if
02906     } //end for
02907     return 0;
02908 } //end of the function BotAllocChatState
02909 //========================================================================
02910 //
02911 // Parameter:               -
02912 // Returns:                 -
02913 // Changes Globals:     -
02914 //========================================================================
02915 void BotFreeChatState(int handle)
02916 {
02917     bot_chatstate_t *cs;
02918     bot_consolemessage_t m;
02919     int h;
02920 
02921     if (handle <= 0 || handle > MAX_CLIENTS)
02922     {
02923         botimport.Print(PRT_FATAL, "chat state handle %d out of range\n", handle);
02924         return;
02925     } //end if
02926     if (!botchatstates[handle])
02927     {
02928         botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle);
02929         return;
02930     } //end if
02931     cs = botchatstates[handle];
02932     if (LibVarGetValue("bot_reloadcharacters"))
02933     {
02934         BotFreeChatFile(handle);
02935     } //end if
02936     //free all the console messages left in the chat state
02937     for (h = BotNextConsoleMessage(handle, &m); h; h = BotNextConsoleMessage(handle, &m))
02938     {
02939         //remove the console message
02940         BotRemoveConsoleMessage(handle, h);
02941     } //end for
02942     FreeMemory(botchatstates[handle]);
02943     botchatstates[handle] = NULL;
02944 } //end of the function BotFreeChatState
02945 //===========================================================================
02946 //
02947 // Parameter:               -
02948 // Returns:                 -
02949 // Changes Globals:     -
02950 //===========================================================================
02951 int BotSetupChatAI(void)
02952 {
02953     char *file;
02954 
02955 #ifdef DEBUG
02956     int starttime = Sys_MilliSeconds();
02957 #endif //DEBUG
02958 
02959     file = LibVarString("synfile", "syn.c");
02960     synonyms = BotLoadSynonyms(file);
02961     file = LibVarString("rndfile", "rnd.c");
02962     randomstrings = BotLoadRandomStrings(file);
02963     file = LibVarString("matchfile", "match.c");
02964     matchtemplates = BotLoadMatchTemplates(file);
02965     //
02966     if (!LibVarValue("nochat", "0"))
02967     {
02968         file = LibVarString("rchatfile", "rchat.c");
02969         replychats = BotLoadReplyChat(file);
02970     } //end if
02971 
02972     InitConsoleMessageHeap();
02973 
02974 #ifdef DEBUG
02975     botimport.Print(PRT_MESSAGE, "setup chat AI %d msec\n", Sys_MilliSeconds() - starttime);
02976 #endif //DEBUG
02977     return BLERR_NOERROR;
02978 } //end of the function BotSetupChatAI
02979 //===========================================================================
02980 //
02981 // Parameter:               -
02982 // Returns:                 -
02983 // Changes Globals:     -
02984 //===========================================================================
02985 void BotShutdownChatAI(void)
02986 {
02987     int i;
02988 
02989     //free all remaining chat states
02990     for(i = 0; i < MAX_CLIENTS; i++)
02991     {
02992         if (botchatstates[i])
02993         {
02994             BotFreeChatState(i);
02995         } //end if
02996     } //end for
02997     //free all cached chats
02998     for(i = 0; i < MAX_CLIENTS; i++)
02999     {
03000         if (ichatdata[i])
03001         {
03002             FreeMemory(ichatdata[i]->chat);
03003             FreeMemory(ichatdata[i]);
03004             ichatdata[i] = NULL;
03005         } //end if
03006     } //end for
03007     if (consolemessageheap) FreeMemory(consolemessageheap);
03008     consolemessageheap = NULL;
03009     if (matchtemplates) BotFreeMatchTemplates(matchtemplates);
03010     matchtemplates = NULL;
03011     if (randomstrings) FreeMemory(randomstrings);
03012     randomstrings = NULL;
03013     if (synonyms) FreeMemory(synonyms);
03014     synonyms = NULL;
03015     if (replychats) BotFreeReplyChat(replychats);
03016     replychats = NULL;
03017 } //end of the function BotShutdownChatAI

Generated on Thu Aug 25 12:37:11 2005 for Quake III Arena by  doxygen 1.3.9.1