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(