2 .float havocbot_role_timeout;
3 .void() havocbot_previous_role;
9 void havocbot_goalrating_items(float ratingscale, vector org, float sradius)
13 local float rating, d, discard, distance, friend_distance, enemy_distance;
14 ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
15 head = findchainfloat(bot_pickup, TRUE);
19 distance = vlen(head.origin - org);
20 friend_distance = 10000; enemy_distance = 10000;
23 if(!head.solid || distance > sradius || (head == self.ignoregoal && time < self.ignoregoaltime) )
29 // Check if the item can be picked up safely
30 if(head.classname == "droppedweapon")
32 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
34 d = pointcontents(trace_endpos + '0 0 1');
35 if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
40 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
48 // Ignore items under water
49 traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
50 if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
61 FOR_EACH_PLAYER(player)
64 if ( self == player || player.deadflag )
67 d = vlen(player.origin - head.origin); // distance between player and item
69 if ( player.team == self.team )
71 if ( clienttype(player) != CLIENTTYPE_REAL || discard )
74 if( d > friend_distance)
81 if( head.health && player.health > self.health )
84 if( head.armorvalue && player.armorvalue > self.armorvalue)
88 if( (player.weapons & head.weapons) != head.weapons)
91 if (head.ammo_shells && player.ammo_shells > self.ammo_shells)
94 if (head.ammo_nails && player.ammo_nails > self.ammo_nails)
97 if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets)
100 if (head.ammo_cells && player.ammo_cells > self.ammo_cells )
107 // If enemy only track distances
108 // TODO: track only if visible ?
109 if( d < enemy_distance )
114 // Rate the item only if no one needs it, or if an enemy is closer to it
115 if ( (enemy_distance < friend_distance && distance < enemy_distance) ||
116 (friend_distance > cvar("bot_ai_friends_aware_pickup_radius") ) || !discard )
117 rating = head.bot_pickupevalfunc(self, head);
121 rating = head.bot_pickupevalfunc(self, head);
124 navigation_routerating(head, rating * ratingscale, 2000);
129 void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius)
132 head = findchain(classname, "dom_controlpoint");
135 if (vlen(head.origin - org) < sradius)
137 if(head.cnt > -1) // this is just being fought for
138 navigation_routerating(head, ratingscale, 5000);
139 else if(head.goalentity.cnt == 0) // unclaimed point
140 navigation_routerating(head, ratingscale * 0.5, 5000);
141 else if(head.goalentity.team != self.team) // other team's point
142 navigation_routerating(head, ratingscale * 0.2, 5000);
149 // LordHavoc: this function was already unused, but for waypoints to be a
150 // useful goal the bots would have to seek out the least-recently-visited
151 // ones, not the closest
152 void havocbot_goalrating_waypoints(float ratingscale, vector org, float sradius)
155 head = findchain(classname, "waypoint");
158 if (vlen(head.origin - org) < sradius && vlen(head.origin - org) > 100)
159 navigation_routerating(head, ratingscale, 2000);
165 void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius)
168 local float t, noteam, distance;
169 noteam = ((self.team == 0) || !teams_matter); // fteqcc sucks
171 if (cvar("bot_nofire"))
174 // don't chase players if we're under water
175 if(self.waterlevel>WATERLEVEL_WETFEET)
178 FOR_EACH_PLAYER(head)
180 // TODO: Merge this logic with the bot_shouldattack function
183 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
185 distance = vlen(head.origin - org);
186 if (distance < 100 || distance > sradius)
190 if(head.items & IT_STRENGTH)
193 // rate only visible enemies
195 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
196 if (trace_fraction < 1 || trace_ent != head)
200 if(head.flags & FL_INWATER || head.flags & FL_PARTIALGROUND)
204 if(head.flags & FL_ONGROUND == 0)
206 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
207 t = pointcontents(trace_endpos + '0 0 1');
208 if( t != CONTENT_SOLID )
209 if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
211 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
215 t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
216 navigation_routerating(head, t * ratingscale, 2000);
221 // choose a role according to the situation
222 void() havocbot_role_dm;
225 //go to best items, or control points you don't own
226 void havocbot_role_dom()
228 if(self.deadflag != DEAD_NO)
231 if (self.bot_strategytime < time)
233 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
234 navigation_goalrating_start();
235 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
236 havocbot_goalrating_items(8000, self.origin, 8000);
237 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
238 //havocbot_goalrating_waypoints(1, self.origin, 1000);
239 navigation_goalrating_end();
245 void havocbot_role_dm()
247 if(self.deadflag != DEAD_NO)
250 if (self.bot_strategytime < time)
252 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
253 navigation_goalrating_start();
254 havocbot_goalrating_items(10000, self.origin, 10000);
255 havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
256 //havocbot_goalrating_waypoints(1, self.origin, 1000);
257 navigation_goalrating_end();
262 //go to next checkpoint, and annoy enemies
263 .float race_checkpoint;
264 void havocbot_role_race()
266 if(self.deadflag != DEAD_NO)
270 if (self.bot_strategytime < time)
272 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
273 navigation_goalrating_start();
275 havocbot_goalrating_items(100, self.origin, 10000);
276 havocbot_goalrating_enemyplayers(500, self.origin, 20000);
279 for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
281 if(e.cnt == self.race_checkpoint)
283 navigation_routerating(e, 1000000, 5000);
285 else if(self.race_checkpoint == -1)
287 navigation_routerating(e, 1000000, 5000);
291 navigation_goalrating_end();
295 void havocbot_chooserole_dm()
297 self.havocbot_role = havocbot_role_dm;
300 void havocbot_chooserole_race()
302 self.havocbot_role = havocbot_role_race;
305 void havocbot_chooserole_dom()
307 self.havocbot_role = havocbot_role_dom;
315 entity kh_worldkeylist;
316 .entity kh_worldkeynext;
317 void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
320 for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
322 if(head.owner == self)
324 if(!kh_tracking_enabled)
326 // if it's carried by our team we know about it
327 // otherwise we have to see it to know about it
328 if(!head.owner || head.team != self.team)
330 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
331 if (trace_fraction < 1 && trace_ent != head)
332 continue; // skip what I can't see
336 navigation_routerating(head, ratingscale_dropped * BOT_PICKUP_RATING_HIGH, 100000);
337 else if(head.team == self.team)
338 navigation_routerating(head.owner, ratingscale_team * BOT_PICKUP_RATING_HIGH, 100000);
340 navigation_routerating(head.owner, ratingscale_enemy * BOT_PICKUP_RATING_HIGH, 100000);
343 havocbot_goalrating_items(1, self.origin, 10000);
346 void() havocbot_role_kh_carrier;
347 void() havocbot_role_kh_defense;
348 void() havocbot_role_kh_offense;
349 void() havocbot_role_kh_freelancer;
350 void havocbot_role_kh_carrier()
352 if(self.deadflag != DEAD_NO)
357 dprint("changing role to freelancer\n");
358 self.havocbot_role = havocbot_role_kh_freelancer;
359 self.havocbot_role_timeout = 0;
363 if (self.bot_strategytime < time)
365 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
366 navigation_goalrating_start();
368 if(kh_Key_AllOwnedByWhichTeam() == self.team)
369 havocbot_goalrating_kh(10, 0.1, 0.1); // bring home
371 havocbot_goalrating_kh(4, 4, 1); // play defensively
373 navigation_goalrating_end();
377 void havocbot_role_kh_defense()
379 if(self.deadflag != DEAD_NO)
384 dprint("changing role to carrier\n");
385 self.havocbot_role = havocbot_role_kh_carrier;
386 self.havocbot_role_timeout = 0;
390 if (!self.havocbot_role_timeout)
391 self.havocbot_role_timeout = time + random() * 10 + 20;
392 if (time > self.havocbot_role_timeout)
394 dprint("changing role to freelancer\n");
395 self.havocbot_role = havocbot_role_kh_freelancer;
396 self.havocbot_role_timeout = 0;
400 if (self.bot_strategytime < time)
402 float key_owner_team;
403 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
404 navigation_goalrating_start();
406 key_owner_team = kh_Key_AllOwnedByWhichTeam();
407 if(key_owner_team == self.team)
408 havocbot_goalrating_kh(10, 0.1, 0.1); // defend key carriers
409 else if(key_owner_team == -1)
410 havocbot_goalrating_kh(4, 1, 0.1); // play defensively
412 havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY
414 navigation_goalrating_end();
418 void havocbot_role_kh_offense()
420 if(self.deadflag != DEAD_NO)
425 dprint("changing role to carrier\n");
426 self.havocbot_role = havocbot_role_kh_carrier;
427 self.havocbot_role_timeout = 0;
431 if (!self.havocbot_role_timeout)
432 self.havocbot_role_timeout = time + random() * 10 + 20;
433 if (time > self.havocbot_role_timeout)
435 dprint("changing role to freelancer\n");
436 self.havocbot_role = havocbot_role_kh_freelancer;
437 self.havocbot_role_timeout = 0;
441 if (self.bot_strategytime < time)
443 float key_owner_team;
445 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
446 navigation_goalrating_start();
448 key_owner_team = kh_Key_AllOwnedByWhichTeam();
449 if(key_owner_team == self.team)
450 havocbot_goalrating_kh(10, 0.1, 0.1); // defend anyway
451 else if(key_owner_team == -1)
452 havocbot_goalrating_kh(0.1, 1, 4); // play offensively
454 havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK! EMERGENCY!
456 navigation_goalrating_end();
460 void havocbot_role_kh_freelancer()
462 if(self.deadflag != DEAD_NO)
467 dprint("changing role to carrier\n");
468 self.havocbot_role = havocbot_role_kh_carrier;
469 self.havocbot_role_timeout = 0;
473 if (!self.havocbot_role_timeout)
474 self.havocbot_role_timeout = time + random() * 10 + 10;
475 if (time > self.havocbot_role_timeout)
479 dprint("changing role to offense\n");
480 self.havocbot_role = havocbot_role_kh_offense;
484 dprint("changing role to defense\n");
485 self.havocbot_role = havocbot_role_kh_defense;
487 self.havocbot_role_timeout = 0;
491 if (self.bot_strategytime < time)
493 float key_owner_team;
495 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
496 navigation_goalrating_start();
498 key_owner_team = kh_Key_AllOwnedByWhichTeam();
499 if(key_owner_team == self.team)
500 havocbot_goalrating_kh(10, 0.1, 0.1); // defend anyway
501 else if(key_owner_team == -1)
502 havocbot_goalrating_kh(1, 10, 4); // prefer dropped keys
504 havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY
506 navigation_goalrating_end();
510 void havocbot_chooserole_kh()
514 if(self.deadflag != DEAD_NO)
519 self.havocbot_role = havocbot_role_kh_offense;
521 self.havocbot_role = havocbot_role_kh_defense;
523 self.havocbot_role = havocbot_role_kh_freelancer;
526 void havocbot_chooserole()
528 dprint("choosing a role...\n");
529 navigation_clearroute();
530 self.bot_strategytime = 0;
532 havocbot_chooserole_ctf();
533 else if (g_domination)
534 havocbot_chooserole_dom();
536 havocbot_chooserole_kh();
537 else if (g_race || g_cts)
538 havocbot_chooserole_race();
539 else if (g_onslaught)
540 havocbot_chooserole_ons();
541 else // assume anything else is deathmatch
542 havocbot_chooserole_dm();