be_ai_char.c (23168B)
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 Foobar; 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 "../game/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 "../game/botlib.h" 42 #include "../game/be_aas.h" 43 #include "be_aas_funcs.h" 44 #include "be_interface.h" 45 #include "../game/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 //======================================================================== 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 //=========================================================================== 105 void BotDumpCharacter(bot_character_t *ch) 106 { 107 int i; 108 109 Log_Write("%s", ch->filename); 110 Log_Write("skill %d\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 //======================================================================== 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 //======================================================================== 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 //======================================================================== 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 //=========================================================================== 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 //=========================================================================== 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 //=========================================================================== 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 //=========================================================================== 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 (bot_developer) 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 //=========================================================================== 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 //=========================================================================== 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 //=========================================================================== 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 //=========================================================================== 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 //=========================================================================== 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 //=========================================================================== 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 //=========================================================================== 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 //=========================================================================== 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 //=========================================================================== 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 //=========================================================================== 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