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 )
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) || (teamplay == 0)); // fteqcc sucks
251 FOR_EACH_PLAYER(head)
255 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
257 if (vlen(head.origin - org) > sradius)
260 // rate only visible enemies
262 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
263 if (trace_fraction < 1 || trace_ent != head)
267 if(head.flags & FL_INWATER || head.flags & FL_PARTIALGROUND)
271 if(head.flags & FL_ONGROUND == 0)
273 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
274 t = pointcontents(trace_endpos + '0 0 1');
275 if( t != CONTENT_SOLID )
276 if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
278 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
282 t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
283 navigation_routerating(head, t * ratingscale, 2000);
289 void() havocbot_role_ctf_middle;
290 void() havocbot_role_ctf_defense;
291 void() havocbot_role_ctf_offense;
292 void() havocbot_role_ctf_interceptor;
294 void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
298 head = findchainfloat(bot_pickup, TRUE);
301 // look for health and armor only
302 if (head.solid) // must be possible to pick up (respawning items don't count)
303 if (head.health || head.armorvalue)
304 if (vlen(head.origin - org) < sradius)
307 //if (!head.bot_pickupevalfunc || head.model == "")
309 // get the value of the item
310 t = head.bot_pickupevalfunc(self, head) * 0.0001;
312 navigation_routerating(head, t * ratingscale, 500);
318 entity ctf_worldflaglist;
319 .entity ctf_worldflagnext;
320 void havocbot_goalrating_ctf_ourflag(float ratingscale)
323 head = ctf_worldflaglist;
326 if (self.team == head.team)
328 head = head.ctf_worldflagnext;
331 navigation_routerating(head, ratingscale, 10000);
334 void havocbot_goalrating_ctf_enemyflag(float ratingscale)
337 head = ctf_worldflaglist;
340 if (self.team != head.team)
342 head = head.ctf_worldflagnext;
345 navigation_routerating(head, ratingscale, 10000);
348 void havocbot_goalrating_ctf_enemybase(float ratingscale)
350 // div0: needs a change in the CTF code
353 void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
356 head = ctf_worldflaglist;
359 if (self.team == head.team)
361 head = head.ctf_worldflagnext;
364 if (head.cnt != FLAG_BASE)
365 navigation_routerating(head, ratingscale, 10000);
368 void havocbot_goalrating_ctf_droppedflags(float ratingscale)
371 head = ctf_worldflaglist;
374 if (head.cnt != FLAG_BASE) // flag is carried or out in the field
375 navigation_routerating(head, ratingscale, 10000);
376 head = head.ctf_worldflagnext;
380 // CTF: (always teamplay)
382 //role rogue: (is this used?)
383 //pick up items and dropped flags (with big rating boost to dropped flags)
384 void havocbot_role_ctf_rogue()
386 if (self.bot_strategytime < time)
388 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
389 navigation_goalrating_start();
390 havocbot_goalrating_ctf_droppedflags(5000);
391 //havocbot_goalrating_enemyplayers(3000, self.origin, 3000);
392 havocbot_goalrating_items(10000, self.origin, 10000);
393 navigation_goalrating_end();
398 //pick up armor and health
399 //go to our flag spot
400 .float bot_cantfindflag;
401 void havocbot_role_ctf_carrier()
403 if (self.flagcarried == world)
405 dprint("changing role to middle\n");
406 self.havocbot_role = havocbot_role_ctf_middle;
407 self.havocbot_role_timeout = 0;
410 if (self.bot_strategytime < time)
412 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
413 navigation_goalrating_start();
414 havocbot_goalrating_ctf_ourflag(50000);
415 if (navigation_bestgoal)
416 self.bot_cantfindflag = time + 10;
417 else if (time > self.bot_cantfindflag)
419 // can't navigate to our own flag :(
420 Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
422 havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
423 navigation_goalrating_end();
428 //pick up armor and health
429 //if rockets < 25 || health < 100, change role to middle
430 //if carrying flag, change role to flag carrier
431 //if our flag taken, change role to interceptor
432 //(60-90 second timer) change role to middle
434 void havocbot_role_ctf_offense()
437 if (self.flagcarried)
439 dprint("changing role to carrier\n");
440 self.havocbot_role = havocbot_role_ctf_carrier;
441 self.havocbot_role_timeout = 0;
442 self.bot_cantfindflag = time + 10;
446 f = ctf_worldflaglist;
449 if (self.team == f.team)
451 f = f.ctf_worldflagnext;
453 if (f.cnt != FLAG_BASE && canreach(f))
455 dprint("changing role to interceptor\n");
456 self.havocbot_previous_role = self.havocbot_role;
457 self.havocbot_role = havocbot_role_ctf_interceptor;
458 self.havocbot_role_timeout = 0;
461 if (!self.havocbot_role_timeout)
462 self.havocbot_role_timeout = time + random() * 30 + 60;
463 if (time > self.havocbot_role_timeout)
465 dprint("changing role to middle\n");
466 self.havocbot_role = havocbot_role_ctf_middle;
467 self.havocbot_role_timeout = 0;
470 if (self.bot_strategytime < time)
472 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
473 navigation_goalrating_start();
474 havocbot_goalrating_ctf_ourstolenflag(50000);
475 havocbot_goalrating_ctf_enemyflag(30000);
476 havocbot_goalrating_ctf_enemybase(20000);
477 havocbot_goalrating_items(10000, self.origin, 10000);
478 navigation_goalrating_end();
482 //role interceptor (temporary role):
484 //if carrying flag, change role to flag carrier
485 //if our flag is back, change role to previous role
487 //go to least recently visited area
488 void havocbot_role_ctf_interceptor()
491 if (self.flagcarried)
493 dprint("changing role to carrier\n");
494 self.havocbot_role = havocbot_role_ctf_carrier;
495 self.havocbot_role_timeout = 0;
496 self.bot_cantfindflag = time + 10;
500 f = ctf_worldflaglist;
503 if (self.team == f.team)
505 f = f.ctf_worldflagnext;
507 if (f.cnt == FLAG_BASE)
509 dprint("changing role back\n");
510 self.havocbot_role = self.havocbot_previous_role;
511 self.havocbot_role_timeout = 0;
515 if (self.bot_strategytime < time)
517 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
518 navigation_goalrating_start();
519 havocbot_goalrating_ctf_ourstolenflag(50000);
520 havocbot_goalrating_ctf_droppedflags(50000);
521 havocbot_goalrating_items(10000, self.origin, 10000);
522 navigation_goalrating_end();
528 //if carrying flag, change role to flag carrier
529 //if our flag taken, change role to interceptor
530 //if see flag (of either team) follow it (this has many implications)
531 //(10-20 second timer) change role to defense or offense
532 //go to least recently visited area
533 void havocbot_role_ctf_middle()
536 if (self.flagcarried)
538 dprint("changing role to carrier\n");
539 self.havocbot_role = havocbot_role_ctf_carrier;
540 self.havocbot_role_timeout = 0;
541 self.bot_cantfindflag = time + 10;
545 f = ctf_worldflaglist;
548 if (self.team == f.team)
550 f = f.ctf_worldflagnext;
552 if (f.cnt != FLAG_BASE && canreach(f))
554 dprint("changing role to interceptor\n");
555 self.havocbot_previous_role = self.havocbot_role;
556 self.havocbot_role = havocbot_role_ctf_interceptor;
557 self.havocbot_role_timeout = 0;
560 if (!self.havocbot_role_timeout)
561 self.havocbot_role_timeout = time + random() * 10 + 10;
562 if (time > self.havocbot_role_timeout)
566 dprint("changing role to offense\n");
567 self.havocbot_role = havocbot_role_ctf_offense;
571 dprint("changing role to defense\n");
572 self.havocbot_role = havocbot_role_ctf_defense;
574 self.havocbot_role_timeout = 0;
578 if (self.bot_strategytime < time)
580 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
581 navigation_goalrating_start();
582 havocbot_goalrating_ctf_ourstolenflag(50000);
583 havocbot_goalrating_ctf_droppedflags(30000);
584 //havocbot_goalrating_enemyplayers(1000, self.origin, 1000);
585 havocbot_goalrating_items(10000, self.origin, 10000);
586 navigation_goalrating_end();
591 //if rockets < 25 || health < 100, change role to middle
592 //if carrying flag, change role to flag carrier
593 //if our flag taken, change role to interceptor
594 //(30-50 second timer) change role to middle
595 //move to nearest unclaimed defense spot
596 void havocbot_role_ctf_defense()
599 if (self.flagcarried)
601 dprint("changing role to carrier\n");
602 self.havocbot_role = havocbot_role_ctf_carrier;
603 self.havocbot_role_timeout = 0;
604 self.bot_cantfindflag = time + 10;
608 f = ctf_worldflaglist;
611 if (self.team == f.team)
613 f = f.ctf_worldflagnext;
615 if (f.cnt != FLAG_BASE && canreach(f))
617 dprint("changing role to interceptor\n");
618 self.havocbot_previous_role = self.havocbot_role;
619 self.havocbot_role = havocbot_role_ctf_interceptor;
620 self.havocbot_role_timeout = 0;
623 if (!self.havocbot_role_timeout)
624 self.havocbot_role_timeout = time + random() * 20 + 30;
625 if (time > self.havocbot_role_timeout)
627 dprint("changing role to middle\n");
628 self.havocbot_role = havocbot_role_ctf_middle;
629 self.havocbot_role_timeout = 0;
632 if (self.bot_strategytime < time)
634 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
635 navigation_goalrating_start();
636 havocbot_goalrating_ctf_ourstolenflag(200000);
637 havocbot_goalrating_ctf_droppedflags(50000);
638 havocbot_goalrating_items(10000, f.origin, 10000);
639 navigation_goalrating_end();
642 // FIXME: place info_ctf_defensepoint entities in CTF maps and use them
643 // change position occasionally
644 if (time > self.bot_strategytime || self.goalentity.classname != "info_ctf_defensepoint")
646 self.bot_strategytime = time + random() * 45 + 15;
647 self.goalentity = world;
648 head = findchain(classname, "info_ctf_defensepoint");
651 if (time > head.count)
653 self.goalentity = head;
658 // if there are no defensepoints defined, switch to middle
659 if (self.goalentity == world)
661 dprint("changing role to middle\n");
662 self.havocbot_role = havocbot_role_ctf_middle;
663 self.havocbot_role_timeout = 0;
667 // keep anyone else from taking this spot
668 if (self.goalentity != world)
669 self.goalentity.count = time + 0.5;
674 // choose a role according to the situation
675 void() havocbot_role_dm;
676 void havocbot_chooserole_ctf()
679 dprint("choose CTF role...\n");
680 if (self.team == COLOR_TEAM3 || self.team == COLOR_TEAM4)
681 self.havocbot_role = havocbot_role_ctf_rogue;
686 self.havocbot_role = havocbot_role_ctf_offense;
688 self.havocbot_role = havocbot_role_ctf_middle;
690 self.havocbot_role = havocbot_role_ctf_defense;
695 //go to best items, or control points you don't own
696 void havocbot_role_dom()
698 if (self.bot_strategytime < time)
700 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
701 navigation_goalrating_start();
702 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
703 havocbot_goalrating_items(8000, self.origin, 8000);
704 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
705 //havocbot_goalrating_waypoints(1, self.origin, 1000);
706 navigation_goalrating_end();
712 void havocbot_role_dm()
714 if (self.bot_strategytime < time)
716 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
717 navigation_goalrating_start();
718 havocbot_goalrating_items(10000, self.origin, 10000);
719 havocbot_goalrating_enemyplayers(5000, self.origin, 10000);
720 //havocbot_goalrating_waypoints(1, self.origin, 1000);
721 navigation_goalrating_end();
726 //go to next checkpoint, and annoy enemies
727 .float race_checkpoint;
728 void havocbot_role_race()
731 if (self.bot_strategytime < time)
733 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
734 navigation_goalrating_start();
736 havocbot_goalrating_items(100, self.origin, 10000);
737 havocbot_goalrating_enemyplayers(500, self.origin, 20000);
740 for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
742 if(e.cnt == self.race_checkpoint)
744 navigation_routerating(e, 1000000, 5000);
746 else if(self.race_checkpoint == -1)
748 navigation_routerating(e, 1000000, 5000);
752 navigation_goalrating_end();
756 void havocbot_chooserole_dm()
758 self.havocbot_role = havocbot_role_dm;
761 void havocbot_chooserole_race()
763 self.havocbot_role = havocbot_role_race;
766 void havocbot_chooserole_dom()
768 self.havocbot_role = havocbot_role_dom;
776 entity kh_worldkeylist;
777 .entity kh_worldkeynext;
778 void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
781 for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
783 if(head.owner == self)
785 if(!kh_tracking_enabled)
787 // if it's carried by our team we know about it
788 // otherwise we have to see it to know about it
789 if(!head.owner || head.team != self.team)
791 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
792 if (trace_fraction < 1 && trace_ent != head)
793 continue; // skip what I can't see
797 navigation_routerating(head, ratingscale_dropped, 100000);
798 else if(head.team == self.team)
799 navigation_routerating(head, ratingscale_team, 100000);
801 navigation_routerating(head, ratingscale_enemy, 100000);
805 void() havocbot_role_kh_carrier;
806 void() havocbot_role_kh_defense;
807 void() havocbot_role_kh_offense;
808 void() havocbot_role_kh_freelancer;
809 void havocbot_role_kh_carrier()
811 if (!(self.items & IT_KEY1))
813 dprint("changing role to freelancer\n");
814 self.havocbot_role = havocbot_role_kh_freelancer;
815 self.havocbot_role_timeout = 0;
819 if (self.bot_strategytime < time)
821 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
822 navigation_goalrating_start();
824 if(kh_Key_AllOwnedByWhichTeam() == self.team)
825 havocbot_goalrating_kh(100000, 1, 1); // bring home
827 havocbot_goalrating_kh(40000, 40000, 1000); // play defensively
829 havocbot_goalrating_items(10000, self.origin, 10000);
830 navigation_goalrating_end();
834 void havocbot_role_kh_defense()
836 if (self.items & IT_KEY1)
838 dprint("changing role to carrier\n");
839 self.havocbot_role = havocbot_role_kh_carrier;
840 self.havocbot_role_timeout = 0;
844 if (!self.havocbot_role_timeout)
845 self.havocbot_role_timeout = time + random() * 10 + 20;
846 if (time > self.havocbot_role_timeout)
848 dprint("changing role to freelancer\n");
849 self.havocbot_role = havocbot_role_kh_freelancer;
850 self.havocbot_role_timeout = 0;
854 if (self.bot_strategytime < time)
856 float key_owner_team;
857 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
858 navigation_goalrating_start();
860 key_owner_team = kh_Key_AllOwnedByWhichTeam();
861 if(key_owner_team == self.team)
862 havocbot_goalrating_kh(100000, 1, 1); // defend key carriers
863 else if(key_owner_team == -1)
864 havocbot_goalrating_kh(40000, 10000, 1); // play defensively
866 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
868 havocbot_goalrating_items(10000, self.origin, 10000);
869 navigation_goalrating_end();
873 void havocbot_role_kh_offense()
875 if (self.items & IT_KEY1)
877 dprint("changing role to carrier\n");
878 self.havocbot_role = havocbot_role_kh_carrier;
879 self.havocbot_role_timeout = 0;
883 if (!self.havocbot_role_timeout)
884 self.havocbot_role_timeout = time + random() * 10 + 20;
885 if (time > self.havocbot_role_timeout)
887 dprint("changing role to freelancer\n");
888 self.havocbot_role = havocbot_role_kh_freelancer;
889 self.havocbot_role_timeout = 0;
893 if (self.bot_strategytime < time)
895 float key_owner_team;
897 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
898 navigation_goalrating_start();
900 key_owner_team = kh_Key_AllOwnedByWhichTeam();
901 if(key_owner_team == self.team)
902 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
903 else if(key_owner_team == -1)
904 havocbot_goalrating_kh(1, 10000, 40000); // play offensively
906 havocbot_goalrating_kh(1, 1, 100000); // ATTACK! EMERGENCY!
908 havocbot_goalrating_items(10000, self.origin, 10000);
909 navigation_goalrating_end();
913 void havocbot_role_kh_freelancer()
915 if (self.items & IT_KEY1)
917 dprint("changing role to carrier\n");
918 self.havocbot_role = havocbot_role_kh_carrier;
919 self.havocbot_role_timeout = 0;
923 if (!self.havocbot_role_timeout)
924 self.havocbot_role_timeout = time + random() * 10 + 10;
925 if (time > self.havocbot_role_timeout)
929 dprint("changing role to offense\n");
930 self.havocbot_role = havocbot_role_kh_offense;
934 dprint("changing role to defense\n");
935 self.havocbot_role = havocbot_role_kh_defense;
937 self.havocbot_role_timeout = 0;
941 if (self.bot_strategytime < time)
943 float key_owner_team;
945 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
946 navigation_goalrating_start();
948 key_owner_team = kh_Key_AllOwnedByWhichTeam();
949 if(key_owner_team == self.team)
950 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
951 else if(key_owner_team == -1)
952 havocbot_goalrating_kh(10000, 40000, 10000); // prefer dropped keys
954 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
956 havocbot_goalrating_items(10000, self.origin, 10000);
957 navigation_goalrating_end();
965 void havocbot_chooserole_kh()
970 self.havocbot_role = havocbot_role_kh_offense;
972 self.havocbot_role = havocbot_role_kh_defense;
974 self.havocbot_role = havocbot_role_kh_freelancer;
977 void havocbot_chooserole()
979 dprint("choose a role...\n");
980 navigation_routetogoal(world);
981 self.bot_strategytime = -1;
983 havocbot_chooserole_ctf();
984 else if (g_domination)
985 havocbot_chooserole_dom();
987 havocbot_chooserole_kh();
989 havocbot_chooserole_race();
990 else // assume anything else is deathmatch
991 havocbot_chooserole_dm();