00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "../game/q_shared.h"
00033 #include "l_log.h"
00034 #include "l_memory.h"
00035 #include "l_utils.h"
00036 #include "l_script.h"
00037 #include "l_precomp.h"
00038 #include "l_struct.h"
00039 #include "l_libvar.h"
00040 #include "aasfile.h"
00041 #include "../game/botlib.h"
00042 #include "../game/be_aas.h"
00043 #include "be_aas_funcs.h"
00044 #include "be_interface.h"
00045 #include "../game/be_ai_char.h"
00046
00047 #define MAX_CHARACTERISTICS 80
00048
00049 #define CT_INTEGER 1
00050 #define CT_FLOAT 2
00051 #define CT_STRING 3
00052
00053 #define DEFAULT_CHARACTER "bots/default_c.c"
00054
00055
00056 union cvalue
00057 {
00058 int integer;
00059 float _float;
00060 char *string;
00061 };
00062
00063 typedef struct bot_characteristic_s
00064 {
00065 char type;
00066 union cvalue value;
00067 } bot_characteristic_t;
00068
00069
00070 typedef struct bot_character_s
00071 {
00072 char filename[MAX_QPATH];
00073 float skill;
00074 bot_characteristic_t c[1];
00075 } bot_character_t;
00076
00077 bot_character_t *botcharacters[MAX_CLIENTS + 1];
00078
00079
00080
00081
00082
00083
00084
00085 bot_character_t *BotCharacterFromHandle(int handle)
00086 {
00087 if (handle <= 0 || handle > MAX_CLIENTS)
00088 {
00089 botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle);
00090 return NULL;
00091 }
00092 if (!botcharacters[handle])
00093 {
00094 botimport.Print(PRT_FATAL, "invalid character %d\n", handle);
00095 return NULL;
00096 }
00097 return botcharacters[handle];
00098 }
00099
00100
00101
00102
00103
00104
00105 void BotDumpCharacter(bot_character_t *ch)
00106 {
00107 int i;
00108
00109 Log_Write("%s", ch->filename);
00110 Log_Write("skill %d\n", ch->skill);
00111 Log_Write("{\n");
00112 for (i = 0; i < MAX_CHARACTERISTICS; i++)
00113 {
00114 switch(ch->c[i].type)
00115 {
00116 case CT_INTEGER: Log_Write(" %4d %d\n", i, ch->c[i].value.integer); break;
00117 case CT_FLOAT: Log_Write(" %4d %f\n", i, ch->c[i].value._float); break;
00118 case CT_STRING: Log_Write(" %4d %s\n", i, ch->c[i].value.string); break;
00119 }
00120 }
00121 Log_Write("}\n");
00122 }
00123
00124
00125
00126
00127
00128
00129 void BotFreeCharacterStrings(bot_character_t *ch)
00130 {
00131 int i;
00132
00133 for (i = 0; i < MAX_CHARACTERISTICS; i++)
00134 {
00135 if (ch->c[i].type == CT_STRING)
00136 {
00137 FreeMemory(ch->c[i].value.string);
00138 }
00139 }
00140 }
00141
00142
00143
00144
00145
00146
00147 void BotFreeCharacter2(int handle)
00148 {
00149 if (handle <= 0 || handle > MAX_CLIENTS)
00150 {
00151 botimport.Print(PRT_FATAL, "character handle %d out of range\n", handle);
00152 return;
00153 }
00154 if (!botcharacters[handle])
00155 {
00156 botimport.Print(PRT_FATAL, "invalid character %d\n", handle);
00157 return;
00158 }
00159 BotFreeCharacterStrings(botcharacters[handle]);
00160 FreeMemory(botcharacters[handle]);
00161 botcharacters[handle] = NULL;
00162 }
00163
00164
00165
00166
00167
00168
00169 void BotFreeCharacter(int handle)
00170 {
00171 if (!LibVarGetValue("bot_reloadcharacters")) return;
00172 BotFreeCharacter2(handle);
00173 }
00174
00175
00176
00177
00178
00179
00180 void BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch)
00181 {
00182 int i;
00183
00184 for (i = 0; i < MAX_CHARACTERISTICS; i++)
00185 {
00186 if (ch->c[i].type) continue;
00187
00188 if (defaultch->c[i].type == CT_FLOAT)
00189 {
00190 ch->c[i].type = CT_FLOAT;
00191 ch->c[i].value._float = defaultch->c[i].value._float;
00192 }
00193 else if (defaultch->c[i].type == CT_INTEGER)
00194 {
00195 ch->c[i].type = CT_INTEGER;
00196 ch->c[i].value.integer = defaultch->c[i].value.integer;
00197 }
00198 else if (defaultch->c[i].type == CT_STRING)
00199 {
00200 ch->c[i].type = CT_STRING;
00201 ch->c[i].value.string = (char *) GetMemory(strlen(defaultch->c[i].value.string)+1);
00202 strcpy(ch->c[i].value.string, defaultch->c[i].value.string);
00203 }
00204 }
00205 }
00206
00207
00208
00209
00210
00211
00212 bot_character_t *BotLoadCharacterFromFile(char *charfile, int skill)
00213 {
00214 int indent, index, foundcharacter;
00215 bot_character_t *ch;
00216 source_t *source;
00217 token_t token;
00218
00219 foundcharacter = qfalse;
00220
00221 PC_SetBaseFolder(BOTFILESBASEFOLDER);
00222 source = LoadSourceFile(charfile);
00223 if (!source)
00224 {
00225 botimport.Print(PRT_ERROR, "counldn't load %s\n", charfile);
00226 return NULL;
00227 }
00228 ch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
00229 MAX_CHARACTERISTICS * sizeof(bot_characteristic_t));
00230 strcpy(ch->filename, charfile);
00231 while(PC_ReadToken(source, &token))
00232 {
00233 if (!strcmp(token.string, "skill"))
00234 {
00235 if (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token))
00236 {
00237 FreeSource(source);
00238 BotFreeCharacterStrings(ch);
00239 FreeMemory(ch);
00240 return NULL;
00241 }
00242 if (!PC_ExpectTokenString(source, "{"))
00243 {
00244 FreeSource(source);
00245 BotFreeCharacterStrings(ch);
00246 FreeMemory(ch);
00247 return NULL;
00248 }
00249
00250 if (skill < 0 || token.intvalue == skill)
00251 {
00252 foundcharacter = qtrue;
00253 ch->skill = token.intvalue;
00254 while(PC_ExpectAnyToken(source, &token))
00255 {
00256 if (!strcmp(token.string, "}")) break;
00257 if (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER))
00258 {
00259 SourceError(source, "expected integer index, found %s\n", token.string);
00260 FreeSource(source);
00261 BotFreeCharacterStrings(ch);
00262 FreeMemory(ch);
00263 return NULL;
00264 }
00265 index = token.intvalue;
00266 if (index < 0 || index > MAX_CHARACTERISTICS)
00267 {
00268 SourceError(source, "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS);
00269 FreeSource(source);
00270 BotFreeCharacterStrings(ch);
00271 FreeMemory(ch);
00272 return NULL;
00273 }
00274 if (ch->c[index].type)
00275 {
00276 SourceError(source, "characteristic %d already initialized\n", index);
00277 FreeSource(source);
00278 BotFreeCharacterStrings(ch);
00279 FreeMemory(ch);
00280 return NULL;
00281 }
00282 if (!PC_ExpectAnyToken(source, &token))
00283 {
00284 FreeSource(source);
00285 BotFreeCharacterStrings(ch);
00286 FreeMemory(ch);
00287 return NULL;
00288 }
00289 if (token.type == TT_NUMBER)
00290 {
00291 if (token.subtype & TT_FLOAT)
00292 {
00293 ch->c[index].value._float = token.floatvalue;
00294 ch->c[index].type = CT_FLOAT;
00295 }
00296 else
00297 {
00298 ch->c[index].value.integer = token.intvalue;
00299 ch->c[index].type = CT_INTEGER;
00300 }
00301 }
00302 else if (token.type == TT_STRING)
00303 {
00304 StripDoubleQuotes(token.string);
00305 ch->c[index].value.string = GetMemory(strlen(token.string)+1);
00306 strcpy(ch->c[index].value.string, token.string);
00307 ch->c[index].type = CT_STRING;
00308 }
00309 else
00310 {
00311 SourceError(source, "expected integer, float or string, found %s\n", token.string);
00312 FreeSource(source);
00313 BotFreeCharacterStrings(ch);
00314 FreeMemory(ch);
00315 return NULL;
00316 }
00317 }
00318 break;
00319 }
00320 else
00321 {
00322 indent = 1;
00323 while(indent)
00324 {
00325 if (!PC_ExpectAnyToken(source, &token))
00326 {
00327 FreeSource(source);
00328 BotFreeCharacterStrings(ch);
00329 FreeMemory(ch);
00330 return NULL;
00331 }
00332 if (!strcmp(token.string, "{")) indent++;
00333 else if (!strcmp(token.string, "}")) indent--;
00334 }
00335 }
00336 }
00337 else
00338 {
00339 SourceError(source, "unknown definition %s\n", token.string);
00340 FreeSource(source);
00341 BotFreeCharacterStrings(ch);
00342 FreeMemory(ch);
00343 return NULL;
00344 }
00345 }
00346 FreeSource(source);
00347
00348 if (!foundcharacter)
00349 {
00350 BotFreeCharacterStrings(ch);
00351 FreeMemory(ch);
00352 return NULL;
00353 }
00354 return ch;
00355 }
00356
00357
00358
00359
00360
00361
00362 int BotFindCachedCharacter(char *charfile, float skill)
00363 {
00364 int handle;
00365
00366 for (handle = 1; handle <= MAX_CLIENTS; handle++)
00367 {
00368 if ( !botcharacters[handle] ) continue;
00369 if ( strcmp( botcharacters[handle]->filename, charfile ) == 0 &&
00370 (skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) )
00371 {
00372 return handle;
00373 }
00374 }
00375 return 0;
00376 }
00377
00378
00379
00380
00381
00382
00383 int BotLoadCachedCharacter(char *charfile, float skill, int reload)
00384 {
00385 int handle, cachedhandle, intskill;
00386 bot_character_t *ch = NULL;
00387 #ifdef DEBUG
00388 int starttime;
00389
00390 starttime = Sys_MilliSeconds();
00391 #endif //DEBUG
00392
00393
00394 for (handle = 1; handle <= MAX_CLIENTS; handle++)
00395 {
00396 if (!botcharacters[handle]) break;
00397 }
00398 if (handle > MAX_CLIENTS) return 0;
00399
00400 if (!reload)
00401 {
00402 cachedhandle = BotFindCachedCharacter(charfile, skill);
00403 if (cachedhandle)
00404 {
00405 botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile);
00406 return cachedhandle;
00407 }
00408 }
00409
00410 intskill = (int) (skill + 0.5);
00411
00412 ch = BotLoadCharacterFromFile(charfile, intskill);
00413 if (ch)
00414 {
00415 botcharacters[handle] = ch;
00416
00417 botimport.Print(PRT_MESSAGE, "loaded skill %d from %s\n", intskill, charfile);
00418 #ifdef DEBUG
00419 if (bot_developer)
00420 {
00421 botimport.Print(PRT_MESSAGE, "skill %d loaded in %d msec from %s\n", intskill, Sys_MilliSeconds() - starttime, charfile);
00422 }
00423 #endif //DEBUG
00424 return handle;
00425 }
00426
00427 botimport.Print(PRT_WARNING, "couldn't find skill %d in %s\n", intskill, charfile);
00428
00429 if (!reload)
00430 {
00431
00432 cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill);
00433 if (cachedhandle)
00434 {
00435 botimport.Print(PRT_MESSAGE, "loaded cached default skill %d from %s\n", intskill, charfile);
00436 return cachedhandle;
00437 }
00438 }
00439
00440 ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill);
00441 if (ch)
00442 {
00443 botcharacters[handle] = ch;
00444 botimport.Print(PRT_MESSAGE, "loaded default skill %d from %s\n", intskill, charfile);
00445 return handle;
00446 }
00447
00448 if (!reload)
00449 {
00450
00451 cachedhandle = BotFindCachedCharacter(charfile, -1);
00452 if (cachedhandle)
00453 {
00454 botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile);
00455 return cachedhandle;
00456 }
00457 }
00458
00459 ch = BotLoadCharacterFromFile(charfile, -1);
00460 if (ch)
00461 {
00462 botcharacters[handle] = ch;
00463 botimport.Print(PRT_MESSAGE, "loaded skill %f from %s\n", ch->skill, charfile);
00464 return handle;
00465 }
00466
00467 if (!reload)
00468 {
00469
00470 cachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1);
00471 if (cachedhandle)
00472 {
00473 botimport.Print(PRT_MESSAGE, "loaded cached default skill %f from %s\n", botcharacters[cachedhandle]->skill, charfile);
00474 return cachedhandle;
00475 }
00476 }
00477
00478 ch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1);
00479 if (ch)
00480 {
00481 botcharacters[handle] = ch;
00482 botimport.Print(PRT_MESSAGE, "loaded default skill %f from %s\n", ch->skill, charfile);
00483 return handle;
00484 }
00485
00486 botimport.Print(PRT_WARNING, "couldn't load any skill from %s\n", charfile);
00487
00488 return 0;
00489 }
00490
00491
00492
00493
00494
00495
00496 int BotLoadCharacterSkill(char *charfile, float skill)
00497 {
00498 int ch, defaultch;
00499
00500 defaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse);
00501 ch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue("bot_reloadcharacters"));
00502
00503 if (defaultch && ch)
00504 {
00505 BotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]);
00506 }
00507
00508 return ch;
00509 }
00510
00511
00512
00513
00514
00515
00516 int BotInterpolateCharacters(int handle1, int handle2, float desiredskill)
00517 {
00518 bot_character_t *ch1, *ch2, *out;
00519 int i, handle;
00520 float scale;
00521
00522 ch1 = BotCharacterFromHandle(handle1);
00523 ch2 = BotCharacterFromHandle(handle2);
00524 if (!ch1 || !ch2)
00525 return 0;
00526
00527 for (handle = 1; handle <= MAX_CLIENTS; handle++)
00528 {
00529 if (!botcharacters[handle]) break;
00530 }
00531 if (handle > MAX_CLIENTS) return 0;
00532 out = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +
00533 MAX_CHARACTERISTICS * sizeof(bot_characteristic_t));
00534 out->skill = desiredskill;
00535 strcpy(out->filename, ch1->filename);
00536 botcharacters[handle] = out;
00537
00538 scale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill);
00539 for (i = 0; i < MAX_CHARACTERISTICS; i++)
00540 {
00541
00542 if (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT)
00543 {
00544 out->c[i].type = CT_FLOAT;
00545 out->c[i].value._float = ch1->c[i].value._float +
00546 (ch2->c[i].value._float - ch1->c[i].value._float) * scale;
00547 }
00548 else if (ch1->c[i].type == CT_INTEGER)
00549 {
00550 out->c[i].type = CT_INTEGER;
00551 out->c[i].value.integer = ch1->c[i].value.integer;
00552 }
00553 else if (ch1->c[i].type == CT_STRING)
00554 {
00555 out->c[i].type = CT_STRING;
00556 out->c[i].value.string = (char *) GetMemory(strlen(ch1->c[i].value.string)+1);
00557 strcpy(out->c[i].value.string, ch1->c[i].value.string);
00558 }
00559 }
00560 return handle;
00561 }
00562
00563
00564
00565
00566
00567
00568 int BotLoadCharacter(char *charfile, float skill)
00569 {
00570 int firstskill, secondskill, handle;
00571
00572
00573 if (skill < 1.0) skill = 1.0;
00574 else if (skill > 5.0) skill = 5.0;
00575
00576 if (skill == 1.0 || skill == 4.0 || skill == 5.0)
00577 {
00578 return BotLoadCharacterSkill(charfile, skill);
00579 }
00580
00581 handle = BotFindCachedCharacter(charfile, skill);
00582 if (handle)
00583 {
00584 botimport.Print(PRT_MESSAGE, "loaded cached skill %f from %s\n", skill, charfile);
00585 return handle;
00586 }
00587 if (skill < 4.0)
00588 {
00589
00590 firstskill = BotLoadCharacterSkill(charfile, 1);
00591 if (!firstskill) return 0;
00592 secondskill = BotLoadCharacterSkill(charfile, 4);
00593 if (!secondskill) return firstskill;
00594 }
00595 else
00596 {
00597
00598 firstskill = BotLoadCharacterSkill(charfile, 4);
00599 if (!firstskill) return 0;
00600 secondskill = BotLoadCharacterSkill(charfile, 5);
00601 if (!secondskill) return firstskill;
00602 }
00603
00604 handle = BotInterpolateCharacters(firstskill, secondskill, skill);
00605 if (!handle) return 0;
00606
00607 BotDumpCharacter(botcharacters[handle]);
00608
00609 return handle;
00610 }
00611
00612
00613
00614
00615
00616
00617 int CheckCharacteristicIndex(int character, int index)
00618 {
00619 bot_character_t *ch;
00620
00621 ch = BotCharacterFromHandle(character);
00622 if (!ch) return qfalse;
00623 if (index < 0 || index >= MAX_CHARACTERISTICS)
00624 {
00625 botimport.Print(PRT_ERROR, "characteristic %d does not exist\n", index);
00626 return qfalse;
00627 }
00628 if (!ch->c[index].type)
00629 {
00630 botimport.Print(PRT_ERROR, "characteristic %d is not initialized\n", index);
00631 return qfalse;
00632 }
00633 return qtrue;
00634 }
00635
00636
00637
00638
00639
00640
00641 float Characteristic_Float(int character, int index)
00642 {
00643 bot_character_t *ch;
00644
00645 ch = BotCharacterFromHandle(character);
00646 if (!ch) return 0;
00647
00648 if (!CheckCharacteristicIndex(character, index)) return 0;
00649
00650 if (ch->c[index].type == CT_INTEGER)
00651 {
00652 return (float) ch->c[index].value.integer;
00653 }
00654
00655 else if (ch->c[index].type == CT_FLOAT)
00656 {
00657 return ch->c[index].value._float;
00658 }
00659
00660 else
00661 {
00662 botimport.Print(PRT_ERROR, "characteristic %d is not a float\n", index);
00663 return 0;
00664 }
00665
00666 }
00667
00668
00669
00670
00671
00672
00673 float Characteristic_BFloat(int character, int index, float min, float max)
00674 {
00675 float value;
00676 bot_character_t *ch;
00677
00678 ch = BotCharacterFromHandle(character);
00679 if (!ch) return 0;
00680 if (min > max)
00681 {
00682 botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %f and %f\n", index, min, max);
00683 return 0;
00684 }
00685 value = Characteristic_Float(character, index);
00686 if (value < min) return min;
00687 if (value > max) return max;
00688 return value;
00689 }
00690
00691
00692
00693
00694
00695
00696 int Characteristic_Integer(int character, int index)
00697 {
00698 bot_character_t *ch;
00699
00700 ch = BotCharacterFromHandle(character);
00701 if (!ch) return 0;
00702
00703 if (!CheckCharacteristicIndex(character, index)) return 0;
00704
00705 if (ch->c[index].type == CT_INTEGER)
00706 {
00707 return ch->c[index].value.integer;
00708 }
00709
00710 else if (ch->c[index].type == CT_FLOAT)
00711 {
00712 return (int) ch->c[index].value._float;
00713 }
00714 else
00715 {
00716 botimport.Print(PRT_ERROR, "characteristic %d is not a integer\n", index);
00717 return 0;
00718 }
00719
00720 }
00721
00722
00723
00724
00725
00726
00727 int Characteristic_BInteger(int character, int index, int min, int max)
00728 {
00729 int value;
00730 bot_character_t *ch;
00731
00732 ch = BotCharacterFromHandle(character);
00733 if (!ch) return 0;
00734 if (min > max)
00735 {
00736 botimport.Print(PRT_ERROR, "cannot bound characteristic %d between %d and %d\n", index, min, max);
00737 return 0;
00738 }
00739 value = Characteristic_Integer(character, index);
00740 if (value < min) return min;
00741 if (value > max) return max;
00742 return value;
00743 }
00744
00745
00746
00747
00748
00749
00750 void Characteristic_String(int character, int index, char *buf, int size)
00751 {
00752 bot_character_t *ch;
00753
00754 ch = BotCharacterFromHandle(character);
00755 if (!ch) return;
00756
00757 if (!CheckCharacteristicIndex(character, index)) return;
00758
00759 if (ch->c[index].type == CT_STRING)
00760 {
00761 strncpy(buf, ch->c[index].value.string, size-1);
00762 buf[size-1] = '\0';
00763 return;
00764 }
00765 else
00766 {
00767 botimport.Print(PRT_ERROR, "characteristic %d is not a string\n", index);
00768 return;
00769 }
00770 return;
00771 }
00772
00773
00774
00775
00776
00777
00778 void BotShutdownCharacters(void)
00779 {
00780 int handle;
00781
00782 for (handle = 1; handle <= MAX_CLIENTS; handle++)
00783 {
00784 if (botcharacters[handle])
00785 {
00786 BotFreeCharacter2(handle);
00787 }
00788 }
00789 }
00790