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