1 ///////////////////////////////////////////////////////////////////////
2 //
3 //  ACE - Quake II Bot Base Code
4 //
5 //  Version 1.0
6 //
7 //  This file is Copyright(c), Steve Yeager 1998, All Rights Reserved
8 //
9 //
10 //	All other files are Copyright(c) Id Software, Inc.
11 //
12 //	Please see liscense.txt in the source directory for the copyright
13 //	information regarding those files belonging to Id Software, Inc.
14 //
15 //	Should you decide to release a modified version of ACE, you MUST
16 //	include the following text (minus the BEGIN and END lines) in the
17 //	documentation for your modification.
18 //
19 //	--- BEGIN ---
20 //
21 //	The ACE Bot is a product of Steve Yeager, and is available from
22 //	the ACE Bot homepage, at http://www.axionfx.com/ace.
23 //
24 //	This program is a modification of the ACE Bot, and is therefore
25 //	in NO WAY supported by Steve Yeager.
26 
27 //	This program MUST NOT be sold in ANY form. If you have paid for
28 //	this product, you should contact Steve Yeager immediately, via
29 //	the ACE Bot homepage.
30 //
31 //	--- END ---
32 //
33 //	I, Steve Yeager, hold no responsibility for any harm caused by the
34 //	use of this source code, especially to small children and animals.
35 //  It is provided as-is with no implied warranty or support.
36 //
37 //  I also wish to thank and acknowledge the great work of others
38 //  that has helped me to develop this code.
39 //
40 //  John Cricket    - For ideas and swapping code.
41 //  Ryan Feltrin    - For ideas and swapping code.
42 //  SABIN           - For showing how to do true client based movement.
43 //  BotEpidemic     - For keeping us up to date.
44 //  Telefragged.com - For giving ACE a home.
45 //  Microsoft       - For giving us such a wonderful crash free OS.
46 //  id              - Need I say more.
47 //
48 //  And to all the other testers, pathers, and players and people
49 //  who I can't remember who the heck they were, but helped out.
50 //
51 ///////////////////////////////////////////////////////////////////////
52 
53 ///////////////////////////////////////////////////////////////////////
54 //
55 //  acebot_items.c - This file contains all of the
56 //                   item handling routines for the
57 //                   ACE bot, including fact table support
58 //
59 ///////////////////////////////////////////////////////////////////////
60 
61 #ifdef HAVE_CONFIG_H
62 #include "config.h"
63 #endif
64 
65 #include "game/g_local.h"
66 #include "acebot.h"
67 
68 int num_items = 0;
69 item_table_t item_table[MAX_EDICTS];
70 
71 static gitem_t *redflag;
72 static gitem_t *blueflag;
73 
74 
ACEAI_InitKnownCTFItems(void)75 void ACEAI_InitKnownCTFItems(void)
76 {
77 	//int		i;
78 
79 	//define gitem_t pointers commonly used for comparisons inside this archive.
80 	redflag = FindItemByClassname("item_flag_red");
81 	blueflag = FindItemByClassname("item_flag_blue");
82 
83 }
84 
85 ///////////////////////////////////////////////////////////////////////
86 // Can we get there?
87 ///////////////////////////////////////////////////////////////////////
ACEIT_IsReachable(edict_t * self,vec3_t goal)88 qboolean ACEIT_IsReachable(edict_t *self, vec3_t goal)
89 {
90 	trace_t trace;
91 	vec3_t v;
92 
93 	VectorCopy(self->mins,v);
94 	v[2] += 18; // Stepsize
95 
96 	trace = gi.trace (self->s.origin, v, self->maxs, goal, self, BOTMASK_OPAQUE);
97 
98 	// Yes we can see it
99 	if (trace.fraction == 1.0)
100 		return true;
101 	else
102 		return false;
103 
104 }
105 
106 ///////////////////////////////////////////////////////////////////////
107 // Visiblilty check
108 ///////////////////////////////////////////////////////////////////////
ACEIT_IsVisible(edict_t * self,vec3_t goal)109 qboolean ACEIT_IsVisible(edict_t *self, vec3_t goal)
110 {
111 	trace_t trace;
112 
113 	trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, goal, self, BOTMASK_OPAQUE);
114 
115 	// Yes we can see it
116 	if (trace.fraction == 1.0)
117 		return true;
118 	else
119 		return false;
120 
121 }
122 
123 ///////////////////////////////////////////////////////////////////////
124 //  Weapon changing support
125 ///////////////////////////////////////////////////////////////////////
ACEIT_ChangeWeapon(edict_t * ent,gitem_t * item)126 qboolean ACEIT_ChangeWeapon (edict_t *ent, gitem_t *item)
127 {
128 	int			ammo_index;
129 	gitem_t		*ammo_item;
130 
131 	// see if we're already using it
132 	if (item == ent->client->pers.weapon)
133 		return true;
134 
135 	// Has not picked up weapon yet
136 	if(!ent->client->pers.inventory[ITEM_INDEX(item)])
137 		return false;
138 
139 	// Do we have ammo for it?
140 	if (item->ammo)
141 	{
142 		ammo_item = FindItem(item->ammo);
143 		ammo_index = ITEM_INDEX(ammo_item);
144 		if (!ent->client->pers.inventory[ammo_index] && !g_select_empty->value)
145 			return false;
146 	}
147 
148 	// Change to this weapon
149 	ent->client->newweapon = item;
150 
151 	// Make weapon swap visible.
152 	ChangeWeapon( ent );
153 
154 	return true;
155 }
156 
157 
158 extern gitem_armor_t jacketarmor_info;
159 extern gitem_armor_t combatarmor_info;
160 extern gitem_armor_t bodyarmor_info;
161 
162 ///////////////////////////////////////////////////////////////////////
163 // Check if we can use the armor
164 ///////////////////////////////////////////////////////////////////////
ACEIT_CanUseArmor(gitem_t * item,edict_t * other)165 qboolean ACEIT_CanUseArmor (gitem_t *item, edict_t *other)
166 {
167 	int				old_armor_index;
168 	gitem_armor_t	*oldinfo;
169 	gitem_armor_t	*newinfo;
170 	int				newcount;
171 	float			salvage;
172 	int				salvagecount;
173 
174 	// get info on new armor
175 	newinfo = (gitem_armor_t *)item->info;
176 
177 	old_armor_index = ArmorIndex (other);
178 
179 	// handle armor shards specially
180 	if (item->tag == ARMOR_SHARD)
181 		return true;
182 
183 	// get info on old armor
184 	if (old_armor_index == ITEM_INDEX(FindItem("Jacket Armor")))
185 		oldinfo = &jacketarmor_info;
186 	else if (old_armor_index == ITEM_INDEX(FindItem("Combat Armor")))
187 		oldinfo = &combatarmor_info;
188 	else // (old_armor_index == body_armor_index)
189 		oldinfo = &bodyarmor_info;
190 
191 	if (newinfo->normal_protection <= oldinfo->normal_protection)
192 	{
193 		// calc new armor values
194 		salvage = newinfo->normal_protection / oldinfo->normal_protection;
195 		salvagecount = salvage * newinfo->base_count;
196 		newcount = other->client->pers.inventory[old_armor_index] + salvagecount;
197 
198 		if (newcount > oldinfo->max_count)
199 			newcount = oldinfo->max_count;
200 
201 		// if we're already maxed out then we don't need the new armor
202 		if (other->client->pers.inventory[old_armor_index] >= newcount)
203 			return false;
204 
205 	}
206 
207 	return true;
208 }
209 //==========================================
210 // find needed flag
211 //
212 //==========================================
ACEIT_WantedFlag(edict_t * self)213 gitem_t	*ACEIT_WantedFlag (edict_t *self)
214 {
215 	qboolean	hasflag;
216 
217 	if (!ctf->value)
218 		return NULL;
219 
220 	//find out if the player has a flag, and what flag is it
221 	if (redflag && self->client->pers.inventory[ITEM_INDEX(redflag)])
222 		hasflag = true;
223 	else if (blueflag && self->client->pers.inventory[ITEM_INDEX(blueflag)])
224 		hasflag = true;
225 	else
226 		hasflag = false;
227 
228 	//jalToDo: see if our flag is at base
229 
230 	if (!hasflag)//if we don't have a flag we want other's team flag
231 	{
232 		if (self->dmteam == RED_TEAM)
233 			return blueflag;
234 		else
235 			return redflag;
236 	}
237 	else	//we have a flag
238 	{
239 		if (self->dmteam == BLUE_TEAM)
240 			return redflag;
241 		else
242 			return blueflag;
243 	}
244 
245 	return NULL;
246 }
247 
248 
249 ///////////////////////////////////////////////////////////////////////
250 // Determins the NEED for an item
251 //
252 // This function can be modified to support new items to pick up
253 // Any other logic that needs to be added for custom decision making
254 // can be added here. For now it is very simple.
255 ///////////////////////////////////////////////////////////////////////
ACEIT_ItemNeed(edict_t * self,int item)256 float ACEIT_ItemNeed(edict_t *self, int item)
257 {
258 	gitem_t		*wantedFlag;
259 	// Make sure item is at least close to being valid
260 	if(item < 0 || item > 100)
261 		return 0.0;
262 
263 	switch(item)
264 	{
265 		// Health
266 		case ITEMLIST_HEALTH_SMALL:
267 		case ITEMLIST_HEALTH_MEDIUM:
268 		case ITEMLIST_HEALTH_LARGE:
269 		case ITEMLIST_HEALTH_MEGA:
270 			if(self->health < 100)
271 				return 1.0 - (float)self->health/100.0f; // worse off, higher priority
272 			else
273 				return 0.0;
274 
275 		case ITEMLIST_QUADDAMAGE:
276 		case ITEMLIST_INVULNERABILITY:
277 		case ITEMLIST_HASTE:
278 		case ITEMLIST_SPROING:
279 		case ITEMLIST_ADRENALINE:
280 			return 0.6;
281 
282 		// Weapons
283 		case ITEMLIST_ROCKETLAUNCHER:
284 		case ITEMLIST_CHAINGUN:
285 		case ITEMLIST_SUPERSHOTGUN:
286 		case ITEMLIST_BFG10K:
287 			if(g_tactical->integer && self->ctype == 0)
288 				return 0.0;
289 			else if(g_tactical->integer && self->ctype == 1)
290 			{
291 				if(!self->client->pers.inventory[item])
292 					return 0.9;
293 				else
294 					return 0.0;
295 			}
296 		case ITEMLIST_SHOTGUN:
297 		case ITEMLIST_HYPERBLASTER:
298 		case ITEMLIST_RAILGUN:
299 		case ITEMLIST_MINDERASER:
300 			if(g_tactical->integer && self->ctype == 1)
301 				return 0.0;
302 			else if(g_tactical->integer && self->ctype == 0)
303 			{
304 				if(!self->client->pers.inventory[item])
305 					return 0.9;
306 				else
307 					return 0.0;
308 			}
309 
310 			//normal game mode
311 			if(!self->client->pers.inventory[item])
312 				return 0.9; //was .7
313 			else
314 				return 0.0;
315 
316 		// Ammo
317 		case ITEMLIST_SLUGS:
318 			if(self->client->pers.inventory[ITEMLIST_SLUGS] < self->client->pers.max_slugs)
319 				return 0.4;
320 			else
321 				return 0.0;
322 
323 		case ITEMLIST_BULLETS:
324 			if(self->client->pers.inventory[ITEMLIST_BULLETS] < self->client->pers.max_bullets)
325 				return 0.3;
326 			else
327 				return 0.0;
328 
329 		case ITEMLIST_SHELLS:
330 		   if(self->client->pers.inventory[ITEMLIST_SHELLS] < self->client->pers.max_shells)
331 				return 0.3;
332 			else
333 				return 0.0;
334 
335 		case ITEMLIST_CELLS:
336 			if(self->client->pers.inventory[ITEMLIST_CELLS] <	self->client->pers.max_cells)
337 				return 0.3;
338 			else
339 				return 0.0;
340 
341 		case ITEMLIST_ROCKETS:
342 			if(self->client->pers.inventory[ITEMLIST_ROCKETS] < self->client->pers.max_rockets)
343 				return 1.5;
344 			else
345 				return 0.0;
346 
347 		case ITEMLIST_GRENADES:
348 			if(self->client->pers.inventory[ITEMLIST_GRENADES] < self->client->pers.max_grenades)
349 				return 0.3;
350 			else
351 				return 0.0;
352 
353 		case ITEMLIST_BODYARMOR:
354 			if(ACEIT_CanUseArmor (FindItem("Body Armor"), self))
355 				return 0.6;
356 			else
357 				return 0.0;
358 
359 		case ITEMLIST_COMBATARMOR:
360 			if(ACEIT_CanUseArmor (FindItem("Combat Armor"), self))
361 				return 0.6;
362 			else
363 				return 0.0;
364 
365 		case ITEMLIST_JACKETARMOR:
366 			if(ACEIT_CanUseArmor (FindItem("Jacket Armor"), self))
367 				return 0.6;
368 			else
369 				return 0.0;
370 		//flags
371 		case ITEMLIST_FLAG1:
372 			wantedFlag = ACEIT_WantedFlag (self); //Returns the flag gitem_t
373 			if (redflag != wantedFlag)
374 				return 0.0;
375 			else
376 				return 3.0;
377 		case ITEMLIST_FLAG2:
378 			wantedFlag = ACEIT_WantedFlag (self); //Returns the flag gitem_t
379 			if (blueflag != wantedFlag)
380 				return 0.0;
381 			else
382 				return 3.0;
383 		//vehicles
384 		case ITEMLIST_BOMBER:
385 		case ITEMLIST_STRAFER:
386 		case ITEMLIST_HOVER:
387 			if(!self->client->pers.inventory[item])
388 				return 0.9;
389 			else
390 				return 0.0;
391 
392 		//deathball
393 		case ITEMLIST_DEATHBALL:
394 			return 3.0;
395 
396 		default:
397 			return 0.0;
398 
399 	}
400 
401 }
402 
403 ///////////////////////////////////////////////////////////////////////
404 // Convert a classname to its index value
405 //
406 // I prefer to use integers/defines for simplicity sake. This routine
407 // can lead to some slowdowns I guess, but makes the rest of the code
408 // easier to deal with.
409 ///////////////////////////////////////////////////////////////////////
ACEIT_ClassnameToIndex(char * classname)410 int ACEIT_ClassnameToIndex(char *classname)
411 {
412 	if(strcmp(classname,"item_armor_body")==0)
413 		return ITEMLIST_BODYARMOR;
414 
415 	if(strcmp(classname,"item_armor_combat")==0)
416 		return ITEMLIST_COMBATARMOR;
417 
418 	if(strcmp(classname,"item_armor_jacket")==0)
419 		return ITEMLIST_JACKETARMOR;
420 
421 	if(strcmp(classname,"item_armor_shard")==0)
422 		return ITEMLIST_ARMORSHARD;
423 
424 	if(strcmp(classname,"weapon_blaster")==0)
425 		return ITEMLIST_BLASTER;
426 
427 	if(strcmp(classname,"weapon_shotgun")==0)
428 		return ITEMLIST_SHOTGUN;
429 
430 	if(strcmp(classname,"weapon_supershotgun")==0)
431 		return ITEMLIST_SUPERSHOTGUN;
432 
433 	if(strcmp(classname,"weapon_chaingun")==0)
434 		return ITEMLIST_CHAINGUN;
435 
436 	if(strcmp(classname,"ammo_grenades")==0)
437 		return ITEMLIST_GRENADES;
438 
439 	if(strcmp(classname,"weapon_rocketlauncher")==0)
440 		return ITEMLIST_ROCKETLAUNCHER;
441 
442 	if(strcmp(classname,"weapon_hyperblaster")==0)
443 		return ITEMLIST_HYPERBLASTER;
444 
445 	if(strcmp(classname,"weapon_railgun")==0)
446 		return ITEMLIST_RAILGUN;
447 
448 	if(strcmp(classname,"weapon_bfg")==0)
449 		return ITEMLIST_BFG10K;
450 
451 	if(strcmp(classname, "weapon_minderaser") == 0)
452 		return ITEMLIST_MINDERASER;
453 
454 	if(strcmp(classname,"ammo_shells")==0)
455 		return ITEMLIST_SHELLS;
456 
457 	if(strcmp(classname,"ammo_bullets")==0)
458 		return ITEMLIST_BULLETS;
459 
460 	if(strcmp(classname,"ammo_cells")==0)
461 		return ITEMLIST_CELLS;
462 
463 	if(strcmp(classname,"ammo_rockets")==0)
464 		return ITEMLIST_ROCKETS;
465 
466 	if(strcmp(classname,"ammo_slugs")==0)
467 		return ITEMLIST_SLUGS;
468 
469 	if(strcmp(classname,"item_quad")==0)
470 		return ITEMLIST_QUADDAMAGE;
471 
472 	if(strcmp(classname,"item_invunerability")==0)
473 		return ITEMLIST_INVULNERABILITY;
474 
475 	if(strcmp(classname,"item_haste")==0)
476 		return ITEMLIST_HASTE;
477 
478 	if(strcmp(classname,"item_sproing")==0)
479 		return ITEMLIST_SPROING;
480 
481 	if(strcmp(classname,"item_adrenaline")==0)
482 		return ITEMLIST_ADRENALINE;
483 
484 	if(strcmp(classname,"item_health")==0) // ??
485 		return ITEMLIST_HEALTH;
486 
487 	if(strcmp(classname,"item_health_small")==0)
488 		return ITEMLIST_HEALTH_SMALL;
489 
490 	if(strcmp(classname,"item_health_medium")==0)
491 		return ITEMLIST_HEALTH_MEDIUM;
492 
493 	if(strcmp(classname,"item_health_large")==0)
494 		return ITEMLIST_HEALTH_LARGE;
495 
496 	if(strcmp(classname,"item_health_mega")==0)
497 		return ITEMLIST_HEALTH_MEGA;
498 
499 	if(strcmp(classname, "item_flag_red") == 0)
500 		return ITEMLIST_FLAG1;
501 
502 	if(strcmp(classname, "item_flag_blue") == 0)
503 		return ITEMLIST_FLAG2;
504 
505 	if(strcmp(classname, "item_bomber") == 0)
506 		return ITEMLIST_BOMBER;
507 
508 	if(strcmp(classname, "item_strafer") == 0)
509 		return ITEMLIST_STRAFER;
510 
511 	if(strcmp(classname, "item_deathball") == 0)
512 		return ITEMLIST_DEATHBALL;
513 
514 	return INVALID;
515 }
516 
517 
518 ///////////////////////////////////////////////////////////////////////
519 // Only called once per level, when saved will not be called again
520 //
521 // Downside of the routine is that items can not move about. If the level
522 // has been saved before and reloaded, it could cause a problem if there
523 // are items that spawn at random locations.
524 //
525 //#define DEBUG // uncomment to write out items to a file.
526 ///////////////////////////////////////////////////////////////////////
ACEIT_BuildItemNodeTable(qboolean rebuild)527 void ACEIT_BuildItemNodeTable (qboolean rebuild)
528 {
529 	edict_t *items;
530 	int i,item_index;
531 	vec3_t v,v1,v2;
532 
533 #ifdef DEBUG
534 	FILE *pOut; // for testing
535 	if((pOut = fopen("items.txt","wt"))==NULL)
536 		return;
537 #endif
538 
539 	num_items = 0;
540 
541 	// Add game items
542 	for(items = g_edicts; items < &g_edicts[globals.num_edicts]; items++)
543 	{
544 		// filter out crap
545 		if(items->solid == SOLID_NOT)
546 			continue;
547 
548 		if(!items->classname)
549 			continue;
550 
551 		/////////////////////////////////////////////////////////////////
552 		// Items
553 		/////////////////////////////////////////////////////////////////
554 		item_index = ACEIT_ClassnameToIndex(items->classname);
555 
556 		////////////////////////////////////////////////////////////////
557 		// SPECIAL NAV NODE DROPPING CODE
558 		////////////////////////////////////////////////////////////////
559 		// Special node dropping for platforms
560 		if(strcmp(items->classname,"func_plat")==0)
561 		{
562 			if(!rebuild)
563 				ACEND_AddNode(items,NODE_PLATFORM);
564 			item_index = 99; // to allow to pass the item index test
565 		}
566 
567 		// Special node dropping for teleporters
568 		if(strcmp(items->classname,"misc_teleporter_dest")==0 || strcmp(items->classname,"misc_teleporter")==0)
569 		{
570 			if(!rebuild)
571 				ACEND_AddNode(items,NODE_TELEPORTER);
572 			item_index = 99;
573 		}
574 
575 		#ifdef DEBUG
576 		if(item_index == INVALID)
577 			fprintf(pOut,"Rejected item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]);
578 		else
579 			fprintf(pOut,"item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]);
580 		#endif
581 
582 		if(item_index == INVALID)
583 			continue;
584 
585 		// add a pointer to the item entity
586 		item_table[num_items].ent = items;
587 		item_table[num_items].item = item_index;
588 
589 		// If new, add nodes for items
590 		if(!rebuild)
591 		{
592 			// Add a new node at the item's location.
593 			item_table[num_items].node = ACEND_AddNode(items,NODE_ITEM);
594 			num_items++;
595 		}
596 		else // Now if rebuilding, just relink ent structures
597 		{
598 			// Find stored location
599 			for(i=0;i<bot_numnodes;i++)
600 			{
601 				if(nodes[i].type == NODE_ITEM ||
602 				   nodes[i].type == NODE_PLATFORM ||
603 				   nodes[i].type == NODE_TELEPORTER) // valid types
604 				{
605 					VectorCopy(items->s.origin,v);
606 
607 					// Add 16 to item type nodes
608 					if(nodes[i].type == NODE_ITEM)
609 						v[2] += 16;
610 
611 					// Add 32 to teleporter
612 					if(nodes[i].type == NODE_TELEPORTER)
613 						v[2] += 32;
614 
615 					if(nodes[i].type == NODE_PLATFORM)
616 					{
617 						VectorCopy(items->maxs,v1);
618 						VectorCopy(items->mins,v2);
619 
620 						// To get the center
621 						v[0] = (v1[0] - v2[0]) / 2 + v2[0];
622 						v[1] = (v1[1] - v2[1]) / 2 + v2[1];
623 						v[2] = items->mins[2]+64;
624 					}
625 
626 					if(v[0] == nodes[i].origin[0] &&
627  					   v[1] == nodes[i].origin[1] &&
628 					   v[2] == nodes[i].origin[2])
629 					{
630 						// found a match now link to facts
631 						item_table[num_items].node = i;
632 
633 #ifdef DEBUG
634 						fprintf(pOut,"Relink item: %s node: %d pos: %f %f %f\n",items->classname,item_table[num_items].node,items->s.origin[0],items->s.origin[1],items->s.origin[2]);
635 #endif
636 						num_items++;
637 					}
638 				}
639 			}
640 		}
641 
642 
643 	}
644 
645 #ifdef DEBUG
646 	fclose(pOut);
647 #endif
648 
649 }
650