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