be_ai_goal.c 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. /*****************************************************************************
  19. * name: be_ai_goal.c
  20. *
  21. * desc: goal AI
  22. *
  23. * $Archive: /MissionPack/code/botlib/be_ai_goal.c $
  24. *
  25. *****************************************************************************/
  26. #include "../game/q_shared.h"
  27. #include "l_utils.h"
  28. #include "l_libvar.h"
  29. #include "l_memory.h"
  30. #include "l_log.h"
  31. #include "l_script.h"
  32. #include "l_precomp.h"
  33. #include "l_struct.h"
  34. #include "aasfile.h"
  35. #include "../game/botlib.h"
  36. #include "../game/be_aas.h"
  37. #include "be_aas_funcs.h"
  38. #include "be_interface.h"
  39. #include "be_ai_weight.h"
  40. #include "../game/be_ai_goal.h"
  41. #include "../game/be_ai_move.h"
  42. //#define DEBUG_AI_GOAL
  43. #ifdef RANDOMIZE
  44. #define UNDECIDEDFUZZY
  45. #endif //RANDOMIZE
  46. #define DROPPEDWEIGHT
  47. //minimum avoid goal time
  48. #define AVOID_MINIMUM_TIME 10
  49. //default avoid goal time
  50. #define AVOID_DEFAULT_TIME 30
  51. //avoid dropped goal time
  52. #define AVOID_DROPPED_TIME 10
  53. //
  54. #define TRAVELTIME_SCALE 0.01
  55. //item flags
  56. #define IFL_NOTFREE 1 //not in free for all
  57. #define IFL_NOTTEAM 2 //not in team play
  58. #define IFL_NOTSINGLE 4 //not in single player
  59. #define IFL_NOTBOT 8 //bot should never go for this
  60. #define IFL_ROAM 16 //bot roam goal
  61. //location in the map "target_location"
  62. typedef struct maplocation_s
  63. {
  64. vec3_t origin;
  65. int areanum;
  66. char name[MAX_EPAIRKEY];
  67. struct maplocation_s *next;
  68. } maplocation_t;
  69. //camp spots "info_camp"
  70. typedef struct campspot_s
  71. {
  72. vec3_t origin;
  73. int areanum;
  74. char name[MAX_EPAIRKEY];
  75. float range;
  76. float weight;
  77. float wait;
  78. float random;
  79. struct campspot_s *next;
  80. } campspot_t;
  81. //FIXME: these are game specific
  82. typedef enum {
  83. GT_FFA, // free for all
  84. GT_TOURNAMENT, // one on one tournament
  85. GT_SINGLE_PLAYER, // single player tournament
  86. //-- team games go after this --
  87. GT_TEAM, // team deathmatch
  88. GT_CTF, // capture the flag
  89. #ifdef MISSIONPACK
  90. GT_1FCTF,
  91. GT_OBELISK,
  92. GT_HARVESTER,
  93. #endif
  94. GT_MAX_GAME_TYPE
  95. } gametype_t;
  96. typedef struct levelitem_s
  97. {
  98. int number; //number of the level item
  99. int iteminfo; //index into the item info
  100. int flags; //item flags
  101. float weight; //fixed roam weight
  102. vec3_t origin; //origin of the item
  103. int goalareanum; //area the item is in
  104. vec3_t goalorigin; //goal origin within the area
  105. int entitynum; //entity number
  106. float timeout; //item is removed after this time
  107. struct levelitem_s *prev, *next;
  108. } levelitem_t;
  109. typedef struct iteminfo_s
  110. {
  111. char classname[32]; //classname of the item
  112. char name[MAX_STRINGFIELD]; //name of the item
  113. char model[MAX_STRINGFIELD]; //model of the item
  114. int modelindex; //model index
  115. int type; //item type
  116. int index; //index in the inventory
  117. float respawntime; //respawn time
  118. vec3_t mins; //mins of the item
  119. vec3_t maxs; //maxs of the item
  120. int number; //number of the item info
  121. } iteminfo_t;
  122. #define ITEMINFO_OFS(x) (int)&(((iteminfo_t *)0)->x)
  123. fielddef_t iteminfo_fields[] =
  124. {
  125. {"name", ITEMINFO_OFS(name), FT_STRING},
  126. {"model", ITEMINFO_OFS(model), FT_STRING},
  127. {"modelindex", ITEMINFO_OFS(modelindex), FT_INT},
  128. {"type", ITEMINFO_OFS(type), FT_INT},
  129. {"index", ITEMINFO_OFS(index), FT_INT},
  130. {"respawntime", ITEMINFO_OFS(respawntime), FT_FLOAT},
  131. {"mins", ITEMINFO_OFS(mins), FT_FLOAT|FT_ARRAY, 3},
  132. {"maxs", ITEMINFO_OFS(maxs), FT_FLOAT|FT_ARRAY, 3},
  133. {0, 0, 0}
  134. };
  135. structdef_t iteminfo_struct =
  136. {
  137. sizeof(iteminfo_t), iteminfo_fields
  138. };
  139. typedef struct itemconfig_s
  140. {
  141. int numiteminfo;
  142. iteminfo_t *iteminfo;
  143. } itemconfig_t;
  144. //goal state
  145. typedef struct bot_goalstate_s
  146. {
  147. struct weightconfig_s *itemweightconfig; //weight config
  148. int *itemweightindex; //index from item to weight
  149. //
  150. int client; //client using this goal state
  151. int lastreachabilityarea; //last area with reachabilities the bot was in
  152. //
  153. bot_goal_t goalstack[MAX_GOALSTACK]; //goal stack
  154. int goalstacktop; //the top of the goal stack
  155. //
  156. int avoidgoals[MAX_AVOIDGOALS]; //goals to avoid
  157. float avoidgoaltimes[MAX_AVOIDGOALS]; //times to avoid the goals
  158. } bot_goalstate_t;
  159. bot_goalstate_t *botgoalstates[MAX_CLIENTS + 1]; // bk001206 - FIXME: init?
  160. //item configuration
  161. itemconfig_t *itemconfig = NULL; // bk001206 - init
  162. //level items
  163. levelitem_t *levelitemheap = NULL; // bk001206 - init
  164. levelitem_t *freelevelitems = NULL; // bk001206 - init
  165. levelitem_t *levelitems = NULL; // bk001206 - init
  166. int numlevelitems = 0;
  167. //map locations
  168. maplocation_t *maplocations = NULL; // bk001206 - init
  169. //camp spots
  170. campspot_t *campspots = NULL; // bk001206 - init
  171. //the game type
  172. int g_gametype = 0; // bk001206 - init
  173. //additional dropped item weight
  174. libvar_t *droppedweight = NULL; // bk001206 - init
  175. //========================================================================
  176. //
  177. // Parameter: -
  178. // Returns: -
  179. // Changes Globals: -
  180. //========================================================================
  181. bot_goalstate_t *BotGoalStateFromHandle(int handle)
  182. {
  183. if (handle <= 0 || handle > MAX_CLIENTS)
  184. {
  185. botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle);
  186. return NULL;
  187. } //end if
  188. if (!botgoalstates[handle])
  189. {
  190. botimport.Print(PRT_FATAL, "invalid goal state %d\n", handle);
  191. return NULL;
  192. } //end if
  193. return botgoalstates[handle];
  194. } //end of the function BotGoalStateFromHandle
  195. //===========================================================================
  196. //
  197. // Parameter: -
  198. // Returns: -
  199. // Changes Globals: -
  200. //===========================================================================
  201. void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child)
  202. {
  203. bot_goalstate_t *p1, *p2, *c;
  204. p1 = BotGoalStateFromHandle(parent1);
  205. p2 = BotGoalStateFromHandle(parent2);
  206. c = BotGoalStateFromHandle(child);
  207. InterbreedWeightConfigs(p1->itemweightconfig, p2->itemweightconfig,
  208. c->itemweightconfig);
  209. } //end of the function BotInterbreedingGoalFuzzyLogic
  210. //===========================================================================
  211. //
  212. // Parameter: -
  213. // Returns: -
  214. // Changes Globals: -
  215. //===========================================================================
  216. void BotSaveGoalFuzzyLogic(int goalstate, char *filename)
  217. {
  218. bot_goalstate_t *gs;
  219. gs = BotGoalStateFromHandle(goalstate);
  220. //WriteWeightConfig(filename, gs->itemweightconfig);
  221. } //end of the function BotSaveGoalFuzzyLogic
  222. //===========================================================================
  223. //
  224. // Parameter: -
  225. // Returns: -
  226. // Changes Globals: -
  227. //===========================================================================
  228. void BotMutateGoalFuzzyLogic(int goalstate, float range)
  229. {
  230. bot_goalstate_t *gs;
  231. gs = BotGoalStateFromHandle(goalstate);
  232. EvolveWeightConfig(gs->itemweightconfig);
  233. } //end of the function BotMutateGoalFuzzyLogic
  234. //===========================================================================
  235. //
  236. // Parameter: -
  237. // Returns: -
  238. // Changes Globals: -
  239. //===========================================================================
  240. itemconfig_t *LoadItemConfig(char *filename)
  241. {
  242. int max_iteminfo;
  243. token_t token;
  244. char path[MAX_PATH];
  245. source_t *source;
  246. itemconfig_t *ic;
  247. iteminfo_t *ii;
  248. max_iteminfo = (int) LibVarValue("max_iteminfo", "256");
  249. if (max_iteminfo < 0)
  250. {
  251. botimport.Print(PRT_ERROR, "max_iteminfo = %d\n", max_iteminfo);
  252. max_iteminfo = 256;
  253. LibVarSet( "max_iteminfo", "256" );
  254. }
  255. strncpy( path, filename, MAX_PATH );
  256. PC_SetBaseFolder(BOTFILESBASEFOLDER);
  257. source = LoadSourceFile( path );
  258. if( !source ) {
  259. botimport.Print( PRT_ERROR, "counldn't load %s\n", path );
  260. return NULL;
  261. } //end if
  262. //initialize item config
  263. ic = (itemconfig_t *) GetClearedHunkMemory(sizeof(itemconfig_t) +
  264. max_iteminfo * sizeof(iteminfo_t));
  265. ic->iteminfo = (iteminfo_t *) ((char *) ic + sizeof(itemconfig_t));
  266. ic->numiteminfo = 0;
  267. //parse the item config file
  268. while(PC_ReadToken(source, &token))
  269. {
  270. if (!strcmp(token.string, "iteminfo"))
  271. {
  272. if (ic->numiteminfo >= max_iteminfo)
  273. {
  274. SourceError(source, "more than %d item info defined\n", max_iteminfo);
  275. FreeMemory(ic);
  276. FreeSource(source);
  277. return NULL;
  278. } //end if
  279. ii = &ic->iteminfo[ic->numiteminfo];
  280. Com_Memset(ii, 0, sizeof(iteminfo_t));
  281. if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
  282. {
  283. FreeMemory(ic);
  284. FreeMemory(source);
  285. return NULL;
  286. } //end if
  287. StripDoubleQuotes(token.string);
  288. strncpy(ii->classname, token.string, sizeof(ii->classname)-1);
  289. if (!ReadStructure(source, &iteminfo_struct, (char *) ii))
  290. {
  291. FreeMemory(ic);
  292. FreeSource(source);
  293. return NULL;
  294. } //end if
  295. ii->number = ic->numiteminfo;
  296. ic->numiteminfo++;
  297. } //end if
  298. else
  299. {
  300. SourceError(source, "unknown definition %s\n", token.string);
  301. FreeMemory(ic);
  302. FreeSource(source);
  303. return NULL;
  304. } //end else
  305. } //end while
  306. FreeSource(source);
  307. //
  308. if (!ic->numiteminfo) botimport.Print(PRT_WARNING, "no item info loaded\n");
  309. botimport.Print(PRT_MESSAGE, "loaded %s\n", path);
  310. return ic;
  311. } //end of the function LoadItemConfig
  312. //===========================================================================
  313. // index to find the weight function of an iteminfo
  314. //
  315. // Parameter: -
  316. // Returns: -
  317. // Changes Globals: -
  318. //===========================================================================
  319. int *ItemWeightIndex(weightconfig_t *iwc, itemconfig_t *ic)
  320. {
  321. int *index, i;
  322. //initialize item weight index
  323. index = (int *) GetClearedMemory(sizeof(int) * ic->numiteminfo);
  324. for (i = 0; i < ic->numiteminfo; i++)
  325. {
  326. index[i] = FindFuzzyWeight(iwc, ic->iteminfo[i].classname);
  327. if (index[i] < 0)
  328. {
  329. Log_Write("item info %d \"%s\" has no fuzzy weight\r\n", i, ic->iteminfo[i].classname);
  330. } //end if
  331. } //end for
  332. return index;
  333. } //end of the function ItemWeightIndex
  334. //===========================================================================
  335. //
  336. // Parameter: -
  337. // Returns: -
  338. // Changes Globals: -
  339. //===========================================================================
  340. void InitLevelItemHeap(void)
  341. {
  342. int i, max_levelitems;
  343. if (levelitemheap) FreeMemory(levelitemheap);
  344. max_levelitems = (int) LibVarValue("max_levelitems", "256");
  345. levelitemheap = (levelitem_t *) GetClearedMemory(max_levelitems * sizeof(levelitem_t));
  346. for (i = 0; i < max_levelitems-1; i++)
  347. {
  348. levelitemheap[i].next = &levelitemheap[i + 1];
  349. } //end for
  350. levelitemheap[max_levelitems-1].next = NULL;
  351. //
  352. freelevelitems = levelitemheap;
  353. } //end of the function InitLevelItemHeap
  354. //===========================================================================
  355. //
  356. // Parameter: -
  357. // Returns: -
  358. // Changes Globals: -
  359. //===========================================================================
  360. levelitem_t *AllocLevelItem(void)
  361. {
  362. levelitem_t *li;
  363. li = freelevelitems;
  364. if (!li)
  365. {
  366. botimport.Print(PRT_FATAL, "out of level items\n");
  367. return NULL;
  368. } //end if
  369. //
  370. freelevelitems = freelevelitems->next;
  371. Com_Memset(li, 0, sizeof(levelitem_t));
  372. return li;
  373. } //end of the function AllocLevelItem
  374. //===========================================================================
  375. //
  376. // Parameter: -
  377. // Returns: -
  378. // Changes Globals: -
  379. //===========================================================================
  380. void FreeLevelItem(levelitem_t *li)
  381. {
  382. li->next = freelevelitems;
  383. freelevelitems = li;
  384. } //end of the function FreeLevelItem
  385. //===========================================================================
  386. //
  387. // Parameter: -
  388. // Returns: -
  389. // Changes Globals: -
  390. //===========================================================================
  391. void AddLevelItemToList(levelitem_t *li)
  392. {
  393. if (levelitems) levelitems->prev = li;
  394. li->prev = NULL;
  395. li->next = levelitems;
  396. levelitems = li;
  397. } //end of the function AddLevelItemToList
  398. //===========================================================================
  399. //
  400. // Parameter: -
  401. // Returns: -
  402. // Changes Globals: -
  403. //===========================================================================
  404. void RemoveLevelItemFromList(levelitem_t *li)
  405. {
  406. if (li->prev) li->prev->next = li->next;
  407. else levelitems = li->next;
  408. if (li->next) li->next->prev = li->prev;
  409. } //end of the function RemoveLevelItemFromList
  410. //===========================================================================
  411. //
  412. // Parameter: -
  413. // Returns: -
  414. // Changes Globals: -
  415. //===========================================================================
  416. void BotFreeInfoEntities(void)
  417. {
  418. maplocation_t *ml, *nextml;
  419. campspot_t *cs, *nextcs;
  420. for (ml = maplocations; ml; ml = nextml)
  421. {
  422. nextml = ml->next;
  423. FreeMemory(ml);
  424. } //end for
  425. maplocations = NULL;
  426. for (cs = campspots; cs; cs = nextcs)
  427. {
  428. nextcs = cs->next;
  429. FreeMemory(cs);
  430. } //end for
  431. campspots = NULL;
  432. } //end of the function BotFreeInfoEntities
  433. //===========================================================================
  434. //
  435. // Parameter: -
  436. // Returns: -
  437. // Changes Globals: -
  438. //===========================================================================
  439. void BotInitInfoEntities(void)
  440. {
  441. char classname[MAX_EPAIRKEY];
  442. maplocation_t *ml;
  443. campspot_t *cs;
  444. int ent, numlocations, numcampspots;
  445. BotFreeInfoEntities();
  446. //
  447. numlocations = 0;
  448. numcampspots = 0;
  449. for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
  450. {
  451. if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
  452. //map locations
  453. if (!strcmp(classname, "target_location"))
  454. {
  455. ml = (maplocation_t *) GetClearedMemory(sizeof(maplocation_t));
  456. AAS_VectorForBSPEpairKey(ent, "origin", ml->origin);
  457. AAS_ValueForBSPEpairKey(ent, "message", ml->name, sizeof(ml->name));
  458. ml->areanum = AAS_PointAreaNum(ml->origin);
  459. ml->next = maplocations;
  460. maplocations = ml;
  461. numlocations++;
  462. } //end if
  463. //camp spots
  464. else if (!strcmp(classname, "info_camp"))
  465. {
  466. cs = (campspot_t *) GetClearedMemory(sizeof(campspot_t));
  467. AAS_VectorForBSPEpairKey(ent, "origin", cs->origin);
  468. //cs->origin[2] += 16;
  469. AAS_ValueForBSPEpairKey(ent, "message", cs->name, sizeof(cs->name));
  470. AAS_FloatForBSPEpairKey(ent, "range", &cs->range);
  471. AAS_FloatForBSPEpairKey(ent, "weight", &cs->weight);
  472. AAS_FloatForBSPEpairKey(ent, "wait", &cs->wait);
  473. AAS_FloatForBSPEpairKey(ent, "random", &cs->random);
  474. cs->areanum = AAS_PointAreaNum(cs->origin);
  475. if (!cs->areanum)
  476. {
  477. botimport.Print(PRT_MESSAGE, "camp spot at %1.1f %1.1f %1.1f in solid\n", cs->origin[0], cs->origin[1], cs->origin[2]);
  478. FreeMemory(cs);
  479. continue;
  480. } //end if
  481. cs->next = campspots;
  482. campspots = cs;
  483. //AAS_DrawPermanentCross(cs->origin, 4, LINECOLOR_YELLOW);
  484. numcampspots++;
  485. } //end else if
  486. } //end for
  487. if (bot_developer)
  488. {
  489. botimport.Print(PRT_MESSAGE, "%d map locations\n", numlocations);
  490. botimport.Print(PRT_MESSAGE, "%d camp spots\n", numcampspots);
  491. } //end if
  492. } //end of the function BotInitInfoEntities
  493. //===========================================================================
  494. //
  495. // Parameter: -
  496. // Returns: -
  497. // Changes Globals: -
  498. //===========================================================================
  499. void BotInitLevelItems(void)
  500. {
  501. int i, spawnflags, value;
  502. char classname[MAX_EPAIRKEY];
  503. vec3_t origin, end;
  504. int ent, goalareanum;
  505. itemconfig_t *ic;
  506. levelitem_t *li;
  507. bsp_trace_t trace;
  508. //initialize the map locations and camp spots
  509. BotInitInfoEntities();
  510. //initialize the level item heap
  511. InitLevelItemHeap();
  512. levelitems = NULL;
  513. numlevelitems = 0;
  514. //
  515. ic = itemconfig;
  516. if (!ic) return;
  517. //if there's no AAS file loaded
  518. if (!AAS_Loaded()) return;
  519. //update the modelindexes of the item info
  520. for (i = 0; i < ic->numiteminfo; i++)
  521. {
  522. //ic->iteminfo[i].modelindex = AAS_IndexFromModel(ic->iteminfo[i].model);
  523. if (!ic->iteminfo[i].modelindex)
  524. {
  525. Log_Write("item %s has modelindex 0", ic->iteminfo[i].classname);
  526. } //end if
  527. } //end for
  528. for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
  529. {
  530. if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
  531. //
  532. spawnflags = 0;
  533. AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
  534. //
  535. for (i = 0; i < ic->numiteminfo; i++)
  536. {
  537. if (!strcmp(classname, ic->iteminfo[i].classname)) break;
  538. } //end for
  539. if (i >= ic->numiteminfo)
  540. {
  541. Log_Write("entity %s unknown item\r\n", classname);
  542. continue;
  543. } //end if
  544. //get the origin of the item
  545. if (!AAS_VectorForBSPEpairKey(ent, "origin", origin))
  546. {
  547. botimport.Print(PRT_ERROR, "item %s without origin\n", classname);
  548. continue;
  549. } //end else
  550. //
  551. goalareanum = 0;
  552. //if it is a floating item
  553. if (spawnflags & 1)
  554. {
  555. //if the item is not floating in water
  556. if (!(AAS_PointContents(origin) & CONTENTS_WATER))
  557. {
  558. VectorCopy(origin, end);
  559. end[2] -= 32;
  560. trace = AAS_Trace(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, end, -1, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
  561. //if the item not near the ground
  562. if (trace.fraction >= 1)
  563. {
  564. //if the item is not reachable from a jumppad
  565. goalareanum = AAS_BestReachableFromJumpPadArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs);
  566. Log_Write("item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum);
  567. //botimport.Print(PRT_MESSAGE, "item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum);
  568. if (!goalareanum) continue;
  569. } //end if
  570. } //end if
  571. } //end if
  572. li = AllocLevelItem();
  573. if (!li) return;
  574. //
  575. li->number = ++numlevelitems;
  576. li->timeout = 0;
  577. li->entitynum = 0;
  578. //
  579. li->flags = 0;
  580. AAS_IntForBSPEpairKey(ent, "notfree", &value);
  581. if (value) li->flags |= IFL_NOTFREE;
  582. AAS_IntForBSPEpairKey(ent, "notteam", &value);
  583. if (value) li->flags |= IFL_NOTTEAM;
  584. AAS_IntForBSPEpairKey(ent, "notsingle", &value);
  585. if (value) li->flags |= IFL_NOTSINGLE;
  586. AAS_IntForBSPEpairKey(ent, "notbot", &value);
  587. if (value) li->flags |= IFL_NOTBOT;
  588. if (!strcmp(classname, "item_botroam"))
  589. {
  590. li->flags |= IFL_ROAM;
  591. AAS_FloatForBSPEpairKey(ent, "weight", &li->weight);
  592. } //end if
  593. //if not a stationary item
  594. if (!(spawnflags & 1))
  595. {
  596. if (!AAS_DropToFloor(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs))
  597. {
  598. botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n",
  599. classname, origin[0], origin[1], origin[2]);
  600. } //end if
  601. } //end if
  602. //item info of the level item
  603. li->iteminfo = i;
  604. //origin of the item
  605. VectorCopy(origin, li->origin);
  606. //
  607. if (goalareanum)
  608. {
  609. li->goalareanum = goalareanum;
  610. VectorCopy(origin, li->goalorigin);
  611. } //end if
  612. else
  613. {
  614. //get the item goal area and goal origin
  615. li->goalareanum = AAS_BestReachableArea(origin,
  616. ic->iteminfo[i].mins, ic->iteminfo[i].maxs,
  617. li->goalorigin);
  618. if (!li->goalareanum)
  619. {
  620. botimport.Print(PRT_MESSAGE, "%s not reachable for bots at (%1.1f %1.1f %1.1f)\n",
  621. classname, origin[0], origin[1], origin[2]);
  622. } //end if
  623. } //end else
  624. //
  625. AddLevelItemToList(li);
  626. } //end for
  627. botimport.Print(PRT_MESSAGE, "found %d level items\n", numlevelitems);
  628. } //end of the function BotInitLevelItems
  629. //===========================================================================
  630. //
  631. // Parameter: -
  632. // Returns: -
  633. // Changes Globals: -
  634. //===========================================================================
  635. void BotGoalName(int number, char *name, int size)
  636. {
  637. levelitem_t *li;
  638. if (!itemconfig) return;
  639. //
  640. for (li = levelitems; li; li = li->next)
  641. {
  642. if (li->number == number)
  643. {
  644. strncpy(name, itemconfig->iteminfo[li->iteminfo].name, size-1);
  645. name[size-1] = '\0';
  646. return;
  647. } //end for
  648. } //end for
  649. strcpy(name, "");
  650. return;
  651. } //end of the function BotGoalName
  652. //===========================================================================
  653. //
  654. // Parameter: -
  655. // Returns: -
  656. // Changes Globals: -
  657. //===========================================================================
  658. void BotResetAvoidGoals(int goalstate)
  659. {
  660. bot_goalstate_t *gs;
  661. gs = BotGoalStateFromHandle(goalstate);
  662. if (!gs) return;
  663. Com_Memset(gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof(int));
  664. Com_Memset(gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof(float));
  665. } //end of the function BotResetAvoidGoals
  666. //===========================================================================
  667. //
  668. // Parameter: -
  669. // Returns: -
  670. // Changes Globals: -
  671. //===========================================================================
  672. void BotDumpAvoidGoals(int goalstate)
  673. {
  674. int i;
  675. bot_goalstate_t *gs;
  676. char name[32];
  677. gs = BotGoalStateFromHandle(goalstate);
  678. if (!gs) return;
  679. for (i = 0; i < MAX_AVOIDGOALS; i++)
  680. {
  681. if (gs->avoidgoaltimes[i] >= AAS_Time())
  682. {
  683. BotGoalName(gs->avoidgoals[i], name, 32);
  684. Log_Write("avoid goal %s, number %d for %f seconds", name,
  685. gs->avoidgoals[i], gs->avoidgoaltimes[i] - AAS_Time());
  686. } //end if
  687. } //end for
  688. } //end of the function BotDumpAvoidGoals
  689. //===========================================================================
  690. //
  691. // Parameter: -
  692. // Returns: -
  693. // Changes Globals: -
  694. //===========================================================================
  695. void BotAddToAvoidGoals(bot_goalstate_t *gs, int number, float avoidtime)
  696. {
  697. int i;
  698. for (i = 0; i < MAX_AVOIDGOALS; i++)
  699. {
  700. //if the avoid goal is already stored
  701. if (gs->avoidgoals[i] == number)
  702. {
  703. gs->avoidgoals[i] = number;
  704. gs->avoidgoaltimes[i] = AAS_Time() + avoidtime;
  705. return;
  706. } //end if
  707. } //end for
  708. for (i = 0; i < MAX_AVOIDGOALS; i++)
  709. {
  710. //if this avoid goal has expired
  711. if (gs->avoidgoaltimes[i] < AAS_Time())
  712. {
  713. gs->avoidgoals[i] = number;
  714. gs->avoidgoaltimes[i] = AAS_Time() + avoidtime;
  715. return;
  716. } //end if
  717. } //end for
  718. } //end of the function BotAddToAvoidGoals
  719. //===========================================================================
  720. //
  721. // Parameter: -
  722. // Returns: -
  723. // Changes Globals: -
  724. //===========================================================================
  725. void BotRemoveFromAvoidGoals(int goalstate, int number)
  726. {
  727. int i;
  728. bot_goalstate_t *gs;
  729. gs = BotGoalStateFromHandle(goalstate);
  730. if (!gs) return;
  731. //don't use the goals the bot wants to avoid
  732. for (i = 0; i < MAX_AVOIDGOALS; i++)
  733. {
  734. if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time())
  735. {
  736. gs->avoidgoaltimes[i] = 0;
  737. return;
  738. } //end if
  739. } //end for
  740. } //end of the function BotRemoveFromAvoidGoals
  741. //===========================================================================
  742. //
  743. // Parameter: -
  744. // Returns: -
  745. // Changes Globals: -
  746. //===========================================================================
  747. float BotAvoidGoalTime(int goalstate, int number)
  748. {
  749. int i;
  750. bot_goalstate_t *gs;
  751. gs = BotGoalStateFromHandle(goalstate);
  752. if (!gs) return 0;
  753. //don't use the goals the bot wants to avoid
  754. for (i = 0; i < MAX_AVOIDGOALS; i++)
  755. {
  756. if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time())
  757. {
  758. return gs->avoidgoaltimes[i] - AAS_Time();
  759. } //end if
  760. } //end for
  761. return 0;
  762. } //end of the function BotAvoidGoalTime
  763. //===========================================================================
  764. //
  765. // Parameter: -
  766. // Returns: -
  767. // Changes Globals: -
  768. //===========================================================================
  769. void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime)
  770. {
  771. bot_goalstate_t *gs;
  772. levelitem_t *li;
  773. gs = BotGoalStateFromHandle(goalstate);
  774. if (!gs)
  775. return;
  776. if (avoidtime < 0)
  777. {
  778. if (!itemconfig)
  779. return;
  780. //
  781. for (li = levelitems; li; li = li->next)
  782. {
  783. if (li->number == number)
  784. {
  785. avoidtime = itemconfig->iteminfo[li->iteminfo].respawntime;
  786. if (!avoidtime)
  787. avoidtime = AVOID_DEFAULT_TIME;
  788. if (avoidtime < AVOID_MINIMUM_TIME)
  789. avoidtime = AVOID_MINIMUM_TIME;
  790. BotAddToAvoidGoals(gs, number, avoidtime);
  791. return;
  792. } //end for
  793. } //end for
  794. return;
  795. } //end if
  796. else
  797. {
  798. BotAddToAvoidGoals(gs, number, avoidtime);
  799. } //end else
  800. } //end of the function BotSetAvoidGoalTime
  801. //===========================================================================
  802. //
  803. // Parameter: -
  804. // Returns: -
  805. // Changes Globals: -
  806. //===========================================================================
  807. int BotGetLevelItemGoal(int index, char *name, bot_goal_t *goal)
  808. {
  809. levelitem_t *li;
  810. if (!itemconfig) return -1;
  811. li = levelitems;
  812. if (index >= 0)
  813. {
  814. for (; li; li = li->next)
  815. {
  816. if (li->number == index)
  817. {
  818. li = li->next;
  819. break;
  820. } //end if
  821. } //end for
  822. } //end for
  823. for (; li; li = li->next)
  824. {
  825. //
  826. if (g_gametype == GT_SINGLE_PLAYER) {
  827. if (li->flags & IFL_NOTSINGLE) continue;
  828. }
  829. else if (g_gametype >= GT_TEAM) {
  830. if (li->flags & IFL_NOTTEAM) continue;
  831. }
  832. else {
  833. if (li->flags & IFL_NOTFREE) continue;
  834. }
  835. if (li->flags & IFL_NOTBOT) continue;
  836. //
  837. if (!Q_stricmp(name, itemconfig->iteminfo[li->iteminfo].name))
  838. {
  839. goal->areanum = li->goalareanum;
  840. VectorCopy(li->goalorigin, goal->origin);
  841. goal->entitynum = li->entitynum;
  842. VectorCopy(itemconfig->iteminfo[li->iteminfo].mins, goal->mins);
  843. VectorCopy(itemconfig->iteminfo[li->iteminfo].maxs, goal->maxs);
  844. goal->number = li->number;
  845. goal->flags = GFL_ITEM;
  846. if (li->timeout) goal->flags |= GFL_DROPPED;
  847. //botimport.Print(PRT_MESSAGE, "found li %s\n", itemconfig->iteminfo[li->iteminfo].name);
  848. return li->number;
  849. } //end if
  850. } //end for
  851. return -1;
  852. } //end of the function BotGetLevelItemGoal
  853. //===========================================================================
  854. //
  855. // Parameter: -
  856. // Returns: -
  857. // Changes Globals: -
  858. //===========================================================================
  859. int BotGetMapLocationGoal(char *name, bot_goal_t *goal)
  860. {
  861. maplocation_t *ml;
  862. vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8};
  863. for (ml = maplocations; ml; ml = ml->next)
  864. {
  865. if (!Q_stricmp(ml->name, name))
  866. {
  867. goal->areanum = ml->areanum;
  868. VectorCopy(ml->origin, goal->origin);
  869. goal->entitynum = 0;
  870. VectorCopy(mins, goal->mins);
  871. VectorCopy(maxs, goal->maxs);
  872. return qtrue;
  873. } //end if
  874. } //end for
  875. return qfalse;
  876. } //end of the function BotGetMapLocationGoal
  877. //===========================================================================
  878. //
  879. // Parameter: -
  880. // Returns: -
  881. // Changes Globals: -
  882. //===========================================================================
  883. int BotGetNextCampSpotGoal(int num, bot_goal_t *goal)
  884. {
  885. int i;
  886. campspot_t *cs;
  887. vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8};
  888. if (num < 0) num = 0;
  889. i = num;
  890. for (cs = campspots; cs; cs = cs->next)
  891. {
  892. if (--i < 0)
  893. {
  894. goal->areanum = cs->areanum;
  895. VectorCopy(cs->origin, goal->origin);
  896. goal->entitynum = 0;
  897. VectorCopy(mins, goal->mins);
  898. VectorCopy(maxs, goal->maxs);
  899. return num+1;
  900. } //end if
  901. } //end for
  902. return 0;
  903. } //end of the function BotGetNextCampSpotGoal
  904. //===========================================================================
  905. //
  906. // Parameter: -
  907. // Returns: -
  908. // Changes Globals: -
  909. //===========================================================================
  910. void BotFindEntityForLevelItem(levelitem_t *li)
  911. {
  912. int ent, modelindex;
  913. itemconfig_t *ic;
  914. aas_entityinfo_t entinfo;
  915. vec3_t dir;
  916. ic = itemconfig;
  917. if (!itemconfig) return;
  918. for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent))
  919. {
  920. //get the model index of the entity
  921. modelindex = AAS_EntityModelindex(ent);
  922. //
  923. if (!modelindex) continue;
  924. //get info about the entity
  925. AAS_EntityInfo(ent, &entinfo);
  926. //if the entity is still moving
  927. if (entinfo.origin[0] != entinfo.lastvisorigin[0] ||
  928. entinfo.origin[1] != entinfo.lastvisorigin[1] ||
  929. entinfo.origin[2] != entinfo.lastvisorigin[2]) continue;
  930. //
  931. if (ic->iteminfo[li->iteminfo].modelindex == modelindex)
  932. {
  933. //check if the entity is very close
  934. VectorSubtract(li->origin, entinfo.origin, dir);
  935. if (VectorLength(dir) < 30)
  936. {
  937. //found an entity for this level item
  938. li->entitynum = ent;
  939. } //end if
  940. } //end if
  941. } //end for
  942. } //end of the function BotFindEntityForLevelItem
  943. //===========================================================================
  944. //
  945. // Parameter: -
  946. // Returns: -
  947. // Changes Globals: -
  948. //===========================================================================
  949. //NOTE: enum entityType_t in bg_public.h
  950. #define ET_ITEM 2
  951. void BotUpdateEntityItems(void)
  952. {
  953. int ent, i, modelindex;
  954. vec3_t dir;
  955. levelitem_t *li, *nextli;
  956. aas_entityinfo_t entinfo;
  957. itemconfig_t *ic;
  958. //timeout current entity items if necessary
  959. for (li = levelitems; li; li = nextli)
  960. {
  961. nextli = li->next;
  962. //if it is a item that will time out
  963. if (li->timeout)
  964. {
  965. //timeout the item
  966. if (li->timeout < AAS_Time())
  967. {
  968. RemoveLevelItemFromList(li);
  969. FreeLevelItem(li);
  970. } //end if
  971. } //end if
  972. } //end for
  973. //find new entity items
  974. ic = itemconfig;
  975. if (!itemconfig) return;
  976. //
  977. for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent))
  978. {
  979. if (AAS_EntityType(ent) != ET_ITEM) continue;
  980. //get the model index of the entity
  981. modelindex = AAS_EntityModelindex(ent);
  982. //
  983. if (!modelindex) continue;
  984. //get info about the entity
  985. AAS_EntityInfo(ent, &entinfo);
  986. //FIXME: don't do this
  987. //skip all floating items for now
  988. //if (entinfo.groundent != ENTITYNUM_WORLD) continue;
  989. //if the entity is still moving
  990. if (entinfo.origin[0] != entinfo.lastvisorigin[0] ||
  991. entinfo.origin[1] != entinfo.lastvisorigin[1] ||
  992. entinfo.origin[2] != entinfo.lastvisorigin[2]) continue;
  993. //check if the entity is already stored as a level item
  994. for (li = levelitems; li; li = li->next)
  995. {
  996. //if the level item is linked to an entity
  997. if (li->entitynum && li->entitynum == ent)
  998. {
  999. //the entity is re-used if the models are different
  1000. if (ic->iteminfo[li->iteminfo].modelindex != modelindex)
  1001. {
  1002. //remove this level item
  1003. RemoveLevelItemFromList(li);
  1004. FreeLevelItem(li);
  1005. li = NULL;
  1006. break;
  1007. } //end if
  1008. else
  1009. {
  1010. if (entinfo.origin[0] != li->origin[0] ||
  1011. entinfo.origin[1] != li->origin[1] ||
  1012. entinfo.origin[2] != li->origin[2])
  1013. {
  1014. VectorCopy(entinfo.origin, li->origin);
  1015. //also update the goal area number
  1016. li->goalareanum = AAS_BestReachableArea(li->origin,
  1017. ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,
  1018. li->goalorigin);
  1019. } //end if
  1020. break;
  1021. } //end else
  1022. } //end if
  1023. } //end for
  1024. if (li) continue;
  1025. //try to link the entity to a level item
  1026. for (li = levelitems; li; li = li->next)
  1027. {
  1028. //if this level item is already linked
  1029. if (li->entitynum) continue;
  1030. //
  1031. if (g_gametype == GT_SINGLE_PLAYER) {
  1032. if (li->flags & IFL_NOTSINGLE) continue;
  1033. }
  1034. else if (g_gametype >= GT_TEAM) {
  1035. if (li->flags & IFL_NOTTEAM) continue;
  1036. }
  1037. else {
  1038. if (li->flags & IFL_NOTFREE) continue;
  1039. }
  1040. //if the model of the level item and the entity are the same
  1041. if (ic->iteminfo[li->iteminfo].modelindex == modelindex)
  1042. {
  1043. //check if the entity is very close
  1044. VectorSubtract(li->origin, entinfo.origin, dir);
  1045. if (VectorLength(dir) < 30)
  1046. {
  1047. //found an entity for this level item
  1048. li->entitynum = ent;
  1049. //if the origin is different
  1050. if (entinfo.origin[0] != li->origin[0] ||
  1051. entinfo.origin[1] != li->origin[1] ||
  1052. entinfo.origin[2] != li->origin[2])
  1053. {
  1054. //update the level item origin
  1055. VectorCopy(entinfo.origin, li->origin);
  1056. //also update the goal area number
  1057. li->goalareanum = AAS_BestReachableArea(li->origin,
  1058. ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,
  1059. li->goalorigin);
  1060. } //end if
  1061. #ifdef DEBUG
  1062. Log_Write("linked item %s to an entity", ic->iteminfo[li->iteminfo].classname);
  1063. #endif //DEBUG
  1064. break;
  1065. } //end if
  1066. } //end else
  1067. } //end for
  1068. if (li) continue;
  1069. //check if the model is from a known item
  1070. for (i = 0; i < ic->numiteminfo; i++)
  1071. {
  1072. if (ic->iteminfo[i].modelindex == modelindex)
  1073. {
  1074. break;
  1075. } //end if
  1076. } //end for
  1077. //if the model is not from a known item
  1078. if (i >= ic->numiteminfo) continue;
  1079. //allocate a new level item
  1080. li = AllocLevelItem();
  1081. //
  1082. if (!li) continue;
  1083. //entity number of the level item
  1084. li->entitynum = ent;
  1085. //number for the level item
  1086. li->number = numlevelitems + ent;
  1087. //set the item info index for the level item
  1088. li->iteminfo = i;
  1089. //origin of the item
  1090. VectorCopy(entinfo.origin, li->origin);
  1091. //get the item goal area and goal origin
  1092. li->goalareanum = AAS_BestReachableArea(li->origin,
  1093. ic->iteminfo[i].mins, ic->iteminfo[i].maxs,
  1094. li->goalorigin);
  1095. //never go for items dropped into jumppads
  1096. if (AAS_AreaJumpPad(li->goalareanum))
  1097. {
  1098. FreeLevelItem(li);
  1099. continue;
  1100. } //end if
  1101. //time this item out after 30 seconds
  1102. //dropped items disappear after 30 seconds
  1103. li->timeout = AAS_Time() + 30;
  1104. //add the level item to the list
  1105. AddLevelItemToList(li);
  1106. //botimport.Print(PRT_MESSAGE, "found new level item %s\n", ic->iteminfo[i].classname);
  1107. } //end for
  1108. /*
  1109. for (li = levelitems; li; li = li->next)
  1110. {
  1111. if (!li->entitynum)
  1112. {
  1113. BotFindEntityForLevelItem(li);
  1114. } //end if
  1115. } //end for*/
  1116. } //end of the function BotUpdateEntityItems
  1117. //===========================================================================
  1118. //
  1119. // Parameter: -
  1120. // Returns: -
  1121. // Changes Globals: -
  1122. //===========================================================================
  1123. void BotDumpGoalStack(int goalstate)
  1124. {
  1125. int i;
  1126. bot_goalstate_t *gs;
  1127. char name[32];
  1128. gs = BotGoalStateFromHandle(goalstate);
  1129. if (!gs) return;
  1130. for (i = 1; i <= gs->goalstacktop; i++)
  1131. {
  1132. BotGoalName(gs->goalstack[i].number, name, 32);
  1133. Log_Write("%d: %s", i, name);
  1134. } //end for
  1135. } //end of the function BotDumpGoalStack
  1136. //===========================================================================
  1137. //
  1138. // Parameter: -
  1139. // Returns: -
  1140. // Changes Globals: -
  1141. //===========================================================================
  1142. void BotPushGoal(int goalstate, bot_goal_t *goal)
  1143. {
  1144. bot_goalstate_t *gs;
  1145. gs = BotGoalStateFromHandle(goalstate);
  1146. if (!gs) return;
  1147. if (gs->goalstacktop >= MAX_GOALSTACK-1)
  1148. {
  1149. botimport.Print(PRT_ERROR, "goal heap overflow\n");
  1150. BotDumpGoalStack(goalstate);
  1151. return;
  1152. } //end if
  1153. gs->goalstacktop++;
  1154. Com_Memcpy(&gs->goalstack[gs->goalstacktop], goal, sizeof(bot_goal_t));
  1155. } //end of the function BotPushGoal
  1156. //===========================================================================
  1157. //
  1158. // Parameter: -
  1159. // Returns: -
  1160. // Changes Globals: -
  1161. //===========================================================================
  1162. void BotPopGoal(int goalstate)
  1163. {
  1164. bot_goalstate_t *gs;
  1165. gs = BotGoalStateFromHandle(goalstate);
  1166. if (!gs) return;
  1167. if (gs->goalstacktop > 0) gs->goalstacktop--;
  1168. } //end of the function BotPopGoal
  1169. //===========================================================================
  1170. //
  1171. // Parameter: -
  1172. // Returns: -
  1173. // Changes Globals: -
  1174. //===========================================================================
  1175. void BotEmptyGoalStack(int goalstate)
  1176. {
  1177. bot_goalstate_t *gs;
  1178. gs = BotGoalStateFromHandle(goalstate);
  1179. if (!gs) return;
  1180. gs->goalstacktop = 0;
  1181. } //end of the function BotEmptyGoalStack
  1182. //===========================================================================
  1183. //
  1184. // Parameter: -
  1185. // Returns: -
  1186. // Changes Globals: -
  1187. //===========================================================================
  1188. int BotGetTopGoal(int goalstate, bot_goal_t *goal)
  1189. {
  1190. bot_goalstate_t *gs;
  1191. gs = BotGoalStateFromHandle(goalstate);
  1192. if (!gs) return qfalse;
  1193. if (!gs->goalstacktop) return qfalse;
  1194. Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop], sizeof(bot_goal_t));
  1195. return qtrue;
  1196. } //end of the function BotGetTopGoal
  1197. //===========================================================================
  1198. //
  1199. // Parameter: -
  1200. // Returns: -
  1201. // Changes Globals: -
  1202. //===========================================================================
  1203. int BotGetSecondGoal(int goalstate, bot_goal_t *goal)
  1204. {
  1205. bot_goalstate_t *gs;
  1206. gs = BotGoalStateFromHandle(goalstate);
  1207. if (!gs) return qfalse;
  1208. if (gs->goalstacktop <= 1) return qfalse;
  1209. Com_Memcpy(goal, &gs->goalstack[gs->goalstacktop-1], sizeof(bot_goal_t));
  1210. return qtrue;
  1211. } //end of the function BotGetSecondGoal
  1212. //===========================================================================
  1213. // pops a new long term goal on the goal stack in the goalstate
  1214. //
  1215. // Parameter: -
  1216. // Returns: -
  1217. // Changes Globals: -
  1218. //===========================================================================
  1219. int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags)
  1220. {
  1221. int areanum, t, weightnum;
  1222. float weight, bestweight, avoidtime;
  1223. iteminfo_t *iteminfo;
  1224. itemconfig_t *ic;
  1225. levelitem_t *li, *bestitem;
  1226. bot_goal_t goal;
  1227. bot_goalstate_t *gs;
  1228. gs = BotGoalStateFromHandle(goalstate);
  1229. if (!gs)
  1230. return qfalse;
  1231. if (!gs->itemweightconfig)
  1232. return qfalse;
  1233. //get the area the bot is in
  1234. areanum = BotReachabilityArea(origin, gs->client);
  1235. //if the bot is in solid or if the area the bot is in has no reachability links
  1236. if (!areanum || !AAS_AreaReachability(areanum))
  1237. {
  1238. //use the last valid area the bot was in
  1239. areanum = gs->lastreachabilityarea;
  1240. } //end if
  1241. //remember the last area with reachabilities the bot was in
  1242. gs->lastreachabilityarea = areanum;
  1243. //if still in solid
  1244. if (!areanum)
  1245. return qfalse;
  1246. //the item configuration
  1247. ic = itemconfig;
  1248. if (!itemconfig)
  1249. return qfalse;
  1250. //best weight and item so far
  1251. bestweight = 0;
  1252. bestitem = NULL;
  1253. Com_Memset(&goal, 0, sizeof(bot_goal_t));
  1254. //go through the items in the level
  1255. for (li = levelitems; li; li = li->next)
  1256. {
  1257. if (g_gametype == GT_SINGLE_PLAYER) {
  1258. if (li->flags & IFL_NOTSINGLE)
  1259. continue;
  1260. }
  1261. else if (g_gametype >= GT_TEAM) {
  1262. if (li->flags & IFL_NOTTEAM)
  1263. continue;
  1264. }
  1265. else {
  1266. if (li->flags & IFL_NOTFREE)
  1267. continue;
  1268. }
  1269. if (li->flags & IFL_NOTBOT)
  1270. continue;
  1271. //if the item is not in a possible goal area
  1272. if (!li->goalareanum)
  1273. continue;
  1274. //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk)
  1275. if (!li->entitynum && !(li->flags & IFL_ROAM))
  1276. continue;
  1277. //get the fuzzy weight function for this item
  1278. iteminfo = &ic->iteminfo[li->iteminfo];
  1279. weightnum = gs->itemweightindex[iteminfo->number];
  1280. if (weightnum < 0)
  1281. continue;
  1282. #ifdef UNDECIDEDFUZZY
  1283. weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum);
  1284. #else
  1285. weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum);
  1286. #endif //UNDECIDEDFUZZY
  1287. #ifdef DROPPEDWEIGHT
  1288. //HACK: to make dropped items more attractive
  1289. if (li->timeout)
  1290. weight += droppedweight->value;
  1291. #endif //DROPPEDWEIGHT
  1292. //use weight scale for item_botroam
  1293. if (li->flags & IFL_ROAM) weight *= li->weight;
  1294. //
  1295. if (weight > 0)
  1296. {
  1297. //get the travel time towards the goal area
  1298. t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags);
  1299. //if the goal is reachable
  1300. if (t > 0)
  1301. {
  1302. //if this item won't respawn before we get there
  1303. avoidtime = BotAvoidGoalTime(goalstate, li->number);
  1304. if (avoidtime - t * 0.009 > 0)
  1305. continue;
  1306. //
  1307. weight /= (float) t * TRAVELTIME_SCALE;
  1308. //
  1309. if (weight > bestweight)
  1310. {
  1311. bestweight = weight;
  1312. bestitem = li;
  1313. } //end if
  1314. } //end if
  1315. } //end if
  1316. } //end for
  1317. //if no goal item found
  1318. if (!bestitem)
  1319. {
  1320. /*
  1321. //if not in lava or slime
  1322. if (!AAS_AreaLava(areanum) && !AAS_AreaSlime(areanum))
  1323. {
  1324. if (AAS_RandomGoalArea(areanum, travelflags, &goal.areanum, goal.origin))
  1325. {
  1326. VectorSet(goal.mins, -15, -15, -15);
  1327. VectorSet(goal.maxs, 15, 15, 15);
  1328. goal.entitynum = 0;
  1329. goal.number = 0;
  1330. goal.flags = GFL_ROAM;
  1331. goal.iteminfo = 0;
  1332. //push the goal on the stack
  1333. BotPushGoal(goalstate, &goal);
  1334. //
  1335. #ifdef DEBUG
  1336. botimport.Print(PRT_MESSAGE, "chosen roam goal area %d\n", goal.areanum);
  1337. #endif //DEBUG
  1338. return qtrue;
  1339. } //end if
  1340. } //end if
  1341. */
  1342. return qfalse;
  1343. } //end if
  1344. //create a bot goal for this item
  1345. iteminfo = &ic->iteminfo[bestitem->iteminfo];
  1346. VectorCopy(bestitem->goalorigin, goal.origin);
  1347. VectorCopy(iteminfo->mins, goal.mins);
  1348. VectorCopy(iteminfo->maxs, goal.maxs);
  1349. goal.areanum = bestitem->goalareanum;
  1350. goal.entitynum = bestitem->entitynum;
  1351. goal.number = bestitem->number;
  1352. goal.flags = GFL_ITEM;
  1353. if (bestitem->timeout)
  1354. goal.flags |= GFL_DROPPED;
  1355. if (bestitem->flags & IFL_ROAM)
  1356. goal.flags |= GFL_ROAM;
  1357. goal.iteminfo = bestitem->iteminfo;
  1358. //if it's a dropped item
  1359. if (bestitem->timeout)
  1360. {
  1361. avoidtime = AVOID_DROPPED_TIME;
  1362. } //end if
  1363. else
  1364. {
  1365. avoidtime = iteminfo->respawntime;
  1366. if (!avoidtime)
  1367. avoidtime = AVOID_DEFAULT_TIME;
  1368. if (avoidtime < AVOID_MINIMUM_TIME)
  1369. avoidtime = AVOID_MINIMUM_TIME;
  1370. } //end else
  1371. //add the chosen goal to the goals to avoid for a while
  1372. BotAddToAvoidGoals(gs, bestitem->number, avoidtime);
  1373. //push the goal on the stack
  1374. BotPushGoal(goalstate, &goal);
  1375. //
  1376. return qtrue;
  1377. } //end of the function BotChooseLTGItem
  1378. //===========================================================================
  1379. //
  1380. // Parameter: -
  1381. // Returns: -
  1382. // Changes Globals: -
  1383. //===========================================================================
  1384. int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags,
  1385. bot_goal_t *ltg, float maxtime)
  1386. {
  1387. int areanum, t, weightnum, ltg_time;
  1388. float weight, bestweight, avoidtime;
  1389. iteminfo_t *iteminfo;
  1390. itemconfig_t *ic;
  1391. levelitem_t *li, *bestitem;
  1392. bot_goal_t goal;
  1393. bot_goalstate_t *gs;
  1394. gs = BotGoalStateFromHandle(goalstate);
  1395. if (!gs)
  1396. return qfalse;
  1397. if (!gs->itemweightconfig)
  1398. return qfalse;
  1399. //get the area the bot is in
  1400. areanum = BotReachabilityArea(origin, gs->client);
  1401. //if the bot is in solid or if the area the bot is in has no reachability links
  1402. if (!areanum || !AAS_AreaReachability(areanum))
  1403. {
  1404. //use the last valid area the bot was in
  1405. areanum = gs->lastreachabilityarea;
  1406. } //end if
  1407. //remember the last area with reachabilities the bot was in
  1408. gs->lastreachabilityarea = areanum;
  1409. //if still in solid
  1410. if (!areanum)
  1411. return qfalse;
  1412. //
  1413. if (ltg) ltg_time = AAS_AreaTravelTimeToGoalArea(areanum, origin, ltg->areanum, travelflags);
  1414. else ltg_time = 99999;
  1415. //the item configuration
  1416. ic = itemconfig;
  1417. if (!itemconfig)
  1418. return qfalse;
  1419. //best weight and item so far
  1420. bestweight = 0;
  1421. bestitem = NULL;
  1422. Com_Memset(&goal, 0, sizeof(bot_goal_t));
  1423. //go through the items in the level
  1424. for (li = levelitems; li; li = li->next)
  1425. {
  1426. if (g_gametype == GT_SINGLE_PLAYER) {
  1427. if (li->flags & IFL_NOTSINGLE)
  1428. continue;
  1429. }
  1430. else if (g_gametype >= GT_TEAM) {
  1431. if (li->flags & IFL_NOTTEAM)
  1432. continue;
  1433. }
  1434. else {
  1435. if (li->flags & IFL_NOTFREE)
  1436. continue;
  1437. }
  1438. if (li->flags & IFL_NOTBOT)
  1439. continue;
  1440. //if the item is in a possible goal area
  1441. if (!li->goalareanum)
  1442. continue;
  1443. //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk)
  1444. if (!li->entitynum && !(li->flags & IFL_ROAM))
  1445. continue;
  1446. //get the fuzzy weight function for this item
  1447. iteminfo = &ic->iteminfo[li->iteminfo];
  1448. weightnum = gs->itemweightindex[iteminfo->number];
  1449. if (weightnum < 0)
  1450. continue;
  1451. //
  1452. #ifdef UNDECIDEDFUZZY
  1453. weight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum);
  1454. #else
  1455. weight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum);
  1456. #endif //UNDECIDEDFUZZY
  1457. #ifdef DROPPEDWEIGHT
  1458. //HACK: to make dropped items more attractive
  1459. if (li->timeout)
  1460. weight += droppedweight->value;
  1461. #endif //DROPPEDWEIGHT
  1462. //use weight scale for item_botroam
  1463. if (li->flags & IFL_ROAM) weight *= li->weight;
  1464. //
  1465. if (weight > 0)
  1466. {
  1467. //get the travel time towards the goal area
  1468. t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags);
  1469. //if the goal is reachable
  1470. if (t > 0 && t < maxtime)
  1471. {
  1472. //if this item won't respawn before we get there
  1473. avoidtime = BotAvoidGoalTime(goalstate, li->number);
  1474. if (avoidtime - t * 0.009 > 0)
  1475. continue;
  1476. //
  1477. weight /= (float) t * TRAVELTIME_SCALE;
  1478. //
  1479. if (weight > bestweight)
  1480. {
  1481. t = 0;
  1482. if (ltg && !li->timeout)
  1483. {
  1484. //get the travel time from the goal to the long term goal
  1485. t = AAS_AreaTravelTimeToGoalArea(li->goalareanum, li->goalorigin, ltg->areanum, travelflags);
  1486. } //end if
  1487. //if the travel back is possible and doesn't take too long
  1488. if (t <= ltg_time)
  1489. {
  1490. bestweight = weight;
  1491. bestitem = li;
  1492. } //end if
  1493. } //end if
  1494. } //end if
  1495. } //end if
  1496. } //end for
  1497. //if no goal item found
  1498. if (!bestitem)
  1499. return qfalse;
  1500. //create a bot goal for this item
  1501. iteminfo = &ic->iteminfo[bestitem->iteminfo];
  1502. VectorCopy(bestitem->goalorigin, goal.origin);
  1503. VectorCopy(iteminfo->mins, goal.mins);
  1504. VectorCopy(iteminfo->maxs, goal.maxs);
  1505. goal.areanum = bestitem->goalareanum;
  1506. goal.entitynum = bestitem->entitynum;
  1507. goal.number = bestitem->number;
  1508. goal.flags = GFL_ITEM;
  1509. if (bestitem->timeout)
  1510. goal.flags |= GFL_DROPPED;
  1511. if (bestitem->flags & IFL_ROAM)
  1512. goal.flags |= GFL_ROAM;
  1513. goal.iteminfo = bestitem->iteminfo;
  1514. //if it's a dropped item
  1515. if (bestitem->timeout)
  1516. {
  1517. avoidtime = AVOID_DROPPED_TIME;
  1518. } //end if
  1519. else
  1520. {
  1521. avoidtime = iteminfo->respawntime;
  1522. if (!avoidtime)
  1523. avoidtime = AVOID_DEFAULT_TIME;
  1524. if (avoidtime < AVOID_MINIMUM_TIME)
  1525. avoidtime = AVOID_MINIMUM_TIME;
  1526. } //end else
  1527. //add the chosen goal to the goals to avoid for a while
  1528. BotAddToAvoidGoals(gs, bestitem->number, avoidtime);
  1529. //push the goal on the stack
  1530. BotPushGoal(goalstate, &goal);
  1531. //
  1532. return qtrue;
  1533. } //end of the function BotChooseNBGItem
  1534. //===========================================================================
  1535. //
  1536. // Parameter: -
  1537. // Returns: -
  1538. // Changes Globals: -
  1539. //===========================================================================
  1540. int BotTouchingGoal(vec3_t origin, bot_goal_t *goal)
  1541. {
  1542. int i;
  1543. vec3_t boxmins, boxmaxs;
  1544. vec3_t absmins, absmaxs;
  1545. vec3_t safety_maxs = {0, 0, 0}; //{4, 4, 10};
  1546. vec3_t safety_mins = {0, 0, 0}; //{-4, -4, 0};
  1547. AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, boxmins, boxmaxs);
  1548. VectorSubtract(goal->mins, boxmaxs, absmins);
  1549. VectorSubtract(goal->maxs, boxmins, absmaxs);
  1550. VectorAdd(absmins, goal->origin, absmins);
  1551. VectorAdd(absmaxs, goal->origin, absmaxs);
  1552. //make the box a little smaller for safety
  1553. VectorSubtract(absmaxs, safety_maxs, absmaxs);
  1554. VectorSubtract(absmins, safety_mins, absmins);
  1555. for (i = 0; i < 3; i++)
  1556. {
  1557. if (origin[i] < absmins[i] || origin[i] > absmaxs[i]) return qfalse;
  1558. } //end for
  1559. return qtrue;
  1560. } //end of the function BotTouchingGoal
  1561. //===========================================================================
  1562. //
  1563. // Parameter: -
  1564. // Returns: -
  1565. // Changes Globals: -
  1566. //===========================================================================
  1567. int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal)
  1568. {
  1569. aas_entityinfo_t entinfo;
  1570. bsp_trace_t trace;
  1571. vec3_t middle;
  1572. if (!(goal->flags & GFL_ITEM)) return qfalse;
  1573. //
  1574. VectorAdd(goal->mins, goal->mins, middle);
  1575. VectorScale(middle, 0.5, middle);
  1576. VectorAdd(goal->origin, middle, middle);
  1577. //
  1578. trace = AAS_Trace(eye, NULL, NULL, middle, viewer, CONTENTS_SOLID);
  1579. //if the goal middle point is visible
  1580. if (trace.fraction >= 1)
  1581. {
  1582. //the goal entity number doesn't have to be valid
  1583. //just assume it's valid
  1584. if (goal->entitynum <= 0)
  1585. return qfalse;
  1586. //
  1587. //if the entity data isn't valid
  1588. AAS_EntityInfo(goal->entitynum, &entinfo);
  1589. //NOTE: for some wacko reason entities are sometimes
  1590. // not updated
  1591. //if (!entinfo.valid) return qtrue;
  1592. if (entinfo.ltime < AAS_Time() - 0.5)
  1593. return qtrue;
  1594. } //end if
  1595. return qfalse;
  1596. } //end of the function BotItemGoalInVisButNotVisible
  1597. //===========================================================================
  1598. //
  1599. // Parameter: -
  1600. // Returns: -
  1601. // Changes Globals: -
  1602. //===========================================================================
  1603. void BotResetGoalState(int goalstate)
  1604. {
  1605. bot_goalstate_t *gs;
  1606. gs = BotGoalStateFromHandle(goalstate);
  1607. if (!gs) return;
  1608. Com_Memset(gs->goalstack, 0, MAX_GOALSTACK * sizeof(bot_goal_t));
  1609. gs->goalstacktop = 0;
  1610. BotResetAvoidGoals(goalstate);
  1611. } //end of the function BotResetGoalState
  1612. //===========================================================================
  1613. //
  1614. // Parameter: -
  1615. // Returns: -
  1616. // Changes Globals: -
  1617. //===========================================================================
  1618. int BotLoadItemWeights(int goalstate, char *filename)
  1619. {
  1620. bot_goalstate_t *gs;
  1621. gs = BotGoalStateFromHandle(goalstate);
  1622. if (!gs) return BLERR_CANNOTLOADITEMWEIGHTS;
  1623. //load the weight configuration
  1624. gs->itemweightconfig = ReadWeightConfig(filename);
  1625. if (!gs->itemweightconfig)
  1626. {
  1627. botimport.Print(PRT_FATAL, "couldn't load weights\n");
  1628. return BLERR_CANNOTLOADITEMWEIGHTS;
  1629. } //end if
  1630. //if there's no item configuration
  1631. if (!itemconfig) return BLERR_CANNOTLOADITEMWEIGHTS;
  1632. //create the item weight index
  1633. gs->itemweightindex = ItemWeightIndex(gs->itemweightconfig, itemconfig);
  1634. //everything went ok
  1635. return BLERR_NOERROR;
  1636. } //end of the function BotLoadItemWeights
  1637. //===========================================================================
  1638. //
  1639. // Parameter: -
  1640. // Returns: -
  1641. // Changes Globals: -
  1642. //===========================================================================
  1643. void BotFreeItemWeights(int goalstate)
  1644. {
  1645. bot_goalstate_t *gs;
  1646. gs = BotGoalStateFromHandle(goalstate);
  1647. if (!gs) return;
  1648. if (gs->itemweightconfig) FreeWeightConfig(gs->itemweightconfig);
  1649. if (gs->itemweightindex) FreeMemory(gs->itemweightindex);
  1650. } //end of the function BotFreeItemWeights
  1651. //===========================================================================
  1652. //
  1653. // Parameter: -
  1654. // Returns: -
  1655. // Changes Globals: -
  1656. //===========================================================================
  1657. int BotAllocGoalState(int client)
  1658. {
  1659. int i;
  1660. for (i = 1; i <= MAX_CLIENTS; i++)
  1661. {
  1662. if (!botgoalstates[i])
  1663. {
  1664. botgoalstates[i] = GetClearedMemory(sizeof(bot_goalstate_t));
  1665. botgoalstates[i]->client = client;
  1666. return i;
  1667. } //end if
  1668. } //end for
  1669. return 0;
  1670. } //end of the function BotAllocGoalState
  1671. //========================================================================
  1672. //
  1673. // Parameter: -
  1674. // Returns: -
  1675. // Changes Globals: -
  1676. //========================================================================
  1677. void BotFreeGoalState(int handle)
  1678. {
  1679. if (handle <= 0 || handle > MAX_CLIENTS)
  1680. {
  1681. botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle);
  1682. return;
  1683. } //end if
  1684. if (!botgoalstates[handle])
  1685. {
  1686. botimport.Print(PRT_FATAL, "invalid goal state handle %d\n", handle);
  1687. return;
  1688. } //end if
  1689. BotFreeItemWeights(handle);
  1690. FreeMemory(botgoalstates[handle]);
  1691. botgoalstates[handle] = NULL;
  1692. } //end of the function BotFreeGoalState
  1693. //===========================================================================
  1694. //
  1695. // Parameter: -
  1696. // Returns: -
  1697. // Changes Globals: -
  1698. //===========================================================================
  1699. int BotSetupGoalAI(void)
  1700. {
  1701. char *filename;
  1702. //check if teamplay is on
  1703. g_gametype = LibVarValue("g_gametype", "0");
  1704. //item configuration file
  1705. filename = LibVarString("itemconfig", "items.c");
  1706. //load the item configuration
  1707. itemconfig = LoadItemConfig(filename);
  1708. if (!itemconfig)
  1709. {
  1710. botimport.Print(PRT_FATAL, "couldn't load item config\n");
  1711. return BLERR_CANNOTLOADITEMCONFIG;
  1712. } //end if
  1713. //
  1714. droppedweight = LibVar("droppedweight", "1000");
  1715. //everything went ok
  1716. return BLERR_NOERROR;
  1717. } //end of the function BotSetupGoalAI
  1718. //===========================================================================
  1719. //
  1720. // Parameter: -
  1721. // Returns: -
  1722. // Changes Globals: -
  1723. //===========================================================================
  1724. void BotShutdownGoalAI(void)
  1725. {
  1726. int i;
  1727. if (itemconfig) FreeMemory(itemconfig);
  1728. itemconfig = NULL;
  1729. if (levelitemheap) FreeMemory(levelitemheap);
  1730. levelitemheap = NULL;
  1731. freelevelitems = NULL;
  1732. levelitems = NULL;
  1733. numlevelitems = 0;
  1734. BotFreeInfoEntities();
  1735. for (i = 1; i <= MAX_CLIENTS; i++)
  1736. {
  1737. if (botgoalstates[i])
  1738. {
  1739. BotFreeGoalState(i);
  1740. } //end if
  1741. } //end for
  1742. } //end of the function BotShutdownGoalAI