1 #define HAVOCBOT_CTF_ROLE_NONE 0
2 #define HAVOCBOT_CTF_ROLE_DEFENSE 2
3 #define HAVOCBOT_CTF_ROLE_MIDDLE 4
4 #define HAVOCBOT_CTF_ROLE_OFFENSE 8
5 #define HAVOCBOT_CTF_ROLE_CARRIER 16
6 #define HAVOCBOT_CTF_ROLE_RETRIEVER 32
7 #define HAVOCBOT_CTF_ROLE_ESCORT 64
10 .void() havocbot_previous_role;
12 void() havocbot_role_ctf_middle;
13 void() havocbot_role_ctf_defense;
14 void() havocbot_role_ctf_offense;
15 void() havocbot_role_ctf_carrier;
16 void() havocbot_role_ctf_retriever;
17 void() havocbot_role_ctf_escort;
19 void(entity bot) havocbot_ctf_reset_role;
20 void(float ratingscale, vector org, float sradius) havocbot_goalrating_items;
21 void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
23 .float havocbot_cantfindflag;
24 .float havocbot_role_timeout;
25 .entity ctf_worldflagnext;
27 vector havocbot_ctf_middlepoint;
28 float havocbot_ctf_middlepoint_radius;
29 entity ctf_worldflaglist;
31 entity havocbot_ctf_find_flag(entity bot)
34 f = ctf_worldflaglist;
37 if (bot.team == f.team)
39 f = f.ctf_worldflagnext;
44 entity havocbot_ctf_find_enemy_flag(entity bot)
47 f = ctf_worldflaglist;
50 if (bot.team != f.team)
52 f = f.ctf_worldflagnext;
57 float havocbot_ctf_teamcount(entity bot, vector org, float radius)
67 if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot)
70 if(vlen(head.origin - org) < radius)
77 void havocbot_goalrating_ctf_ourflag(float ratingscale)
80 head = ctf_worldflaglist;
83 if (self.team == head.team)
85 head = head.ctf_worldflagnext;
88 navigation_routerating(head, ratingscale, 10000);
91 void havocbot_goalrating_ctf_ourbase(float ratingscale)
93 if not(bot_waypoints_for_items)
95 havocbot_goalrating_ctf_ourflag(ratingscale);
100 head = ctf_worldflaglist;
103 if (self.team == head.team)
105 head = head.ctf_worldflagnext;
110 // dropped_origin is set by ctf code whenever the flag is picked up
111 head = findradius(head.dropped_origin, 100);
114 if(head.classname=="waypoint")
116 navigation_routerating(head, ratingscale, 10000);
123 void havocbot_goalrating_ctf_enemyflag(float ratingscale)
126 head = ctf_worldflaglist;
129 if (self.team != head.team)
131 head = head.ctf_worldflagnext;
134 navigation_routerating(head, ratingscale, 10000);
137 void havocbot_goalrating_ctf_enemybase(float ratingscale)
139 if not(bot_waypoints_for_items)
141 havocbot_goalrating_ctf_enemyflag(ratingscale);
147 head = havocbot_ctf_find_enemy_flag(self);
152 head = findradius(head.dropped_origin, 100);
155 if(head.classname=="waypoint")
157 navigation_routerating(head, ratingscale, 10000);
164 void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
168 mf = havocbot_ctf_find_flag(self);
170 if(mf.cnt == FLAG_BASE)
173 navigation_routerating(mf, ratingscale, 10000);
176 void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float radius)
179 head = ctf_worldflaglist;
182 // flag is out in the field
183 if(head.cnt != FLAG_BASE)
184 if(head.tag_entity==world) // dropped
188 if(vlen(org-head.origin)<radius)
189 navigation_routerating(head, ratingscale, 10000);
192 navigation_routerating(head, ratingscale, 10000);
195 head = head.ctf_worldflagnext;
199 void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
203 head = findchainfloat(bot_pickup, TRUE);
206 // gather health and armor only
208 if (head.health || head.armorvalue)
209 if (vlen(head.origin - org) < sradius)
211 // get the value of the item
212 t = head.bot_pickupevalfunc(self, head) * 0.0001;
214 navigation_routerating(head, t * ratingscale, 500);
220 void havocbot_role_ctf_setrole(entity bot, float role)
222 dprint(strcat(bot.netname," switched to "));
225 case HAVOCBOT_CTF_ROLE_CARRIER:
227 bot.havocbot_role = havocbot_role_ctf_carrier;
228 bot.havocbot_role_timeout = 0;
229 bot.havocbot_cantfindflag = time + 10;
231 case HAVOCBOT_CTF_ROLE_DEFENSE:
233 bot.havocbot_role = havocbot_role_ctf_defense;
234 bot.havocbot_role_timeout = 0;
236 case HAVOCBOT_CTF_ROLE_MIDDLE:
238 bot.havocbot_role = havocbot_role_ctf_middle;
239 bot.havocbot_role_timeout = 0;
241 case HAVOCBOT_CTF_ROLE_OFFENSE:
243 bot.havocbot_role = havocbot_role_ctf_offense;
244 bot.havocbot_role_timeout = 0;
246 case HAVOCBOT_CTF_ROLE_RETRIEVER:
248 bot.havocbot_previous_role = bot.havocbot_role;
249 bot.havocbot_role = havocbot_role_ctf_retriever;
250 bot.havocbot_role_timeout = time + 10;
252 case HAVOCBOT_CTF_ROLE_ESCORT:
254 bot.havocbot_previous_role = bot.havocbot_role;
255 bot.havocbot_role = havocbot_role_ctf_escort;
256 bot.havocbot_role_timeout = time + 30;
262 void havocbot_role_ctf_carrier()
264 if(self.deadflag != DEAD_NO)
266 havocbot_ctf_reset_role(self);
270 if (self.flagcarried == world)
272 havocbot_ctf_reset_role(self);
276 if (self.bot_strategytime < time)
278 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
280 navigation_goalrating_start();
281 havocbot_goalrating_ctf_ourbase(50000);
284 havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
286 navigation_goalrating_end();
288 if (self.navigation_hasgoals)
289 self.havocbot_cantfindflag = time + 10;
290 else if (time > self.havocbot_cantfindflag)
292 // Can't navigate to my own base, suicide!
293 // TODO: drop it and wander around
294 Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
300 void havocbot_role_ctf_escort()
304 if(self.deadflag != DEAD_NO)
306 havocbot_ctf_reset_role(self);
310 if (self.flagcarried)
312 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
316 // If enemy flag is back on the base switch to previous role
317 ef = havocbot_ctf_find_enemy_flag(self);
318 if(ef.cnt==FLAG_BASE)
320 self.havocbot_role = self.havocbot_previous_role;
321 self.havocbot_role_timeout = 0;
325 // If the flag carrier reached the base switch to defense
326 mf = havocbot_ctf_find_flag(self);
327 if(mf.cnt!=FLAG_BASE)
328 if(vlen(ef.origin - mf.dropped_origin) < 300)
330 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE);
334 // Set the role timeout if necessary
335 if (!self.havocbot_role_timeout)
337 self.havocbot_role_timeout = time + random() * 30 + 60;
340 // If nothing happened just switch to previous role
341 if (time > self.havocbot_role_timeout)
343 self.havocbot_role = self.havocbot_previous_role;
344 self.havocbot_role_timeout = 0;
348 // Chase the flag carrier
349 if (self.bot_strategytime < time)
351 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
352 navigation_goalrating_start();
353 havocbot_goalrating_ctf_enemyflag(30000);
354 havocbot_goalrating_ctf_ourstolenflag(40000);
355 havocbot_goalrating_items(10000, self.origin, 10000);
356 navigation_goalrating_end();
360 void havocbot_role_ctf_offense()
365 if(self.deadflag != DEAD_NO)
367 havocbot_ctf_reset_role(self);
371 if (self.flagcarried)
373 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
378 mf = havocbot_ctf_find_flag(self);
379 ef = havocbot_ctf_find_enemy_flag(self);
382 if(mf.cnt!=FLAG_BASE)
385 pos = mf.tag_entity.origin;
389 // Try to get it if closer than the enemy base
390 if(vlen(self.origin-ef.dropped_origin)>vlen(self.origin-pos))
392 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
397 // Escort flag carrier
398 if(ef.cnt!=FLAG_BASE)
401 pos = ef.tag_entity.origin;
405 if(vlen(pos-mf.dropped_origin)>700)
407 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT);
412 // About to fail, switch to middlefield
415 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE);
419 // Set the role timeout if necessary
420 if (!self.havocbot_role_timeout)
421 self.havocbot_role_timeout = time + 120;
423 if (time > self.havocbot_role_timeout)
425 havocbot_ctf_reset_role(self);
429 if (self.bot_strategytime < time)
431 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
432 navigation_goalrating_start();
433 havocbot_goalrating_ctf_ourstolenflag(50000);
434 havocbot_goalrating_ctf_enemybase(20000);
435 havocbot_goalrating_items(5000, self.origin, 1000);
436 havocbot_goalrating_items(1000, self.origin, 10000);
437 navigation_goalrating_end();
441 // Retriever (temporary role):
442 void havocbot_role_ctf_retriever()
446 if(self.deadflag != DEAD_NO)
448 havocbot_ctf_reset_role(self);
452 if (self.flagcarried)
454 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
458 // If flag is back on the base switch to previous role
459 mf = havocbot_ctf_find_flag(self);
460 if(mf.cnt==FLAG_BASE)
462 havocbot_ctf_reset_role(self);
466 if (!self.havocbot_role_timeout)
467 self.havocbot_role_timeout = time + 20;
469 if (time > self.havocbot_role_timeout)
471 havocbot_ctf_reset_role(self);
475 if (self.bot_strategytime < time)
480 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
481 navigation_goalrating_start();
482 havocbot_goalrating_ctf_ourstolenflag(50000);
483 havocbot_goalrating_ctf_droppedflags(40000, self.origin, radius);
484 havocbot_goalrating_ctf_enemybase(30000);
485 havocbot_goalrating_items(500, self.origin, radius);
486 navigation_goalrating_end();
490 void havocbot_role_ctf_middle()
494 if(self.deadflag != DEAD_NO)
496 havocbot_ctf_reset_role(self);
500 if (self.flagcarried)
502 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
506 mf = havocbot_ctf_find_flag(self);
507 if(mf.cnt!=FLAG_BASE)
509 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
513 if (!self.havocbot_role_timeout)
514 self.havocbot_role_timeout = time + 10;
516 if (time > self.havocbot_role_timeout)
518 havocbot_ctf_reset_role(self);
522 if (self.bot_strategytime < time)
526 org = havocbot_ctf_middlepoint;
527 org_z = self.origin_z;
529 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
530 navigation_goalrating_start();
531 havocbot_goalrating_ctf_ourstolenflag(50000);
532 havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000);
533 havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5);
534 havocbot_goalrating_items(5000, org, havocbot_ctf_middlepoint_radius * 0.5);
535 havocbot_goalrating_items(2500, self.origin, 10000);
536 havocbot_goalrating_ctf_enemybase(2500);
537 navigation_goalrating_end();
541 void havocbot_role_ctf_defense()
545 if(self.deadflag != DEAD_NO)
547 havocbot_ctf_reset_role(self);
551 if (self.flagcarried)
553 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
557 // If own flag was captured
558 mf = havocbot_ctf_find_flag(self);
559 if(mf.cnt!=FLAG_BASE)
561 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
565 if (!self.havocbot_role_timeout)
566 self.havocbot_role_timeout = time + 30;
568 if (time > self.havocbot_role_timeout)
570 havocbot_ctf_reset_role(self);
573 if (self.bot_strategytime < time)
578 org = mf.dropped_origin;
579 radius = havocbot_ctf_middlepoint_radius;
581 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
582 navigation_goalrating_start();
584 // if enemies are closer to our base, go there
585 local entity head, closestplayer;
586 local float distance, bestdistance;
588 FOR_EACH_PLAYER(head)
590 if(head.deadflag!=DEAD_NO)
593 distance = vlen(org - head.origin);
594 if(distance<bestdistance)
596 closestplayer = head;
597 bestdistance = distance;
602 if(closestplayer.team!=self.team)
603 if(vlen(org - self.origin)>1000)
604 if(checkpvs(self.origin,closestplayer)||random()<0.5)
605 havocbot_goalrating_ctf_ourbase(30000);
607 havocbot_goalrating_ctf_ourstolenflag(20000);
608 havocbot_goalrating_ctf_droppedflags(20000, org, radius);
609 havocbot_goalrating_enemyplayers(15000, org, radius);
610 havocbot_goalrating_items(10000, org, radius);
611 havocbot_goalrating_items(5000, self.origin, 10000);
612 navigation_goalrating_end();
616 void havocbot_calculate_middlepoint()
621 f = ctf_worldflaglist;
629 f = f.ctf_worldflagnext;
631 havocbot_ctf_middlepoint = p1 + ((p2-p1) * 0.5);
632 havocbot_ctf_middlepoint_radius = vlen(p2-p1) * 0.5;
635 void havocbot_ctf_reset_role(entity bot)
637 local float cdefense, cmiddle, coffense;
638 local entity mf, ef, head;
641 if(bot.deadflag != DEAD_NO)
644 if(vlen(havocbot_ctf_middlepoint)==0)
645 havocbot_calculate_middlepoint();
650 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_CARRIER);
654 mf = havocbot_ctf_find_flag(bot);
655 ef = havocbot_ctf_find_enemy_flag(bot);
657 // Retrieve stolen flag
658 if(mf.cnt!=FLAG_BASE)
660 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER);
664 // If enemy flag is taken go to the middle to intercept pursuers
665 if(ef.cnt!=FLAG_BASE)
667 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
671 // if there is only me on the team switch to offense
673 FOR_EACH_PLAYER(head)
674 if(head.team==bot.team)
679 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
683 // Evaluate best position to take
684 // Count mates on middle position
685 cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5);
687 // Count mates on defense position
688 cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5);
690 // Count mates on offense position
691 coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius);
693 if(cdefense<=coffense)
694 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE);
695 else if(coffense<=cmiddle)
696 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
698 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
701 void havocbot_chooserole_ctf()
703 havocbot_ctf_reset_role(self);