1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22
23 /*****************************************************************************
24 * name: be_ai_char.c
25 *
26 * desc: bot characters
27 *
28 * $Archive: /MissionPack/code/botlib/be_ai_char.c $
29 *
30 *****************************************************************************/
31
32 #include "../qcommon/q_shared.h"
33 #include "l_log.h"
34 #include "l_memory.h"
35 #include "l_utils.h"
36 #include "l_script.h"
37 #include "l_precomp.h"
38 #include "l_struct.h"
39 #include "l_libvar.h"
40 #include "aasfile.h"
41 #include "botlib.h"
42 #include "be_aas.h"
43 #include "be_aas_funcs.h"
44 #include "be_interface.h"
45 #include "be_ai_char.h"
46
47 #define MAX_CHARACTERISTICS 80
48
49 #define CT_INTEGER 1
50 #define CT_FLOAT 2
51 #define CT_STRING 3
52
53 #define DEFAULT_CHARACTER "bots/default_c.c"
54
55 //characteristic value
56 union cvalue
57 {
58 int integer;
59 float _float;
60 char *string;
61 };
62 //a characteristic
63 typedef struct bot_characteristic_s
64 {
65 char type; //characteristic type
66 union cvalue value; //characteristic value
67 } bot_characteristic_t;
68
69 //a bot character
70 typedef struct bot_character_s
71 {
72 char filename[MAX_QPATH];
73 float skill;
74 bot_characteristic_t c[1]; //variable sized
75 } bot_character_t;
76
77 bot_character_t *botcharacters[MAX_CLIENTS + 1];
78
79 //========================================================================
80 //
81 // Parameter: -
82 // Returns: -
83 // Changes Globals: -
84 //========================================================================
BotCharacterFromHandle(int handle)85 bot_character_t *BotCharacterFromHandle(int handle)
86 {
87 if (handle <= 0 || handle > MAX_CLIENTS)
88 {
89 botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle);
90 return NULL;
91 } //end if
92 if (!botcharacters[handle])
93 {
94 botimport.Print(PRT_FATAL, "invalid character %d\n", handle);
95 return NULL;
96 } //end if
97 return botcharacters[handle];
98 } //end of the function BotCharacterFromHandle
99 //===========================================================================
100 //
101 // Parameter: -
102 // Returns: -
103 // Changes Globals: -
104 //===========================================================================
BotDumpCharacter(bot_character_t * ch)105 void BotDumpCharacter(bot_character_t *ch)
106 {
107 int i;
108
109 Log_Write("%s\n", ch->filename);
110 Log_Write("skill %.1f\n", ch->skill);
111 Log_Write("{\n");
112 for (i = 0; i < MAX_CHARACTERISTICS; i++)
113 {
114 switch(ch->c[i].type)
115 {
116 case CT_INTEGER: Log_Write(" %4d %d\n", i, ch->c[i].value.integer); break;
117 case CT_FLOAT: Log_Write(" %4d %f\n", i, ch->c[i].value._float); break;
118 case CT_STRING: Log_Write(" %4d %s\n", i, ch->c[i].value.string); break;
119 } //end case
120 } //end for
121 Log_Write("}\n");
122 } //end of the function BotDumpCharacter
123 //========================================================================
124 //
125 // Parameter: -
126 // Returns: -
127 // Changes Globals: -
128 //========================================================================
BotFreeCharacterStrings(bot_character_t * ch)129 void BotFreeCharacterStrings(bot_character_t *ch)
130 {
131 int i;
132
133 for (i = 0; i < MAX_CHARACTERISTICS; i++)
134 {
135 if (ch->c[i].type == CT_STRING)
136 {
137 FreeMemory(ch->c[i].value.string);
138 } //end if
139 } //end for
140 } //end of the function BotFreeCharacterStrings
141 //========================================================================
142 //
143 // Parameter: -
144 // Returns: -
145 // Changes Globals: -
146 //========================================================================
BotFreeCharacter2(int handle)147 void BotFreeCharacter2(int handle)
148 {
149 if (handle <= 0 || handle > MAX_CLIENTS)
150 {
151 botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle);
152 return;
153 } //end if
154 if (!botcharacters[handle])
155 {
156 botimport.Print(PRT_FATAL, "invalid character %d\n", handle);
157 return;
158 } //end if
159 BotFreeCharacterStrings(botcharacters[handle]);
160 FreeMemory(botcharacters[handle]);
161 botcharacters[handle] = NULL;
162 } //end of the function BotFreeCharacter2
163 //========================================================================
164 //
165 // Parameter: -
166 // Returns: -
167 // Changes Globals: -
168 //========================================================================
BotFreeCharacter(int handle)169 void BotFreeCharacter(int handle)
170 {
171 if (!LibVarGetValue("bot_reloadcharacters")) return;
172 BotFreeCharacter2(handle);
173 } //end of the function BotFreeCharacter
174 //===========================================================================
175 //
176 // Parameter: -
177 // Returns: -
178 // Changes Globals: -
179 //===========================================================================
BotDefaultCharacteristics(bot_character_t * ch,bot_character_t * defaultch)180 void BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch)
181 {
182 int i;
183
184 for (i = 0; i < MAX_CHARACTERISTICS; i++)
185 {
186 if (ch->c[i].type) continue;
187 //
188 if (defaultch->c[i].type == CT_FLOAT)
189 {
190 ch->c[i].type = CT_FLOAT;
191 ch->c[i].value._float = defaultch->c[i].value._float;
192 } //end if
193 else if (defaultch->c[i].type == CT_INTEGER)
194 {
195 ch->c[i].type = CT_INTEGER;
196 ch->c[i].value.integer = defaultch->c[i].value.integer;
197 } //end else if
198 else if (defaultch->c[i].type == CT_STRING)
199 {
200 ch->c[i].type = CT_STRING;
201 ch->c[i].value.string = (char *) GetMemory(strlen(defaultch->c[i].value.string)+1);
202 strcpy(ch->c[i].value.string, defaultch->c[i].value.string);
203 } //end else if
204 } //end for
205 } //end of the function BotDefaultCharacteristics
206 //===========================================================================
207 //
208 // Parameter: -
209 // Returns: -
210 // Changes Globals: -
211 //===========================================================================
BotLoadCharacterFromFile(char * charfile,int skill)212 bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill)
213 {
214 int indent, index, foundcharacter;
215 bot_character_t *ch;
216 source_t *source;
217 token_t token;
218
219 foundcharacter = qfalse;
220 //a bot character is parsed in two phases
221 PC_SetBaseFolder(BOTFILESBASEFOLDER);
222 source = LoadSourceFile(charfile);
223 if (!source)
224 {
225 botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile);
226 return NULL;
227 } //end if
228 ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
229 MAX_CHARACTERISTICS * sizeof(bot_characteristic_t));
230 strcpy(ch->filename, charfile);
231 while(PC_ReadToken(source, &token))
232 {
233 if (!strcmp(token.string, "skill"))
234 {
235 if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token))
236 {
237 FreeSource(source);
238 BotFreeCharacterStrings(ch);
239 FreeMemory(ch);
240 return NULL;
241 } //end if
242 if (!PC_ExpectTokenString(source, "{"))
243 {
244 FreeSource(source);
245 BotFreeCharacterStrings(ch);
246 FreeMemory(ch);
247 return NULL;
248 } //end if
249 //if it's the correct skill
250 if (skill < 0 || token.intvalue == skill)
251 {
252 foundcharacter = qtrue;
253 ch->skill = token.intvalue;
254 while(PC_ExpectAnyToken(source, &token))
255 {
256 if (!strcmp(token.string, "}")) break;
257 if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER))
258 {
259 SourceError(source, "expected integer index, found %s\n", token.string);
260 FreeSource(source);
261 BotFreeCharacterStrings(ch);
262 FreeMemory(ch);
263 return NULL;
264 } //end if
265 index = token.intvalue;
266 if (index < 0 || index > MAX_CHARACTERISTICS)
267 {
268 SourceError(source, "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS);
269 FreeSource(source);
270 BotFreeCharacterStrings(ch);
271 FreeMemory(ch);
272 return NULL;
273 } //end if
274 if (ch->c[index].type)
275 {
276 SourceError(source, "characteristic %d already initialized\n", index);
277 FreeSource(source);
278 BotFreeCharacterStrings(ch);
279 FreeMemory(ch);
280 return NULL;
281 } //end if
282 if (!PC_ExpectAnyToken(source, &token))
283 {
284 FreeSource(source);
285 BotFreeCharacterStrings(ch);
286 FreeMemory(ch);
287 return NULL;
288 } //end if
289 if (token.type == TT_NUMBER)
290 {
291 if (token.subtype & TT_FLOAT)
292 {
293 ch->c[index].value._float = token.floatvalue;
294 ch->c[index].type = CT_FLOAT;
295 } //end if
296 else
297 {
298 ch->c[index].value.integer = token.intvalue;
299 ch->c[index].type = CT_INTEGER;
300 } //end else
301 } //end if
302 else if (token.type == TT_STRING)
303 {
304 StripDoubleQuotes(token.string);
305 ch->c[index].value.string = GetMemory(strlen(token.string)+1);
306 strcpy(ch->c[index].value.string, token.string);
307 ch->c[index].type = CT_STRING;
308 } //end else if
309 else
310 {
311 SourceError(source, "expected integer, float or string, found %s\n", token.string);
312 FreeSource(source);
313 BotFreeCharacterStrings(ch);
314 FreeMemory(ch);
315 return NULL;
316 } //end else
317 } //end if
318 break;
319 } //end if
320 else
321 {
322 indent = 1;
323 while(indent)
324 {
325 if (!PC_ExpectAnyToken(source, &token))
326 {
327 FreeSource(source);
328 BotFreeCharacterStrings(ch);
329 FreeMemory(ch);
330 return NULL;
331 } //end if
332 if (!strcmp(token.string, "{")) indent++;
333 else if (!strcmp(token.string, "}")) indent--;
334 } //end while
335 } //end else
336 } //end if
337 else
338 {
339 SourceError(source, "unknown definition %s\n", token.string);
340 FreeSource(source);
341 BotFreeCharacterStrings(ch);
342 FreeMemory(ch);
343 return NULL;
344 } //end else
345 } //end while
346 FreeSource(source);
347 //
348 if (!foundcharacter)
349 {
350 BotFreeCharacterStrings(ch);
351 FreeMemory(ch);
352 return NULL;
353 } //end if
354 return ch;
355 } //end of the function BotLoadCharacterFromFile
356 //===========================================================================
357 //
358 // Parameter: -
359 // Returns: -
360 // Changes Globals: -
361 //===========================================================================
BotFindCachedCharacter(char * charfile,float skill)362 int BotFindCachedCharacter(char *charfile, float skill)
363 {
364 int handle;
365
366 for (handle = 1; handle <= MAX_CLIENTS; handle++)
367 {
368 if ( !botcharacters[handle] ) continue;
369 if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 &&
370 (skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) )
371 {
372 return handle;
373 } //end if
374 } //end for
375 return 0;
376 } //end of the function BotFindCachedCharacter
377 //===========================================================================
378 //
379 // Parameter: -
380 // Returns: -
381 // Changes Globals: -
382 //===========================================================================
BotLoadCachedCharacter(char * charfile,float skill,int reload)383 int BotLoadCachedCharacter(char *charfile, float skill, int reload)
384 {
385 int handle, cachedhandle, intskill;
386 bot_character_t *ch = NULL;
387 #ifdef DEBUG
388 int starttime;
389
390 starttime = Sys_MilliSeconds();
391 #endif //DEBUG
392
393 //find a free spot for a character
394 for (handle = 1; handle <= MAX_CLIENTS; handle++)
395 {
396 if (!botcharacters[handle]) break;
397 } //end for
398 if (handle > MAX_CLIENTS) return 0;
399 //try to load a cached character with the given skill
400 if (!reload)
401 {
402 cachedhandle = BotFindCachedCharacter(charfile, skill);
403 if (cachedhandle)
404 {
405 botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile);
406 return cachedhandle;
407 } //end if
408 } //end else
409 //
410 intskill = (int) (skill + 0.5);
411 //try to load the character with the given skill
412 ch = BotLoadCharacterFromFile(charfile, intskill);
413 if (ch)
414 {
415 botcharacters[handle] = ch;
416 //
417 botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", intskill, charfile);
418 #ifdef DEBUG
419 if (botDeveloper)
420 {
421 botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", intskill, Sys_MilliSeconds() - starttime, charfile);
422 } //end if
423 #endif //DEBUG
424 return handle;
425 } //end if
426 //
427 botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", intskill, charfile);
428 //
429 if (!reload)
430 {
431 //try to load a cached default character with the given skill
432 cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill);
433 if (cachedhandle)
434 {
435 botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", intskill, charfile);
436 return cachedhandle;
437 } //end if
438 } //end if
439 //try to load the default character with the given skill
440 ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill);
441 if (ch)
442 {
443 botcharacters[handle] = ch;
444 botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", intskill, charfile);
445 return handle;
446 } //end if
447 //
448 if (!reload)
449 {
450 //try to load a cached character with any skill
451 cachedhandle = BotFindCachedCharacter(charfile, -1);
452 if (cachedhandle)
453 {
454 botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile);
455 return cachedhandle;
456 } //end if
457 } //end if
458 //try to load a character with any skill
459 ch = BotLoadCharacterFromFile(charfile, -1);
460 if (ch)
461 {
462 botcharacters[handle] = ch;
463 botimport.Print(PRT_MESSAGE, "loaded skill %f from %s\n", ch->skill, charfile);
464 return handle;
465 } //end if
466 //
467 if (!reload)
468 {
469 //try to load a cached character with any skill
470 cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1);
471 if (cachedhandle)
472 {
473 botimport.Print(PRT_MESSAGE, "loaded cached default skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile);
474 return cachedhandle;
475 } //end if
476 } //end if
477 //try to load a character with any skill
478 ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1);
479 if (ch)
480 {
481 botcharacters[handle] = ch;
482 botimport.Print(PRT_MESSAGE, "loaded default skill %f from %s\n", ch->skill, charfile);
483 return handle;
484 } //end if
485 //
486 botimport.Print(PRT_WARNING, "couldn't load any skill from %s\n", charfile);
487 //couldn't load any character
488 return 0;
489 } //end of the function BotLoadCachedCharacter
490 //===========================================================================
491 //
492 // Parameter: -
493 // Returns: -
494 // Changes Globals: -
495 //===========================================================================
BotLoadCharacterSkill(char * charfile,float skill)496 int BotLoadCharacterSkill(char *charfile, float skill)
497 {
498 int ch, defaultch;
499
500 defaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse);
501 ch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue("bot_reloadcharacters"));
502
503 if (defaultch && ch)
504 {
505 BotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]);
506 } //end if
507
508 return ch;
509 } //end of the function BotLoadCharacterSkill
510 //===========================================================================
511 //
512 // Parameter: -
513 // Returns: -
514 // Changes Globals: -
515 //===========================================================================
BotInterpolateCharacters(int handle1,int handle2,float desiredskill)516 int BotInterpolateCharacters(int handle1, int handle2, float desiredskill)
517 {
518 bot_character_t *ch1, *ch2, *out;
519 int i, handle;
520 float scale;
521
522 ch1 = BotCharacterFromHandle(handle1);
523 ch2 = BotCharacterFromHandle(handle2);
524 if (!ch1 || !ch2)
525 return 0;
526 //find a free spot for a character
527 for (handle = 1; handle <= MAX_CLIENTS; handle++)
528 {
529 if (!botcharacters[handle]) break;
530 } //end for
531 if (handle > MAX_CLIENTS) return 0;
532 out = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
533 MAX_CHARACTERISTICS * sizeof(bot_characteristic_t));
534 out->skill = desiredskill;
535 strcpy(out->filename, ch1->filename);
536 botcharacters[handle] = out;
537
538 scale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill);
539 for (i = 0; i < MAX_CHARACTERISTICS; i++)
540 {
541 //
542 if (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT)
543 {
544 out->c[i].type = CT_FLOAT;
545 out->c[i].value._float = ch1->c[i].value._float +
546 (ch2->c[i].value._float - ch1->c[i].value._float) * scale;
547 } //end if
548 else if (ch1->c[i].type == CT_INTEGER)
549 {
550 out->c[i].type = CT_INTEGER;
551 out->c[i].value.integer = ch1->c[i].value.integer;
552 } //end else if
553 else if (ch1->c[i].type == CT_STRING)
554 {
555 out->c[i].type = CT_STRING;
556 out->c[i].value.string = (char *) GetMemory(strlen(ch1->c[i].value.string)+1);
557 strcpy(out->c[i].value.string, ch1->c[i].value.string);
558 } //end else if
559 } //end for
560 return handle;
561 } //end of the function BotInterpolateCharacters
562 //===========================================================================
563 //
564 // Parameter: -
565 // Returns: -
566 // Changes Globals: -
567 //===========================================================================
BotLoadCharacter(char * charfile,float skill)568 int BotLoadCharacter(char *charfile, float skill)
569 {
570 int firstskill, secondskill, handle;
571
572 //make sure the skill is in the valid range
573 if (skill < 1.0) skill = 1.0;
574 else if (skill > 5.0) skill = 5.0;
575 //skill 1, 4 and 5 should be available in the character files
576 if (skill == 1.0 || skill == 4.0 || skill == 5.0)
577 {
578 return BotLoadCharacterSkill(charfile, skill);
579 } //end if
580 //check if there's a cached skill
581 handle = BotFindCachedCharacter(charfile, skill);
582 if (handle)
583 {
584 botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile);
585 return handle;
586 } //end if
587 if (skill < 4.0)
588 {
589 //load skill 1 and 4
590 firstskill = BotLoadCharacterSkill(charfile, 1);
591 if (!firstskill) return 0;
592 secondskill = BotLoadCharacterSkill(charfile, 4);
593 if (!secondskill) return firstskill;
594 } //end if
595 else
596 {
597 //load skill 4 and 5
598 firstskill = BotLoadCharacterSkill(charfile, 4);
599 if (!firstskill) return 0;
600 secondskill = BotLoadCharacterSkill(charfile, 5);
601 if (!secondskill) return firstskill;
602 } //end else
603 //interpolate between the two skills
604 handle = BotInterpolateCharacters(firstskill, secondskill, skill);
605 if (!handle) return 0;
606 //write the character to the log file
607 BotDumpCharacter(botcharacters[handle]);
608 //
609 return handle;
610 } //end of the function BotLoadCharacter
611 //===========================================================================
612 //
613 // Parameter: -
614 // Returns: -
615 // Changes Globals: -
616 //===========================================================================
CheckCharacteristicIndex(int character,int index)617 int CheckCharacteristicIndex(int character, int index)
618 {
619 bot_character_t *ch;
620
621 ch = BotCharacterFromHandle(character);
622 if (!ch) return qfalse;
623 if (index < 0 || index >= MAX_CHARACTERISTICS)
624 {
625 botimport.Print(PRT_ERROR, "characteristic %d does not exist\n", index);
626 return qfalse;
627 } //end if
628 if (!ch->c[index].type)
629 {
630 botimport.Print(PRT_ERROR, "characteristic %d is not initialized\n", index);
631 return qfalse;
632 } //end if
633 return qtrue;
634 } //end of the function CheckCharacteristicIndex
635 //===========================================================================
636 //
637 // Parameter: -
638 // Returns: -
639 // Changes Globals: -
640 //===========================================================================
Characteristic_Float(int character,int index)641 float Characteristic_Float(int character, int index)
642 {
643 bot_character_t *ch;
644
645 ch = BotCharacterFromHandle(character);
646 if (!ch) return 0;
647 //check if the index is in range
648 if (!CheckCharacteristicIndex(character, index)) return 0;
649 //an integer will be converted to a float
650 if (ch->c[index].type == CT_INTEGER)
651 {
652 return (float) ch->c[index].value.integer;
653 } //end if
654 //floats are just returned
655 else if (ch->c[index].type == CT_FLOAT)
656 {
657 return ch->c[index].value._float;
658 } //end else if
659 //cannot convert a string pointer to a float
660 else
661 {
662 botimport.Print(PRT_ERROR, "characteristic %d is not a float\n", index);
663 return 0;
664 } //end else if
665 // return 0;
666 } //end of the function Characteristic_Float
667 //===========================================================================
668 //
669 // Parameter: -
670 // Returns: -
671 // Changes Globals: -
672 //===========================================================================
Characteristic_BFloat(int character,int index,float min,float max)673 float Characteristic_BFloat(int character, int index, float min, float max)
674 {
675 float value;
676 bot_character_t *ch;
677
678 ch = BotCharacterFromHandle(character);
679 if (!ch) return 0;
680 if (min > max)
681 {
682 botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %f and %f\n", index, min, max);
683 return 0;
684 } //end if
685 value = Characteristic_Float(character, index);
686 if (value < min) return min;
687 if (value > max) return max;
688 return value;
689 } //end of the function Characteristic_BFloat
690 //===========================================================================
691 //
692 // Parameter: -
693 // Returns: -
694 // Changes Globals: -
695 //===========================================================================
Characteristic_Integer(int character,int index)696 int Characteristic_Integer(int character, int index)
697 {
698 bot_character_t *ch;
699
700 ch = BotCharacterFromHandle(character);
701 if (!ch) return 0;
702 //check if the index is in range
703 if (!CheckCharacteristicIndex(character, index)) return 0;
704 //an integer will just be returned
705 if (ch->c[index].type == CT_INTEGER)
706 {
707 return ch->c[index].value.integer;
708 } //end if
709 //floats are casted to integers
710 else if (ch->c[index].type == CT_FLOAT)
711 {
712 return (int) ch->c[index].value._float;
713 } //end else if
714 else
715 {
716 botimport.Print(PRT_ERROR, "characteristic %d is not a integer\n", index);
717 return 0;
718 } //end else if
719 // return 0;
720 } //end of the function Characteristic_Integer
721 //===========================================================================
722 //
723 // Parameter: -
724 // Returns: -
725 // Changes Globals: -
726 //===========================================================================
Characteristic_BInteger(int character,int index,int min,int max)727 int Characteristic_BInteger(int character, int index, int min, int max)
728 {
729 int value;
730 bot_character_t *ch;
731
732 ch = BotCharacterFromHandle(character);
733 if (!ch) return 0;
734 if (min > max)
735 {
736 botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %d and %d\n", index, min, max);
737 return 0;
738 } //end if
739 value = Characteristic_Integer(character, index);
740 if (value < min) return min;
741 if (value > max) return max;
742 return value;
743 } //end of the function Characteristic_BInteger
744 //===========================================================================
745 //
746 // Parameter: -
747 // Returns: -
748 // Changes Globals: -
749 //===========================================================================
Characteristic_String(int character,int index,char * buf,int size)750 void Characteristic_String(int character, int index, char *buf, int size)
751 {
752 bot_character_t *ch;
753
754 ch = BotCharacterFromHandle(character);
755 if (!ch) return;
756 //check if the index is in range
757 if (!CheckCharacteristicIndex(character, index)) return;
758 //an integer will be converted to a float
759 if (ch->c[index].type == CT_STRING)
760 {
761 strncpy(buf, ch->c[index].value.string, size-1);
762 buf[size-1] = '\0';
763 return;
764 } //end if
765 else
766 {
767 botimport.Print(PRT_ERROR, "characteristic %d is not a string\n", index);
768 return;
769 } //end else if
770 return;
771 } //end of the function Characteristic_String
772 //===========================================================================
773 //
774 // Parameter: -
775 // Returns: -
776 // Changes Globals: -
777 //===========================================================================
BotShutdownCharacters(void)778 void BotShutdownCharacters(void)
779 {
780 int handle;
781
782 for (handle = 1; handle <= MAX_CLIENTS; handle++)
783 {
784 if (botcharacters[handle])
785 {
786 BotFreeCharacter2(handle);
787 } //end if
788 } //end for
789 } //end of the function BotShutdownCharacters
790
791