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_utils.h"
00034 #include "l_libvar.h"
00035 #include "l_memory.h"
00036 #include "l_log.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_goal.h"
00047 #include "../game/be_ai_move.h"
00048
00049
00050 #ifdef RANDOMIZE
00051 #define UNDECIDEDFUZZY
00052 #endif //RANDOMIZE
00053 #define DROPPEDWEIGHT
00054
00055 #define AVOID_MINIMUM_TIME 10
00056
00057 #define AVOID_DEFAULT_TIME 30
00058
00059 #define AVOID_DROPPED_TIME 10
00060
00061 #define TRAVELTIME_SCALE 0.01
00062
00063 #define IFL_NOTFREE 1 //not in free for all
00064 #define IFL_NOTTEAM 2 //not in team play
00065 #define IFL_NOTSINGLE 4 //not in single player
00066 #define IFL_NOTBOT 8 //bot should never go for this
00067 #define IFL_ROAM 16 //bot roam goal
00068
00069
00070 typedef struct maplocation_s
00071 {
00072 vec3_t origin;
00073 int areanum;
00074 char name[MAX_EPAIRKEY];
00075 struct maplocation_s *next;
00076 } maplocation_t;
00077
00078
00079 typedef struct campspot_s
00080 {
00081 vec3_t origin;
00082 int areanum;
00083 char name[MAX_EPAIRKEY];
00084 float range;
00085 float weight;
00086 float wait;
00087 float random;
00088 struct campspot_s *next;
00089 } campspot_t;
00090
00091
00092 typedef enum {
00093 GT_FFA,
00094 GT_TOURNAMENT,
00095 GT_SINGLE_PLAYER,
00096
00097
00098
00099 GT_TEAM,
00100 GT_CTF,
00101 #ifdef MISSIONPACK
00102 GT_1FCTF,
00103 GT_OBELISK,
00104 GT_HARVESTER,
00105 #endif
00106 GT_MAX_GAME_TYPE
00107 } gametype_t;
00108
00109 typedef struct levelitem_s
00110 {
00111 int number;
00112 int iteminfo;
00113 int flags;
00114 float weight;
00115 vec3_t origin;
00116 int goalareanum;
00117 vec3_t goalorigin;
00118 int entitynum;
00119 float timeout;
00120 struct levelitem_s *prev, *next;
00121 } levelitem_t;
00122
00123 typedef struct iteminfo_s
00124 {
00125 char classname[32];
00126 char name[MAX_STRINGFIELD];
00127 char model[MAX_STRINGFIELD];
00128 int modelindex;
00129 int type;
00130 int index;
00131 float respawntime;
00132 vec3_t mins;
00133 vec3_t maxs;
00134 int number;
00135 } iteminfo_t;
00136
00137 #define ITEMINFO_OFS(x) (int)&(((iteminfo_t *)0)->x)
00138
00139 fielddef_t iteminfo_fields[] =
00140 {
00141 {"name", ITEMINFO_OFS(name), FT_STRING},
00142 {"model", ITEMINFO_OFS(model), FT_STRING},
00143 {"modelindex", ITEMINFO_OFS(modelindex), FT_INT},
00144 {"type", ITEMINFO_OFS(type), FT_INT},
00145 {"index", ITEMINFO_OFS(index), FT_INT},
00146 {"respawntime", ITEMINFO_OFS(respawntime), FT_FLOAT},
00147 {"mins", ITEMINFO_OFS(mins), FT_FLOAT|FT_ARRAY, 3},
00148 {"maxs", ITEMINFO_OFS(maxs), FT_FLOAT|FT_ARRAY, 3},
00149 {0, 0, 0}
00150 };
00151
00152 structdef_t iteminfo_struct =
00153 {
00154 sizeof(iteminfo_t), iteminfo_fields
00155 };
00156
00157 typedef struct itemconfig_s
00158 {
00159 int numiteminfo;
00160 iteminfo_t *iteminfo;
00161 } itemconfig_t;
00162
00163
00164 typedef struct bot_goalstate_s
00165 {
00166 struct weightconfig_s *itemweightconfig;
00167 int *itemweightindex;
00168
00169 int client;
00170 int lastreachabilityarea;
00171
00172 bot_goal_t goalstack[MAX_GOALSTACK];
00173 int goalstacktop;
00174
00175 int avoidgoals[MAX_AVOIDGOALS];
00176 float avoidgoaltimes[MAX_AVOIDGOALS];
00177 } bot_goalstate_t;
00178
00179 bot_goalstate_t *botgoalstates[MAX_CLIENTS + 1];
00180
00181 itemconfig_t *itemconfig = NULL;
00182
00183 levelitem_t *levelitemheap = NULL;
00184 levelitem_t *freelevelitems = NULL;
00185 levelitem_t *levelitems = NULL;
00186 int numlevelitems = 0;
00187
00188 maplocation_t *maplocations = NULL;
00189
00190 campspot_t *campspots = NULL;
00191
00192 int g_gametype = 0;
00193
00194 libvar_t *droppedweight = NULL;
00195
00196
00197
00198
00199
00200
00201
00202 bot_goalstate_t *BotGoalStateFromHandle(int handle)
00203 {
00204 if (handle <= 0 || handle > MAX_CLIENTS)
00205 {
00206 botimport.Print(PRT_FATAL, "goal state handle %d out of range\n", handle);
00207 return NULL;
00208 }
00209 if (!botgoalstates[handle])
00210 {
00211 botimport.Print(PRT_FATAL, "invalid goal state %d\n", handle);
00212 return NULL;
00213 }
00214 return botgoalstates[handle];
00215 }
00216
00217
00218
00219
00220
00221
00222 void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child)
00223 {
00224 bot_goalstate_t *p1, *p2, *c;
00225
00226 p1 = BotGoalStateFromHandle(parent1);
00227 p2 = BotGoalStateFromHandle(parent2);
00228 c = BotGoalStateFromHandle(child);
00229
00230 InterbreedWeightConfigs(p1->itemweightconfig, p2->itemweightconfig,
00231 c->itemweightconfig);
00232 }
00233
00234
00235
00236
00237
00238
00239 void BotSaveGoalFuzzyLogic(int goalstate, char *filename)
00240 {
00241 bot_goalstate_t *gs;
00242
00243 gs = BotGoalStateFromHandle(goalstate);
00244
00245
00246 }
00247
00248
00249
00250
00251
00252
00253 void BotMutateGoalFuzzyLogic(int goalstate, float range)
00254 {
00255 bot_goalstate_t *gs;
00256
00257 gs = BotGoalStateFromHandle(goalstate);
00258
00259 EvolveWeightConfig(gs->itemweightconfig);
00260 }
00261
00262
00263
00264
00265
00266
00267 itemconfig_t *LoadItemConfig(char *filename)
00268 {
00269 int max_iteminfo;
00270 token_t token;
00271 char path[MAX_PATH];
00272 source_t *source;
00273 itemconfig_t *ic;
00274 iteminfo_t *ii;
00275
00276 max_iteminfo = (int) LibVarValue("max_iteminfo", "256");
00277 if (max_iteminfo < 0)
00278 {
00279 botimport.Print(PRT_ERROR, "max_iteminfo = %d\n", max_iteminfo);
00280 max_iteminfo = 256;
00281 LibVarSet( "max_iteminfo", "256" );
00282 }
00283
00284 strncpy( path, filename, MAX_PATH );
00285 PC_SetBaseFolder(BOTFILESBASEFOLDER);
00286 source = LoadSourceFile( path );
00287 if( !source ) {
00288 botimport.Print( PRT_ERROR, "counldn't load %s\n", path );
00289 return NULL;
00290 }
00291
00292 ic = (itemconfig_t *) GetClearedHunkMemory(sizeof(itemconfig_t) +
00293 max_iteminfo * sizeof(iteminfo_t));
00294 ic->iteminfo = (iteminfo_t *) ((char *) ic + sizeof(itemconfig_t));
00295 ic->numiteminfo = 0;
00296
00297 while(PC_ReadToken(source, &token))
00298 {
00299 if (!strcmp(token.string, "iteminfo"))
00300 {
00301 if (ic->numiteminfo >= max_iteminfo)
00302 {
00303 SourceError(source, "more than %d item info defined\n", max_iteminfo);
00304 FreeMemory(ic);
00305 FreeSource(source);
00306 return NULL;
00307 }
00308 ii = &ic->iteminfo[ic->numiteminfo];
00309 Com_Memset(ii, 0, sizeof(iteminfo_t));
00310 if (!PC_ExpectTokenType(source, TT_STRING, 0, &token))
00311 {
00312 FreeMemory(ic);
00313 FreeMemory(source);
00314 return NULL;
00315 }
00316 StripDoubleQuotes(token.string);
00317 strncpy(ii->classname, token.string, sizeof(ii->classname)-1);
00318 if (!ReadStructure(source, &iteminfo_struct, (char *) ii))
00319 {
00320 FreeMemory(ic);
00321 FreeSource(source);
00322 return NULL;
00323 }
00324 ii->number = ic->numiteminfo;
00325 ic->numiteminfo++;
00326 }
00327 else
00328 {
00329 SourceError(source, "unknown definition %s\n", token.string);
00330 FreeMemory(ic);
00331 FreeSource(source);
00332 return NULL;
00333 }
00334 }
00335 FreeSource(source);
00336
00337 if (!ic->numiteminfo) botimport.Print(PRT_WARNING, "no item info loaded\n");
00338 botimport.Print(PRT_MESSAGE, "loaded %s\n", path);
00339 return ic;
00340 }
00341
00342
00343
00344
00345
00346
00347
00348 int *ItemWeightIndex(weightconfig_t *iwc, itemconfig_t *ic)
00349 {
00350 int *index, i;
00351
00352
00353 index = (int *) GetClearedMemory(sizeof(int) * ic->numiteminfo);
00354
00355 for (i = 0; i < ic->numiteminfo; i++)
00356 {
00357 index[i] = FindFuzzyWeight(iwc, ic->iteminfo[i].classname);
00358 if (index[i] < 0)
00359 {
00360 Log_Write("item info %d \"%s\" has no fuzzy weight\r\n", i, ic->iteminfo[i].classname);
00361 }
00362 }
00363 return index;
00364 }
00365
00366
00367
00368
00369
00370
00371 void InitLevelItemHeap(void)
00372 {
00373 int i, max_levelitems;
00374
00375 if (levelitemheap) FreeMemory(levelitemheap);
00376
00377 max_levelitems = (int) LibVarValue("max_levelitems", "256");
00378 levelitemheap = (levelitem_t *) GetClearedMemory(max_levelitems * sizeof(levelitem_t));
00379
00380 for (i = 0; i < max_levelitems-1; i++)
00381 {
00382 levelitemheap[i].next = &levelitemheap[i + 1];
00383 }
00384 levelitemheap[max_levelitems-1].next = NULL;
00385
00386 freelevelitems = levelitemheap;
00387 }
00388
00389
00390
00391
00392
00393
00394 levelitem_t *AllocLevelItem(void)
00395 {
00396 levelitem_t *li;
00397
00398 li = freelevelitems;
00399 if (!li)
00400 {
00401 botimport.Print(PRT_FATAL, "out of level items\n");
00402 return NULL;
00403 }
00404
00405 freelevelitems = freelevelitems->next;
00406 Com_Memset(li, 0, sizeof(levelitem_t));
00407 return li;
00408 }
00409
00410
00411
00412
00413
00414
00415 void FreeLevelItem(levelitem_t *li)
00416 {
00417 li->next = freelevelitems;
00418 freelevelitems = li;
00419 }
00420
00421
00422
00423
00424
00425
00426 void AddLevelItemToList(levelitem_t *li)
00427 {
00428 if (levelitems) levelitems->prev = li;
00429 li->prev = NULL;
00430 li->next = levelitems;
00431 levelitems = li;
00432 }
00433
00434
00435
00436
00437
00438
00439 void RemoveLevelItemFromList(levelitem_t *li)
00440 {
00441 if (li->prev) li->prev->next = li->next;
00442 else levelitems = li->next;
00443 if (li->next) li->next->prev = li->prev;
00444 }
00445
00446
00447
00448
00449
00450
00451 void BotFreeInfoEntities(void)
00452 {
00453 maplocation_t *ml, *nextml;
00454 campspot_t *cs, *nextcs;
00455
00456 for (ml = maplocations; ml; ml = nextml)
00457 {
00458 nextml = ml->next;
00459 FreeMemory(ml);
00460 }
00461 maplocations = NULL;
00462 for (cs = campspots; cs; cs = nextcs)
00463 {
00464 nextcs = cs->next;
00465 FreeMemory(cs);
00466 }
00467 campspots = NULL;
00468 }
00469
00470
00471
00472
00473
00474
00475 void BotInitInfoEntities(void)
00476 {
00477 char classname[MAX_EPAIRKEY];
00478 maplocation_t *ml;
00479 campspot_t *cs;
00480 int ent, numlocations, numcampspots;
00481
00482 BotFreeInfoEntities();
00483
00484 numlocations = 0;
00485 numcampspots = 0;
00486 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
00487 {
00488 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
00489
00490
00491 if (!strcmp(classname, "target_location"))
00492 {
00493 ml = (maplocation_t *) GetClearedMemory(sizeof(maplocation_t));
00494 AAS_VectorForBSPEpairKey(ent, "origin", ml->origin);
00495 AAS_ValueForBSPEpairKey(ent, "message", ml->name, sizeof(ml->name));
00496 ml->areanum = AAS_PointAreaNum(ml->origin);
00497 ml->next = maplocations;
00498 maplocations = ml;
00499 numlocations++;
00500 }
00501
00502 else if (!strcmp(classname, "info_camp"))
00503 {
00504 cs = (campspot_t *) GetClearedMemory(sizeof(campspot_t));
00505 AAS_VectorForBSPEpairKey(ent, "origin", cs->origin);
00506
00507 AAS_ValueForBSPEpairKey(ent, "message", cs->name, sizeof(cs->name));
00508 AAS_FloatForBSPEpairKey(ent, "range", &cs->range);
00509 AAS_FloatForBSPEpairKey(ent, "weight", &cs->weight);
00510 AAS_FloatForBSPEpairKey(ent, "wait", &cs->wait);
00511 AAS_FloatForBSPEpairKey(ent, "random", &cs->random);
00512 cs->areanum = AAS_PointAreaNum(cs->origin);
00513 if (!cs->areanum)
00514 {
00515 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]);
00516 FreeMemory(cs);
00517 continue;
00518 }
00519 cs->next = campspots;
00520 campspots = cs;
00521
00522 numcampspots++;
00523 }
00524 }
00525 if (bot_developer)
00526 {
00527 botimport.Print(PRT_MESSAGE, "%d map locations\n", numlocations);
00528 botimport.Print(PRT_MESSAGE, "%d camp spots\n", numcampspots);
00529 }
00530 }
00531
00532
00533
00534
00535
00536
00537 void BotInitLevelItems(void)
00538 {
00539 int i, spawnflags, value;
00540 char classname[MAX_EPAIRKEY];
00541 vec3_t origin, end;
00542 int ent, goalareanum;
00543 itemconfig_t *ic;
00544 levelitem_t *li;
00545 bsp_trace_t trace;
00546
00547
00548 BotInitInfoEntities();
00549
00550
00551 InitLevelItemHeap();
00552 levelitems = NULL;
00553 numlevelitems = 0;
00554
00555 ic = itemconfig;
00556 if (!ic) return;
00557
00558
00559 if (!AAS_Loaded()) return;
00560
00561
00562 for (i = 0; i < ic->numiteminfo; i++)
00563 {
00564
00565 if (!ic->iteminfo[i].modelindex)
00566 {
00567 Log_Write("item %s has modelindex 0", ic->iteminfo[i].classname);
00568 }
00569 }
00570
00571 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
00572 {
00573 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
00574
00575 spawnflags = 0;
00576 AAS_IntForBSPEpairKey(ent, "spawnflags", &spawnflags);
00577
00578 for (i = 0; i < ic->numiteminfo; i++)
00579 {
00580 if (!strcmp(classname, ic->iteminfo[i].classname)) break;
00581 }
00582 if (i >= ic->numiteminfo)
00583 {
00584 Log_Write("entity %s unknown item\r\n", classname);
00585 continue;
00586 }
00587
00588 if (!AAS_VectorForBSPEpairKey(ent, "origin", origin))
00589 {
00590 botimport.Print(PRT_ERROR, "item %s without origin\n", classname);
00591 continue;
00592 }
00593
00594 goalareanum = 0;
00595
00596 if (spawnflags & 1)
00597 {
00598
00599 if (!(AAS_PointContents(origin) & CONTENTS_WATER))
00600 {
00601 VectorCopy(origin, end);
00602 end[2] -= 32;
00603 trace = AAS_Trace(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, end, -1, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
00604
00605 if (trace.fraction >= 1)
00606 {
00607
00608 goalareanum = AAS_BestReachableFromJumpPadArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs);
00609 Log_Write("item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum);
00610
00611 if (!goalareanum) continue;
00612 }
00613 }
00614 }
00615
00616 li = AllocLevelItem();
00617 if (!li) return;
00618
00619 li->number = ++numlevelitems;
00620 li->timeout = 0;
00621 li->entitynum = 0;
00622
00623 li->flags = 0;
00624 AAS_IntForBSPEpairKey(ent, "notfree", &value);
00625 if (value) li->flags |= IFL_NOTFREE;
00626 AAS_IntForBSPEpairKey(ent, "notteam", &value);
00627 if (value) li->flags |= IFL_NOTTEAM;
00628 AAS_IntForBSPEpairKey(ent, "notsingle", &value);
00629 if (value) li->flags |= IFL_NOTSINGLE;
00630 AAS_IntForBSPEpairKey(ent, "notbot", &value);
00631 if (value) li->flags |= IFL_NOTBOT;
00632 if (!strcmp(classname, "item_botroam"))
00633 {
00634 li->flags |= IFL_ROAM;
00635 AAS_FloatForBSPEpairKey(ent, "weight", &li->weight);
00636 }
00637
00638 if (!(spawnflags & 1))
00639 {
00640 if (!AAS_DropToFloor(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs))
00641 {
00642 botimport.Print(PRT_MESSAGE, "%s in solid at (%1.1f %1.1f %1.1f)\n",
00643 classname, origin[0], origin[1], origin[2]);
00644 }
00645 }
00646
00647 li->iteminfo = i;
00648
00649 VectorCopy(origin, li->origin);
00650
00651 if (goalareanum)
00652 {
00653 li->goalareanum = goalareanum;
00654 VectorCopy(origin, li->goalorigin);
00655 }
00656 else
00657 {
00658
00659 li->goalareanum = AAS_BestReachableArea(origin,
00660 ic->iteminfo[i].mins, ic->iteminfo[i].maxs,
00661 li->goalorigin);
00662 if (!li->goalareanum)
00663 {
00664 botimport.Print(PRT_MESSAGE, "%s not reachable for bots at (%1.1f %1.1f %1.1f)\n",
00665 classname, origin[0], origin[1], origin[2]);
00666 }
00667 }
00668
00669 AddLevelItemToList(li);
00670 }
00671 botimport.Print(PRT_MESSAGE, "found %d level items\n", numlevelitems);
00672 }
00673
00674
00675
00676
00677
00678
00679 void BotGoalName(int number, char *name, int size)
00680 {
00681 levelitem_t *li;
00682
00683 if (!itemconfig) return;
00684
00685 for (li = levelitems; li; li = li->next)
00686 {
00687 if (li->number == number)
00688 {
00689 strncpy(name, itemconfig->iteminfo[li->iteminfo].name, size-1);
00690 name[size-1] = '\0';
00691 return;
00692 }
00693 }
00694 strcpy(name, "");
00695 return;
00696 }
00697
00698
00699
00700
00701
00702
00703 void BotResetAvoidGoals(int goalstate)
00704 {
00705 bot_goalstate_t *gs;
00706
00707 gs = BotGoalStateFromHandle(goalstate);
00708 if (!gs) return;
00709 Com_Memset(gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof(int));
00710 Com_Memset(gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof(float));
00711 }
00712
00713
00714
00715
00716
00717
00718 void BotDumpAvoidGoals(int goalstate)
00719 {
00720 int i;
00721 bot_goalstate_t *gs;
00722 char name[32];
00723
00724 gs = BotGoalStateFromHandle(goalstate);
00725 if (!gs) return;
00726 for (i = 0; i < MAX_AVOIDGOALS; i++)
00727 {
00728 if (gs->avoidgoaltimes[i] >= AAS_Time())
00729 {
00730 BotGoalName(gs->avoidgoals[i], name, 32);
00731 Log_Write("avoid goal %s, number %d for %f seconds", name,
00732 gs->avoidgoals[i], gs->avoidgoaltimes[i] - AAS_Time());
00733 }
00734 }
00735 }
00736
00737
00738
00739
00740
00741
00742 void BotAddToAvoidGoals(bot_goalstate_t *gs, int number, float avoidtime)
00743 {
00744 int i;
00745
00746 for (i = 0; i < MAX_AVOIDGOALS; i++)
00747 {
00748
00749 if (gs->avoidgoals[i] == number)
00750 {
00751 gs->avoidgoals[i] = number;
00752 gs->avoidgoaltimes[i] = AAS_Time() + avoidtime;
00753 return;
00754 }
00755 }
00756
00757 for (i = 0; i < MAX_AVOIDGOALS; i++)
00758 {
00759
00760 if (gs->avoidgoaltimes[i] < AAS_Time())
00761 {
00762 gs->avoidgoals[i] = number;
00763 gs->avoidgoaltimes[i] = AAS_Time() + avoidtime;
00764 return;
00765 }
00766 }
00767 }
00768
00769
00770
00771
00772
00773
00774 void BotRemoveFromAvoidGoals(int goalstate, int number)
00775 {
00776 int i;
00777 bot_goalstate_t *gs;
00778
00779 gs = BotGoalStateFromHandle(goalstate);
00780 if (!gs) return;
00781
00782 for (i = 0; i < MAX_AVOIDGOALS; i++)
00783 {
00784 if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time())
00785 {
00786 gs->avoidgoaltimes[i] = 0;
00787 return;
00788 }
00789 }
00790 }
00791
00792
00793
00794
00795
00796
00797 float BotAvoidGoalTime(int goalstate, int number)
00798 {
00799 int i;
00800 bot_goalstate_t *gs;
00801
00802 gs = BotGoalStateFromHandle(goalstate);
00803 if (!gs) return 0;
00804
00805 for (i = 0; i < MAX_AVOIDGOALS; i++)
00806 {
00807 if (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time())
00808 {
00809 return gs->avoidgoaltimes[i] - AAS_Time();
00810 }
00811 }
00812 return 0;
00813 }
00814
00815
00816
00817
00818
00819
00820 void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime)
00821 {
00822 bot_goalstate_t *gs;
00823 levelitem_t *li;
00824
00825 gs = BotGoalStateFromHandle(goalstate);
00826 if (!gs)
00827 return;
00828 if (avoidtime < 0)
00829 {
00830 if (!itemconfig)
00831 return;
00832
00833 for (li = levelitems; li; li = li->next)
00834 {
00835 if (li->number == number)
00836 {
00837 avoidtime = itemconfig->iteminfo[li->iteminfo].respawntime;
00838 if (!avoidtime)
00839 avoidtime = AVOID_DEFAULT_TIME;
00840 if (avoidtime < AVOID_MINIMUM_TIME)
00841 avoidtime = AVOID_MINIMUM_TIME;
00842 BotAddToAvoidGoals(gs, number, avoidtime);
00843 return;
00844 }
00845 }
00846 return;
00847 }
00848 else
00849 {
00850 BotAddToAvoidGoals(gs, number, avoidtime);
00851 }
00852 }
00853
00854
00855
00856
00857
00858
00859 int BotGetLevelItemGoal(int index, char *name, bot_goal_t *goal)
00860 {
00861 levelitem_t *li;
00862
00863 if (!itemconfig) return -1;
00864 li = levelitems;
00865 if (index >= 0)
00866 {
00867 for (; li; li = li->next)
00868 {
00869 if (li->number == index)
00870 {
00871 li = li->next;
00872 break;
00873 }
00874 }
00875 }
00876 for (; li; li = li->next)
00877 {
00878
00879 if (g_gametype == GT_SINGLE_PLAYER) {
00880 if (li->flags & IFL_NOTSINGLE) continue;
00881 }
00882 else if (g_gametype >= GT_TEAM) {
00883 if (li->flags & IFL_NOTTEAM) continue;
00884 }
00885 else {
00886 if (li->flags & IFL_NOTFREE) continue;
00887 }
00888 if (li->flags & IFL_NOTBOT) continue;
00889
00890 if (!Q_stricmp(name, itemconfig->iteminfo[li->iteminfo].name))
00891 {
00892 goal->areanum = li->goalareanum;
00893 VectorCopy(li->goalorigin, goal->origin);
00894 goal->entitynum = li->entitynum;
00895 VectorCopy(itemconfig->iteminfo[li->iteminfo].mins, goal->mins);
00896 VectorCopy(itemconfig->iteminfo[li->iteminfo].maxs, goal->maxs);
00897 goal->number = li->number;
00898 goal->flags = GFL_ITEM;
00899 if (li->timeout) goal->flags |= GFL_DROPPED;
00900
00901 return li->number;
00902 }
00903 }
00904 return -1;
00905 }
00906
00907
00908
00909
00910
00911
00912 int BotGetMapLocationGoal(char *name, bot_goal_t *goal)
00913 {
00914 maplocation_t *ml;
00915 vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8};
00916
00917 for (ml = maplocations; ml; ml = ml->next)
00918 {
00919 if (!Q_stricmp(ml->name, name))
00920 {
00921 goal->areanum = ml->areanum;
00922 VectorCopy(ml->origin, goal->origin);
00923 goal->entitynum = 0;
00924 VectorCopy(mins, goal->mins);
00925 VectorCopy(maxs, goal->maxs);
00926 return qtrue;
00927 }
00928 }
00929 return qfalse;
00930 }
00931
00932
00933
00934
00935
00936
00937 int BotGetNextCampSpotGoal(int num, bot_goal_t *goal)
00938 {
00939 int i;
00940 campspot_t *cs;
00941 vec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8};
00942
00943 if (num < 0) num = 0;
00944 i = num;
00945 for (cs = campspots; cs; cs = cs->next)
00946 {
00947 if (--i < 0)
00948 {
00949 goal->areanum = cs->areanum;
00950 VectorCopy(cs->origin, goal->origin);
00951 goal->entitynum = 0;
00952 VectorCopy(mins, goal->mins);
00953 VectorCopy(maxs, goal->maxs);
00954 return num+1;
00955 }
00956 }
00957 return 0;
00958 }
00959
00960
00961
00962
00963
00964
00965 void BotFindEntityForLevelItem(levelitem_t *li)
00966 {
00967 int ent, modelindex;
00968 itemconfig_t *ic;
00969 aas_entityinfo_t entinfo;
00970 vec3_t dir;
00971
00972 ic = itemconfig;
00973 if (!itemconfig) return;
00974 for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent))
00975 {
00976
00977 modelindex = AAS_EntityModelindex(ent);
00978
00979 if (!modelindex) continue;
00980
00981 AAS_EntityInfo(ent, &entinfo);
00982
00983 if (entinfo.origin[0] != entinfo.lastvisorigin[0] ||
00984 entinfo.origin[1] != entinfo.lastvisorigin[1] ||
00985 entinfo.origin[2] != entinfo.lastvisorigin[2]) continue;
00986
00987 if (ic->iteminfo[li->iteminfo].modelindex == modelindex)
00988 {
00989
00990 VectorSubtract(li->origin, entinfo.origin, dir);
00991 if (VectorLength(dir) < 30)
00992 {
00993
00994 li->entitynum = ent;
00995 }
00996 }
00997 }
00998 }
00999
01000
01001
01002
01003
01004
01005
01006
01007 #define ET_ITEM 2
01008
01009 void BotUpdateEntityItems(void)
01010 {
01011 int ent, i, modelindex;
01012 vec3_t dir;
01013 levelitem_t *li, *nextli;
01014 aas_entityinfo_t entinfo;
01015 itemconfig_t *ic;
01016
01017
01018 for (li = levelitems; li; li = nextli)
01019 {
01020 nextli = li->next;
01021
01022 if (li->timeout)
01023 {
01024
01025 if (li->timeout < AAS_Time())
01026 {
01027 RemoveLevelItemFromList(li);
01028 FreeLevelItem(li);
01029 }
01030 }
01031 }
01032
01033 ic = itemconfig;
01034 if (!itemconfig) return;
01035
01036 for (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent))
01037 {
01038 if (AAS_EntityType(ent) != ET_ITEM) continue;
01039
01040 modelindex = AAS_EntityModelindex(ent);
01041
01042 if (!modelindex) continue;
01043
01044 AAS_EntityInfo(ent, &entinfo);
01045
01046
01047
01048
01049 if (entinfo.origin[0] != entinfo.lastvisorigin[0] ||
01050 entinfo.origin[1] != entinfo.lastvisorigin[1] ||
01051 entinfo.origin[2] != entinfo.lastvisorigin[2]) continue;
01052
01053 for (li = levelitems; li; li = li->next)
01054 {
01055
01056 if (li->entitynum && li->entitynum == ent)
01057 {
01058
01059 if (ic->iteminfo[li->iteminfo].modelindex != modelindex)
01060 {
01061
01062 RemoveLevelItemFromList(li);
01063 FreeLevelItem(li);
01064 li = NULL;
01065 break;
01066 }
01067 else
01068 {
01069 if (entinfo.origin[0] != li->origin[0] ||
01070 entinfo.origin[1] != li->origin[1] ||
01071 entinfo.origin[2] != li->origin[2])
01072 {
01073 VectorCopy(entinfo.origin, li->origin);
01074
01075 li->goalareanum = AAS_BestReachableArea(li->origin,
01076 ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,
01077 li->goalorigin);
01078 }
01079 break;
01080 }
01081 }
01082 }
01083 if (li) continue;
01084
01085 for (li = levelitems; li; li = li->next)
01086 {
01087
01088 if (li->entitynum) continue;
01089
01090 if (g_gametype == GT_SINGLE_PLAYER) {
01091 if (li->flags & IFL_NOTSINGLE) continue;
01092 }
01093 else if (g_gametype >= GT_TEAM) {
01094 if (li->flags & IFL_NOTTEAM) continue;
01095 }
01096 else {
01097 if (li->flags & IFL_NOTFREE) continue;
01098 }
01099
01100 if (ic->iteminfo[li->iteminfo].modelindex == modelindex)
01101 {
01102
01103 VectorSubtract(li->origin, entinfo.origin, dir);
01104 if (VectorLength(dir) < 30)
01105 {
01106
01107 li->entitynum = ent;
01108
01109 if (entinfo.origin[0] != li->origin[0] ||
01110 entinfo.origin[1] != li->origin[1] ||
01111 entinfo.origin[2] != li->origin[2])
01112 {
01113
01114 VectorCopy(entinfo.origin, li->origin);
01115
01116 li->goalareanum = AAS_BestReachableArea(li->origin,
01117 ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,
01118 li->goalorigin);
01119 }
01120 #ifdef DEBUG
01121 Log_Write("linked item %s to an entity", ic->iteminfo[li->iteminfo].classname);
01122 #endif //DEBUG
01123 break;
01124 }
01125 }
01126 }
01127 if (li) continue;
01128
01129 for (i = 0; i < ic->numiteminfo; i++)
01130 {
01131 if (ic->iteminfo[i].modelindex == modelindex)
01132 {
01133 break;
01134 }
01135 }
01136
01137 if (i >= ic->numiteminfo) continue;
01138
01139 li = AllocLevelItem();
01140
01141 if (!li) continue;
01142
01143 li->entitynum = ent;
01144
01145 li->number = numlevelitems + ent;
01146
01147 li->iteminfo = i;
01148
01149 VectorCopy(entinfo.origin, li->origin);
01150
01151 li->goalareanum = AAS_BestReachableArea(li->origin,
01152 ic->iteminfo[i].mins, ic->iteminfo[i].maxs,
01153 li->goalorigin);
01154
01155 if (AAS_AreaJumpPad(li->goalareanum))
01156 {
01157 FreeLevelItem(li);
01158 continue;
01159 }
01160
01161
01162 li->timeout = AAS_Time() + 30;
01163
01164 AddLevelItemToList(li);
01165
01166 }
01167
01168
01169
01170
01171
01172
01173
01174
01175 }
01176
01177
01178
01179
01180
01181
01182 void BotDumpGoalStack(int goalstate)
01183 {
01184 int i;
01185 bot_goalstate_t *gs;
01186 char name[32];
01187
01188 gs = BotGoalStateFromHandle(goalstate);
01189 if (!gs) return;
01190 for (i = 1; i <= gs->goalstacktop; i++)
01191 {
01192 BotGoalName(gs->goalstack[i].number, name, 32);
01193 Log_Write("%d: %s", i, name);
01194 }
01195 }
01196
01197
01198
01199
01200
01201
01202 void BotPushGoal(int goalstate, bot_goal_t *goal)
01203 {
01204 bot_goalstate_t *gs;
01205
01206 gs = BotGoalStateFromHandle(goal