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(