00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
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
00050 #define ESCAPE_CHAR 0x01 //'_'
00051
00052
00053
00054
00055
00056
00057
00058 #define MT_VARIABLE 1 //variable match piece
00059 #define MT_STRING 2 //string match piece
00060
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
00071 #define CHATMESSAGE_RECENTTIME 20
00072
00073
00074 typedef struct bot_chatmessage_s
00075 {
00076 char *chatmessage;
00077 float time;
00078 struct bot_chatmessage_s *next;
00079 } bot_chatmessage_t;
00080
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
00089 typedef struct bot_chat_s
00090 {
00091 bot_chattype_t *types;
00092 } bot_chat_t;
00093
00094
00095 typedef struct bot_randomstring_s
00096 {
00097 char *string;
00098 struct bot_randomstring_s *next;
00099 } bot_randomstring_t;
00100
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
00110 typedef struct bot_synonym_s
00111 {
00112 char *string;
00113 float weight;
00114 struct bot_synonym_s *next;
00115 } bot_synonym_t;
00116
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
00126 typedef struct bot_matchstring_s
00127 {
00128 char *string;
00129 struct bot_matchstring_s *next;
00130 } bot_matchstring_t;
00131
00132
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
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
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
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
00169 typedef struct bot_stringlist_s
00170 {
00171 char *string;
00172 struct bot_stringlist_s *next;
00173 } bot_stringlist_t;
00174
00175
00176 typedef struct bot_chatstate_s
00177 {
00178 int gender;
00179 int client;
00180 char name[32];
00181 char chatmessage[MAX_MESSAGE_SIZE];
00182 int handle;
00183
00184 bot_consolemessage_t *firstmessage;
00185 bot_consolemessage_t *lastmessage;
00186
00187 int numconsolemessages;
00188
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
00202 bot_consolemessage_t *consolemessageheap = NULL;
00203 bot_consolemessage_t *freeconsolemessages = NULL;
00204
00205 bot_matchtemplate_t *matchtemplates = NULL;
00206
00207 bot_synonymlist_t *synonyms = NULL;
00208
00209 bot_randomlist_t *randomstrings = NULL;
00210
00211 bot_replychat_t *replychats = NULL;
00212
00213
00214
00215
00216
00217
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 }
00226 if (!botchatstates[handle])
00227 {
00228 botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle);
00229 return NULL;
00230 }
00231 return botchatstates[handle];
00232 }
00233
00234
00235
00236
00237
00238
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 }
00256 consolemessageheap[max_messages-1].prev = &consolemessageheap[max_messages-2];
00257 consolemessageheap[max_messages-1].next = NULL;
00258
00259 freeconsolemessages = consolemessageheap;
00260 }
00261
00262
00263
00264
00265
00266
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 }
00276
00277
00278
00279
00280
00281
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 }
00290
00291
00292
00293
00294
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 }
00318 }
00319 }
00320
00321
00322
00323
00324
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 }
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 }
00353 else
00354 {
00355 cs->lastmessage = m;
00356 cs->firstmessage = m;
00357 m->prev = NULL;
00358 }
00359 cs->numconsolemessages++;
00360 }
00361
00362
00363
00364
00365
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 }
00379 return 0;
00380 }
00381
00382
00383
00384
00385
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 }
00395
00396
00397
00398
00399
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 }
00415
00416
00417
00418
00419
00420
00421 void BotRemoveTildes(char *message)
00422 {
00423 int i;
00424
00425
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 }
00432 }
00433 }
00434
00435
00436
00437
00438
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
00450
00451 if (oldptr > string && *ptr) *oldptr++ = ' ';
00452
00453 if (ptr > oldptr) memmove(oldptr, ptr, strlen(ptr)+1);
00454 }
00455 while(*ptr && !IsWhiteSpace(*ptr)) ptr++;
00456 }
00457 }
00458
00459
00460
00461
00462
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 }
00480 else
00481 {
00482 if (toupper(str1[j]) != toupper(str2[j])) break;
00483 }
00484 }
00485 if (!str2[j]) return index;
00486 }
00487 return -1;
00488 }
00489
00490
00491
00492
00493
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
00503 if (i)
00504 {
00505
00506 while(*str1 && *str1 != ' ' && *str1 != '.' && *str1 != ',' && *str1 != '!') str1++;
00507 if (!*str1) break;
00508 str1++;
00509 }
00510
00511 for (j = 0; str2[j]; j++)
00512 {
00513 if (casesensitive)
00514 {
00515 if (str1[j] != str2[j]) break;
00516 }
00517 else
00518 {
00519 if (toupper(str1[j]) != toupper(str2[j])) break;
00520 }
00521 }
00522
00523 if (!str2[j])
00524 {
00525
00526 if (!str1[j] || str1[j] == ' ' || str1[j] == '.' || str1[j] == ',' || str1[j] == '!') return str1;
00527 }
00528 }
00529 return NULL;
00530 }
00531
00532
00533
00534
00535
00536
00537 void StringReplaceWords(char *string, char *synonym, char *replacement)
00538 {
00539 char *str, *str2;
00540
00541
00542 str = StringContainsWord(string, synonym, qfalse);
00543
00544 while(str)
00545 {
00546
00547
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 }
00554 if (!str2)
00555 {
00556 memmove(str + strlen(replacement), str+strlen(synonym), strlen(str+strlen(synonym))+1);
00557
00558 Com_Memcpy(str, replacement, strlen(replacement));
00559 }
00560
00561 str = StringContainsWord(str+strlen(replacement), synonym, qfalse);
00562 }
00563 }
00564
00565
00566
00567
00568
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 }
00586 fprintf(fp, "]\n");
00587 }
00588 }
00589
00590
00591
00592
00593
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;
00607 syn = NULL;
00608 synonym = NULL;
00609
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 }
00622
00623 context = 0;
00624 contextlevel = 0;
00625 synlist = NULL;
00626 lastsyn = NULL;
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 }
00641 if (!PC_ExpectTokenString(source, "{"))
00642 {
00643 FreeSource(source);
00644 return NULL;
00645 }
00646 }
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 }
00658 context &= ~contextstack[contextlevel];
00659 }
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 }
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 }
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 }
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 }
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 }
00712 if (pass)
00713 {
00714 synonym->weight = token.floatvalue;
00715 syn->totalweight += synonym->weight;
00716 }
00717 if (PC_CheckTokenString(source, "]")) break;
00718 if (!PC_ExpectTokenString(source, ","))
00719 {
00720 FreeSource(source);
00721 return NULL;
00722 }
00723 }
00724 if (numsynonyms < 2)
00725 {
00726 SourceError(source, "synonym must have at least two entries\n");
00727 FreeSource(source);
00728 return NULL;
00729 }
00730 }
00731 else
00732 {
00733 SourceError(source, "unexpected %s", token.string);
00734 FreeSource(source);
00735 return NULL;
00736 }
00737 }
00738 }
00739
00740 FreeSource(source);
00741
00742 if (contextlevel > 0)
00743 {
00744 SourceError(source, "missing }");
00745 return NULL;
00746 }
00747 }
00748 botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
00749
00750
00751
00752 return synlist;
00753 }
00754
00755
00756
00757
00758
00759
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 }
00773 }
00774 }
00775
00776
00777
00778
00779
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
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 }
00799 if (!replacement) continue;
00800
00801 for (synonym = syn->firstsynonym; synonym; synonym = synonym->next)
00802 {
00803 if (synonym == replacement) continue;
00804 StringReplaceWords(string, synonym->string, replacement->string);
00805 }
00806 }
00807 }
00808
00809
00810
00811
00812
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
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
00833 str2 = StringContainsWord(str1, synonym->string, qfalse);
00834 if (!str2 || str2 != str1) continue;
00835
00836 replacement = syn->firstsynonym->string;
00837
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
00844 Com_Memcpy(str1, replacement, strlen(replacement));
00845
00846 break;
00847 }
00848
00849 if (synonym) break;
00850 }
00851
00852 while(*str1 && *str1 > ' ') str1++;
00853 if (!*str1) break;
00854 }
00855 }
00856
00857
00858
00859
00860
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
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 }
00882 strcat(ptr, token.string);
00883 }
00884
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 }
00892 sprintf(&ptr[strlen(ptr)], "%cv%ld%c", ESCAPE_CHAR, token.intvalue, ESCAPE_CHAR);
00893 }
00894
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 }
00902 sprintf(&ptr[strlen(ptr)], "%cr%s%c", ESCAPE_CHAR, token.string, ESCAPE_CHAR);
00903 }
00904 else
00905 {
00906 SourceError(source, "unknown message component %s\n", token.string);
00907 return qfalse;
00908 }
00909 if (PC_CheckTokenString(source, ";")) break;
00910 if (!PC_ExpectTokenString(source, ",")) return qfalse;
00911 }
00912
00913 return qtrue;
00914 }
00915
00916
00917
00918
00919
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 }
00938 }
00939 }
00940
00941
00942
00943
00944
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
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 }
00975
00976 randomlist = NULL;
00977 lastrandom = NULL;
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 }
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 }
01002 if (!PC_ExpectTokenString(source, "=") ||
01003 !PC_ExpectTokenString(source, "{"))
01004 {
01005 FreeSource(source);
01006 return NULL;
01007 }
01008 while(!PC_CheckTokenString(source, "}"))
01009 {
01010 if (!BotLoadChatMessage(source, chatmessagestring))
01011 {
01012 FreeSource(source);
01013 return NULL;
01014 }
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 }
01028 }
01029 }
01030
01031 FreeSource(source);
01032 }
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
01038 #endif //DEBUG
01039
01040 return randomlist;
01041 }
01042
01043
01044
01045
01046
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 }
01063 if (rs)
01064 {
01065 return rs->string;
01066 }
01067 }
01068 }
01069 return NULL;
01070 }
01071
01072
01073
01074
01075
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 }
01098 }
01099 else if (mp->type == MT_VARIABLE)
01100 {
01101 fprintf(fp, "%d", mp->variable);
01102 }
01103 if (mp->next) fprintf(fp, ", ");
01104 }
01105 fprintf(fp, " = (%d, %d);}\n", mt->type, mt->subtype);
01106 }
01107 }
01108
01109
01110
01111
01112
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 }
01129 }
01130 FreeMemory(mp);
01131 }
01132 }
01133
01134
01135
01136
01137
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 }
01162 if (lastwasvariable)
01163 {
01164 SourceError(source, "not allowed to have adjacent variables\n");
01165 FreeSource(source);
01166 BotFreeMatchPieces(firstpiece);
01167 return NULL;
01168 }
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 }
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 }
01204 }
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
01216 if (!emptystring) lastwasvariable = qfalse;
01217 }
01218 else
01219 {
01220 SourceError(source, "invalid token %s\n", token.string);
01221 FreeSource(source);
01222 BotFreeMatchPieces(firstpiece);
01223 return NULL;
01224 }
01225 if (PC_CheckTokenString(source, endtoken)) break;
01226 if (!PC_ExpectTokenString(source, ","))
01227 {
01228 FreeSource(source);
01229 BotFreeMatchPieces(firstpiece);
01230 return NULL;
01231 }
01232 }
01233 return firstpiece;
01234 }
01235
01236
01237
01238
01239
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 }
01251 }
01252
01253
01254
01255
01256
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 }
01272
01273 matches = NULL;
01274 lastmatch = NULL;
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 }
01285
01286 context = token.intvalue;
01287
01288 if (!PC_ExpectTokenString(source, "{"))
01289 {
01290 BotFreeMatchTemplates(matches);
01291 FreeSource(source);
01292 return NULL;
01293 }
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
01305 if (lastmatch) lastmatch->next = matchtemplate;
01306 else matches = matchtemplate;
01307 lastmatch = matchtemplate;
01308
01309 matchtemplate->first = BotLoadMatchPieces(source, "=");
01310 if (!matchtemplate->first)
01311 {
01312 BotFreeMatchTemplates(matches);
01313 return NULL;
01314 }
01315
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 }
01323 matchtemplate->type = token.intvalue;
01324
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 }
01332 matchtemplate->subtype = token.intvalue;
01333
01334 if (!PC_ExpectTokenString(source, ")") ||
01335 !PC_ExpectTokenString(source, ";"))
01336 {
01337 BotFreeMatchTemplates(matches);
01338 FreeSource(source);
01339 return NULL;
01340 }
01341 }
01342 }
01343
01344 FreeSource(source);
01345 botimport.Print(PRT_MESSAGE, "loaded %s\n", matchfile);
01346
01347
01348
01349 return matches;
01350 }
01351
01352
01353
01354
01355
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
01365 lastvariable = -1;
01366
01367 strptr = match->string;
01368
01369
01370 for (mp = pieces; mp; mp = mp->next)
01371 {
01372
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 }
01383
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
01393 lastvariable = -1;
01394 break;
01395 }
01396 else if (index == 0)
01397 {
01398 break;
01399 }
01400 newstrptr = NULL;
01401 }
01402 }
01403 if (!newstrptr) return qfalse;
01404 strptr = newstrptr + strlen(ms->string);
01405 }
01406
01407 else if (mp->type == MT_VARIABLE)
01408 {
01409
01410 match->variables[mp->variable].offset = strptr - match->string;
01411 lastvariable = mp->variable;
01412 }
01413 }
01414
01415 if (!mp && (lastvariable >= 0 || !strlen(strptr)))
01416 {
01417
01418 if (lastvariable >= 0)
01419 {
01420 assert( match->variables[lastvariable].offset >= 0 );
01421 match->variables[lastvariable].length =
01422 strlen(&match->string[ (int) match->variables[lastvariable].offset]);
01423 }
01424 return qtrue;
01425 }
01426 return qfalse;
01427 }
01428
01429
01430
01431
01432
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
01441 while(strlen(match->string) &&
01442 match->string[strlen(match->string)-1] == '\n')
01443 {
01444 match->string[strlen(match->string)-1] = '\0';
01445 }
01446
01447 for (ms = matchtemplates; ms; ms = ms->next)
01448 {
01449 if (!(ms->context & context)) continue;
01450
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 }
01459 }
01460 return qfalse;
01461 }
01462
01463
01464
01465
01466
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 }
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 );
01482 strncpy(buf, &match->string[ (int) match->variables[variable].offset], size-1);
01483 buf[size-1] = '\0';
01484 }
01485 else
01486 {
01487 strcpy(buf, "");
01488 }
01489 return;
01490 }
01491
01492
01493
01494
01495
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 }
01505 return NULL;
01506 }
01507
01508
01509
01510
01511
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':
01530 {
01531
01532 msgptr++;
01533 while(*msgptr && *msgptr != ESCAPE_CHAR) msgptr++;
01534
01535 if (*msgptr) msgptr++;
01536 break;
01537 }
01538 case 'r':
01539 {
01540
01541 msgptr++;
01542 for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++)
01543 {
01544 temp[i] = *msgptr++;
01545 }
01546 temp[i] = '\0';
01547
01548 if (*msgptr) msgptr++;
01549
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 }
01561 }
01562 break;
01563 }
01564 default:
01565 {
01566 botimport.Print(PRT_FATAL, "BotCheckChatMessageIntegrety: message \"%s\" invalid escape char\n", message);
01567 break;
01568 }
01569 }
01570 }
01571 else
01572 {
01573 msgptr++;
01574 }
01575 }
01576 return stringlist;
01577 }
01578
01579
01580
01581
01582
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 }
01597 }
01598 for (s = stringlist; s; s = nexts)
01599 {
01600 nexts = s->next;
01601 FreeMemory(s);
01602 }
01603 }
01604
01605
01606
01607
01608
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 }
01623 }
01624 for (s = stringlist; s; s = nexts)
01625 {
01626 nexts = s->next;
01627 FreeMemory(s);
01628 }
01629 }
01630
01631
01632
01633
01634
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 }
01668 fprintf(fp, ")");
01669 }
01670 else if (key->flags & RCKFL_STRING)
01671 {
01672 fprintf(fp, "\"%s\"", key->string);
01673 }
01674 if (key->next) fprintf(fp, ", ");
01675 else fprintf(fp, "] = %1.0f\n", rp->priority);
01676 }
01677 fprintf(fp, "{\n");
01678 for (cm = rp->firstchatmessage; cm; cm = cm->next)
01679 {
01680 fprintf(fp, "\t\"%s\";\n", cm->chatmessage);
01681 }
01682 fprintf(fp, "}\n");
01683 }
01684 }
01685
01686
01687
01688
01689
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 }
01707 for (cm = rp->firstchatmessage; cm; cm = nextcm)
01708 {
01709 nextcm = cm->next;
01710 FreeMemory(cm);
01711 }
01712 FreeMemory(rp);
01713 }
01714 }
01715
01716
01717
01718
01719
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 }
01742 }
01743 else if (key->flags & RCKFL_STRING)
01744 {
01745 hasstringkey = qtrue;
01746 }
01747 }
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 }
01766 }
01767 if (ms) break;
01768 }
01769 else if (m->type == MT_VARIABLE)
01770 {
01771 break;
01772 }
01773 }
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 }
01779 }
01780 }
01781 }
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 }
01794 }
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 }
01808 }
01809 }
01810 }
01811 }
01812 }
01813 }
01814 }
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 }
01821 }
01822
01823
01824
01825
01826
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 }
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 }
01857
01858 replychat = GetClearedHunkMemory(sizeof(bot_replychat_t));
01859 replychat->keys = NULL;
01860 replychat->next = replychatlist;
01861 replychatlist = replychat;
01862
01863 do
01864 {
01865
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
01873 if (PC_CheckTokenString(source, "&")) key->flags |= RCKFL_AND;
01874 else if (PC_CheckTokenString(source, "!")) key->flags |= RCKFL_NOT;
01875
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, "("))
01881 {
01882 key->flags |= RCKFL_VARIABLES;
01883 key->match = BotLoadMatchPieces(source, ")");
01884 if (!key->match)
01885 {
01886 BotFreeReplyChat(replychatlist);
01887 return NULL;
01888 }
01889 }
01890 else if (PC_CheckTokenString(source, "<"))
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 }
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 }
01912 key->string = (char *) GetClearedHunkMemory(strlen(namebuffer) + 1);
01913 strcpy(key->string, namebuffer);
01914 }
01915 else
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 }
01924 StripDoubleQuotes(token.string);
01925 key->string = (char *) GetClearedHunkMemory(strlen(token.string) + 1);
01926 strcpy(key->string, token.string);
01927 }
01928
01929 PC_CheckTokenString(source, ",");
01930 } while(!PC_CheckTokenString(source, "]"));
01931
01932 BotCheckValidReplyChatKeySet(source, replychat->keys);
01933
01934 if (!PC_ExpectTokenString(source, "=") ||
01935 !PC_ExpectTokenType(source, TT_NUMBER, 0, &token))
01936 {
01937 BotFreeReplyChat(replychatlist);
01938 FreeSource(source);
01939 return NULL;
01940 }
01941 replychat->priority = token.floatvalue;
01942
01943 if (!PC_ExpectTokenString(source, "{"))
01944 {
01945 BotFreeReplyChat(replychatlist);
01946 FreeSource(source);
01947 return NULL;
01948 }
01949 replychat->numchatmessages = 0;
01950
01951 while(!PC_CheckTokenString(source, "}"))
01952 {
01953 if (!BotLoadChatMessage(source, chatmessagestring))
01954 {
01955 BotFreeReplyChat(replychatlist);
01956 FreeSource(source);
01957 return NULL;
01958 }
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
01965 replychat->firstchatmessage = chatmessage;
01966 replychat->numchatmessages++;
01967 }
01968 }
01969 FreeSource(source);
01970 botimport.Print(PRT_MESSAGE, "loaded %s\n", filename);
01971
01972
01973 if (bot_developer)
01974 {
01975 BotCheckReplyChatIntegrety(replychatlist);
01976 }
01977
01978 if (!replychatlist) botimport.Print(PRT_MESSAGE, "no rchats\n");
01979
01980 return replychatlist;
01981 }
01982
01983
01984
01985
01986
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 }
02003 Log_Write(" }");
02004 }
02005 Log_Write("}");
02006 }
02007
02008
02009
02010
02011
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
02032 for (pass = 0; pass < 2; pass++)
02033 {
02034
02035 if (pass && size) ptr = (char *) GetClearedMemory(size);
02036
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 }
02044
02045 if (pass)
02046 {
02047 chat = (bot_chat_t *) ptr;
02048 ptr += sizeof(bot_chat_t);
02049 }
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 }
02061 StripDoubleQuotes(token.string);
02062
02063 if (!PC_ExpectTokenString(source, "{"))
02064 {
02065 FreeSource(source);
02066 return NULL;
02067 }
02068
02069 if (!Q_stricmp(token.string, chatname))
02070 {
02071 foundchat = qtrue;
02072
02073 while(1)
02074 {
02075 if (!PC_ExpectAnyToken(source, &token))
02076 {
02077 FreeSource(source);
02078 return NULL;
02079 }
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 }
02087
02088 if (!PC_ExpectTokenType(source, TT_STRING, 0, &token) ||
02089 !PC_ExpectTokenString(source, "{"))
02090 {
02091 FreeSource(source);
02092 return NULL;
02093 }
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
02101 chattype->next = chat->types;
02102 chat->types = chattype;
02103
02104 ptr += sizeof(bot_chattype_t);
02105 }
02106 size += sizeof(bot_chattype_t);
02107
02108 while(!PC_CheckTokenString(source, "}"))
02109 {
02110 if (!BotLoadChatMessage(source, chatmessagestring))
02111 {
02112 FreeSource(source);
02113 return NULL;
02114 }
02115 if (pass)
02116 {
02117 chatmessage = (bot_chatmessage_t *) ptr;
02118 chatmessage->time = -2*CHATMESSAGE_RECENTTIME;
02119
02120 chatmessage->next = chattype->firstchatmessage;
02121 chattype->firstchatmessage = chatmessage;
02122
02123 ptr += sizeof(bot_chatmessage_t);
02124 chatmessage->chatmessage = ptr;
02125 strcpy(chatmessage->chatmessage, chatmessagestring);
02126 ptr += strlen(chatmessagestring) + 1;
02127
02128 chattype->numchatmessages++;
02129 }
02130 size += sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1;
02131 }
02132 }
02133 }
02134 else
02135 {
02136 indent = 1;
02137 while(indent)
02138 {
02139 if (!PC_ExpectAnyToken(source, &token))
02140 {
02141 FreeSource(source);
02142 return NULL;
02143 }
02144 if (!strcmp(token.string, "{")) indent++;
02145 else if (!strcmp(token.string, "}")) indent--;
02146 }
02147 }
02148 }
02149 else
02150 {
02151 SourceError(source, "unknown definition %s\n", token.string);
02152 FreeSource(source);
02153 return NULL;
02154 }
02155 }
02156
02157 FreeSource(source);
02158
02159 if (!foundchat)
02160 {
02161 botimport.Print(PRT_ERROR, "couldn't find chat %s in %s\n", chatname, chatfile);
02162 return NULL;
02163 }
02164 }
02165
02166 botimport.Print(PRT_MESSAGE, "loaded %s from %s\n", chatname, chatfile);
02167
02168
02169 if (bot_developer)
02170 {
02171 BotCheckInitialChatIntegrety(chat);
02172 }
02173 #ifdef DEBUG
02174 botimport.Print(PRT_MESSAGE, "initial chats loaded in %d msec\n", Sys_MilliSeconds() - starttime);
02175 #endif //DEBUG
02176
02177 return chat;
02178 }
02179
02180
02181
02182
02183
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 }
02194
02195
02196
02197
02198
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
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 }
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 }
02249
02250 return BLERR_NOERROR;
02251 }
02252
02253
02254
02255
02256
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':
02278 {
02279 msgptr++;
02280 num = 0;
02281 while(*msgptr && *msgptr != ESCAPE_CHAR)
02282 {
02283 num = num * 10 + (*msgptr++) - '0';
02284 }
02285
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 }
02292 if (match->variables[num].offset >= 0)
02293 {
02294 assert( match->variables[num].offset >= 0 );
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 }
02300 temp[i] = 0;
02301
02302 if (reply)
02303 {
02304
02305 BotReplaceReplySynonyms(temp, vcontext);
02306 }
02307 else
02308 {
02309
02310 BotReplaceSynonyms(temp, vcontext);
02311 }
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 }
02318 strcpy(&outputbuf[len], temp);
02319 len += strlen(temp);
02320 }
02321 break;
02322 }
02323 case 'r':
02324 {
02325 msgptr++;
02326 for (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++)
02327 {
02328 temp[i] = *msgptr++;
02329 }
02330 temp[i] = '\0';
02331
02332 if (*msgptr) msgptr++;
02333
02334 ptr = RandomString(temp);
02335 if (!ptr)
02336 {
02337 botimport.Print(PRT_ERROR, "BotConstructChat: unknown random string %s\n", temp);
02338 return qfalse;
02339 }
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 }
02345 strcpy(&outputbuf[len], ptr);
02346 len += strlen(ptr);
02347 expansion = qtrue;
02348 break;
02349 }
02350 default:
02351 {
02352 botimport.Print(PRT_FATAL, "BotConstructChat: message \"%s\" invalid escape char\n", message);
02353 break;
02354 }
02355 }
02356 }
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 }
02365 }
02366 }
02367 outputbuf[len] = '\0';
02368
02369 BotReplaceWeightedSynonyms(outputbuf, mcontext);
02370
02371 return expansion;
02372 }
02373
02374
02375
02376
02377
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 }
02392 strcpy(srcmessage, chatstate->chatmessage);
02393 }
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 }
02399 }
02400
02401
02402
02403
02404
02405
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 }
02426
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 }
02438 }
02439 if (bestchatmessage) return bestchatmessage->chatmessage;
02440 }
02441 else
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 }
02452 }
02453 }
02454 return NULL;
02455 }
02456 }
02457 return NULL;
02458 }
02459
02460
02461
02462
02463
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 }
02483 }
02484 return 0;
02485 }
02486
02487
02488
02489
02490
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
02502 if (!cs->chat) return;
02503
02504 message = BotChooseInitialChatMessage(cs, type);
02505
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 }
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 }
02567
02568
02569
02570
02571
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 }
02597 botimport.Print(PRT_MESSAGE, ")");
02598 }
02599 else if (key->flags & RCKFL_STRING)
02600 {
02601 botimport.Print(PRT_MESSAGE, "\"%s\"", key->string);
02602 }
02603 if (key->next) botimport.Print(PRT_MESSAGE, ", ");
02604 else botimport.Print(PRT_MESSAGE, "] = %1.0f\n", replychat->priority);
02605 }
02606 botimport.Print(PRT_MESSAGE, "{\n");
02607 }
02608
02609
02610
02611
02612
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
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
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
02646 if (key->flags & RCKFL_AND)
02647 {
02648 if (!res)
02649 {
02650 found = qfalse;
02651 break;
02652 }
02653 }
02654
02655 else if (key->flags & RCKFL_NOT)
02656 {
02657 if (res)
02658 {
02659 found = qfalse;
02660 break;
02661 }
02662 }
02663 else if (res)
02664 {
02665 found = qtrue;
02666 }
02667 }
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 }
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 }
02685
02686 if (m)
02687 {
02688 Com_Memcpy(&bestmatch, &match, sizeof(bot_match_t));
02689 bestchatmessage = m;
02690 bestrchat = rchat;
02691 bestpriority = rchat->priority;
02692 }
02693 }
02694 }
02695 }
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 }
02755 }
02756 else
02757 {
02758 bestchatmessage->time = AAS_Time() + CHATMESSAGE_RECENTTIME;
02759 BotConstructChatMessage(cs, bestchatmessage->chatmessage, mcontext, &bestmatch, vcontext, qtrue);
02760 }
02761 return qtrue;
02762 }
02763 return qfalse;
02764 }
02765
02766
02767
02768
02769
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 }
02779
02780
02781
02782
02783
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:
02807 EA_Command(cs->client, va("say %s", cs->chatmessage));
02808 break;
02809 }
02810 }
02811
02812 strcpy(cs->chatmessage, "");
02813 }
02814 }
02815
02816
02817
02818
02819
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
02832 strcpy(cs->chatmessage, "");
02833 }
02834
02835
02836
02837
02838
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 }
02852 }
02853
02854
02855
02856
02857
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 }
02870
02871
02872
02873
02874
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 }
02887 }
02888 }
02889
02890
02891
02892
02893
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 }
02906 }
02907 return 0;
02908 }
02909
02910
02911
02912
02913
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 }
02926 if (!botchatstates[handle])
02927 {
02928 botimport.Print(PRT_FATAL, "invalid chat state %d\n", handle);
02929 return;
02930 }
02931 cs = botchatstates[handle];
02932 if (LibVarGetValue("bot_reloadcharacters"))
02933 {
02934 BotFreeChatFile(handle);
02935 }
02936
02937 for (h = BotNextConsoleMessage(handle, &m); h; h = BotNextConsoleMessage(handle, &m))
02938 {
02939
02940 BotRemoveConsoleMessage(handle, h);
02941 }
02942 FreeMemory(botchatstates[handle]);
02943 botchatstates[handle] = NULL;
02944 }
02945
02946
02947
02948
02949
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 }
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 }
02979
02980
02981
02982
02983
02984
02985 void BotShutdownChatAI(void)
02986 {
02987 int i;
02988
02989
02990 for(i = 0; i < MAX_CLIENTS; i++)
02991 {
02992 if (botchatstates[i])
02993 {
02994 BotFreeChatState(i);
02995 }
02996 }
02997
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 }
03006 }
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 }