2 .float havocbot_role_timeout;
3 .void() havocbot_previous_role;
8 float havocbot_pickupevalfunc(entity item)
10 float i, j, rating, base, position, need_shells, need_nails, need_rockets, need_cells;
13 base = item.bot_pickupbasevalue;
15 need_shells = self.weapons & WEPBIT_SHOTGUN;
16 need_nails = self.weapons & WEPBIT_UZI;
17 need_cells = self.weapons & ( WEPBIT_HOOK | WEPBIT_HLAC | WEPBIT_MINSTANEX | WEPBIT_NEX | WEPBIT_ELECTRO | WEPBIT_CRYLINK );
18 need_rockets = self.weapons & ( WEPBIT_ROCKET_LAUNCHER | WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_SEEKER );
22 if (self.ammo_shells < g_pickup_shells_max && need_cells )
23 rating = rating + max(0, 1 - self.ammo_shells / g_pickup_shells_max);
26 if (self.ammo_nails < g_pickup_nails_max && need_nails )
27 rating = rating + max(0, 1 - self.ammo_nails / g_pickup_nails_max);
29 if (item.ammo_rockets)
30 if (self.ammo_rockets < g_pickup_rockets_max && need_rockets)
31 rating = rating + max(0, 1 - self.ammo_rockets / g_pickup_rockets_max);
34 if (self.ammo_cells < g_pickup_cells_max && need_cells)
35 rating = rating + max(0, 1 - self.ammo_cells / g_pickup_cells_max);
37 // Rate health items (aim to grab half the max capacity)
39 if (self.armorvalue < item.max_armorvalue * 0.5)
40 rating = rating + max(0, 1 - self.armorvalue / (item.max_armorvalue * 0.5));
43 if (self.health < item.max_health * 0.5)
45 rating = rating + max(0, 1 - self.health / (item.max_health * 0.5));
51 // See if I have it already
52 if( self.weapons & item.weapons == item.weapons )
54 // If I can pick it up
56 if not(cvar("g_weapon_stay"))
58 // Skilled bots will grab more
59 local float divisor = 2;
60 rating += bound(0, skill / (10*divisor), 1/divisor);
66 // If custom weapon priorities for bots is enabled rate most wanted weapons higher
67 if( bot_custom_weapon && rating )
69 for(i = WEP_FIRST; i < WEP_LAST ; ++i)
72 if( power2of(i-1) & item.weapons != item.weapons )
75 // Find the highest position on any range
77 for(j = 0; j < WEP_LAST ; ++j){
79 bot_weapons_far[j] == i ||
80 bot_weapons_mid[j] == i ||
81 bot_weapons_close[j] == i
92 position = WEP_LAST - position;
93 // item.bot_pickupbasevalue is overwritten here
94 base = BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ));
101 // TODO: if the item is not recognized then default to item.bot_pickupevalfunc(self, item);
102 return base * rating;
105 void havocbot_goalrating_items(float ratingscale, vector org, float sradius)
109 local float rating, d, discard, distance, friend_distance, enemy_distance;
110 ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
111 head = findchainfloat(bot_pickup, TRUE);
115 distance = vlen(head.origin - org);
116 friend_distance = 10000; enemy_distance = 10000;
119 if(!head.solid || distance > sradius || (head == self.ignoregoal && time < self.ignoregoaltime) )
125 // Check if the item can be picked up safely
126 if(head.classname == "droppedweapon")
128 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
130 d = pointcontents(trace_endpos + '0 0 1');
131 if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
136 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
144 // Ignore items under water
145 traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
146 if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
157 FOR_EACH_PLAYER(player)
160 if ( self == player || player.deadflag )
163 d = vlen(player.origin - head.origin); // distance between player and item
165 if ( player.team == self.team )
167 if ( clienttype(player) != CLIENTTYPE_REAL || discard )
170 if( d > friend_distance)
177 if( head.health && player.health > self.health )
180 if( head.armorvalue && player.armorvalue > self.armorvalue)
184 if( (player.weapons & head.weapons) != head.weapons)
187 if (head.ammo_shells && player.ammo_shells > self.ammo_shells)
190 if (head.ammo_nails && player.ammo_nails > self.ammo_nails)
193 if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets)
196 if (head.ammo_cells && player.ammo_cells > self.ammo_cells )
203 // If enemy only track distances
204 // TODO: track only if visible ?
205 if( d < enemy_distance )
210 // Rate the item only if no one needs it, or if an enemy is closer to it
211 if ( (enemy_distance < friend_distance && distance < enemy_distance) ||
212 (friend_distance > cvar("bot_ai_friends_aware_pickup_radius") ) || !discard )
214 // rating = head.bot_pickupevalfunc(self, head);
215 rating = havocbot_pickupevalfunc(head);
220 // rating = head.bot_pickupevalfunc(self, head);
221 rating = havocbot_pickupevalfunc(head);
225 navigation_routerating(head, rating * ratingscale, 2000);
230 void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius)
233 head = findchain(classname, "dom_controlpoint");
236 if (vlen(head.origin - org) < sradius)
238 if(head.cnt > -1) // this is just being fought for
239 navigation_routerating(head, ratingscale, 5000);
240 else if(head.goalentity.cnt == 0) // unclaimed point
241 navigation_routerating(head, ratingscale * 0.5, 5000);
242 else if(head.goalentity.team != self.team) // other team's point
243 navigation_routerating(head, ratingscale * 0.2, 5000);
250 // LordHavoc: this function was already unused, but for waypoints to be a
251 // useful goal the bots would have to seek out the least-recently-visited
252 // ones, not the closest
253 void havocbot_goalrating_waypoints(float ratingscale, vector org, float sradius)
256 head = findchain(classname, "waypoint");
259 if (vlen(head.origin - org) < sradius && vlen(head.origin - org) > 100)
260 navigation_routerating(head, ratingscale, 2000);
266 void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius)
269 local float t, noteam, distance;
270 noteam = ((self.team == 0) || !teams_matter); // fteqcc sucks
272 if (cvar("bot_nofire"))
275 // don't chase players if we're under water
276 if(self.waterlevel>WATERLEVEL_WETFEET)
279 FOR_EACH_PLAYER(head)
281 // TODO: Merge this logic with the bot_shouldattack function
284 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
286 distance = vlen(head.origin - org);
287 if (distance < 100 || distance > sradius)
291 if(head.items & IT_STRENGTH)
294 // rate only visible enemies
296 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
297 if (trace_fraction < 1 || trace_ent != head)
301 if(head.flags & FL_INWATER || head.flags & FL_PARTIALGROUND)
305 if(head.flags & FL_ONGROUND == 0)
307 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
308 t = pointcontents(trace_endpos + '0 0 1');
309 if( t != CONTENT_SOLID )
310 if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
312 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
316 t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
317 navigation_routerating(head, t * ratingscale, 2000);
322 // choose a role according to the situation
323 void() havocbot_role_dm;
326 //go to best items, or control points you don't own
327 void havocbot_role_dom()
329 if(self.deadflag != DEAD_NO)
332 if (self.bot_strategytime < time)
334 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
335 navigation_goalrating_start();
336 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
337 havocbot_goalrating_items(8000, self.origin, 8000);
338 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
339 //havocbot_goalrating_waypoints(1, self.origin, 1000);
340 navigation_goalrating_end();
346 void havocbot_role_dm()
348 if(self.deadflag != DEAD_NO)
351 if (self.bot_strategytime < time)
353 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
354 navigation_goalrating_start();
355 havocbot_goalrating_items(10000, self.origin, 10000);
356 havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
357 //havocbot_goalrating_waypoints(1, self.origin, 1000);
358 navigation_goalrating_end();
363 //go to next checkpoint, and annoy enemies
364 .float race_checkpoint;
365 void havocbot_role_race()
367 if(self.deadflag != DEAD_NO)
371 if (self.bot_strategytime < time)
373 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
374 navigation_goalrating_start();
376 havocbot_goalrating_items(100, self.origin, 10000);
377 havocbot_goalrating_enemyplayers(500, self.origin, 20000);
380 for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
382 if(e.cnt == self.race_checkpoint)
384 navigation_routerating(e, 1000000, 5000);
386 else if(self.race_checkpoint == -1)
388 navigation_routerating(e, 1000000, 5000);
392 navigation_goalrating_end();
396 void havocbot_chooserole_dm()
398 self.havocbot_role = havocbot_role_dm;
401 void havocbot_chooserole_race()
403 self.havocbot_role = havocbot_role_race;
406 void havocbot_chooserole_dom()
408 self.havocbot_role = havocbot_role_dom;
416 entity kh_worldkeylist;
417 .entity kh_worldkeynext;
418 void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
421 for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
423 if(head.owner == self)
425 if(!kh_tracking_enabled)
427 // if it's carried by our team we know about it
428 // otherwise we have to see it to know about it
429 if(!head.owner || head.team != self.team)
431 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
432 if (trace_fraction < 1 && trace_ent != head)
433 continue; // skip what I can't see
437 navigation_routerating(head, ratingscale_dropped, 100000);
438 else if(head.team == self.team)
439 navigation_routerating(head, ratingscale_team, 100000);
441 navigation_routerating(head, ratingscale_enemy, 100000);
445 void() havocbot_role_kh_carrier;
446 void() havocbot_role_kh_defense;
447 void() havocbot_role_kh_offense;
448 void() havocbot_role_kh_freelancer;
449 void havocbot_role_kh_carrier()
451 if(self.deadflag != DEAD_NO)
454 if (!(self.items & IT_KEY1))
456 dprint("changing role to freelancer\n");
457 self.havocbot_role = havocbot_role_kh_freelancer;
458 self.havocbot_role_timeout = 0;
462 if (self.bot_strategytime < time)
464 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
465 navigation_goalrating_start();
467 if(kh_Key_AllOwnedByWhichTeam() == self.team)
468 havocbot_goalrating_kh(100000, 1, 1); // bring home
470 havocbot_goalrating_kh(40000, 40000, 1000); // play defensively
472 havocbot_goalrating_items(10000, self.origin, 10000);
473 navigation_goalrating_end();
477 void havocbot_role_kh_defense()
479 if(self.deadflag != DEAD_NO)
482 if (self.items & IT_KEY1)
484 dprint("changing role to carrier\n");
485 self.havocbot_role = havocbot_role_kh_carrier;
486 self.havocbot_role_timeout = 0;
490 if (!self.havocbot_role_timeout)
491 self.havocbot_role_timeout = time + random() * 10 + 20;
492 if (time > self.havocbot_role_timeout)
494 dprint("changing role to freelancer\n");
495 self.havocbot_role = havocbot_role_kh_freelancer;
496 self.havocbot_role_timeout = 0;
500 if (self.bot_strategytime < time)
502 float key_owner_team;
503 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
504 navigation_goalrating_start();
506 key_owner_team = kh_Key_AllOwnedByWhichTeam();
507 if(key_owner_team == self.team)
508 havocbot_goalrating_kh(100000, 1, 1); // defend key carriers
509 else if(key_owner_team == -1)
510 havocbot_goalrating_kh(40000, 10000, 1); // play defensively
512 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
514 havocbot_goalrating_items(10000, self.origin, 10000);
515 navigation_goalrating_end();
519 void havocbot_role_kh_offense()
521 if(self.deadflag != DEAD_NO)
524 if (self.items & IT_KEY1)
526 dprint("changing role to carrier\n");
527 self.havocbot_role = havocbot_role_kh_carrier;
528 self.havocbot_role_timeout = 0;
532 if (!self.havocbot_role_timeout)
533 self.havocbot_role_timeout = time + random() * 10 + 20;
534 if (time > self.havocbot_role_timeout)
536 dprint("changing role to freelancer\n");
537 self.havocbot_role = havocbot_role_kh_freelancer;
538 self.havocbot_role_timeout = 0;
542 if (self.bot_strategytime < time)
544 float key_owner_team;
546 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
547 navigation_goalrating_start();
549 key_owner_team = kh_Key_AllOwnedByWhichTeam();
550 if(key_owner_team == self.team)
551 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
552 else if(key_owner_team == -1)
553 havocbot_goalrating_kh(1, 10000, 40000); // play offensively
555 havocbot_goalrating_kh(1, 1, 100000); // ATTACK! EMERGENCY!
557 havocbot_goalrating_items(10000, self.origin, 10000);
558 navigation_goalrating_end();
562 void havocbot_role_kh_freelancer()
564 if(self.deadflag != DEAD_NO)
567 if (self.items & IT_KEY1)
569 dprint("changing role to carrier\n");
570 self.havocbot_role = havocbot_role_kh_carrier;
571 self.havocbot_role_timeout = 0;
575 if (!self.havocbot_role_timeout)
576 self.havocbot_role_timeout = time + random() * 10 + 10;
577 if (time > self.havocbot_role_timeout)
581 dprint("changing role to offense\n");
582 self.havocbot_role = havocbot_role_kh_offense;
586 dprint("changing role to defense\n");
587 self.havocbot_role = havocbot_role_kh_defense;
589 self.havocbot_role_timeout = 0;
593 if (self.bot_strategytime < time)
595 float key_owner_team;
597 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
598 navigation_goalrating_start();
600 key_owner_team = kh_Key_AllOwnedByWhichTeam();
601 if(key_owner_team == self.team)
602 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
603 else if(key_owner_team == -1)
604 havocbot_goalrating_kh(10000, 40000, 10000); // prefer dropped keys
606 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
608 havocbot_goalrating_items(10000, self.origin, 10000);
609 navigation_goalrating_end();
613 void havocbot_chooserole_kh()
617 if(self.deadflag != DEAD_NO)
622 self.havocbot_role = havocbot_role_kh_offense;
624 self.havocbot_role = havocbot_role_kh_defense;
626 self.havocbot_role = havocbot_role_kh_freelancer;
629 void havocbot_chooserole()
631 dprint("choosing a role...\n");
632 navigation_clearroute();
633 self.bot_strategytime = 0;
635 havocbot_chooserole_ctf();
636 else if (g_domination)
637 havocbot_chooserole_dom();
639 havocbot_chooserole_kh();
641 havocbot_chooserole_race();
642 else if (g_onslaught)
643 havocbot_chooserole_ons();
644 else // assume anything else is deathmatch
645 havocbot_chooserole_dm();