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))
135 FOR_EACH_PLAYER(player)
138 if ( self == player || player.deadflag )
141 d = vlen(player.origin - head.origin); // distance between player and item
143 if ( player.team == self.team )
145 if ( clienttype(player) != CLIENTTYPE_REAL || discard )
148 if( d > friend_distance)
155 if( head.health && player.health > self.health )
158 if( head.armorvalue && player.armorvalue > self.armorvalue)
162 if( (player.weapons & head.weapons) != head.weapons)
165 if (head.ammo_shells && player.ammo_shells > self.ammo_shells)
168 if (head.ammo_nails && player.ammo_nails > self.ammo_nails)
171 if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets)
174 if (head.ammo_cells && player.ammo_cells > self.ammo_cells )
181 // If enemy only track distances
182 // TODO: track only if visible ?
183 if( d < enemy_distance )
188 // Rate the item only if no one needs it, or if an enemy is closer to it
189 if ( (enemy_distance < friend_distance && distance < enemy_distance) ||
190 (friend_distance > cvar("bot_ai_friends_aware_pickup_radius") ) || !discard )
192 // rating = head.bot_pickupevalfunc(self, head);
193 rating = havocbot_pickupevalfunc(head);
198 // rating = head.bot_pickupevalfunc(self, head);
199 rating = havocbot_pickupevalfunc(head);
203 navigation_routerating(head, rating * ratingscale, 2000);
209 void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius)
212 head = findchain(classname, "dom_controlpoint");
215 if (vlen(head.origin - org) < sradius)
217 if(head.cnt > -1) // this is just being fought for
218 navigation_routerating(head, ratingscale, 5000);
219 else if(head.goalentity.cnt == 0) // unclaimed point
220 navigation_routerating(head, ratingscale * 0.5, 5000);
221 else if(head.goalentity.team != self.team) // other team's point
222 navigation_routerating(head, ratingscale * 0.2, 5000);
229 // LordHavoc: this function was already unused, but for waypoints to be a
230 // useful goal the bots would have to seek out the least-recently-visited
231 // ones, not the closest
232 void havocbot_goalrating_waypoints(float ratingscale, vector org, float sradius)
235 head = findchain(classname, "waypoint");
238 if (vlen(head.origin - org) < sradius && vlen(head.origin - org) > 100)
239 navigation_routerating(head, ratingscale, 2000);
245 void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius)
248 local float t, noteam;
249 noteam = ((self.team == 0) || !teams_matter); // fteqcc sucks
251 FOR_EACH_PLAYER(head)
253 // TODO: Merge this logic with the bot_shouldattack function
256 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
258 if (vlen(head.origin - org) > sradius)
262 if(head.items & IT_STRENGTH)
265 // rate only visible enemies
267 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
268 if (trace_fraction < 1 || trace_ent != head)
272 if(head.flags & FL_INWATER || head.flags & FL_PARTIALGROUND)
276 if(head.flags & FL_ONGROUND == 0)
278 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
279 t = pointcontents(trace_endpos + '0 0 1');
280 if( t != CONTENT_SOLID )
281 if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
283 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
287 t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
288 navigation_routerating(head, t * ratingscale, 2000);
294 void() havocbot_role_ctf_middle;
295 void() havocbot_role_ctf_defense;
296 void() havocbot_role_ctf_offense;
297 void() havocbot_role_ctf_interceptor;
299 void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
303 head = findchainfloat(bot_pickup, TRUE);
306 // look for health and armor only
307 if (head.solid) // must be possible to pick up (respawning items don't count)
308 if (head.health || head.armorvalue)
309 if (vlen(head.origin - org) < sradius)
312 //if (!head.bot_pickupevalfunc || head.model == "")
314 // get the value of the item
315 t = head.bot_pickupevalfunc(self, head) * 0.0001;
317 navigation_routerating(head, t * ratingscale, 500);
323 entity ctf_worldflaglist;
324 .entity ctf_worldflagnext;
325 void havocbot_goalrating_ctf_ourflag(float ratingscale)
328 head = ctf_worldflaglist;
331 if (self.team == head.team)
333 head = head.ctf_worldflagnext;
336 navigation_routerating(head, ratingscale, 10000);
339 void havocbot_goalrating_ctf_enemyflag(float ratingscale)
342 head = ctf_worldflaglist;
345 if (self.team != head.team)
347 head = head.ctf_worldflagnext;
350 navigation_routerating(head, ratingscale, 10000);
353 void havocbot_goalrating_ctf_enemybase(float ratingscale)
355 // div0: needs a change in the CTF code
358 void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
361 head = ctf_worldflaglist;
364 if (self.team == head.team)
366 head = head.ctf_worldflagnext;
369 if (head.cnt != FLAG_BASE)
370 navigation_routerating(head, ratingscale, 10000);
373 void havocbot_goalrating_ctf_droppedflags(float ratingscale)
376 head = ctf_worldflaglist;
379 if (head.cnt != FLAG_BASE) // flag is carried or out in the field
380 navigation_routerating(head, ratingscale, 10000);
381 head = head.ctf_worldflagnext;
385 // CTF: (always teamplay)
387 //role rogue: (is this used?)
388 //pick up items and dropped flags (with big rating boost to dropped flags)
389 void havocbot_role_ctf_rogue()
391 if (self.bot_strategytime < time)
393 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
394 navigation_goalrating_start();
395 havocbot_goalrating_ctf_droppedflags(5000);
396 //havocbot_goalrating_enemyplayers(3000, self.origin, 3000);
397 havocbot_goalrating_items(10000, self.origin, 10000);
398 navigation_goalrating_end();
403 //pick up armor and health
404 //go to our flag spot
405 .float bot_cantfindflag;
406 void havocbot_role_ctf_carrier()
408 if (self.flagcarried == world)
410 dprint("changing role to middle\n");
411 self.havocbot_role = havocbot_role_ctf_middle;
412 self.havocbot_role_timeout = 0;
415 if (self.bot_strategytime < time)
417 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
418 navigation_goalrating_start();
419 havocbot_goalrating_ctf_ourflag(50000);
420 if (navigation_bestgoal)
421 self.bot_cantfindflag = time + 10;
422 else if (time > self.bot_cantfindflag)
424 // can't navigate to our own flag :(
425 Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
427 havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
428 navigation_goalrating_end();
433 //pick up armor and health
434 //if rockets < 25 || health < 100, change role to middle
435 //if carrying flag, change role to flag carrier
436 //if our flag taken, change role to interceptor
437 //(60-90 second timer) change role to middle
439 void havocbot_role_ctf_offense()
442 if (self.flagcarried)
444 dprint("changing role to carrier\n");
445 self.havocbot_role = havocbot_role_ctf_carrier;
446 self.havocbot_role_timeout = 0;
447 self.bot_cantfindflag = time + 10;
451 f = ctf_worldflaglist;
454 if (self.team == f.team)
456 f = f.ctf_worldflagnext;
458 if (f.cnt != FLAG_BASE && canreach(f))
460 dprint("changing role to interceptor\n");
461 self.havocbot_previous_role = self.havocbot_role;
462 self.havocbot_role = havocbot_role_ctf_interceptor;
463 self.havocbot_role_timeout = 0;
466 if (!self.havocbot_role_timeout)
467 self.havocbot_role_timeout = time + random() * 30 + 60;
468 if (time > self.havocbot_role_timeout)
470 dprint("changing role to middle\n");
471 self.havocbot_role = havocbot_role_ctf_middle;
472 self.havocbot_role_timeout = 0;
475 if (self.bot_strategytime < time)
477 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
478 navigation_goalrating_start();
479 havocbot_goalrating_ctf_ourstolenflag(50000);
480 havocbot_goalrating_ctf_enemyflag(30000);
481 havocbot_goalrating_ctf_enemybase(20000);
482 havocbot_goalrating_items(10000, self.origin, 10000);
483 navigation_goalrating_end();
487 //role interceptor (temporary role):
489 //if carrying flag, change role to flag carrier
490 //if our flag is back, change role to previous role
492 //go to least recently visited area
493 void havocbot_role_ctf_interceptor()
496 if (self.flagcarried)
498 dprint("changing role to carrier\n");
499 self.havocbot_role = havocbot_role_ctf_carrier;
500 self.havocbot_role_timeout = 0;
501 self.bot_cantfindflag = time + 10;
505 f = ctf_worldflaglist;
508 if (self.team == f.team)
510 f = f.ctf_worldflagnext;
512 if (f.cnt == FLAG_BASE)
514 dprint("changing role back\n");
515 self.havocbot_role = self.havocbot_previous_role;
516 self.havocbot_role_timeout = 0;
520 if (self.bot_strategytime < time)
522 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
523 navigation_goalrating_start();
524 havocbot_goalrating_ctf_ourstolenflag(50000);
525 havocbot_goalrating_ctf_droppedflags(50000);
526 havocbot_goalrating_items(10000, self.origin, 10000);
527 navigation_goalrating_end();
533 //if carrying flag, change role to flag carrier
534 //if our flag taken, change role to interceptor
535 //if see flag (of either team) follow it (this has many implications)
536 //(10-20 second timer) change role to defense or offense
537 //go to least recently visited area
538 void havocbot_role_ctf_middle()
541 if (self.flagcarried)
543 dprint("changing role to carrier\n");
544 self.havocbot_role = havocbot_role_ctf_carrier;
545 self.havocbot_role_timeout = 0;
546 self.bot_cantfindflag = time + 10;
550 f = ctf_worldflaglist;
553 if (self.team == f.team)
555 f = f.ctf_worldflagnext;
557 if (f.cnt != FLAG_BASE && canreach(f))
559 dprint("changing role to interceptor\n");
560 self.havocbot_previous_role = self.havocbot_role;
561 self.havocbot_role = havocbot_role_ctf_interceptor;
562 self.havocbot_role_timeout = 0;
565 if (!self.havocbot_role_timeout)
566 self.havocbot_role_timeout = time + random() * 10 + 10;
567 if (time > self.havocbot_role_timeout)
571 dprint("changing role to offense\n");
572 self.havocbot_role = havocbot_role_ctf_offense;
576 dprint("changing role to defense\n");
577 self.havocbot_role = havocbot_role_ctf_defense;
579 self.havocbot_role_timeout = 0;
583 if (self.bot_strategytime < time)
585 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
586 navigation_goalrating_start();
587 havocbot_goalrating_ctf_ourstolenflag(50000);
588 havocbot_goalrating_ctf_droppedflags(30000);
589 //havocbot_goalrating_enemyplayers(1000, self.origin, 1000);
590 havocbot_goalrating_items(10000, self.origin, 10000);
591 navigation_goalrating_end();
596 //if rockets < 25 || health < 100, change role to middle
597 //if carrying flag, change role to flag carrier
598 //if our flag taken, change role to interceptor
599 //(30-50 second timer) change role to middle
600 //move to nearest unclaimed defense spot
601 void havocbot_role_ctf_defense()
604 if (self.flagcarried)
606 dprint("changing role to carrier\n");
607 self.havocbot_role = havocbot_role_ctf_carrier;
608 self.havocbot_role_timeout = 0;
609 self.bot_cantfindflag = time + 10;
613 f = ctf_worldflaglist;
616 if (self.team == f.team)
618 f = f.ctf_worldflagnext;
620 if (f.cnt != FLAG_BASE && canreach(f))
622 dprint("changing role to interceptor\n");
623 self.havocbot_previous_role = self.havocbot_role;
624 self.havocbot_role = havocbot_role_ctf_interceptor;
625 self.havocbot_role_timeout = 0;
628 if (!self.havocbot_role_timeout)
629 self.havocbot_role_timeout = time + random() * 20 + 30;
630 if (time > self.havocbot_role_timeout)
632 dprint("changing role to middle\n");
633 self.havocbot_role = havocbot_role_ctf_middle;
634 self.havocbot_role_timeout = 0;
637 if (self.bot_strategytime < time)
639 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
640 navigation_goalrating_start();
641 havocbot_goalrating_ctf_ourstolenflag(200000);
642 havocbot_goalrating_ctf_droppedflags(50000);
643 havocbot_goalrating_items(10000, f.origin, 10000);
644 navigation_goalrating_end();
647 // FIXME: place info_ctf_defensepoint entities in CTF maps and use them
648 // change position occasionally
649 if (time > self.bot_strategytime || self.goalentity.classname != "info_ctf_defensepoint")
651 self.bot_strategytime = time + random() * 45 + 15;
652 self.goalentity = world;
653 head = findchain(classname, "info_ctf_defensepoint");
656 if (time > head.count)
658 self.goalentity = head;
663 // if there are no defensepoints defined, switch to middle
664 if (self.goalentity == world)
666 dprint("changing role to middle\n");
667 self.havocbot_role = havocbot_role_ctf_middle;
668 self.havocbot_role_timeout = 0;
672 // keep anyone else from taking this spot
673 if (self.goalentity != world)
674 self.goalentity.count = time + 0.5;
679 // choose a role according to the situation
680 void() havocbot_role_dm;
681 void havocbot_chooserole_ctf()
684 dprint("choose CTF role...\n");
685 if (self.team == COLOR_TEAM3 || self.team == COLOR_TEAM4)
686 self.havocbot_role = havocbot_role_ctf_rogue;
691 self.havocbot_role = havocbot_role_ctf_offense;
693 self.havocbot_role = havocbot_role_ctf_middle;
695 self.havocbot_role = havocbot_role_ctf_defense;
700 //go to best items, or control points you don't own
701 void havocbot_role_dom()
703 if (self.bot_strategytime < time)
705 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
706 navigation_goalrating_start();
707 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
708 havocbot_goalrating_items(8000, self.origin, 8000);
709 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
710 //havocbot_goalrating_waypoints(1, self.origin, 1000);
711 navigation_goalrating_end();
717 void havocbot_role_dm()
719 if (self.bot_strategytime < time)
721 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
722 navigation_goalrating_start();
723 havocbot_goalrating_items(10000, self.origin, 10000);
724 havocbot_goalrating_enemyplayers(20000, self.origin, 20000);
725 //havocbot_goalrating_waypoints(1, self.origin, 1000);
726 navigation_goalrating_end();
731 //go to next checkpoint, and annoy enemies
732 .float race_checkpoint;
733 void havocbot_role_race()
736 if (self.bot_strategytime < time)
738 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
739 navigation_goalrating_start();
741 havocbot_goalrating_items(100, self.origin, 10000);
742 havocbot_goalrating_enemyplayers(500, self.origin, 20000);
745 for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
747 if(e.cnt == self.race_checkpoint)
749 navigation_routerating(e, 1000000, 5000);
751 else if(self.race_checkpoint == -1)
753 navigation_routerating(e, 1000000, 5000);
757 navigation_goalrating_end();
761 void havocbot_chooserole_dm()
763 self.havocbot_role = havocbot_role_dm;
766 void havocbot_chooserole_race()
768 self.havocbot_role = havocbot_role_race;
771 void havocbot_chooserole_dom()
773 self.havocbot_role = havocbot_role_dom;
781 entity kh_worldkeylist;
782 .entity kh_worldkeynext;
783 void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
786 for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
788 if(head.owner == self)
790 if(!kh_tracking_enabled)
792 // if it's carried by our team we know about it
793 // otherwise we have to see it to know about it
794 if(!head.owner || head.team != self.team)
796 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
797 if (trace_fraction < 1 && trace_ent != head)
798 continue; // skip what I can't see
802 navigation_routerating(head, ratingscale_dropped, 100000);
803 else if(head.team == self.team)
804 navigation_routerating(head, ratingscale_team, 100000);
806 navigation_routerating(head, ratingscale_enemy, 100000);
810 void() havocbot_role_kh_carrier;
811 void() havocbot_role_kh_defense;
812 void() havocbot_role_kh_offense;
813 void() havocbot_role_kh_freelancer;
814 void havocbot_role_kh_carrier()
816 if (!(self.items & IT_KEY1))
818 dprint("changing role to freelancer\n");
819 self.havocbot_role = havocbot_role_kh_freelancer;
820 self.havocbot_role_timeout = 0;
824 if (self.bot_strategytime < time)
826 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
827 navigation_goalrating_start();
829 if(kh_Key_AllOwnedByWhichTeam() == self.team)
830 havocbot_goalrating_kh(100000, 1, 1); // bring home
832 havocbot_goalrating_kh(40000, 40000, 1000); // play defensively
834 havocbot_goalrating_items(10000, self.origin, 10000);
835 navigation_goalrating_end();
839 void havocbot_role_kh_defense()
841 if (self.items & IT_KEY1)
843 dprint("changing role to carrier\n");
844 self.havocbot_role = havocbot_role_kh_carrier;
845 self.havocbot_role_timeout = 0;
849 if (!self.havocbot_role_timeout)
850 self.havocbot_role_timeout = time + random() * 10 + 20;
851 if (time > self.havocbot_role_timeout)
853 dprint("changing role to freelancer\n");
854 self.havocbot_role = havocbot_role_kh_freelancer;
855 self.havocbot_role_timeout = 0;
859 if (self.bot_strategytime < time)
861 float key_owner_team;
862 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
863 navigation_goalrating_start();
865 key_owner_team = kh_Key_AllOwnedByWhichTeam();
866 if(key_owner_team == self.team)
867 havocbot_goalrating_kh(100000, 1, 1); // defend key carriers
868 else if(key_owner_team == -1)
869 havocbot_goalrating_kh(40000, 10000, 1); // play defensively
871 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
873 havocbot_goalrating_items(10000, self.origin, 10000);
874 navigation_goalrating_end();
878 void havocbot_role_kh_offense()
880 if (self.items & IT_KEY1)
882 dprint("changing role to carrier\n");
883 self.havocbot_role = havocbot_role_kh_carrier;
884 self.havocbot_role_timeout = 0;
888 if (!self.havocbot_role_timeout)
889 self.havocbot_role_timeout = time + random() * 10 + 20;
890 if (time > self.havocbot_role_timeout)
892 dprint("changing role to freelancer\n");
893 self.havocbot_role = havocbot_role_kh_freelancer;
894 self.havocbot_role_timeout = 0;
898 if (self.bot_strategytime < time)
900 float key_owner_team;
902 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
903 navigation_goalrating_start();
905 key_owner_team = kh_Key_AllOwnedByWhichTeam();
906 if(key_owner_team == self.team)
907 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
908 else if(key_owner_team == -1)
909 havocbot_goalrating_kh(1, 10000, 40000); // play offensively
911 havocbot_goalrating_kh(1, 1, 100000); // ATTACK! EMERGENCY!
913 havocbot_goalrating_items(10000, self.origin, 10000);
914 navigation_goalrating_end();
918 void havocbot_role_kh_freelancer()
920 if (self.items & IT_KEY1)
922 dprint("changing role to carrier\n");
923 self.havocbot_role = havocbot_role_kh_carrier;
924 self.havocbot_role_timeout = 0;
928 if (!self.havocbot_role_timeout)
929 self.havocbot_role_timeout = time + random() * 10 + 10;
930 if (time > self.havocbot_role_timeout)
934 dprint("changing role to offense\n");
935 self.havocbot_role = havocbot_role_kh_offense;
939 dprint("changing role to defense\n");
940 self.havocbot_role = havocbot_role_kh_defense;
942 self.havocbot_role_timeout = 0;
946 if (self.bot_strategytime < time)
948 float key_owner_team;
950 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
951 navigation_goalrating_start();
953 key_owner_team = kh_Key_AllOwnedByWhichTeam();
954 if(key_owner_team == self.team)
955 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
956 else if(key_owner_team == -1)
957 havocbot_goalrating_kh(10000, 40000, 10000); // prefer dropped keys
959 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
961 havocbot_goalrating_items(10000, self.origin, 10000);
962 navigation_goalrating_end();
970 void havocbot_chooserole_kh()
975 self.havocbot_role = havocbot_role_kh_offense;
977 self.havocbot_role = havocbot_role_kh_defense;
979 self.havocbot_role = havocbot_role_kh_freelancer;
982 void havocbot_chooserole()
984 dprint("choose a role...\n");
985 navigation_routetogoal(world);
986 self.bot_strategytime = -1;
988 havocbot_chooserole_ctf();
989 else if (g_domination)
990 havocbot_chooserole_dom();
992 havocbot_chooserole_kh();
994 havocbot_chooserole_race();
995 else // assume anything else is deathmatch
996 havocbot_chooserole_dm();