2 .float havocbot_role_timeout;
3 .void() havocbot_previous_role;
8 float havocbot_pickupevalfunc(entity item)
10 float i, j, rating, base, position, need_shells, need_nails, need_rockets, need_cells;
13 base = item.bot_pickupbasevalue;
15 need_shells = self.weapons & WEPBIT_SHOTGUN;
16 need_nails = self.weapons & WEPBIT_UZI;
17 need_cells = self.weapons & ( WEPBIT_HOOK | WEPBIT_HLAC | WEPBIT_MINSTANEX | WEPBIT_NEX | WEPBIT_ELECTRO | WEPBIT_CRYLINK );
18 need_rockets = self.weapons & ( WEPBIT_ROCKET_LAUNCHER | WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_SEEKER );
22 if (self.ammo_shells < g_pickup_shells_max && need_cells )
23 rating = rating + max(0, 1 - self.ammo_shells / g_pickup_shells_max);
26 if (self.ammo_nails < g_pickup_nails_max && need_nails )
27 rating = rating + max(0, 1 - self.ammo_nails / g_pickup_nails_max);
29 if (item.ammo_rockets)
30 if (self.ammo_rockets < g_pickup_rockets_max && need_rockets)
31 rating = rating + max(0, 1 - self.ammo_rockets / g_pickup_rockets_max);
34 if (self.ammo_cells < g_pickup_cells_max && need_cells)
35 rating = rating + max(0, 1 - self.ammo_cells / g_pickup_cells_max);
37 // Rate health items (aim to grab half the max capacity)
39 if (self.armorvalue < item.max_armorvalue * 0.5)
40 rating = rating + max(0, 1 - self.armorvalue / (item.max_armorvalue * 0.5));
43 if (self.health < item.max_health * 0.5)
45 rating = rating + max(0, 1 - self.health / (item.max_health * 0.5));
51 // See if I have it already
52 if( self.weapons & item.weapons == item.weapons )
54 // If I can pick it up
56 if not(cvar("g_weapon_stay"))
58 // Skilled bots will grab more
59 local float divisor = 2;
60 rating += bound(0, skill / (10*divisor), 1/divisor);
66 // If custom weapon priorities for bots is enabled rate most wanted weapons higher
67 if( bot_custom_weapon && rating )
69 for(i = WEP_FIRST; i < WEP_LAST ; ++i)
72 if( power2of(i-1) & item.weapons != item.weapons )
75 // Find the highest position on any range
77 for(j = 0; j < WEP_LAST ; ++j){
79 bot_weapons_far[j] == i ||
80 bot_weapons_mid[j] == i ||
81 bot_weapons_close[j] == i
92 position = WEP_LAST - position;
93 // item.bot_pickupbasevalue is overwritten here
94 base = BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ));
101 // TODO: if the item is not recognized then default to item.bot_pickupevalfunc(self, item);
102 return base * rating;
105 void havocbot_goalrating_items(float ratingscale, vector org, float sradius)
109 local float rating, d, discard, distance, friend_distance, enemy_distance;
110 ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
111 head = findchainfloat(bot_pickup, TRUE);
115 distance = vlen(head.origin - org);
116 friend_distance = 10000; enemy_distance = 10000;
119 if(!head.solid || distance > sradius || (head == self.ignoregoal && time < self.ignoregoaltime) )
125 // Check if the item can be picked up safely
126 if(head.classname == "droppedweapon")
128 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
130 d = pointcontents(trace_endpos + '0 0 1');
131 if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
136 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
144 // Ignore items under water
145 traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
146 if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
157 FOR_EACH_PLAYER(player)
160 if ( self == player || player.deadflag )
163 d = vlen(player.origin - head.origin); // distance between player and item
165 if ( player.team == self.team )
167 if ( clienttype(player) != CLIENTTYPE_REAL || discard )
170 if( d > friend_distance)
177 if( head.health && player.health > self.health )
180 if( head.armorvalue && player.armorvalue > self.armorvalue)
184 if( (player.weapons & head.weapons) != head.weapons)
187 if (head.ammo_shells && player.ammo_shells > self.ammo_shells)
190 if (head.ammo_nails && player.ammo_nails > self.ammo_nails)
193 if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets)
196 if (head.ammo_cells && player.ammo_cells > self.ammo_cells )
203 // If enemy only track distances
204 // TODO: track only if visible ?
205 if( d < enemy_distance )
210 // Rate the item only if no one needs it, or if an enemy is closer to it
211 if ( (enemy_distance < friend_distance && distance < enemy_distance) ||
212 (friend_distance > cvar("bot_ai_friends_aware_pickup_radius") ) || !discard )
214 // rating = head.bot_pickupevalfunc(self, head);
215 rating = havocbot_pickupevalfunc(head);
220 // rating = head.bot_pickupevalfunc(self, head);
221 rating = havocbot_pickupevalfunc(head);
225 navigation_routerating(head, rating * ratingscale, 2000);
230 void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius)
233 head = findchain(classname, "dom_controlpoint");
236 if (vlen(head.origin - org) < sradius)
238 if(head.cnt > -1) // this is just being fought for
239 navigation_routerating(head, ratingscale, 5000);
240 else if(head.goalentity.cnt == 0) // unclaimed point
241 navigation_routerating(head, ratingscale * 0.5, 5000);
242 else if(head.goalentity.team != self.team) // other team's point
243 navigation_routerating(head, ratingscale * 0.2, 5000);
250 // LordHavoc: this function was already unused, but for waypoints to be a
251 // useful goal the bots would have to seek out the least-recently-visited
252 // ones, not the closest
253 void havocbot_goalrating_waypoints(float ratingscale, vector org, float sradius)
256 head = findchain(classname, "waypoint");
259 if (vlen(head.origin - org) < sradius && vlen(head.origin - org) > 100)
260 navigation_routerating(head, ratingscale, 2000);
266 void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius)
269 local float t, noteam, distance;
270 noteam = ((self.team == 0) || !teams_matter); // fteqcc sucks
272 if (cvar("bot_nofire"))
275 // don't chase players if we're under water
276 if(self.waterlevel>WATERLEVEL_WETFEET)
279 FOR_EACH_PLAYER(head)
281 // TODO: Merge this logic with the bot_shouldattack function
284 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
286 distance = vlen(head.origin - org);
287 if (distance < 100 || distance > sradius)
291 if(head.items & IT_STRENGTH)
294 // rate only visible enemies
296 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
297 if (trace_fraction < 1 || trace_ent != head)
301 if(head.flags & FL_INWATER || head.flags & FL_PARTIALGROUND)
305 if(head.flags & FL_ONGROUND == 0)
307 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
308 t = pointcontents(trace_endpos + '0 0 1');
309 if( t != CONTENT_SOLID )
310 if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
312 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
316 t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
317 navigation_routerating(head, t * ratingscale, 2000);
324 float canreach(entity e)
326 return vlen(self.origin - e.origin) < 1500;
337 FOR_EACH_PLAYER(head)
338 if(head.team==self.team)
344 void() havocbot_role_ctf_middle;
345 void() havocbot_role_ctf_defense;
346 void() havocbot_role_ctf_offense;
347 void() havocbot_role_ctf_interceptor;
349 void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
353 head = findchainfloat(bot_pickup, TRUE);
356 // look for health and armor only
357 if (head.solid) // must be possible to pick up (respawning items don't count)
358 if (head.health || head.armorvalue)
359 if (vlen(head.origin - org) < sradius)
362 //if (!head.bot_pickupevalfunc || head.model == "")
364 // get the value of the item
365 t = head.bot_pickupevalfunc(self, head) * 0.0001;
367 navigation_routerating(head, t * ratingscale, 500);
373 entity ctf_worldflaglist;
374 .entity ctf_worldflagnext;
375 void havocbot_goalrating_ctf_ourflag(float ratingscale)
378 head = ctf_worldflaglist;
381 if (self.team == head.team)
383 head = head.ctf_worldflagnext;
386 navigation_routerating(head, ratingscale, 10000);
389 void havocbot_goalrating_ctf_ourbase(float ratingscale)
391 if not(bot_waypoints_for_items)
393 havocbot_goalrating_ctf_ourflag(ratingscale);
398 head = ctf_worldflaglist;
401 if (self.team == head.team)
403 head = head.ctf_worldflagnext;
408 head = findradius(head.dropped_origin, 100);
411 if(head.classname=="waypoint")
413 navigation_routerating(head, ratingscale, 10000);
420 void havocbot_goalrating_ctf_enemyflag(float ratingscale)
423 head = ctf_worldflaglist;
426 if (self.team != head.team)
428 head = head.ctf_worldflagnext;
431 navigation_routerating(head, ratingscale, 10000);
434 void havocbot_goalrating_ctf_enemybase(float ratingscale)
436 // div0: needs a change in the CTF code
439 void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
442 head = ctf_worldflaglist;
445 if (self.team == head.team)
447 head = head.ctf_worldflagnext;
450 if (head.cnt != FLAG_BASE)
451 navigation_routerating(head, ratingscale, 10000);
454 void havocbot_goalrating_ctf_droppedflags(float ratingscale)
457 head = ctf_worldflaglist;
460 if (head.cnt != FLAG_BASE) // flag is carried or out in the field
461 navigation_routerating(head, ratingscale, 10000);
462 head = head.ctf_worldflagnext;
466 // CTF: (always teamplay)
468 //role rogue: (is this used?)
469 //pick up items and dropped flags (with big rating boost to dropped flags)
470 void havocbot_role_ctf_rogue()
472 if (self.bot_strategytime < time)
474 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
475 navigation_goalrating_start();
476 havocbot_goalrating_ctf_droppedflags(5000);
477 //havocbot_goalrating_enemyplayers(3000, self.origin, 3000);
478 havocbot_goalrating_items(10000, self.origin, 10000);
479 navigation_goalrating_end();
484 //pick up armor and health
485 //go to our flag spot
486 .float bot_cantfindflag;
487 void havocbot_role_ctf_carrier()
489 if (self.flagcarried == world)
491 dprint("changing role to middle\n");
492 self.havocbot_role = havocbot_role_ctf_middle;
493 self.havocbot_role_timeout = 0;
496 if (self.bot_strategytime < time)
498 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
499 navigation_goalrating_start();
500 havocbot_goalrating_ctf_ourbase(50000);
502 havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
503 navigation_goalrating_end();
505 if (self.navigation_hasgoals)
506 self.bot_cantfindflag = time + 10;
507 else if (time > self.bot_cantfindflag)
509 // can't navigate to our own flag :(
510 Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
517 //pick up armor and health
518 //if rockets < 25 || health < 100, change role to middle
519 //if carrying flag, change role to flag carrier
520 //if our flag taken, change role to interceptor
521 //(60-90 second timer) change role to middle
523 void havocbot_role_ctf_offense()
526 if (self.flagcarried)
528 dprint("changing role to carrier\n");
529 self.havocbot_role = havocbot_role_ctf_carrier;
530 self.havocbot_role_timeout = 0;
531 self.bot_cantfindflag = time + 10;
535 f = ctf_worldflaglist;
538 if (self.team == f.team)
540 f = f.ctf_worldflagnext;
542 if (f.cnt != FLAG_BASE && canreach(f))
544 dprint("changing role to interceptor\n");
545 self.havocbot_previous_role = self.havocbot_role;
546 self.havocbot_role = havocbot_role_ctf_interceptor;
547 self.havocbot_role_timeout = 0;
550 if (!self.havocbot_role_timeout)
551 self.havocbot_role_timeout = time + random() * 30 + 60;
552 if (time > self.havocbot_role_timeout)
554 dprint("changing role to middle\n");
555 self.havocbot_role = havocbot_role_ctf_middle;
556 self.havocbot_role_timeout = 0;
559 if (self.bot_strategytime < time)
561 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
562 navigation_goalrating_start();
563 havocbot_goalrating_ctf_ourstolenflag(50000);
564 havocbot_goalrating_ctf_enemyflag(30000);
565 havocbot_goalrating_ctf_enemybase(20000);
566 havocbot_goalrating_items(10000, self.origin, 10000);
567 navigation_goalrating_end();
571 //role interceptor (temporary role):
573 //if carrying flag, change role to flag carrier
574 //if our flag is back, change role to previous role
576 //go to least recently visited area
577 void havocbot_role_ctf_interceptor()
580 if (self.flagcarried)
582 dprint("changing role to carrier\n");
583 self.havocbot_role = havocbot_role_ctf_carrier;
584 self.havocbot_role_timeout = 0;
585 self.bot_cantfindflag = time + 10;
589 f = ctf_worldflaglist;
592 if (self.team == f.team)
594 f = f.ctf_worldflagnext;
596 if (f.cnt == FLAG_BASE)
598 dprint("changing role back\n");
599 self.havocbot_role = self.havocbot_previous_role;
600 self.havocbot_role_timeout = 0;
604 if (self.bot_strategytime < time)
606 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
607 navigation_goalrating_start();
608 havocbot_goalrating_ctf_ourstolenflag(50000);
609 havocbot_goalrating_ctf_droppedflags(50000);
610 havocbot_goalrating_items(10000, self.origin, 10000);
611 navigation_goalrating_end();
617 //if carrying flag, change role to flag carrier
618 //if our flag taken, change role to interceptor
619 //if see flag (of either team) follow it (this has many implications)
620 //(10-20 second timer) change role to defense or offense
621 //go to least recently visited area
622 void havocbot_role_ctf_middle()
625 if (self.flagcarried)
627 dprint("changing role to carrier\n");
628 self.havocbot_role = havocbot_role_ctf_carrier;
629 self.havocbot_role_timeout = 0;
630 self.bot_cantfindflag = time + 10;
634 f = ctf_worldflaglist;
637 if (self.team == f.team)
639 f = f.ctf_worldflagnext;
641 if (f.cnt != FLAG_BASE && canreach(f))
643 dprint("changing role to interceptor\n");
644 self.havocbot_previous_role = self.havocbot_role;
645 self.havocbot_role = havocbot_role_ctf_interceptor;
646 self.havocbot_role_timeout = 0;
649 if (!self.havocbot_role_timeout)
650 self.havocbot_role_timeout = time + random() * 10 + 10;
651 if (time > self.havocbot_role_timeout)
655 dprint("changing role to offense\n");
656 self.havocbot_role = havocbot_role_ctf_offense;
660 dprint("changing role to defense\n");
661 self.havocbot_role = havocbot_role_ctf_defense;
663 self.havocbot_role_timeout = 0;
667 if (self.bot_strategytime < time)
669 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
670 navigation_goalrating_start();
671 havocbot_goalrating_ctf_ourstolenflag(50000);
672 havocbot_goalrating_ctf_droppedflags(30000);
673 //havocbot_goalrating_enemyplayers(1000, self.origin, 1000);
674 havocbot_goalrating_items(10000, self.origin, 10000);
675 navigation_goalrating_end();
680 //if rockets < 25 || health < 100, change role to middle
681 //if carrying flag, change role to flag carrier
682 //if our flag taken, change role to interceptor
683 //(30-50 second timer) change role to middle
684 //move to nearest unclaimed defense spot
685 void havocbot_role_ctf_defense()
688 if (self.flagcarried)
690 dprint("changing role to carrier\n");
691 self.havocbot_role = havocbot_role_ctf_carrier;
692 self.havocbot_role_timeout = 0;
693 self.bot_cantfindflag = time + 10;
697 f = ctf_worldflaglist;
700 if (self.team == f.team)
702 f = f.ctf_worldflagnext;
704 if (f.cnt != FLAG_BASE && canreach(f))
706 dprint("changing role to interceptor\n");
707 self.havocbot_previous_role = self.havocbot_role;
708 self.havocbot_role = havocbot_role_ctf_interceptor;
709 self.havocbot_role_timeout = 0;
712 if (!self.havocbot_role_timeout)
713 self.havocbot_role_timeout = time + random() * 20 + 30;
714 if (time > self.havocbot_role_timeout)
716 dprint("changing role to middle\n");
717 self.havocbot_role = havocbot_role_ctf_middle;
718 self.havocbot_role_timeout = 0;
721 if (self.bot_strategytime < time)
723 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
724 navigation_goalrating_start();
725 havocbot_goalrating_ctf_ourstolenflag(200000);
726 havocbot_goalrating_ctf_droppedflags(50000);
727 havocbot_goalrating_items(10000, f.origin, 10000);
728 navigation_goalrating_end();
731 // FIXME: place info_ctf_defensepoint entities in CTF maps and use them
732 // change position occasionally
733 if (time > self.bot_strategytime || self.goalentity.classname != "info_ctf_defensepoint")
735 self.bot_strategytime = time + random() * 45 + 15;
736 self.goalentity = world;
737 head = findchain(classname, "info_ctf_defensepoint");
740 if (time > head.count)
742 self.goalentity = head;
747 // if there are no defensepoints defined, switch to middle
748 if (self.goalentity == world)
750 dprint("changing role to middle\n");
751 self.havocbot_role = havocbot_role_ctf_middle;
752 self.havocbot_role_timeout = 0;
756 // keep anyone else from taking this spot
757 if (self.goalentity != world)
758 self.goalentity.count = time + 0.5;
763 // choose a role according to the situation
764 void() havocbot_role_dm;
765 void havocbot_chooserole_ctf()
768 dprint("choose CTF role...\n");
769 if (self.team == COLOR_TEAM3 || self.team == COLOR_TEAM4)
770 self.havocbot_role = havocbot_role_ctf_rogue;
775 self.havocbot_role = havocbot_role_ctf_offense;
777 self.havocbot_role = havocbot_role_ctf_middle;
779 self.havocbot_role = havocbot_role_ctf_defense;
784 //go to best items, or control points you don't own
785 void havocbot_role_dom()
787 if (self.bot_strategytime < time)
789 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
790 navigation_goalrating_start();
791 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
792 havocbot_goalrating_items(8000, self.origin, 8000);
793 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
794 //havocbot_goalrating_waypoints(1, self.origin, 1000);
795 navigation_goalrating_end();
801 void havocbot_role_dm()
803 if (self.bot_strategytime < time)
805 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
806 navigation_goalrating_start();
807 havocbot_goalrating_items(10000, self.origin, 10000);
808 havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
809 //havocbot_goalrating_waypoints(1, self.origin, 1000);
810 navigation_goalrating_end();
815 //go to next checkpoint, and annoy enemies
816 .float race_checkpoint;
817 void havocbot_role_race()
820 if (self.bot_strategytime < time)
822 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
823 navigation_goalrating_start();
825 havocbot_goalrating_items(100, self.origin, 10000);
826 havocbot_goalrating_enemyplayers(500, self.origin, 20000);
829 for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
831 if(e.cnt == self.race_checkpoint)
833 navigation_routerating(e, 1000000, 5000);
835 else if(self.race_checkpoint == -1)
837 navigation_routerating(e, 1000000, 5000);
841 navigation_goalrating_end();
845 void havocbot_chooserole_dm()
847 self.havocbot_role = havocbot_role_dm;
850 void havocbot_chooserole_race()
852 self.havocbot_role = havocbot_role_race;
855 void havocbot_chooserole_dom()
857 self.havocbot_role = havocbot_role_dom;
865 entity kh_worldkeylist;
866 .entity kh_worldkeynext;
867 void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
870 for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
872 if(head.owner == self)
874 if(!kh_tracking_enabled)
876 // if it's carried by our team we know about it
877 // otherwise we have to see it to know about it
878 if(!head.owner || head.team != self.team)
880 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
881 if (trace_fraction < 1 && trace_ent != head)
882 continue; // skip what I can't see
886 navigation_routerating(head, ratingscale_dropped, 100000);
887 else if(head.team == self.team)
888 navigation_routerating(head, ratingscale_team, 100000);
890 navigation_routerating(head, ratingscale_enemy, 100000);
894 void() havocbot_role_kh_carrier;
895 void() havocbot_role_kh_defense;
896 void() havocbot_role_kh_offense;
897 void() havocbot_role_kh_freelancer;
898 void havocbot_role_kh_carrier()
900 if (!(self.items & IT_KEY1))
902 dprint("changing role to freelancer\n");
903 self.havocbot_role = havocbot_role_kh_freelancer;
904 self.havocbot_role_timeout = 0;
908 if (self.bot_strategytime < time)
910 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
911 navigation_goalrating_start();
913 if(kh_Key_AllOwnedByWhichTeam() == self.team)
914 havocbot_goalrating_kh(100000, 1, 1); // bring home
916 havocbot_goalrating_kh(40000, 40000, 1000); // play defensively
918 havocbot_goalrating_items(10000, self.origin, 10000);
919 navigation_goalrating_end();
923 void havocbot_role_kh_defense()
925 if (self.items & IT_KEY1)
927 dprint("changing role to carrier\n");
928 self.havocbot_role = havocbot_role_kh_carrier;
929 self.havocbot_role_timeout = 0;
933 if (!self.havocbot_role_timeout)
934 self.havocbot_role_timeout = time + random() * 10 + 20;
935 if (time > self.havocbot_role_timeout)
937 dprint("changing role to freelancer\n");
938 self.havocbot_role = havocbot_role_kh_freelancer;
939 self.havocbot_role_timeout = 0;
943 if (self.bot_strategytime < time)
945 float key_owner_team;
946 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
947 navigation_goalrating_start();
949 key_owner_team = kh_Key_AllOwnedByWhichTeam();
950 if(key_owner_team == self.team)
951 havocbot_goalrating_kh(100000, 1, 1); // defend key carriers
952 else if(key_owner_team == -1)
953 havocbot_goalrating_kh(40000, 10000, 1); // play defensively
955 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
957 havocbot_goalrating_items(10000, self.origin, 10000);
958 navigation_goalrating_end();
962 void havocbot_role_kh_offense()
964 if (self.items & IT_KEY1)
966 dprint("changing role to carrier\n");
967 self.havocbot_role = havocbot_role_kh_carrier;
968 self.havocbot_role_timeout = 0;
972 if (!self.havocbot_role_timeout)
973 self.havocbot_role_timeout = time + random() * 10 + 20;
974 if (time > self.havocbot_role_timeout)
976 dprint("changing role to freelancer\n");
977 self.havocbot_role = havocbot_role_kh_freelancer;
978 self.havocbot_role_timeout = 0;
982 if (self.bot_strategytime < time)
984 float key_owner_team;
986 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
987 navigation_goalrating_start();
989 key_owner_team = kh_Key_AllOwnedByWhichTeam();
990 if(key_owner_team == self.team)
991 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
992 else if(key_owner_team == -1)
993 havocbot_goalrating_kh(1, 10000, 40000); // play offensively
995 havocbot_goalrating_kh(1, 1, 100000); // ATTACK! EMERGENCY!
997 havocbot_goalrating_items(10000, self.origin, 10000);
998 navigation_goalrating_end();
1002 void havocbot_role_kh_freelancer()
1004 if (self.items & IT_KEY1)
1006 dprint("changing role to carrier\n");
1007 self.havocbot_role = havocbot_role_kh_carrier;
1008 self.havocbot_role_timeout = 0;
1012 if (!self.havocbot_role_timeout)
1013 self.havocbot_role_timeout = time + random() * 10 + 10;
1014 if (time > self.havocbot_role_timeout)
1018 dprint("changing role to offense\n");
1019 self.havocbot_role = havocbot_role_kh_offense;
1023 dprint("changing role to defense\n");
1024 self.havocbot_role = havocbot_role_kh_defense;
1026 self.havocbot_role_timeout = 0;
1030 if (self.bot_strategytime < time)
1032 float key_owner_team;
1034 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
1035 navigation_goalrating_start();
1037 key_owner_team = kh_Key_AllOwnedByWhichTeam();
1038 if(key_owner_team == self.team)
1039 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
1040 else if(key_owner_team == -1)
1041 havocbot_goalrating_kh(10000, 40000, 10000); // prefer dropped keys
1043 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
1045 havocbot_goalrating_items(10000, self.origin, 10000);
1046 navigation_goalrating_end();
1050 void havocbot_chooserole_kh()
1055 self.havocbot_role = havocbot_role_kh_offense;
1057 self.havocbot_role = havocbot_role_kh_defense;
1059 self.havocbot_role = havocbot_role_kh_freelancer;
1062 void havocbot_chooserole()
1064 dprint("choosing a role...\n");
1065 navigation_clearroute();
1066 self.bot_strategytime = 0;
1068 havocbot_chooserole_ctf();
1069 else if (g_domination)
1070 havocbot_chooserole_dom();
1072 havocbot_chooserole_kh();
1074 havocbot_chooserole_race();
1075 else // assume anything else is deathmatch
1076 havocbot_chooserole_dm();