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;
258 noteam = ((self.team == 0) || !teams_matter); // fteqcc sucks
260 // don't chase players if we're under water
261 if(self.waterlevel>1)
264 FOR_EACH_PLAYER(head)
266 // TODO: Merge this logic with the bot_shouldattack function
269 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
271 if (vlen(head.origin - org) > sradius)
275 if(head.items & IT_STRENGTH)
278 // rate only visible enemies
280 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
281 if (trace_fraction < 1 || trace_ent != head)
285 if(head.flags & FL_INWATER || head.flags & FL_PARTIALGROUND)
289 if(head.flags & FL_ONGROUND == 0)
291 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
292 t = pointcontents(trace_endpos + '0 0 1');
293 if( t != CONTENT_SOLID )
294 if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
296 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
300 t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
301 navigation_routerating(head, t * ratingscale, 2000);
307 void() havocbot_role_ctf_middle;
308 void() havocbot_role_ctf_defense;
309 void() havocbot_role_ctf_offense;
310 void() havocbot_role_ctf_interceptor;
312 void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
316 head = findchainfloat(bot_pickup, TRUE);
319 // look for health and armor only
320 if (head.solid) // must be possible to pick up (respawning items don't count)
321 if (head.health || head.armorvalue)
322 if (vlen(head.origin - org) < sradius)
325 //if (!head.bot_pickupevalfunc || head.model == "")
327 // get the value of the item
328 t = head.bot_pickupevalfunc(self, head) * 0.0001;
330 navigation_routerating(head, t * ratingscale, 500);
336 entity ctf_worldflaglist;
337 .entity ctf_worldflagnext;
338 void havocbot_goalrating_ctf_ourflag(float ratingscale)
341 head = ctf_worldflaglist;
344 if (self.team == head.team)
346 head = head.ctf_worldflagnext;
349 navigation_routerating(head, ratingscale, 10000);
352 void havocbot_goalrating_ctf_enemyflag(float ratingscale)
355 head = ctf_worldflaglist;
358 if (self.team != head.team)
360 head = head.ctf_worldflagnext;
363 navigation_routerating(head, ratingscale, 10000);
366 void havocbot_goalrating_ctf_enemybase(float ratingscale)
368 // div0: needs a change in the CTF code
371 void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
374 head = ctf_worldflaglist;
377 if (self.team == head.team)
379 head = head.ctf_worldflagnext;
382 if (head.cnt != FLAG_BASE)
383 navigation_routerating(head, ratingscale, 10000);
386 void havocbot_goalrating_ctf_droppedflags(float ratingscale)
389 head = ctf_worldflaglist;
392 if (head.cnt != FLAG_BASE) // flag is carried or out in the field
393 navigation_routerating(head, ratingscale, 10000);
394 head = head.ctf_worldflagnext;
398 // CTF: (always teamplay)
400 //role rogue: (is this used?)
401 //pick up items and dropped flags (with big rating boost to dropped flags)
402 void havocbot_role_ctf_rogue()
404 if (self.bot_strategytime < time)
406 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
407 navigation_goalrating_start();
408 havocbot_goalrating_ctf_droppedflags(5000);
409 //havocbot_goalrating_enemyplayers(3000, self.origin, 3000);
410 havocbot_goalrating_items(10000, self.origin, 10000);
411 navigation_goalrating_end();
416 //pick up armor and health
417 //go to our flag spot
418 .float bot_cantfindflag;
419 void havocbot_role_ctf_carrier()
421 if (self.flagcarried == world)
423 dprint("changing role to middle\n");
424 self.havocbot_role = havocbot_role_ctf_middle;
425 self.havocbot_role_timeout = 0;
428 if (self.bot_strategytime < time)
430 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
431 navigation_goalrating_start();
432 havocbot_goalrating_ctf_ourflag(50000);
433 if (navigation_bestgoal)
434 self.bot_cantfindflag = time + 10;
435 else if (time > self.bot_cantfindflag)
437 // can't navigate to our own flag :(
438 Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
440 havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
441 navigation_goalrating_end();
446 //pick up armor and health
447 //if rockets < 25 || health < 100, change role to middle
448 //if carrying flag, change role to flag carrier
449 //if our flag taken, change role to interceptor
450 //(60-90 second timer) change role to middle
452 void havocbot_role_ctf_offense()
455 if (self.flagcarried)
457 dprint("changing role to carrier\n");
458 self.havocbot_role = havocbot_role_ctf_carrier;
459 self.havocbot_role_timeout = 0;
460 self.bot_cantfindflag = time + 10;
464 f = ctf_worldflaglist;
467 if (self.team == f.team)
469 f = f.ctf_worldflagnext;
471 if (f.cnt != FLAG_BASE && canreach(f))
473 dprint("changing role to interceptor\n");
474 self.havocbot_previous_role = self.havocbot_role;
475 self.havocbot_role = havocbot_role_ctf_interceptor;
476 self.havocbot_role_timeout = 0;
479 if (!self.havocbot_role_timeout)
480 self.havocbot_role_timeout = time + random() * 30 + 60;
481 if (time > self.havocbot_role_timeout)
483 dprint("changing role to middle\n");
484 self.havocbot_role = havocbot_role_ctf_middle;
485 self.havocbot_role_timeout = 0;
488 if (self.bot_strategytime < time)
490 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
491 navigation_goalrating_start();
492 havocbot_goalrating_ctf_ourstolenflag(50000);
493 havocbot_goalrating_ctf_enemyflag(30000);
494 havocbot_goalrating_ctf_enemybase(20000);
495 havocbot_goalrating_items(10000, self.origin, 10000);
496 navigation_goalrating_end();
500 //role interceptor (temporary role):
502 //if carrying flag, change role to flag carrier
503 //if our flag is back, change role to previous role
505 //go to least recently visited area
506 void havocbot_role_ctf_interceptor()
509 if (self.flagcarried)
511 dprint("changing role to carrier\n");
512 self.havocbot_role = havocbot_role_ctf_carrier;
513 self.havocbot_role_timeout = 0;
514 self.bot_cantfindflag = time + 10;
518 f = ctf_worldflaglist;
521 if (self.team == f.team)
523 f = f.ctf_worldflagnext;
525 if (f.cnt == FLAG_BASE)
527 dprint("changing role back\n");
528 self.havocbot_role = self.havocbot_previous_role;
529 self.havocbot_role_timeout = 0;
533 if (self.bot_strategytime < time)
535 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
536 navigation_goalrating_start();
537 havocbot_goalrating_ctf_ourstolenflag(50000);
538 havocbot_goalrating_ctf_droppedflags(50000);
539 havocbot_goalrating_items(10000, self.origin, 10000);
540 navigation_goalrating_end();
546 //if carrying flag, change role to flag carrier
547 //if our flag taken, change role to interceptor
548 //if see flag (of either team) follow it (this has many implications)
549 //(10-20 second timer) change role to defense or offense
550 //go to least recently visited area
551 void havocbot_role_ctf_middle()
554 if (self.flagcarried)
556 dprint("changing role to carrier\n");
557 self.havocbot_role = havocbot_role_ctf_carrier;
558 self.havocbot_role_timeout = 0;
559 self.bot_cantfindflag = time + 10;
563 f = ctf_worldflaglist;
566 if (self.team == f.team)
568 f = f.ctf_worldflagnext;
570 if (f.cnt != FLAG_BASE && canreach(f))
572 dprint("changing role to interceptor\n");
573 self.havocbot_previous_role = self.havocbot_role;
574 self.havocbot_role = havocbot_role_ctf_interceptor;
575 self.havocbot_role_timeout = 0;
578 if (!self.havocbot_role_timeout)
579 self.havocbot_role_timeout = time + random() * 10 + 10;
580 if (time > self.havocbot_role_timeout)
584 dprint("changing role to offense\n");
585 self.havocbot_role = havocbot_role_ctf_offense;
589 dprint("changing role to defense\n");
590 self.havocbot_role = havocbot_role_ctf_defense;
592 self.havocbot_role_timeout = 0;
596 if (self.bot_strategytime < time)
598 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
599 navigation_goalrating_start();
600 havocbot_goalrating_ctf_ourstolenflag(50000);
601 havocbot_goalrating_ctf_droppedflags(30000);
602 //havocbot_goalrating_enemyplayers(1000, self.origin, 1000);
603 havocbot_goalrating_items(10000, self.origin, 10000);
604 navigation_goalrating_end();
609 //if rockets < 25 || health < 100, change role to middle
610 //if carrying flag, change role to flag carrier
611 //if our flag taken, change role to interceptor
612 //(30-50 second timer) change role to middle
613 //move to nearest unclaimed defense spot
614 void havocbot_role_ctf_defense()
617 if (self.flagcarried)
619 dprint("changing role to carrier\n");
620 self.havocbot_role = havocbot_role_ctf_carrier;
621 self.havocbot_role_timeout = 0;
622 self.bot_cantfindflag = time + 10;
626 f = ctf_worldflaglist;
629 if (self.team == f.team)
631 f = f.ctf_worldflagnext;
633 if (f.cnt != FLAG_BASE && canreach(f))
635 dprint("changing role to interceptor\n");
636 self.havocbot_previous_role = self.havocbot_role;
637 self.havocbot_role = havocbot_role_ctf_interceptor;
638 self.havocbot_role_timeout = 0;
641 if (!self.havocbot_role_timeout)
642 self.havocbot_role_timeout = time + random() * 20 + 30;
643 if (time > self.havocbot_role_timeout)
645 dprint("changing role to middle\n");
646 self.havocbot_role = havocbot_role_ctf_middle;
647 self.havocbot_role_timeout = 0;
650 if (self.bot_strategytime < time)
652 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
653 navigation_goalrating_start();
654 havocbot_goalrating_ctf_ourstolenflag(200000);
655 havocbot_goalrating_ctf_droppedflags(50000);
656 havocbot_goalrating_items(10000, f.origin, 10000);
657 navigation_goalrating_end();
660 // FIXME: place info_ctf_defensepoint entities in CTF maps and use them
661 // change position occasionally
662 if (time > self.bot_strategytime || self.goalentity.classname != "info_ctf_defensepoint")
664 self.bot_strategytime = time + random() * 45 + 15;
665 self.goalentity = world;
666 head = findchain(classname, "info_ctf_defensepoint");
669 if (time > head.count)
671 self.goalentity = head;
676 // if there are no defensepoints defined, switch to middle
677 if (self.goalentity == world)
679 dprint("changing role to middle\n");
680 self.havocbot_role = havocbot_role_ctf_middle;
681 self.havocbot_role_timeout = 0;
685 // keep anyone else from taking this spot
686 if (self.goalentity != world)
687 self.goalentity.count = time + 0.5;
692 // choose a role according to the situation
693 void() havocbot_role_dm;
694 void havocbot_chooserole_ctf()
697 dprint("choose CTF role...\n");
698 if (self.team == COLOR_TEAM3 || self.team == COLOR_TEAM4)
699 self.havocbot_role = havocbot_role_ctf_rogue;
704 self.havocbot_role = havocbot_role_ctf_offense;
706 self.havocbot_role = havocbot_role_ctf_middle;
708 self.havocbot_role = havocbot_role_ctf_defense;
713 //go to best items, or control points you don't own
714 void havocbot_role_dom()
716 if (self.bot_strategytime < time)
718 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
719 navigation_goalrating_start();
720 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
721 havocbot_goalrating_items(8000, self.origin, 8000);
722 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
723 //havocbot_goalrating_waypoints(1, self.origin, 1000);
724 navigation_goalrating_end();
730 void havocbot_role_dm()
732 if (self.bot_strategytime < time)
734 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
735 navigation_goalrating_start();
736 havocbot_goalrating_items(10000, self.origin, 10000);
737 havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
738 //havocbot_goalrating_waypoints(1, self.origin, 1000);
739 navigation_goalrating_end();
744 //go to next checkpoint, and annoy enemies
745 .float race_checkpoint;
746 void havocbot_role_race()
749 if (self.bot_strategytime < time)
751 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
752 navigation_goalrating_start();
754 havocbot_goalrating_items(100, self.origin, 10000);
755 havocbot_goalrating_enemyplayers(500, self.origin, 20000);
758 for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
760 if(e.cnt == self.race_checkpoint)
762 navigation_routerating(e, 1000000, 5000);
764 else if(self.race_checkpoint == -1)
766 navigation_routerating(e, 1000000, 5000);
770 navigation_goalrating_end();
774 void havocbot_chooserole_dm()
776 self.havocbot_role = havocbot_role_dm;
779 void havocbot_chooserole_race()
781 self.havocbot_role = havocbot_role_race;
784 void havocbot_chooserole_dom()
786 self.havocbot_role = havocbot_role_dom;
794 entity kh_worldkeylist;
795 .entity kh_worldkeynext;
796 void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
799 for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
801 if(head.owner == self)
803 if(!kh_tracking_enabled)
805 // if it's carried by our team we know about it
806 // otherwise we have to see it to know about it
807 if(!head.owner || head.team != self.team)
809 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
810 if (trace_fraction < 1 && trace_ent != head)
811 continue; // skip what I can't see
815 navigation_routerating(head, ratingscale_dropped, 100000);
816 else if(head.team == self.team)
817 navigation_routerating(head, ratingscale_team, 100000);
819 navigation_routerating(head, ratingscale_enemy, 100000);
823 void() havocbot_role_kh_carrier;
824 void() havocbot_role_kh_defense;
825 void() havocbot_role_kh_offense;
826 void() havocbot_role_kh_freelancer;
827 void havocbot_role_kh_carrier()
829 if (!(self.items & IT_KEY1))
831 dprint("changing role to freelancer\n");
832 self.havocbot_role = havocbot_role_kh_freelancer;
833 self.havocbot_role_timeout = 0;
837 if (self.bot_strategytime < time)
839 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
840 navigation_goalrating_start();
842 if(kh_Key_AllOwnedByWhichTeam() == self.team)
843 havocbot_goalrating_kh(100000, 1, 1); // bring home
845 havocbot_goalrating_kh(40000, 40000, 1000); // play defensively
847 havocbot_goalrating_items(10000, self.origin, 10000);
848 navigation_goalrating_end();
852 void havocbot_role_kh_defense()
854 if (self.items & IT_KEY1)
856 dprint("changing role to carrier\n");
857 self.havocbot_role = havocbot_role_kh_carrier;
858 self.havocbot_role_timeout = 0;
862 if (!self.havocbot_role_timeout)
863 self.havocbot_role_timeout = time + random() * 10 + 20;
864 if (time > self.havocbot_role_timeout)
866 dprint("changing role to freelancer\n");
867 self.havocbot_role = havocbot_role_kh_freelancer;
868 self.havocbot_role_timeout = 0;
872 if (self.bot_strategytime < time)
874 float key_owner_team;
875 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
876 navigation_goalrating_start();
878 key_owner_team = kh_Key_AllOwnedByWhichTeam();
879 if(key_owner_team == self.team)
880 havocbot_goalrating_kh(100000, 1, 1); // defend key carriers
881 else if(key_owner_team == -1)
882 havocbot_goalrating_kh(40000, 10000, 1); // play defensively
884 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
886 havocbot_goalrating_items(10000, self.origin, 10000);
887 navigation_goalrating_end();
891 void havocbot_role_kh_offense()
893 if (self.items & IT_KEY1)
895 dprint("changing role to carrier\n");
896 self.havocbot_role = havocbot_role_kh_carrier;
897 self.havocbot_role_timeout = 0;
901 if (!self.havocbot_role_timeout)
902 self.havocbot_role_timeout = time + random() * 10 + 20;
903 if (time > self.havocbot_role_timeout)
905 dprint("changing role to freelancer\n");
906 self.havocbot_role = havocbot_role_kh_freelancer;
907 self.havocbot_role_timeout = 0;
911 if (self.bot_strategytime < time)
913 float key_owner_team;
915 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
916 navigation_goalrating_start();
918 key_owner_team = kh_Key_AllOwnedByWhichTeam();
919 if(key_owner_team == self.team)
920 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
921 else if(key_owner_team == -1)
922 havocbot_goalrating_kh(1, 10000, 40000); // play offensively
924 havocbot_goalrating_kh(1, 1, 100000); // ATTACK! EMERGENCY!
926 havocbot_goalrating_items(10000, self.origin, 10000);
927 navigation_goalrating_end();
931 void havocbot_role_kh_freelancer()
933 if (self.items & IT_KEY1)
935 dprint("changing role to carrier\n");
936 self.havocbot_role = havocbot_role_kh_carrier;
937 self.havocbot_role_timeout = 0;
941 if (!self.havocbot_role_timeout)
942 self.havocbot_role_timeout = time + random() * 10 + 10;
943 if (time > self.havocbot_role_timeout)
947 dprint("changing role to offense\n");
948 self.havocbot_role = havocbot_role_kh_offense;
952 dprint("changing role to defense\n");
953 self.havocbot_role = havocbot_role_kh_defense;
955 self.havocbot_role_timeout = 0;
959 if (self.bot_strategytime < time)
961 float key_owner_team;
963 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
964 navigation_goalrating_start();
966 key_owner_team = kh_Key_AllOwnedByWhichTeam();
967 if(key_owner_team == self.team)
968 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
969 else if(key_owner_team == -1)
970 havocbot_goalrating_kh(10000, 40000, 10000); // prefer dropped keys
972 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
974 havocbot_goalrating_items(10000, self.origin, 10000);
975 navigation_goalrating_end();
983 void havocbot_chooserole_kh()
988 self.havocbot_role = havocbot_role_kh_offense;
990 self.havocbot_role = havocbot_role_kh_defense;
992 self.havocbot_role = havocbot_role_kh_freelancer;
995 void havocbot_chooserole()
997 dprint("choose a role...\n");
998 navigation_routetogoal(world);
999 self.bot_strategytime = -1;
1001 havocbot_chooserole_ctf();
1002 else if (g_domination)
1003 havocbot_chooserole_dom();
1005 havocbot_chooserole_kh();
1007 havocbot_chooserole_race();
1008 else // assume anything else is deathmatch
1009 havocbot_chooserole_dm();