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