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