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