2 #define HAVOCBOT_ROLE_NONE 0
\r
3 #define HAVOCBOT_CTF_ROLE_DEFENSE 2
\r
4 #define HAVOCBOT_CTF_ROLE_MIDDLE 4
\r
5 #define HAVOCBOT_CTF_ROLE_OFFENSE 8
\r
6 #define HAVOCBOT_CTF_ROLE_CARRIER 16
\r
7 #define HAVOCBOT_CTF_ROLE_RETRIEVER 32
\r
8 #define HAVOCBOT_CTF_ROLE_ESCORT 64
\r
10 .void() havocbot_role;
11 .void() havocbot_previous_role;
13 void() havocbot_role_ctf_middle;
14 void() havocbot_role_ctf_defense;
15 void() havocbot_role_ctf_offense;
16 void() havocbot_role_ctf_carrier;
17 void() havocbot_role_ctf_retriever;
18 void() havocbot_role_ctf_escort;
20 void(entity bot) havocbot_ctf_reset_role;
21 void(float ratingscale, vector org, float sradius) havocbot_goalrating_items;
22 void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
24 .float havocbot_cantfindflag;
25 .float havocbot_role_timeout;
26 .entity ctf_worldflagnext;
28 vector havocbot_ctf_middlepoint;
29 float havocbot_ctf_middlepoint_radius;
30 entity ctf_worldflaglist;
32 entity havocbot_ctf_find_flag(entity bot)
35 f = ctf_worldflaglist;
38 if (bot.team == f.team)
40 f = f.ctf_worldflagnext;
45 entity havocbot_ctf_find_enemy_flag(entity bot)
48 f = ctf_worldflaglist;
51 if (bot.team != f.team)
53 f = f.ctf_worldflagnext;
58 float havocbot_ctf_teamcount(entity bot, vector org, float radius)
\r
60 if not(teams_matter)
\r
66 FOR_EACH_PLAYER(head)
\r
68 if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot)
\r
71 if(vlen(head.origin - org) < radius)
\r
78 void havocbot_goalrating_ctf_ourflag(float ratingscale)
81 head = ctf_worldflaglist;
84 if (self.team == head.team)
86 head = head.ctf_worldflagnext;
89 navigation_routerating(head, ratingscale, 10000);
92 void havocbot_goalrating_ctf_ourbase(float ratingscale)
94 if not(bot_waypoints_for_items)
96 havocbot_goalrating_ctf_ourflag(ratingscale);
101 head = ctf_worldflaglist;
104 if (self.team == head.team)
106 head = head.ctf_worldflagnext;
111 // dropped_origin is set by ctf code whenever the flag is picked up
112 head = findradius(head.dropped_origin, 100);
115 if(head.classname=="waypoint")
117 navigation_routerating(head, ratingscale, 10000);
124 void havocbot_goalrating_ctf_enemyflag(float ratingscale)
127 head = ctf_worldflaglist;
130 if (self.team != head.team)
132 head = head.ctf_worldflagnext;
135 navigation_routerating(head, ratingscale, 10000);
138 void havocbot_goalrating_ctf_enemybase(float ratingscale)
140 if not(bot_waypoints_for_items)
142 havocbot_goalrating_ctf_enemyflag(ratingscale);
148 head = havocbot_ctf_find_enemy_flag(self);
153 head = findradius(head.dropped_origin, 100);
156 if(head.classname=="waypoint")
158 navigation_routerating(head, ratingscale, 10000);
165 void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
169 mf = havocbot_ctf_find_flag(self);
171 if(mf.cnt == FLAG_BASE)
174 navigation_routerating(mf, ratingscale, 10000);
177 void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float radius)
180 head = ctf_worldflaglist;
183 // flag is out in the field
184 if(head.cnt != FLAG_BASE)
185 if(head.tag_entity==world) // dropped
189 if(vlen(org-head.origin)<radius)
190 navigation_routerating(head, ratingscale, 10000);
193 navigation_routerating(head, ratingscale, 10000);
196 head = head.ctf_worldflagnext;
200 void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
204 head = findchainfloat(bot_pickup, TRUE);
207 // gather health and armor only
209 if (head.health || head.armorvalue)
210 if (vlen(head.origin - org) < sradius)
212 // get the value of the item
213 t = head.bot_pickupevalfunc(self, head) * 0.0001;
215 navigation_routerating(head, t * ratingscale, 500);
221 void havocbot_role_ctf_setrole(entity bot, float role)
\r
223 dprint(strcat(bot.netname," switched to "));
\r
226 case HAVOCBOT_CTF_ROLE_CARRIER:
\r
228 bot.havocbot_role = havocbot_role_ctf_carrier;
\r
229 bot.havocbot_role_timeout = 0;
\r
230 bot.havocbot_cantfindflag = time + 10;
\r
232 case HAVOCBOT_CTF_ROLE_DEFENSE:
\r
234 bot.havocbot_role = havocbot_role_ctf_defense;
\r
235 bot.havocbot_role_timeout = 0;
\r
237 case HAVOCBOT_CTF_ROLE_MIDDLE:
\r
239 bot.havocbot_role = havocbot_role_ctf_middle;
\r
240 bot.havocbot_role_timeout = 0;
\r
242 case HAVOCBOT_CTF_ROLE_OFFENSE:
\r
244 bot.havocbot_role = havocbot_role_ctf_offense;
\r
245 bot.havocbot_role_timeout = 0;
\r
247 case HAVOCBOT_CTF_ROLE_RETRIEVER:
\r
249 bot.havocbot_previous_role = bot.havocbot_role;
\r
250 bot.havocbot_role = havocbot_role_ctf_retriever;
\r
251 bot.havocbot_role_timeout = time + 10;
\r
253 case HAVOCBOT_CTF_ROLE_ESCORT:
\r
255 bot.havocbot_previous_role = bot.havocbot_role;
\r
256 bot.havocbot_role = havocbot_role_ctf_escort;
\r
257 bot.havocbot_role_timeout = time + 30;
\r
263 void havocbot_role_ctf_carrier()
\r
265 if(self.deadflag != DEAD_NO)
267 havocbot_ctf_reset_role(self);
\r
271 if (self.flagcarried == world)
\r
273 havocbot_ctf_reset_role(self);
\r
277 if (self.bot_strategytime < time)
\r
279 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
\r
281 navigation_goalrating_start();
\r
282 havocbot_goalrating_ctf_ourbase(50000);
\r
284 if(self.health<100)
\r
285 havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
\r
287 navigation_goalrating_end();
\r
289 if (self.navigation_hasgoals)
\r
290 self.havocbot_cantfindflag = time + 10;
\r
291 else if (time > self.havocbot_cantfindflag)
\r
293 // Can't navigate to my own base, suicide!
\r
294 // TODO: drop it and wander around
\r
295 Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
\r
301 void havocbot_role_ctf_escort()
\r
303 local entity mf, ef;
\r
305 if(self.deadflag != DEAD_NO)
307 havocbot_ctf_reset_role(self);
\r
311 if (self.flagcarried)
\r
313 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
\r
317 // If enemy flag is back on the base switch to previous role
\r
318 ef = havocbot_ctf_find_enemy_flag(self);
\r
319 if(ef.cnt==FLAG_BASE)
\r
321 self.havocbot_role = self.havocbot_previous_role;
\r
322 self.havocbot_role_timeout = 0;
\r
326 // If the flag carrier reached the base switch to defense
\r
327 mf = havocbot_ctf_find_flag(self);
\r
328 if(mf.cnt!=FLAG_BASE)
\r
329 if(vlen(ef.origin - mf.dropped_origin) < 300)
\r
331 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE);
\r
335 // Set the role timeout if necessary
\r
336 if (!self.havocbot_role_timeout)
338 self.havocbot_role_timeout = time + random() * 30 + 60;
341 // If nothing happened just switch to previous role
\r
342 if (time > self.havocbot_role_timeout)
\r
344 self.havocbot_role = self.havocbot_previous_role;
\r
345 self.havocbot_role_timeout = 0;
\r
349 // Chase the flag carrier
\r
350 if (self.bot_strategytime < time)
\r
352 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
\r
353 navigation_goalrating_start();
\r
354 havocbot_goalrating_ctf_enemyflag(30000);
\r
355 havocbot_goalrating_ctf_ourstolenflag(40000);
\r
356 havocbot_goalrating_items(10000, self.origin, 10000);
\r
357 navigation_goalrating_end();
\r
361 void havocbot_role_ctf_offense()
\r
366 if(self.deadflag != DEAD_NO)
368 havocbot_ctf_reset_role(self);
\r
372 if (self.flagcarried)
\r
374 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
\r
379 mf = havocbot_ctf_find_flag(self);
\r
380 ef = havocbot_ctf_find_enemy_flag(self);
\r
383 if(mf.cnt!=FLAG_BASE)
\r
386 pos = mf.tag_entity.origin;
390 // Try to get it if closer than the enemy base
391 if(vlen(self.origin-ef.dropped_origin)>vlen(self.origin-pos))
\r
393 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
\r
398 // Escort flag carrier
399 if(ef.cnt!=FLAG_BASE)
402 pos = ef.tag_entity.origin;
406 if(vlen(pos-mf.dropped_origin)>700)
\r
408 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT);
\r
413 // About to fail, switch to middlefield
\r
416 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE);
\r
420 // Set the role timeout if necessary
\r
421 if (!self.havocbot_role_timeout)
\r
422 self.havocbot_role_timeout = time + 120;
\r
424 if (time > self.havocbot_role_timeout)
\r
426 havocbot_ctf_reset_role(self);
\r
430 if (self.bot_strategytime < time)
\r
432 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
\r
433 navigation_goalrating_start();
\r
434 havocbot_goalrating_ctf_ourstolenflag(50000);
\r
435 havocbot_goalrating_ctf_enemybase(20000);
\r
436 havocbot_goalrating_items(5000, self.origin, 1000);
437 havocbot_goalrating_items(1000, self.origin, 10000);
\r
438 navigation_goalrating_end();
\r
442 // Retriever (temporary role):
\r
443 void havocbot_role_ctf_retriever()
\r
447 if(self.deadflag != DEAD_NO)
449 havocbot_ctf_reset_role(self);
\r
453 if (self.flagcarried)
\r
455 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
\r
459 // If flag is back on the base switch to previous role
\r
460 mf = havocbot_ctf_find_flag(self);
461 if(mf.cnt==FLAG_BASE)
\r
463 havocbot_ctf_reset_role(self);
\r
467 if (!self.havocbot_role_timeout)
468 self.havocbot_role_timeout = time + 20;
470 if (time > self.havocbot_role_timeout)
472 havocbot_ctf_reset_role(self);
476 if (self.bot_strategytime < time)
\r
478 local float radius;
\r
481 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
482 navigation_goalrating_start();
483 havocbot_goalrating_ctf_ourstolenflag(50000);
484 havocbot_goalrating_ctf_droppedflags(40000, self.origin, radius);
485 havocbot_goalrating_ctf_enemybase(30000);
486 havocbot_goalrating_items(500, self.origin, radius);
\r
487 navigation_goalrating_end();
491 void havocbot_role_ctf_middle()
\r
495 if(self.deadflag != DEAD_NO)
497 havocbot_ctf_reset_role(self);
\r
501 if (self.flagcarried)
\r
503 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
\r
507 mf = havocbot_ctf_find_flag(self);
\r
508 if(mf.cnt!=FLAG_BASE)
\r
510 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
\r
514 if (!self.havocbot_role_timeout)
\r
515 self.havocbot_role_timeout = time + 10;
\r
517 if (time > self.havocbot_role_timeout)
\r
519 havocbot_ctf_reset_role(self);
\r
523 if (self.bot_strategytime < time)
\r
527 org = havocbot_ctf_middlepoint;
528 org_z = self.origin_z;
530 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
\r
531 navigation_goalrating_start();
\r
532 havocbot_goalrating_ctf_ourstolenflag(50000);
\r
533 havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000);
\r
534 havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5);
\r
535 havocbot_goalrating_items(5000, org, havocbot_ctf_middlepoint_radius * 0.5);
536 havocbot_goalrating_items(2500, self.origin, 10000);
537 havocbot_goalrating_ctf_enemybase(2500);
538 navigation_goalrating_end();
\r
542 void havocbot_role_ctf_defense()
\r
546 if(self.deadflag != DEAD_NO)
548 havocbot_ctf_reset_role(self);
\r
552 if (self.flagcarried)
\r
554 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
\r
558 // If own flag was captured
\r
559 mf = havocbot_ctf_find_flag(self);
\r
560 if(mf.cnt!=FLAG_BASE)
\r
562 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
\r
566 if (!self.havocbot_role_timeout)
\r
567 self.havocbot_role_timeout = time + 30;
\r
569 if (time > self.havocbot_role_timeout)
\r
571 havocbot_ctf_reset_role(self);
\r
574 if (self.bot_strategytime < time)
\r
579 org = mf.dropped_origin;
580 radius = havocbot_ctf_middlepoint_radius;
\r
582 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
\r
583 navigation_goalrating_start();
585 // if enemies are closer to our base, go there
586 local entity head, closestplayer;
587 local float distance, bestdistance;
589 FOR_EACH_PLAYER(head)
591 if(head.deadflag!=DEAD_NO)
594 distance = vlen(org - head.origin);
595 if(distance<bestdistance)
597 closestplayer = head;
598 bestdistance = distance;
603 if(closestplayer.team!=self.team)
604 if(vlen(org - self.origin)>1000)
605 if(checkpvs(self.origin,closestplayer)||random()<0.5)
606 havocbot_goalrating_ctf_ourbase(30000);
608 havocbot_goalrating_ctf_ourstolenflag(20000);
\r
609 havocbot_goalrating_ctf_droppedflags(20000, org, radius);
\r
610 havocbot_goalrating_enemyplayers(15000, org, radius);
611 havocbot_goalrating_items(10000, org, radius);
612 havocbot_goalrating_items(5000, self.origin, 10000);
\r
613 navigation_goalrating_end();
\r
617 void havocbot_calculate_middlepoint()
\r
622 f = ctf_worldflaglist;
\r
630 f = f.ctf_worldflagnext;
\r
632 havocbot_ctf_middlepoint = p1 + ((p2-p1) * 0.5);
633 havocbot_ctf_middlepoint_radius = vlen(p2-p1) * 0.5;
\r
636 void havocbot_ctf_reset_role(entity bot)
\r
638 local float cdefense, cmiddle, coffense;
\r
639 local entity mf, ef, head;
642 if(self.deadflag != DEAD_NO)
645 if(vlen(havocbot_ctf_middlepoint)==0)
646 havocbot_calculate_middlepoint();
649 if (self.flagcarried)
651 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
655 mf = havocbot_ctf_find_flag(bot);
\r
656 ef = havocbot_ctf_find_enemy_flag(bot);
\r
658 // Retrieve stolen flag
\r
659 if(mf.cnt!=FLAG_BASE)
\r
661 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER);
\r
665 // If enemy flag is taken go to the middle to intercept pursuers
\r
666 if(ef.cnt!=FLAG_BASE)
\r
668 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
\r
672 // if there is only me on the team switch to offense
674 FOR_EACH_PLAYER(head)
675 if(head.team==self.team)
680 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
684 // Evaluate best position to take
\r
685 // Count mates on middle position
\r
686 cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5);
\r
688 // Count mates on defense position
\r
689 cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5);
\r
691 // Count mates on offense position
\r
692 coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius);
\r
694 if(cdefense<=coffense)
\r
695 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE);
\r
696 else if(coffense<=cmiddle)
\r
697 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
\r
699 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
\r
702 void havocbot_chooserole_ctf()
\r
704 havocbot_ctf_reset_role(self);
\r