Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

be_ai_goal.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 
00023 /*****************************************************************************
00024  * name:        be_ai_goal.c
00025  *
00026  * desc:        goal AI
00027  *
00028  * $Archive: /MissionPack/code/botlib/be_ai_goal.c $
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 //#define DEBUG_AI_GOAL
00050 #ifdef RANDOMIZE
00051 #define UNDECIDEDFUZZY
00052 #endif //RANDOMIZE
00053 #define DROPPEDWEIGHT
00054 //minimum avoid goal time
00055 #define AVOID_MINIMUM_TIME      10
00056 //default avoid goal time
00057 #define AVOID_DEFAULT_TIME      30
00058 //avoid dropped goal time
00059 #define AVOID_DROPPED_TIME      10
00060 //
00061 #define TRAVELTIME_SCALE        0.01
00062 //item flags
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 //location in the map "target_location"
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 //camp spots "info_camp"
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 //FIXME: these are game specific
00092 typedef enum {
00093     GT_FFA,             // free for all
00094     GT_TOURNAMENT,      // one on one tournament
00095     GT_SINGLE_PLAYER,   // single player tournament
00096 
00097     //-- team games go after this --
00098 
00099     GT_TEAM,            // team deathmatch
00100     GT_CTF,             // capture the flag
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;                         //number of the level item
00112     int iteminfo;                       //index into the item info
00113     int flags;                          //item flags
00114     float weight;                       //fixed roam weight
00115     vec3_t origin;                      //origin of the item
00116     int goalareanum;                    //area the item is in
00117     vec3_t goalorigin;                  //goal origin within the area
00118     int entitynum;                      //entity number
00119     float timeout;                      //item is removed after this time
00120     struct levelitem_s *prev, *next;
00121 } levelitem_t;
00122 
00123 typedef struct iteminfo_s
00124 {
00125     char classname[32];                 //classname of the item
00126     char name[MAX_STRINGFIELD];         //name of the item
00127     char model[MAX_STRINGFIELD];        //model of the item
00128     int modelindex;                     //model index
00129     int type;                           //item type
00130     int index;                          //index in the inventory
00131     float respawntime;                  //respawn time
00132     vec3_t mins;                        //mins of the item
00133     vec3_t maxs;                        //maxs of the item
00134     int number;                         //number of the item info
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 //goal state
00164 typedef struct bot_goalstate_s
00165 {
00166     struct weightconfig_s *itemweightconfig;    //weight config
00167     int *itemweightindex;                       //index from item to weight
00168     //
00169     int client;                                 //client using this goal state
00170     int lastreachabilityarea;                   //last area with reachabilities the bot was in
00171     //
00172     bot_goal_t goalstack[MAX_GOALSTACK];        //goal stack
00173     int goalstacktop;                           //the top of the goal stack
00174     //
00175     int avoidgoals[MAX_AVOIDGOALS];             //goals to avoid
00176     float avoidgoaltimes[MAX_AVOIDGOALS];       //times to avoid the goals
00177 } bot_goalstate_t;
00178 
00179 bot_goalstate_t *botgoalstates[MAX_CLIENTS + 1]; // bk001206 - FIXME: init?
00180 //item configuration
00181 itemconfig_t *itemconfig = NULL; // bk001206 - init
00182 //level items
00183 levelitem_t *levelitemheap = NULL; // bk001206 - init
00184 levelitem_t *freelevelitems = NULL; // bk001206 - init
00185 levelitem_t *levelitems = NULL; // bk001206 - init
00186 int numlevelitems = 0;
00187 //map locations
00188 maplocation_t *maplocations = NULL; // bk001206 - init
00189 //camp spots
00190 campspot_t *campspots = NULL; // bk001206 - init
00191 //the game type
00192 int g_gametype = 0; // bk001206 - init
00193 //additional dropped item weight
00194 libvar_t *droppedweight = NULL; // bk001206 - init
00195 
00196 //========================================================================
00197 //
00198 // Parameter:               -
00199 // Returns:                 -
00200 // Changes Globals:     -
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     } //end if
00209     if (!botgoalstates[handle])
00210     {
00211         botimport.Print(PRT_FATAL, "invalid goal state %d\n", handle);
00212         return NULL;
00213     } //end if
00214     return botgoalstates[handle];
00215 } //end of the function BotGoalStateFromHandle
00216 //===========================================================================
00217 //
00218 // Parameter:               -
00219 // Returns:                 -
00220 // Changes Globals:     -
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 } //end of the function BotInterbreedingGoalFuzzyLogic
00233 //===========================================================================
00234 //
00235 // Parameter:               -
00236 // Returns:                 -
00237 // Changes Globals:     -
00238 //===========================================================================
00239 void BotSaveGoalFuzzyLogic(int goalstate, char *filename)
00240 {
00241     bot_goalstate_t *gs;
00242 
00243     gs = BotGoalStateFromHandle(goalstate);
00244 
00245     //WriteWeightConfig(filename, gs->itemweightconfig);
00246 } //end of the function BotSaveGoalFuzzyLogic
00247 //===========================================================================
00248 //
00249 // Parameter:               -
00250 // Returns:                 -
00251 // Changes Globals:     -
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 } //end of the function BotMutateGoalFuzzyLogic
00261 //===========================================================================
00262 //
00263 // Parameter:               -
00264 // Returns:                 -
00265 // Changes Globals:     -
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     } //end if
00291     //initialize item config
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     //parse the item config file
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             } //end if
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             } //end if
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             } //end if
00324             ii->number = ic->numiteminfo;
00325             ic->numiteminfo++;
00326         } //end if
00327         else
00328         {
00329             SourceError(source, "unknown definition %s\n", token.string);
00330             FreeMemory(ic);
00331             FreeSource(source);
00332             return NULL;
00333         } //end else
00334     } //end while
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 } //end of the function LoadItemConfig
00341 //===========================================================================
00342 // index to find the weight function of an iteminfo
00343 //
00344 // Parameter:               -
00345 // Returns:                 -
00346 // Changes Globals:     -
00347 //===========================================================================
00348 int *ItemWeightIndex(weightconfig_t *iwc, itemconfig_t *ic)
00349 {
00350     int *index, i;
00351 
00352     //initialize item weight index
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         } //end if
00362     } //end for
00363     return index;
00364 } //end of the function ItemWeightIndex
00365 //===========================================================================
00366 //
00367 // Parameter:               -
00368 // Returns:                 -
00369 // Changes Globals:     -
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     } //end for
00384     levelitemheap[max_levelitems-1].next = NULL;
00385     //
00386     freelevelitems = levelitemheap;
00387 } //end of the function InitLevelItemHeap
00388 //===========================================================================
00389 //
00390 // Parameter:               -
00391 // Returns:                 -
00392 // Changes Globals:     -
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     } //end if
00404     //
00405     freelevelitems = freelevelitems->next;
00406     Com_Memset(li, 0, sizeof(levelitem_t));
00407     return li;
00408 } //end of the function AllocLevelItem
00409 //===========================================================================
00410 //
00411 // Parameter:               -
00412 // Returns:                 -
00413 // Changes Globals:     -
00414 //===========================================================================
00415 void FreeLevelItem(levelitem_t *li)
00416 {
00417     li->next = freelevelitems;
00418     freelevelitems = li;
00419 } //end of the function FreeLevelItem
00420 //===========================================================================
00421 //
00422 // Parameter:               -
00423 // Returns:                 -
00424 // Changes Globals:     -
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 } //end of the function AddLevelItemToList
00433 //===========================================================================
00434 //
00435 // Parameter:               -
00436 // Returns:                 -
00437 // Changes Globals:     -
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 } //end of the function RemoveLevelItemFromList
00445 //===========================================================================
00446 //
00447 // Parameter:           -
00448 // Returns:             -
00449 // Changes Globals:     -
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     } //end for
00461     maplocations = NULL;
00462     for (cs = campspots; cs; cs = nextcs)
00463     {
00464         nextcs = cs->next;
00465         FreeMemory(cs);
00466     } //end for
00467     campspots = NULL;
00468 } //end of the function BotFreeInfoEntities
00469 //===========================================================================
00470 //
00471 // Parameter:           -
00472 // Returns:             -
00473 // Changes Globals:     -
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         //map locations
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         } //end if
00501         //camp spots
00502         else if (!strcmp(classname, "info_camp"))
00503         {
00504             cs = (campspot_t *) GetClearedMemory(sizeof(campspot_t));
00505             AAS_VectorForBSPEpairKey(ent, "origin", cs->origin);
00506             //cs->origin[2] += 16;
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             } //end if
00519             cs->next = campspots;
00520             campspots = cs;
00521             //AAS_DrawPermanentCross(cs->origin, 4, LINECOLOR_YELLOW);
00522             numcampspots++;
00523         } //end else if
00524     } //end for
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     } //end if
00530 } //end of the function BotInitInfoEntities
00531 //===========================================================================
00532 //
00533 // Parameter:           -
00534 // Returns:             -
00535 // Changes Globals:     -
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     //initialize the map locations and camp spots
00548     BotInitInfoEntities();
00549 
00550     //initialize the level item heap
00551     InitLevelItemHeap();
00552     levelitems = NULL;
00553     numlevelitems = 0;
00554     //
00555     ic = itemconfig;
00556     if (!ic) return;
00557 
00558     //if there's no AAS file loaded
00559     if (!AAS_Loaded()) return;
00560 
00561     //update the modelindexes of the item info
00562     for (i = 0; i < ic->numiteminfo; i++)
00563     {
00564         //ic->iteminfo[i].modelindex = AAS_IndexFromModel(ic->iteminfo[i].model);
00565         if (!ic->iteminfo[i].modelindex)
00566         {
00567             Log_Write("item %s has modelindex 0", ic->iteminfo[i].classname);
00568         } //end if
00569     } //end for
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         } //end for
00582         if (i >= ic->numiteminfo)
00583         {
00584             Log_Write("entity %s unknown item\r\n", classname);
00585             continue;
00586         } //end if
00587         //get the origin of the item
00588         if (!AAS_VectorForBSPEpairKey(ent, "origin", origin))
00589         {
00590             botimport.Print(PRT_ERROR, "item %s without origin\n", classname);
00591             continue;
00592         } //end else
00593         //
00594         goalareanum = 0;
00595         //if it is a floating item
00596         if (spawnflags & 1)
00597         {
00598             //if the item is not floating in water
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                 //if the item not near the ground
00605                 if (trace.fraction >= 1)
00606                 {
00607                     //if the item is not reachable from a jumppad
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                     //botimport.Print(PRT_MESSAGE, "item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum);
00611                     if (!goalareanum) continue;
00612                 } //end if
00613             } //end if
00614         } //end if
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         } //end if
00637         //if not a stationary item
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             } //end if
00645         } //end if
00646         //item info of the level item
00647         li->iteminfo = i;
00648         //origin of the item
00649         VectorCopy(origin, li->origin);
00650         //
00651         if (goalareanum)
00652         {
00653             li->goalareanum = goalareanum;
00654             VectorCopy(origin, li->goalorigin);
00655         } //end if
00656         else
00657         {
00658             //get the item goal area and goal origin
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             } //end if
00667         } //end else
00668         //
00669         AddLevelItemToList(li);
00670     } //end for
00671     botimport.Print(PRT_MESSAGE, "found %d level items\n", numlevelitems);
00672 } //end of the function BotInitLevelItems
00673 //===========================================================================
00674 //
00675 // Parameter:               -
00676 // Returns:                 -
00677 // Changes Globals:     -
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         } //end for
00693     } //end for
00694     strcpy(name, "");
00695     return;
00696 } //end of the function BotGoalName
00697 //===========================================================================
00698 //
00699 // Parameter:               -
00700 // Returns:                 -
00701 // Changes Globals:     -
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 } //end of the function BotResetAvoidGoals
00712 //===========================================================================
00713 //
00714 // Parameter:               -
00715 // Returns:                 -
00716 // Changes Globals:     -
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         } //end if
00734     } //end for
00735 } //end of the function BotDumpAvoidGoals
00736 //===========================================================================
00737 //
00738 // Parameter:               -
00739 // Returns:                 -
00740 // Changes Globals:     -
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         //if the avoid goal is already stored
00749         if (gs->avoidgoals[i] == number)
00750         {
00751             gs->avoidgoals[i] = number;
00752             gs->avoidgoaltimes[i] = AAS_Time() + avoidtime;
00753             return;
00754         } //end if
00755     } //end for
00756 
00757     for (i = 0; i < MAX_AVOIDGOALS; i++)
00758     {
00759         //if this avoid goal has expired
00760         if (gs->avoidgoaltimes[i] < AAS_Time())
00761         {
00762             gs->avoidgoals[i] = number;
00763             gs->avoidgoaltimes[i] = AAS_Time() + avoidtime;
00764             return;
00765         } //end if
00766     } //end for
00767 } //end of the function BotAddToAvoidGoals
00768 //===========================================================================
00769 //
00770 // Parameter:           -
00771 // Returns:             -
00772 // Changes Globals:     -
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     //don't use the goals the bot wants to avoid
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         } //end if
00789     } //end for
00790 } //end of the function BotRemoveFromAvoidGoals
00791 //===========================================================================
00792 //
00793 // Parameter:               -
00794 // Returns:                 -
00795 // Changes Globals:     -
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     //don't use the goals the bot wants to avoid
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         } //end if
00811     } //end for
00812     return 0;
00813 } //end of the function BotAvoidGoalTime
00814 //===========================================================================
00815 //
00816 // Parameter:           -
00817 // Returns:             -
00818 // Changes Globals:     -
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             } //end for
00845         } //end for
00846         return;
00847     } //end if
00848     else
00849     {
00850         BotAddToAvoidGoals(gs, number, avoidtime);
00851     } //end else
00852 } //end of the function BotSetAvoidGoalTime
00853 //===========================================================================
00854 //
00855 // Parameter:           -
00856 // Returns:             -
00857 // Changes Globals:     -
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             } //end if
00874         } //end for
00875     } //end for
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             //botimport.Print(PRT_MESSAGE, "found li %s\n", itemconfig->iteminfo[li->iteminfo].name);
00901             return li->number;
00902         } //end if
00903     } //end for
00904     return -1;
00905 } //end of the function BotGetLevelItemGoal
00906 //===========================================================================
00907 //
00908 // Parameter:           -
00909 // Returns:             -
00910 // Changes Globals:     -
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         } //end if
00928     } //end for
00929     return qfalse;
00930 } //end of the function BotGetMapLocationGoal
00931 //===========================================================================
00932 //
00933 // Parameter:           -
00934 // Returns:             -
00935 // Changes Globals:     -
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         } //end if
00956     } //end for
00957     return 0;
00958 } //end of the function BotGetNextCampSpotGoal
00959 //===========================================================================
00960 //
00961 // Parameter:           -
00962 // Returns:             -
00963 // Changes Globals:     -
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         //get the model index of the entity
00977         modelindex = AAS_EntityModelindex(ent);
00978         //
00979         if (!modelindex) continue;
00980         //get info about the entity
00981         AAS_EntityInfo(ent, &entinfo);
00982         //if the entity is still moving
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             //check if the entity is very close
00990             VectorSubtract(li->origin, entinfo.origin, dir);
00991             if (VectorLength(dir) < 30)
00992             {
00993                 //found an entity for this level item
00994                 li->entitynum = ent;
00995             } //end if
00996         } //end if
00997     } //end for
00998 } //end of the function BotFindEntityForLevelItem
00999 //===========================================================================
01000 //
01001 // Parameter:           -
01002 // Returns:             -
01003 // Changes Globals:     -
01004 //===========================================================================
01005 
01006 //NOTE: enum entityType_t in bg_public.h
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     //timeout current entity items if necessary
01018     for (li = levelitems; li; li = nextli)
01019     {
01020         nextli = li->next;
01021         //if it is a item that will time out
01022         if (li->timeout)
01023         {
01024             //timeout the item
01025             if (li->timeout < AAS_Time())
01026             {
01027                 RemoveLevelItemFromList(li);
01028                 FreeLevelItem(li);
01029             } //end if
01030         } //end if
01031     } //end for
01032     //find new entity items
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         //get the model index of the entity
01040         modelindex = AAS_EntityModelindex(ent);
01041         //
01042         if (!modelindex) continue;
01043         //get info about the entity
01044         AAS_EntityInfo(ent, &entinfo);
01045         //FIXME: don't do this
01046         //skip all floating items for now
01047         //if (entinfo.groundent != ENTITYNUM_WORLD) continue;
01048         //if the entity is still moving
01049         if (entinfo.origin[0] != entinfo.lastvisorigin[0] ||
01050                 entinfo.origin[1] != entinfo.lastvisorigin[1] ||
01051                 entinfo.origin[2] != entinfo.lastvisorigin[2]) continue;
01052         //check if the entity is already stored as a level item
01053         for (li = levelitems; li; li = li->next)
01054         {
01055             //if the level item is linked to an entity
01056             if (li->entitynum && li->entitynum == ent)
01057             {
01058                 //the entity is re-used if the models are different
01059                 if (ic->iteminfo[li->iteminfo].modelindex != modelindex)
01060                 {
01061                     //remove this level item
01062                     RemoveLevelItemFromList(li);
01063                     FreeLevelItem(li);
01064                     li = NULL;
01065                     break;
01066                 } //end if
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                         //also update the goal area number
01075                         li->goalareanum = AAS_BestReachableArea(li->origin,
01076                                         ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,
01077                                         li->goalorigin);
01078                     } //end if
01079                     break;
01080                 } //end else
01081             } //end if
01082         } //end for
01083         if (li) continue;
01084         //try to link the entity to a level item
01085         for (li = levelitems; li; li = li->next)
01086         {
01087             //if this level item is already linked
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             //if the model of the level item and the entity are the same
01100             if (ic->iteminfo[li->iteminfo].modelindex == modelindex)
01101             {
01102                 //check if the entity is very close
01103                 VectorSubtract(li->origin, entinfo.origin, dir);
01104                 if (VectorLength(dir) < 30)
01105                 {
01106                     //found an entity for this level item
01107                     li->entitynum = ent;
01108                     //if the origin is different
01109                     if (entinfo.origin[0] != li->origin[0] ||
01110                         entinfo.origin[1] != li->origin[1] ||
01111                         entinfo.origin[2] != li->origin[2])
01112                     {
01113                         //update the level item origin
01114                         VectorCopy(entinfo.origin, li->origin);
01115                         //also update the goal area number
01116                         li->goalareanum = AAS_BestReachableArea(li->origin,
01117                                         ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,
01118                                         li->goalorigin);
01119                     } //end if
01120 #ifdef DEBUG
01121                     Log_Write("linked item %s to an entity", ic->iteminfo[li->iteminfo].classname);
01122 #endif //DEBUG
01123                     break;
01124                 } //end if
01125             } //end else
01126         } //end for
01127         if (li) continue;
01128         //check if the model is from a known item
01129         for (i = 0; i < ic->numiteminfo; i++)
01130         {
01131             if (ic->iteminfo[i].modelindex == modelindex)
01132             {
01133                 break;
01134             } //end if
01135         } //end for
01136         //if the model is not from a known item
01137         if (i >= ic->numiteminfo) continue;
01138         //allocate a new level item
01139         li = AllocLevelItem();
01140         //
01141         if (!li) continue;
01142         //entity number of the level item
01143         li->entitynum = ent;
01144         //number for the level item
01145         li->number = numlevelitems + ent;
01146         //set the item info index for the level item
01147         li->iteminfo = i;
01148         //origin of the item
01149         VectorCopy(entinfo.origin, li->origin);
01150         //get the item goal area and goal origin
01151         li->goalareanum = AAS_BestReachableArea(li->origin,
01152                                     ic->iteminfo[i].mins, ic->iteminfo[i].maxs,
01153                                     li->goalorigin);
01154         //never go for items dropped into jumppads
01155         if (AAS_AreaJumpPad(li->goalareanum))
01156         {
01157             FreeLevelItem(li);
01158             continue;
01159         } //end if
01160         //time this item out after 30 seconds
01161         //dropped items disappear after 30 seconds
01162         li->timeout = AAS_Time() + 30;
01163         //add the level item to the list
01164         AddLevelItemToList(li);
01165         //botimport.Print(PRT_MESSAGE, "found new level item %s\n", ic->iteminfo[i].classname);
01166     } //end for
01167     /*
01168     for (li = levelitems; li; li = li->next)
01169     {
01170         if (!li->entitynum)
01171         {
01172             BotFindEntityForLevelItem(li);
01173         } //end if
01174     } //end for*/
01175 } //end of the function BotUpdateEntityItems
01176 //===========================================================================
01177 //
01178 // Parameter:               -
01179 // Returns:                 -
01180 // Changes Globals:     -
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     } //end for
01195 } //end of the function BotDumpGoalStack
01196 //===========================================================================
01197 //
01198 // Parameter:               -
01199 // Returns:                 -
01200 // Changes Globals:     -
01201 //===========================================================================
01202 void BotPushGoal(int goalstate, bot_goal_t *goal)
01203 {
01204     bot_goalstate_t *gs;
01205 
01206     gs = BotGoalStateFromHandle(goal