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