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_libvar.h"
00034 #include "l_log.h"
00035 #include "l_memory.h"
00036 #include "l_utils.h"
00037 #include "l_script.h"
00038 #include "l_precomp.h"
00039 #include "l_struct.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 "be_ai_weight.h"
00046 #include "../game/be_ai_weap.h"
00047
00048
00049
00050
00051 #define WEAPON_OFS(x) (int)&(((weaponinfo_t *)0)->x)
00052 #define PROJECTILE_OFS(x) (int)&(((projectileinfo_t *)0)->x)
00053
00054
00055 static fielddef_t weaponinfo_fields[] =
00056 {
00057 {"number", WEAPON_OFS(number), FT_INT},
00058 {"name", WEAPON_OFS(name), FT_STRING},
00059 {"level", WEAPON_OFS(level), FT_INT},
00060 {"model", WEAPON_OFS(model), FT_STRING},
00061 {"weaponindex", WEAPON_OFS(weaponindex), FT_INT},
00062 {"flags", WEAPON_OFS(flags), FT_INT},
00063 {"projectile", WEAPON_OFS(projectile), FT_STRING},
00064 {"numprojectiles", WEAPON_OFS(numprojectiles), FT_INT},
00065 {"hspread", WEAPON_OFS(hspread), FT_FLOAT},
00066 {"vspread", WEAPON_OFS(vspread), FT_FLOAT},
00067 {"speed", WEAPON_OFS(speed), FT_FLOAT},
00068 {"acceleration", WEAPON_OFS(acceleration), FT_FLOAT},
00069 {"recoil", WEAPON_OFS(recoil), FT_FLOAT|FT_ARRAY, 3},
00070 {"offset", WEAPON_OFS(offset), FT_FLOAT|FT_ARRAY, 3},
00071 {"angleoffset", WEAPON_OFS(angleoffset), FT_FLOAT|FT_ARRAY, 3},
00072 {"extrazvelocity", WEAPON_OFS(extrazvelocity), FT_FLOAT},
00073 {"ammoamount", WEAPON_OFS(ammoamount), FT_INT},
00074 {"ammoindex", WEAPON_OFS(ammoindex), FT_INT},
00075 {"activate", WEAPON_OFS(activate), FT_FLOAT},
00076 {"reload", WEAPON_OFS(reload), FT_FLOAT},
00077 {"spinup", WEAPON_OFS(spinup), FT_FLOAT},
00078 {"spindown", WEAPON_OFS(spindown), FT_FLOAT},
00079 {NULL, 0, 0, 0}
00080 };
00081
00082
00083 static fielddef_t projectileinfo_fields[] =
00084 {
00085 {"name", PROJECTILE_OFS(name), FT_STRING},
00086 {"model", WEAPON_OFS(model), FT_STRING},
00087 {"flags", PROJECTILE_OFS(flags), FT_INT},
00088 {"gravity", PROJECTILE_OFS(gravity), FT_FLOAT},
00089 {"damage", PROJECTILE_OFS(damage), FT_INT},
00090 {"radius", PROJECTILE_OFS(radius), FT_FLOAT},
00091 {"visdamage", PROJECTILE_OFS(visdamage), FT_INT},
00092 {"damagetype", PROJECTILE_OFS(damagetype), FT_INT},
00093 {"healthinc", PROJECTILE_OFS(healthinc), FT_INT},
00094 {"push", PROJECTILE_OFS(push), FT_FLOAT},
00095 {"detonation", PROJECTILE_OFS(detonation), FT_FLOAT},
00096 {"bounce", PROJECTILE_OFS(bounce), FT_FLOAT},
00097 {"bouncefric", PROJECTILE_OFS(bouncefric), FT_FLOAT},
00098 {"bouncestop", PROJECTILE_OFS(bouncestop), FT_FLOAT},
00099
00100 {NULL, 0, 0, 0}
00101 };
00102
00103 static structdef_t weaponinfo_struct =
00104 {
00105 sizeof(weaponinfo_t), weaponinfo_fields
00106 };
00107 static structdef_t projectileinfo_struct =
00108 {
00109 sizeof(projectileinfo_t), projectileinfo_fields
00110 };
00111
00112
00113 typedef struct weaponconfig_s
00114 {
00115 int numweapons;
00116 int numprojectiles;
00117 projectileinfo_t *projectileinfo;
00118 weaponinfo_t *weaponinfo;
00119 } weaponconfig_t;
00120
00121
00122 typedef struct bot_weaponstate_s
00123 {
00124 struct weightconfig_s *weaponweightconfig;
00125 int *weaponweightindex;
00126 } bot_weaponstate_t;
00127
00128 static bot_weaponstate_t *botweaponstates[MAX_CLIENTS+1];
00129 static weaponconfig_t *weaponconfig;
00130
00131
00132
00133
00134
00135
00136
00137 int BotValidWeaponNumber(int weaponnum)
00138 {
00139 if (weaponnum <= 0 || weaponnum > weaponconfig->numweapons)
00140 {
00141 botimport.Print(PRT_ERROR, "weapon number out of range\n");
00142 return qfalse;
00143 }
00144 return qtrue;
00145 }
00146
00147
00148
00149
00150
00151
00152 bot_weaponstate_t *BotWeaponStateFromHandle(int handle)
00153 {
00154 if (handle <= 0 || handle > MAX_CLIENTS)
00155 {
00156 botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
00157 return NULL;
00158 }
00159 if (!botweaponstates[handle])
00160 {
00161 botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
00162 return NULL;
00163 }
00164 return botweaponstates[handle];
00165 }
00166
00167
00168
00169
00170
00171
00172 #ifdef DEBUG_AI_WEAP
00173 void DumpWeaponConfig(weaponconfig_t *wc)
00174 {
00175 FILE *fp;
00176 int i;
00177
00178 fp = Log_FileStruct();
00179 if (!fp) return;
00180 for (i = 0; i < wc->numprojectiles; i++)
00181 {
00182 WriteStructure(fp, &projectileinfo_struct, (char *) &wc->projectileinfo[i]);
00183 Log_Flush();
00184 }
00185 for (i = 0; i < wc->numweapons; i++)
00186 {
00187 WriteStructure(fp, &weaponinfo_struct, (char *) &wc->weaponinfo[i]);
00188 Log_Flush();
00189 }
00190 }
00191 #endif //DEBUG_AI_WEAP
00192
00193
00194
00195
00196
00197
00198 weaponconfig_t *LoadWeaponConfig(char *filename)
00199 {
00200 int max_weaponinfo, max_projectileinfo;
00201 token_t token;
00202 char path[MAX_PATH];
00203 int i, j;
00204 source_t *source;
00205 weaponconfig_t *wc;
00206 weaponinfo_t weaponinfo;
00207
00208 max_weaponinfo = (int) LibVarValue("max_weaponinfo", "32");
00209 if (max_weaponinfo < 0)
00210 {
00211 botimport.Print(PRT_ERROR, "max_weaponinfo = %d\n", max_weaponinfo);
00212 max_weaponinfo = 32;
00213 LibVarSet("max_weaponinfo", "32");
00214 }
00215 max_projectileinfo = (int) LibVarValue("max_projectileinfo", "32");
00216 if (max_projectileinfo < 0)
00217 {
00218 botimport.Print(PRT_ERROR, "max_projectileinfo = %d\n", max_projectileinfo);
00219 max_projectileinfo = 32;
00220 LibVarSet("max_projectileinfo", "32");
00221 }
00222 strncpy(path, filename, MAX_PATH);
00223 PC_SetBaseFolder(BOTFILESBASEFOLDER);
00224 source = LoadSourceFile(path);
00225 if (!source)
00226 {
00227 botimport.Print(PRT_ERROR, "counldn't load %s\n", path);
00228 return NULL;
00229 }
00230
00231 wc = (weaponconfig_t *) GetClearedHunkMemory(sizeof(weaponconfig_t) +
00232 max_weaponinfo * sizeof(weaponinfo_t) +
00233 max_projectileinfo * sizeof(projectileinfo_t));
00234 wc->weaponinfo = (weaponinfo_t *) ((char *) wc + sizeof(weaponconfig_t));
00235 wc->projectileinfo = (projectileinfo_t *) ((char *) wc->weaponinfo +
00236 max_weaponinfo * sizeof(weaponinfo_t));
00237 wc->numweapons = max_weaponinfo;
00238 wc->numprojectiles = 0;
00239
00240 while(PC_ReadToken(source, &token))
00241 {
00242 if (!strcmp(token.string, "weaponinfo"))
00243 {
00244 Com_Memset(&weaponinfo, 0, sizeof(weaponinfo_t));
00245 if (!ReadStructure(source, &weaponinfo_struct, (char *) &weaponinfo))
00246 {
00247 FreeMemory(wc);
00248 FreeSource(source);
00249 return NULL;
00250 }
00251 if (weaponinfo.number < 0 || weaponinfo.number >= max_weaponinfo)
00252 {
00253 botimport.Print(PRT_ERROR, "weapon info number %d out of range in %s\n", weaponinfo.number, path);
00254 FreeMemory(wc);
00255 FreeSource(source);
00256 return NULL;
00257 }
00258 Com_Memcpy(&wc->weaponinfo[weaponinfo.number], &weaponinfo, sizeof(weaponinfo_t));
00259 wc->weaponinfo[weaponinfo.number].valid = qtrue;
00260 }
00261 else if (!strcmp(token.string, "projectileinfo"))
00262 {
00263 if (wc->numprojectiles >= max_projectileinfo)
00264 {
00265 botimport.Print(PRT_ERROR, "more than %d projectiles defined in %s\n", max_projectileinfo, path);
00266 FreeMemory(wc);
00267 FreeSource(source);
00268 return NULL;
00269 }
00270 Com_Memset(&wc->projectileinfo[wc->numprojectiles], 0, sizeof(projectileinfo_t));
00271 if (!ReadStructure(source, &projectileinfo_struct, (char *) &wc->projectileinfo[wc->numprojectiles]))
00272 {
00273 FreeMemory(wc);
00274 FreeSource(source);
00275 return NULL;
00276 }
00277 wc->numprojectiles++;
00278 }
00279 else
00280 {
00281 botimport.Print(PRT_ERROR, "unknown definition %s in %s\n", token.string, path);
00282 FreeMemory(wc);
00283 FreeSource(source);
00284 return NULL;
00285 }
00286 }
00287 FreeSource(source);
00288
00289 for (i = 0; i < wc->numweapons; i++)
00290 {
00291 if (!wc->weaponinfo[i].valid) continue;
00292 if (!wc->weaponinfo[i].name[0])
00293 {
00294 botimport.Print(PRT_ERROR, "weapon %d has no name in %s\n", i, path);
00295 FreeMemory(wc);
00296 return NULL;
00297 }
00298 if (!wc->weaponinfo[i].projectile[0])
00299 {
00300 botimport.Print(PRT_ERROR, "weapon %s has no projectile in %s\n", wc->weaponinfo[i].name, path);
00301 FreeMemory(wc);
00302 return NULL;
00303 }
00304
00305 for (j = 0; j < wc->numprojectiles; j++)
00306 {
00307 if (!strcmp(wc->projectileinfo[j].name, wc->weaponinfo[i].projectile))
00308 {
00309 Com_Memcpy(&wc->weaponinfo[i].proj, &wc->projectileinfo[j], sizeof(projectileinfo_t));
00310 break;
00311 }
00312 }
00313 if (j == wc->numprojectiles)
00314 {
00315 botimport.Print(PRT_ERROR, "weapon %s uses undefined projectile in %s\n", wc->weaponinfo[i].name, path);
00316 FreeMemory(wc);
00317 return NULL;
00318 }
00319 }
00320 if (!wc->numweapons) botimport.Print(PRT_WARNING, "no weapon info loaded\n");
00321 botimport.Print(PRT_MESSAGE, "loaded %s\n", path);
00322 return wc;
00323 }
00324
00325
00326
00327
00328
00329
00330 int *WeaponWeightIndex(weightconfig_t *wwc, weaponconfig_t *wc)
00331 {
00332 int *index, i;
00333
00334
00335 index = (int *) GetClearedMemory(sizeof(int) * wc->numweapons);
00336
00337 for (i = 0; i < wc->numweapons; i++)
00338 {
00339 index[i] = FindFuzzyWeight(wwc, wc->weaponinfo[i].name);
00340 }
00341 return index;
00342 }
00343
00344
00345
00346
00347
00348
00349 void BotFreeWeaponWeights(int weaponstate)
00350 {
00351 bot_weaponstate_t *ws;
00352
00353 ws = BotWeaponStateFromHandle(weaponstate);
00354 if (!ws) return;
00355 if (ws->weaponweightconfig) FreeWeightConfig(ws->weaponweightconfig);
00356 if (ws->weaponweightindex) FreeMemory(ws->weaponweightindex);
00357 }
00358
00359
00360
00361
00362
00363
00364 int BotLoadWeaponWeights(int weaponstate, char *filename)
00365 {
00366 bot_weaponstate_t *ws;
00367
00368 ws = BotWeaponStateFromHandle(weaponstate);
00369 if (!ws) return BLERR_CANNOTLOADWEAPONWEIGHTS;
00370 BotFreeWeaponWeights(weaponstate);
00371
00372 ws->weaponweightconfig = ReadWeightConfig(filename);
00373 if (!ws->weaponweightconfig)
00374 {
00375 botimport.Print(PRT_FATAL, "couldn't load weapon config %s\n", filename);
00376 return BLERR_CANNOTLOADWEAPONWEIGHTS;
00377 }
00378 if (!weaponconfig) return BLERR_CANNOTLOADWEAPONCONFIG;
00379 ws->weaponweightindex = WeaponWeightIndex(ws->weaponweightconfig, weaponconfig);
00380 return BLERR_NOERROR;
00381 }
00382
00383
00384
00385
00386
00387
00388 void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo)
00389 {
00390 bot_weaponstate_t *ws;
00391
00392 if (!BotValidWeaponNumber(weapon)) return;
00393 ws = BotWeaponStateFromHandle(weaponstate);
00394 if (!ws) return;
00395 if (!weaponconfig) return;
00396 Com_Memcpy(weaponinfo, &weaponconfig->weaponinfo[weapon], sizeof(weaponinfo_t));
00397 }
00398
00399
00400
00401
00402
00403
00404 int BotChooseBestFightWeapon(int weaponstate, int *inventory)
00405 {
00406 int i, index, bestweapon;
00407 float weight, bestweight;
00408 weaponconfig_t *wc;
00409 bot_weaponstate_t *ws;
00410
00411 ws = BotWeaponStateFromHandle(weaponstate);
00412 if (!ws) return 0;
00413 wc = weaponconfig;
00414 if (!weaponconfig) return 0;
00415
00416
00417 if (!ws->weaponweightconfig) return 0;
00418
00419 bestweight = 0;
00420 bestweapon = 0;
00421 for (i = 0; i < wc->numweapons; i++)
00422 {
00423 if (!wc->weaponinfo[i].valid) continue;
00424 index = ws->weaponweightindex[i];
00425 if (index < 0) continue;
00426 weight = FuzzyWeight(inventory, ws->weaponweightconfig, index);
00427 if (weight > bestweight)
00428 {
00429 bestweight = weight;
00430 bestweapon = i;
00431 }
00432 }
00433 return bestweapon;
00434 }
00435
00436
00437
00438
00439
00440
00441 void BotResetWeaponState(int weaponstate)
00442 {
00443 struct weightconfig_s *weaponweightconfig;
00444 int *weaponweightindex;
00445 bot_weaponstate_t *ws;
00446
00447 ws = BotWeaponStateFromHandle(weaponstate);
00448 if (!ws) return;
00449 weaponweightconfig = ws->weaponweightconfig;
00450 weaponweightindex = ws->weaponweightindex;
00451
00452
00453 ws->weaponweightconfig = weaponweightconfig;
00454 ws->weaponweightindex = weaponweightindex;
00455 }
00456
00457
00458
00459
00460
00461
00462 int BotAllocWeaponState(void)
00463 {
00464 int i;
00465
00466 for (i = 1; i <= MAX_CLIENTS; i++)
00467 {
00468 if (!botweaponstates[i])
00469 {
00470 botweaponstates[i] = GetClearedMemory(sizeof(bot_weaponstate_t));
00471 return i;
00472 }
00473 }
00474 return 0;
00475 }
00476
00477
00478
00479
00480
00481
00482 void BotFreeWeaponState(int handle)
00483 {
00484 if (handle <= 0 || handle > MAX_CLIENTS)
00485 {
00486 botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
00487 return;
00488 }
00489 if (!botweaponstates[handle])
00490 {
00491 botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
00492 return;
00493 }
00494 BotFreeWeaponWeights(handle);
00495 FreeMemory(botweaponstates[handle]);
00496 botweaponstates[handle] = NULL;
00497 }
00498
00499
00500
00501
00502
00503
00504 int BotSetupWeaponAI(void)
00505 {
00506 char *file;
00507
00508 file = LibVarString("weaponconfig", "weapons.c");
00509 weaponconfig = LoadWeaponConfig(file);
00510 if (!weaponconfig)
00511 {
00512 botimport.Print(PRT_FATAL, "couldn't load the weapon config\n");
00513 return BLERR_CANNOTLOADWEAPONCONFIG;
00514 }
00515
00516 #ifdef DEBUG_AI_WEAP
00517 DumpWeaponConfig(weaponconfig);
00518 #endif //DEBUG_AI_WEAP
00519
00520 return BLERR_NOERROR;
00521 }
00522
00523
00524
00525
00526
00527
00528 void BotShutdownWeaponAI(void)
00529 {
00530 int i;
00531
00532 if (weaponconfig) FreeMemory(weaponconfig);
00533 weaponconfig = NULL;
00534
00535 for (i = 1; i <= MAX_CLIENTS; i++)
00536 {
00537 if (botweaponstates[i])
00538 {
00539 BotFreeWeaponState(i);
00540 }
00541 }
00542 }
00543