2 .float havocbot_role_timeout;
3 .void() havocbot_previous_role;
7 float canreach(entity e)
9 return vlen(self.origin - e.origin) < 1500;
12 .float max_armorvalue;
13 float havocbot_pickupevalfunc(entity item)
15 float i, j, rating, base, position, need_shells, need_nails, need_rockets, need_cells;
18 base = item.bot_pickupbasevalue;
20 need_shells = self.weapons & WEPBIT_SHOTGUN;
21 need_nails = self.weapons & WEPBIT_UZI;
22 need_cells = self.weapons & ( WEPBIT_HOOK | WEPBIT_HLAC | WEPBIT_MINSTANEX | WEPBIT_NEX | WEPBIT_ELECTRO | WEPBIT_CRYLINK );
23 need_rockets = self.weapons & ( WEPBIT_ROCKET_LAUNCHER | WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_SEEKER );
27 if( self.weapons & item.weapons == item.weapons )
28 rating = 0.5 + bound(0, skill / 20, 0.5);
32 if( bot_custom_weapon )
34 for(i = WEP_FIRST; i < WEP_LAST ; ++i){
35 if( power2of(i-1) & item.weapons != item.weapons )
39 for(j = 0; j < WEP_LAST ; ++j){
41 bot_weapons_far[j] == i ||
42 bot_weapons_mid[j] == i ||
43 bot_weapons_close[j] == i
53 position = WEP_LAST - position;
54 // item.bot_pickupbasevalue is overwritten here
55 base = BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ));
63 if (self.ammo_shells < g_pickup_shells_max && need_cells )
64 rating = rating + max(0, 1 - self.ammo_shells / g_pickup_shells_max);
67 if (self.ammo_nails < g_pickup_nails_max && need_nails )
68 rating = rating + max(0, 1 - self.ammo_nails / g_pickup_nails_max);
70 if (item.ammo_rockets)
71 if (self.ammo_rockets < g_pickup_rockets_max && need_rockets)
72 rating = rating + max(0, 1 - self.ammo_rockets / g_pickup_rockets_max);
75 if (self.ammo_cells < g_pickup_cells_max && need_cells)
76 rating = rating + max(0, 1 - self.ammo_cells / g_pickup_cells_max);
79 if (self.armorvalue < item.max_armorvalue * 0.5)
80 rating = rating + max(0, 1 - self.armorvalue / (item.max_armorvalue * 0.5));
83 if (self.health < item.max_health * 0.5)
85 rating = rating + max(0, 1 - self.health / (item.max_health * 0.5));
88 // TODO: if the item is not recognized then default to item.bot_pickupevalfunc(self, item);
93 void havocbot_goalrating_items(float ratingscale, vector org, float sradius)
97 local float rating, d, discard, distance, friend_distance, enemy_distance;
98 ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
99 head = findchainfloat(bot_pickup, TRUE);
103 distance = vlen(head.origin - org);
104 friend_distance = 10000; enemy_distance = 10000;
107 if(!head.solid || distance > sradius || (head == self.ignoregoal && time < self.ignoregoaltime) )
113 // Check if the item can be picked up safely
114 if(head.classname == "droppedweapon")
116 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
118 d = pointcontents(trace_endpos + '0 0 1');
119 if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
124 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
132 // Ignore items under water
133 traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
134 if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
145 FOR_EACH_PLAYER(player)
148 if ( self == player || player.deadflag )
151 d = vlen(player.origin - head.origin); // distance between player and item
153 if ( player.team == self.team )
155 if ( clienttype(player) != CLIENTTYPE_REAL || discard )
158 if( d > friend_distance)
165 if( head.health && player.health > self.health )
168 if( head.armorvalue && player.armorvalue > self.armorvalue)
172 if( (player.weapons & head.weapons) != head.weapons)
175 if (head.ammo_shells && player.ammo_shells > self.ammo_shells)
178 if (head.ammo_nails && player.ammo_nails > self.ammo_nails)
181 if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets)
184 if (head.ammo_cells && player.ammo_cells > self.ammo_cells )
191 // If enemy only track distances
192 // TODO: track only if visible ?
193 if( d < enemy_distance )
198 // Rate the item only if no one needs it, or if an enemy is closer to it
199 if ( (enemy_distance < friend_distance && distance < enemy_distance) ||
200 (friend_distance > cvar("bot_ai_friends_aware_pickup_radius") ) || !discard )
202 // rating = head.bot_pickupevalfunc(self, head);
203 rating = havocbot_pickupevalfunc(head);
208 // rating = head.bot_pickupevalfunc(self, head);
209 rating = havocbot_pickupevalfunc(head);
213 navigation_routerating(head, rating * ratingscale, 2000);
218 void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius)
221 head = findchain(classname, "dom_controlpoint");
224 if (vlen(head.origin - org) < sradius)
226 if(head.cnt > -1) // this is just being fought for
227 navigation_routerating(head, ratingscale, 5000);
228 else if(head.goalentity.cnt == 0) // unclaimed point
229 navigation_routerating(head, ratingscale * 0.5, 5000);
230 else if(head.goalentity.team != self.team) // other team's point
231 navigation_routerating(head, ratingscale * 0.2, 5000);
238 // LordHavoc: this function was already unused, but for waypoints to be a
239 // useful goal the bots would have to seek out the least-recently-visited
240 // ones, not the closest
241 void havocbot_goalrating_waypoints(float ratingscale, vector org, float sradius)
244 head = findchain(classname, "waypoint");
247 if (vlen(head.origin - org) < sradius && vlen(head.origin - org) > 100)
248 navigation_routerating(head, ratingscale, 2000);
254 void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius)
257 local float t, noteam, distance;
258 noteam = ((self.team == 0) || !teams_matter); // fteqcc sucks
260 if (cvar("bot_nofire"))
263 // don't chase players if we're under water
264 if(self.waterlevel>WATERLEVEL_WETFEET)
267 FOR_EACH_PLAYER(head)
269 // TODO: Merge this logic with the bot_shouldattack function
272 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
274 distance = vlen(head.origin - org);
275 if (distance < 100 || distance > sradius)
279 if(head.items & IT_STRENGTH)
282 // rate only visible enemies
284 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
285 if (trace_fraction < 1 || trace_ent != head)
289 if(head.flags & FL_INWATER || head.flags & FL_PARTIALGROUND)
293 if(head.flags & FL_ONGROUND == 0)
295 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
296 t = pointcontents(trace_endpos + '0 0 1');
297 if( t != CONTENT_SOLID )
298 if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
300 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
304 t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
305 navigation_routerating(head, t * ratingscale, 2000);
311 void() havocbot_role_ctf_middle;
312 void() havocbot_role_ctf_defense;
313 void() havocbot_role_ctf_offense;
314 void() havocbot_role_ctf_interceptor;
316 void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
320 head = findchainfloat(bot_pickup, TRUE);
323 // look for health and armor only
324 if (head.solid) // must be possible to pick up (respawning items don't count)
325 if (head.health || head.armorvalue)
326 if (vlen(head.origin - org) < sradius)
329 //if (!head.bot_pickupevalfunc || head.model == "")
331 // get the value of the item
332 t = head.bot_pickupevalfunc(self, head) * 0.0001;
334 navigation_routerating(head, t * ratingscale, 500);
340 entity ctf_worldflaglist;
341 .entity ctf_worldflagnext;
342 void havocbot_goalrating_ctf_ourflag(float ratingscale)
345 head = ctf_worldflaglist;
348 if (self.team == head.team)
350 head = head.ctf_worldflagnext;
353 navigation_routerating(head, ratingscale, 10000);
356 void havocbot_goalrating_ctf_enemyflag(float ratingscale)
359 head = ctf_worldflaglist;
362 if (self.team != head.team)
364 head = head.ctf_worldflagnext;
367 navigation_routerating(head, ratingscale, 10000);
370 void havocbot_goalrating_ctf_enemybase(float ratingscale)
372 // div0: needs a change in the CTF code
375 void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
378 head = ctf_worldflaglist;
381 if (self.team == head.team)
383 head = head.ctf_worldflagnext;
386 if (head.cnt != FLAG_BASE)
387 navigation_routerating(head, ratingscale, 10000);
390 void havocbot_goalrating_ctf_droppedflags(float ratingscale)
393 head = ctf_worldflaglist;
396 if (head.cnt != FLAG_BASE) // flag is carried or out in the field
397 navigation_routerating(head, ratingscale, 10000);
398 head = head.ctf_worldflagnext;
402 // CTF: (always teamplay)
404 //role rogue: (is this used?)
405 //pick up items and dropped flags (with big rating boost to dropped flags)
406 void havocbot_role_ctf_rogue()
408 if (self.bot_strategytime < time)
410 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
411 navigation_goalrating_start();
412 havocbot_goalrating_ctf_droppedflags(5000);
413 //havocbot_goalrating_enemyplayers(3000, self.origin, 3000);
414 havocbot_goalrating_items(10000, self.origin, 10000);
415 navigation_goalrating_end();
420 //pick up armor and health
421 //go to our flag spot
422 .float bot_cantfindflag;
423 void havocbot_role_ctf_carrier()
425 if (self.flagcarried == world)
427 dprint("changing role to middle\n");
428 self.havocbot_role = havocbot_role_ctf_middle;
429 self.havocbot_role_timeout = 0;
432 if (self.bot_strategytime < time)
434 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
435 navigation_goalrating_start();
436 havocbot_goalrating_ctf_ourflag(50000);
437 if (navigation_bestgoal)
438 self.bot_cantfindflag = time + 10;
439 else if (time > self.bot_cantfindflag)
441 // can't navigate to our own flag :(
442 Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
446 havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
447 navigation_goalrating_end();
452 //pick up armor and health
453 //if rockets < 25 || health < 100, change role to middle
454 //if carrying flag, change role to flag carrier
455 //if our flag taken, change role to interceptor
456 //(60-90 second timer) change role to middle
458 void havocbot_role_ctf_offense()
461 if (self.flagcarried)
463 dprint("changing role to carrier\n");
464 self.havocbot_role = havocbot_role_ctf_carrier;
465 self.havocbot_role_timeout = 0;
466 self.bot_cantfindflag = time + 10;
470 f = ctf_worldflaglist;
473 if (self.team == f.team)
475 f = f.ctf_worldflagnext;
477 if (f.cnt != FLAG_BASE && canreach(f))
479 dprint("changing role to interceptor\n");
480 self.havocbot_previous_role = self.havocbot_role;
481 self.havocbot_role = havocbot_role_ctf_interceptor;
482 self.havocbot_role_timeout = 0;
485 if (!self.havocbot_role_timeout)
486 self.havocbot_role_timeout = time + random() * 30 + 60;
487 if (time > self.havocbot_role_timeout)
489 dprint("changing role to middle\n");
490 self.havocbot_role = havocbot_role_ctf_middle;
491 self.havocbot_role_timeout = 0;
494 if (self.bot_strategytime < time)
496 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
497 navigation_goalrating_start();
498 havocbot_goalrating_ctf_ourstolenflag(50000);
499 havocbot_goalrating_ctf_enemyflag(30000);
500 havocbot_goalrating_ctf_enemybase(20000);
501 havocbot_goalrating_items(10000, self.origin, 10000);
502 navigation_goalrating_end();
506 //role interceptor (temporary role):
508 //if carrying flag, change role to flag carrier
509 //if our flag is back, change role to previous role
511 //go to least recently visited area
512 void havocbot_role_ctf_interceptor()
515 if (self.flagcarried)
517 dprint("changing role to carrier\n");
518 self.havocbot_role = havocbot_role_ctf_carrier;
519 self.havocbot_role_timeout = 0;
520 self.bot_cantfindflag = time + 10;
524 f = ctf_worldflaglist;
527 if (self.team == f.team)
529 f = f.ctf_worldflagnext;
531 if (f.cnt == FLAG_BASE)
533 dprint("changing role back\n");
534 self.havocbot_role = self.havocbot_previous_role;
535 self.havocbot_role_timeout = 0;
539 if (self.bot_strategytime < time)
541 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
542 navigation_goalrating_start();
543 havocbot_goalrating_ctf_ourstolenflag(50000);
544 havocbot_goalrating_ctf_droppedflags(50000);
545 havocbot_goalrating_items(10000, self.origin, 10000);
546 navigation_goalrating_end();
552 //if carrying flag, change role to flag carrier
553 //if our flag taken, change role to interceptor
554 //if see flag (of either team) follow it (this has many implications)
555 //(10-20 second timer) change role to defense or offense
556 //go to least recently visited area
557 void havocbot_role_ctf_middle()
560 if (self.flagcarried)
562 dprint("changing role to carrier\n");
563 self.havocbot_role = havocbot_role_ctf_carrier;
564 self.havocbot_role_timeout = 0;
565 self.bot_cantfindflag = time + 10;
569 f = ctf_worldflaglist;
572 if (self.team == f.team)
574 f = f.ctf_worldflagnext;
576 if (f.cnt != FLAG_BASE && canreach(f))
578 dprint("changing role to interceptor\n");
579 self.havocbot_previous_role = self.havocbot_role;
580 self.havocbot_role = havocbot_role_ctf_interceptor;
581 self.havocbot_role_timeout = 0;
584 if (!self.havocbot_role_timeout)
585 self.havocbot_role_timeout = time + random() * 10 + 10;
586 if (time > self.havocbot_role_timeout)
590 dprint("changing role to offense\n");
591 self.havocbot_role = havocbot_role_ctf_offense;
595 dprint("changing role to defense\n");
596 self.havocbot_role = havocbot_role_ctf_defense;
598 self.havocbot_role_timeout = 0;
602 if (self.bot_strategytime < time)
604 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
605 navigation_goalrating_start();
606 havocbot_goalrating_ctf_ourstolenflag(50000);
607 havocbot_goalrating_ctf_droppedflags(30000);
608 //havocbot_goalrating_enemyplayers(1000, self.origin, 1000);
609 havocbot_goalrating_items(10000, self.origin, 10000);
610 navigation_goalrating_end();
615 //if rockets < 25 || health < 100, change role to middle
616 //if carrying flag, change role to flag carrier
617 //if our flag taken, change role to interceptor
618 //(30-50 second timer) change role to middle
619 //move to nearest unclaimed defense spot
620 void havocbot_role_ctf_defense()
623 if (self.flagcarried)
625 dprint("changing role to carrier\n");
626 self.havocbot_role = havocbot_role_ctf_carrier;
627 self.havocbot_role_timeout = 0;
628 self.bot_cantfindflag = time + 10;
632 f = ctf_worldflaglist;
635 if (self.team == f.team)
637 f = f.ctf_worldflagnext;
639 if (f.cnt != FLAG_BASE && canreach(f))
641 dprint("changing role to interceptor\n");
642 self.havocbot_previous_role = self.havocbot_role;
643 self.havocbot_role = havocbot_role_ctf_interceptor;
644 self.havocbot_role_timeout = 0;
647 if (!self.havocbot_role_timeout)
648 self.havocbot_role_timeout = time + random() * 20 + 30;
649 if (time > self.havocbot_role_timeout)
651 dprint("changing role to middle\n");
652 self.havocbot_role = havocbot_role_ctf_middle;
653 self.havocbot_role_timeout = 0;
656 if (self.bot_strategytime < time)
658 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
659 navigation_goalrating_start();
660 havocbot_goalrating_ctf_ourstolenflag(200000);
661 havocbot_goalrating_ctf_droppedflags(50000);
662 havocbot_goalrating_items(10000, f.origin, 10000);
663 navigation_goalrating_end();
666 // FIXME: place info_ctf_defensepoint entities in CTF maps and use them
667 // change position occasionally
668 if (time > self.bot_strategytime || self.goalentity.classname != "info_ctf_defensepoint")
670 self.bot_strategytime = time + random() * 45 + 15;
671 self.goalentity = world;
672 head = findchain(classname, "info_ctf_defensepoint");
675 if (time > head.count)
677 self.goalentity = head;
682 // if there are no defensepoints defined, switch to middle
683 if (self.goalentity == world)
685 dprint("changing role to middle\n");
686 self.havocbot_role = havocbot_role_ctf_middle;
687 self.havocbot_role_timeout = 0;
691 // keep anyone else from taking this spot
692 if (self.goalentity != world)
693 self.goalentity.count = time + 0.5;
698 // choose a role according to the situation
699 void() havocbot_role_dm;
700 void havocbot_chooserole_ctf()
703 dprint("choose CTF role...\n");
704 if (self.team == COLOR_TEAM3 || self.team == COLOR_TEAM4)
705 self.havocbot_role = havocbot_role_ctf_rogue;
710 self.havocbot_role = havocbot_role_ctf_offense;
712 self.havocbot_role = havocbot_role_ctf_middle;
714 self.havocbot_role = havocbot_role_ctf_defense;
719 //go to best items, or control points you don't own
720 void havocbot_role_dom()
722 if (self.bot_strategytime < time)
724 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
725 navigation_goalrating_start();
726 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
727 havocbot_goalrating_items(8000, self.origin, 8000);
728 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
729 //havocbot_goalrating_waypoints(1, self.origin, 1000);
730 navigation_goalrating_end();
736 void havocbot_role_dm()
738 if (self.bot_strategytime < time)
740 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
741 navigation_goalrating_start();
742 havocbot_goalrating_items(10000, self.origin, 10000);
743 havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
744 //havocbot_goalrating_waypoints(1, self.origin, 1000);
745 navigation_goalrating_end();
750 //go to next checkpoint, and annoy enemies
751 .float race_checkpoint;
752 void havocbot_role_race()
755 if (self.bot_strategytime < time)
757 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
758 navigation_goalrating_start();
760 havocbot_goalrating_items(100, self.origin, 10000);
761 havocbot_goalrating_enemyplayers(500, self.origin, 20000);
764 for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
766 if(e.cnt == self.race_checkpoint)
768 navigation_routerating(e, 1000000, 5000);
770 else if(self.race_checkpoint == -1)
772 navigation_routerating(e, 1000000, 5000);
776 navigation_goalrating_end();
780 void havocbot_chooserole_dm()
782 self.havocbot_role = havocbot_role_dm;
785 void havocbot_chooserole_race()
787 self.havocbot_role = havocbot_role_race;
790 void havocbot_chooserole_dom()
792 self.havocbot_role = havocbot_role_dom;
800 entity kh_worldkeylist;
801 .entity kh_worldkeynext;
802 void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
805 for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
807 if(head.owner == self)
809 if(!kh_tracking_enabled)
811 // if it's carried by our team we know about it
812 // otherwise we have to see it to know about it
813 if(!head.owner || head.team != self.team)
815 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
816 if (trace_fraction < 1 && trace_ent != head)
817 continue; // skip what I can't see
821 navigation_routerating(head, ratingscale_dropped, 100000);
822 else if(head.team == self.team)
823 navigation_routerating(head, ratingscale_team, 100000);
825 navigation_routerating(head, ratingscale_enemy, 100000);
829 void() havocbot_role_kh_carrier;
830 void() havocbot_role_kh_defense;
831 void() havocbot_role_kh_offense;
832 void() havocbot_role_kh_freelancer;
833 void havocbot_role_kh_carrier()
835 if (!(self.items & IT_KEY1))
837 dprint("changing role to freelancer\n");
838 self.havocbot_role = havocbot_role_kh_freelancer;
839 self.havocbot_role_timeout = 0;
843 if (self.bot_strategytime < time)
845 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
846 navigation_goalrating_start();
848 if(kh_Key_AllOwnedByWhichTeam() == self.team)
849 havocbot_goalrating_kh(100000, 1, 1); // bring home
851 havocbot_goalrating_kh(40000, 40000, 1000); // play defensively
853 havocbot_goalrating_items(10000, self.origin, 10000);
854 navigation_goalrating_end();
858 void havocbot_role_kh_defense()
860 if (self.items & IT_KEY1)
862 dprint("changing role to carrier\n");
863 self.havocbot_role = havocbot_role_kh_carrier;
864 self.havocbot_role_timeout = 0;
868 if (!self.havocbot_role_timeout)
869 self.havocbot_role_timeout = time + random() * 10 + 20;
870 if (time > self.havocbot_role_timeout)
872 dprint("changing role to freelancer\n");
873 self.havocbot_role = havocbot_role_kh_freelancer;
874 self.havocbot_role_timeout = 0;
878 if (self.bot_strategytime < time)
880 float key_owner_team;
881 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
882 navigation_goalrating_start();
884 key_owner_team = kh_Key_AllOwnedByWhichTeam();
885 if(key_owner_team == self.team)
886 havocbot_goalrating_kh(100000, 1, 1); // defend key carriers
887 else if(key_owner_team == -1)
888 havocbot_goalrating_kh(40000, 10000, 1); // play defensively
890 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
892 havocbot_goalrating_items(10000, self.origin, 10000);
893 navigation_goalrating_end();
897 void havocbot_role_kh_offense()
899 if (self.items & IT_KEY1)
901 dprint("changing role to carrier\n");
902 self.havocbot_role = havocbot_role_kh_carrier;
903 self.havocbot_role_timeout = 0;
907 if (!self.havocbot_role_timeout)
908 self.havocbot_role_timeout = time + random() * 10 + 20;
909 if (time > self.havocbot_role_timeout)
911 dprint("changing role to freelancer\n");
912 self.havocbot_role = havocbot_role_kh_freelancer;
913 self.havocbot_role_timeout = 0;
917 if (self.bot_strategytime < time)
919 float key_owner_team;
921 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
922 navigation_goalrating_start();
924 key_owner_team = kh_Key_AllOwnedByWhichTeam();
925 if(key_owner_team == self.team)
926 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
927 else if(key_owner_team == -1)
928 havocbot_goalrating_kh(1, 10000, 40000); // play offensively
930 havocbot_goalrating_kh(1, 1, 100000); // ATTACK! EMERGENCY!
932 havocbot_goalrating_items(10000, self.origin, 10000);
933 navigation_goalrating_end();
937 void havocbot_role_kh_freelancer()
939 if (self.items & IT_KEY1)
941 dprint("changing role to carrier\n");
942 self.havocbot_role = havocbot_role_kh_carrier;
943 self.havocbot_role_timeout = 0;
947 if (!self.havocbot_role_timeout)
948 self.havocbot_role_timeout = time + random() * 10 + 10;
949 if (time > self.havocbot_role_timeout)
953 dprint("changing role to offense\n");
954 self.havocbot_role = havocbot_role_kh_offense;
958 dprint("changing role to defense\n");
959 self.havocbot_role = havocbot_role_kh_defense;
961 self.havocbot_role_timeout = 0;
965 if (self.bot_strategytime < time)
967 float key_owner_team;
969 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
970 navigation_goalrating_start();
972 key_owner_team = kh_Key_AllOwnedByWhichTeam();
973 if(key_owner_team == self.team)
974 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
975 else if(key_owner_team == -1)
976 havocbot_goalrating_kh(10000, 40000, 10000); // prefer dropped keys
978 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
980 havocbot_goalrating_items(10000, self.origin, 10000);
981 navigation_goalrating_end();
989 void havocbot_chooserole_kh()
994 self.havocbot_role = havocbot_role_kh_offense;
996 self.havocbot_role = havocbot_role_kh_defense;
998 self.havocbot_role = havocbot_role_kh_freelancer;
1001 void havocbot_chooserole()
1003 dprint("choose a role...\n");
1004 navigation_routetogoal(world);
1005 self.bot_strategytime = -1;
1007 havocbot_chooserole_ctf();
1008 else if (g_domination)
1009 havocbot_chooserole_dom();
1011 havocbot_chooserole_kh();
1013 havocbot_chooserole_race();
1014 else // assume anything else is deathmatch
1015 havocbot_chooserole_dm();