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