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