2 .float havocbot_role_timeout;
3 .void() havocbot_previous_role;
8 float havocbot_pickupevalfunc(entity item)
11 r = item.bot_pickupevalfunc(self, item);
12 // print(item.classname, " ", ftos(r), "\n");
16 void havocbot_goalrating_items(float ratingscale, vector org, float sradius)
20 local float rating, d, discard, distance, friend_distance, enemy_distance;
21 ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
22 head = findchainfloat(bot_pickup, TRUE);
26 distance = vlen(head.origin - org);
27 friend_distance = 10000; enemy_distance = 10000;
30 if(!head.solid || distance > sradius || (head == self.ignoregoal && time < self.ignoregoaltime) )
36 // Check if the item can be picked up safely
37 if(head.classname == "droppedweapon")
39 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
41 d = pointcontents(trace_endpos + '0 0 1');
42 if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
47 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
55 // Ignore items under water
56 traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
57 if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
68 FOR_EACH_PLAYER(player)
71 if ( self == player || player.deadflag )
74 d = vlen(player.origin - head.origin); // distance between player and item
76 if ( player.team == self.team )
78 if ( clienttype(player) != CLIENTTYPE_REAL || discard )
81 if( d > friend_distance)
88 if( head.health && player.health > self.health )
91 if( head.armorvalue && player.armorvalue > self.armorvalue)
95 if( (player.weapons & head.weapons) != head.weapons)
98 if (head.ammo_shells && player.ammo_shells > self.ammo_shells)
101 if (head.ammo_nails && player.ammo_nails > self.ammo_nails)
104 if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets)
107 if (head.ammo_cells && player.ammo_cells > self.ammo_cells )
114 // If enemy only track distances
115 // TODO: track only if visible ?
116 if( d < enemy_distance )
121 // Rate the item only if no one needs it, or if an enemy is closer to it
122 if ( (enemy_distance < friend_distance && distance < enemy_distance) ||
123 (friend_distance > cvar("bot_ai_friends_aware_pickup_radius") ) || !discard )
125 // rating = head.bot_pickupevalfunc(self, head);
126 rating = havocbot_pickupevalfunc(head);
131 // rating = head.bot_pickupevalfunc(self, head);
132 rating = havocbot_pickupevalfunc(head);
136 navigation_routerating(head, rating * ratingscale, 2000);
141 void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius)
144 head = findchain(classname, "dom_controlpoint");
147 if (vlen(head.origin - org) < sradius)
149 if(head.cnt > -1) // this is just being fought for
150 navigation_routerating(head, ratingscale, 5000);
151 else if(head.goalentity.cnt == 0) // unclaimed point
152 navigation_routerating(head, ratingscale * 0.5, 5000);
153 else if(head.goalentity.team != self.team) // other team's point
154 navigation_routerating(head, ratingscale * 0.2, 5000);
161 // LordHavoc: this function was already unused, but for waypoints to be a
162 // useful goal the bots would have to seek out the least-recently-visited
163 // ones, not the closest
164 void havocbot_goalrating_waypoints(float ratingscale, vector org, float sradius)
167 head = findchain(classname, "waypoint");
170 if (vlen(head.origin - org) < sradius && vlen(head.origin - org) > 100)
171 navigation_routerating(head, ratingscale, 2000);
177 void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius)
180 local float t, noteam, distance;
181 noteam = ((self.team == 0) || !teams_matter); // fteqcc sucks
183 if (cvar("bot_nofire"))
186 // don't chase players if we're under water
187 if(self.waterlevel>WATERLEVEL_WETFEET)
190 FOR_EACH_PLAYER(head)
192 // TODO: Merge this logic with the bot_shouldattack function
195 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
197 distance = vlen(head.origin - org);
198 if (distance < 100 || distance > sradius)
202 if(head.items & IT_STRENGTH)
205 // rate only visible enemies
207 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
208 if (trace_fraction < 1 || trace_ent != head)
212 if(head.flags & FL_INWATER || head.flags & FL_PARTIALGROUND)
216 if(head.flags & FL_ONGROUND == 0)
218 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
219 t = pointcontents(trace_endpos + '0 0 1');
220 if( t != CONTENT_SOLID )
221 if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
223 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
227 t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
228 navigation_routerating(head, t * ratingscale, 2000);
233 // choose a role according to the situation
234 void() havocbot_role_dm;
237 //go to best items, or control points you don't own
238 void havocbot_role_dom()
240 if(self.deadflag != DEAD_NO)
243 if (self.bot_strategytime < time)
245 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
246 navigation_goalrating_start();
247 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
248 havocbot_goalrating_items(8000, self.origin, 8000);
249 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
250 //havocbot_goalrating_waypoints(1, self.origin, 1000);
251 navigation_goalrating_end();
257 void havocbot_role_dm()
259 if(self.deadflag != DEAD_NO)
262 if (self.bot_strategytime < time)
264 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
265 navigation_goalrating_start();
266 havocbot_goalrating_items(10000, self.origin, 10000);
267 havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
268 //havocbot_goalrating_waypoints(1, self.origin, 1000);
269 navigation_goalrating_end();
274 //go to next checkpoint, and annoy enemies
275 .float race_checkpoint;
276 void havocbot_role_race()
278 if(self.deadflag != DEAD_NO)
282 if (self.bot_strategytime < time)
284 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
285 navigation_goalrating_start();
287 havocbot_goalrating_items(100, self.origin, 10000);
288 havocbot_goalrating_enemyplayers(500, self.origin, 20000);
291 for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
293 if(e.cnt == self.race_checkpoint)
295 navigation_routerating(e, 1000000, 5000);
297 else if(self.race_checkpoint == -1)
299 navigation_routerating(e, 1000000, 5000);
303 navigation_goalrating_end();
307 void havocbot_chooserole_dm()
309 self.havocbot_role = havocbot_role_dm;
312 void havocbot_chooserole_race()
314 self.havocbot_role = havocbot_role_race;
317 void havocbot_chooserole_dom()
319 self.havocbot_role = havocbot_role_dom;
327 entity kh_worldkeylist;
328 .entity kh_worldkeynext;
329 void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
332 for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
334 if(head.owner == self)
336 if(!kh_tracking_enabled)
338 // if it's carried by our team we know about it
339 // otherwise we have to see it to know about it
340 if(!head.owner || head.team != self.team)
342 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
343 if (trace_fraction < 1 && trace_ent != head)
344 continue; // skip what I can't see
348 navigation_routerating(head, ratingscale_dropped, 100000);
349 else if(head.team == self.team)
350 navigation_routerating(head, ratingscale_team, 100000);
352 navigation_routerating(head, ratingscale_enemy, 100000);
356 void() havocbot_role_kh_carrier;
357 void() havocbot_role_kh_defense;
358 void() havocbot_role_kh_offense;
359 void() havocbot_role_kh_freelancer;
360 void havocbot_role_kh_carrier()
362 if(self.deadflag != DEAD_NO)
365 if (!(self.items & IT_KEY1))
367 dprint("changing role to freelancer\n");
368 self.havocbot_role = havocbot_role_kh_freelancer;
369 self.havocbot_role_timeout = 0;
373 if (self.bot_strategytime < time)
375 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
376 navigation_goalrating_start();
378 if(kh_Key_AllOwnedByWhichTeam() == self.team)
379 havocbot_goalrating_kh(100000, 1, 1); // bring home
381 havocbot_goalrating_kh(40000, 40000, 1000); // play defensively
383 havocbot_goalrating_items(10000, self.origin, 10000);
384 navigation_goalrating_end();
388 void havocbot_role_kh_defense()
390 if(self.deadflag != DEAD_NO)
393 if (self.items & IT_KEY1)
395 dprint("changing role to carrier\n");
396 self.havocbot_role = havocbot_role_kh_carrier;
397 self.havocbot_role_timeout = 0;
401 if (!self.havocbot_role_timeout)
402 self.havocbot_role_timeout = time + random() * 10 + 20;
403 if (time > self.havocbot_role_timeout)
405 dprint("changing role to freelancer\n");
406 self.havocbot_role = havocbot_role_kh_freelancer;
407 self.havocbot_role_timeout = 0;
411 if (self.bot_strategytime < time)
413 float key_owner_team;
414 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
415 navigation_goalrating_start();
417 key_owner_team = kh_Key_AllOwnedByWhichTeam();
418 if(key_owner_team == self.team)
419 havocbot_goalrating_kh(100000, 1, 1); // defend key carriers
420 else if(key_owner_team == -1)
421 havocbot_goalrating_kh(40000, 10000, 1); // play defensively
423 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
425 havocbot_goalrating_items(10000, self.origin, 10000);
426 navigation_goalrating_end();
430 void havocbot_role_kh_offense()
432 if(self.deadflag != DEAD_NO)
435 if (self.items & IT_KEY1)
437 dprint("changing role to carrier\n");
438 self.havocbot_role = havocbot_role_kh_carrier;
439 self.havocbot_role_timeout = 0;
443 if (!self.havocbot_role_timeout)
444 self.havocbot_role_timeout = time + random() * 10 + 20;
445 if (time > self.havocbot_role_timeout)
447 dprint("changing role to freelancer\n");
448 self.havocbot_role = havocbot_role_kh_freelancer;
449 self.havocbot_role_timeout = 0;
453 if (self.bot_strategytime < time)
455 float key_owner_team;
457 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
458 navigation_goalrating_start();
460 key_owner_team = kh_Key_AllOwnedByWhichTeam();
461 if(key_owner_team == self.team)
462 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
463 else if(key_owner_team == -1)
464 havocbot_goalrating_kh(1, 10000, 40000); // play offensively
466 havocbot_goalrating_kh(1, 1, 100000); // ATTACK! EMERGENCY!
468 havocbot_goalrating_items(10000, self.origin, 10000);
469 navigation_goalrating_end();
473 void havocbot_role_kh_freelancer()
475 if(self.deadflag != DEAD_NO)
478 if (self.items & IT_KEY1)
480 dprint("changing role to carrier\n");
481 self.havocbot_role = havocbot_role_kh_carrier;
482 self.havocbot_role_timeout = 0;
486 if (!self.havocbot_role_timeout)
487 self.havocbot_role_timeout = time + random() * 10 + 10;
488 if (time > self.havocbot_role_timeout)
492 dprint("changing role to offense\n");
493 self.havocbot_role = havocbot_role_kh_offense;
497 dprint("changing role to defense\n");
498 self.havocbot_role = havocbot_role_kh_defense;
500 self.havocbot_role_timeout = 0;
504 if (self.bot_strategytime < time)
506 float key_owner_team;
508 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
509 navigation_goalrating_start();
511 key_owner_team = kh_Key_AllOwnedByWhichTeam();
512 if(key_owner_team == self.team)
513 havocbot_goalrating_kh(100000, 1, 1); // defend anyway
514 else if(key_owner_team == -1)
515 havocbot_goalrating_kh(10000, 40000, 10000); // prefer dropped keys
517 havocbot_goalrating_kh(1, 1, 100000); // ATTACK ANYWAY
519 havocbot_goalrating_items(10000, self.origin, 10000);
520 navigation_goalrating_end();
524 void havocbot_chooserole_kh()
528 if(self.deadflag != DEAD_NO)
533 self.havocbot_role = havocbot_role_kh_offense;
535 self.havocbot_role = havocbot_role_kh_defense;
537 self.havocbot_role = havocbot_role_kh_freelancer;
540 void havocbot_chooserole()
542 dprint("choosing a role...\n");
543 navigation_clearroute();
544 self.bot_strategytime = 0;
546 havocbot_chooserole_ctf();
547 else if (g_domination)
548 havocbot_chooserole_dom();
550 havocbot_chooserole_kh();
551 else if (g_race || g_cts)
552 havocbot_chooserole_race();
553 else if (g_onslaught)
554 havocbot_chooserole_ons();
555 else // assume anything else is deathmatch
556 havocbot_chooserole_dm();