1 #define HAVOCBOT_CTF_ROLE_NONE 0
\r
2 #define HAVOCBOT_CTF_ROLE_DEFENSE 2
\r
3 #define HAVOCBOT_CTF_ROLE_MIDDLE 4
\r
4 #define HAVOCBOT_CTF_ROLE_OFFENSE 8
\r
5 #define HAVOCBOT_CTF_ROLE_CARRIER 16
\r
6 #define HAVOCBOT_CTF_ROLE_RETRIEVER 32
\r
7 #define HAVOCBOT_CTF_ROLE_ESCORT 64
\r
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)
\r
59 if not(teams_matter)
\r
65 FOR_EACH_PLAYER(head)
\r
67 if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot)
\r
70 if(vlen(head.origin - org) < radius)
\r
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)
\r
222 dprint(strcat(bot.netname," switched to "));
\r
225 case HAVOCBOT_CTF_ROLE_CARRIER:
\r
227 bot.havocbot_role = havocbot_role_ctf_carrier;
\r
228 bot.havocbot_role_timeout = 0;
\r
229 bot.havocbot_cantfindflag = time + 10;
\r
231 case HAVOCBOT_CTF_ROLE_DEFENSE:
\r
233 bot.havocbot_role = havocbot_role_ctf_defense;
\r
234 bot.havocbot_role_timeout = 0;
\r
236 case HAVOCBOT_CTF_ROLE_MIDDLE:
\r
238 bot.havocbot_role = havocbot_role_ctf_middle;
\r
239 bot.havocbot_role_timeout = 0;
\r
241 case HAVOCBOT_CTF_ROLE_OFFENSE:
\r
243 bot.havocbot_role = havocbot_role_ctf_offense;
\r
244 bot.havocbot_role_timeout = 0;
\r
246 case HAVOCBOT_CTF_ROLE_RETRIEVER:
\r
248 bot.havocbot_previous_role = bot.havocbot_role;
\r
249 bot.havocbot_role = havocbot_role_ctf_retriever;
\r
250 bot.havocbot_role_timeout = time + 10;
\r
252 case HAVOCBOT_CTF_ROLE_ESCORT:
\r
254 bot.havocbot_previous_role = bot.havocbot_role;
\r
255 bot.havocbot_role = havocbot_role_ctf_escort;
\r
256 bot.havocbot_role_timeout = time + 30;
\r
262 void havocbot_role_ctf_carrier()
\r
264 if(self.deadflag != DEAD_NO)
266 havocbot_ctf_reset_role(self);
\r
270 if (self.flagcarried == world)
\r
272 havocbot_ctf_reset_role(self);
\r
276 if (self.bot_strategytime < time)
\r
278 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
\r
280 navigation_goalrating_start();
\r
281 havocbot_goalrating_ctf_ourbase(50000);
\r
283 if(self.health<100)
\r
284 havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
\r
286 navigation_goalrating_end();
\r
288 if (self.navigation_hasgoals)
\r
289 self.havocbot_cantfindflag = time + 10;
\r
290 else if (time > self.havocbot_cantfindflag)
\r
292 // Can't navigate to my own base, suicide!
\r
293 // TODO: drop it and wander around
\r
294 Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
\r
300 void havocbot_role_ctf_escort()
\r
302 local entity mf, ef;
\r
304 if(self.deadflag != DEAD_NO)
306 havocbot_ctf_reset_role(self);
\r
310 if (self.flagcarried)
\r
312 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
\r
316 // If enemy flag is back on the base switch to previous role
\r
317 ef = havocbot_ctf_find_enemy_flag(self);
\r
318 if(ef.cnt==FLAG_BASE)
\r
320 self.havocbot_role = self.havocbot_previous_role;
\r
321 self.havocbot_role_timeout = 0;
\r
325 // If the flag carrier reached the base switch to defense
\r
326 mf = havocbot_ctf_find_flag(self);
\r
327 if(mf.cnt!=FLAG_BASE)
\r
328 if(vlen(ef.origin - mf.dropped_origin) < 300)
\r
330 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE);
\r
334 // Set the role timeout if necessary
\r
335 if (!self.havocbot_role_timeout)
337 self.havocbot_role_timeout = time + random() * 30 + 60;
340 // If nothing happened just switch to previous role
\r
341 if (time > self.havocbot_role_timeout)
\r
343 self.havocbot_role = self.havocbot_previous_role;
\r
344 self.havocbot_role_timeout = 0;
\r
348 // Chase the flag carrier
\r
349 if (self.bot_strategytime < time)
\r
351 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
\r
352 navigation_goalrating_start();
\r
353 havocbot_goalrating_ctf_enemyflag(30000);
\r
354 havocbot_goalrating_ctf_ourstolenflag(40000);
\r
355 havocbot_goalrating_items(10000, self.origin, 10000);
\r
356 navigation_goalrating_end();
\r
360 void havocbot_role_ctf_offense()
\r
365 if(self.deadflag != DEAD_NO)
367 havocbot_ctf_reset_role(self);
\r
371 if (self.flagcarried)
\r
373 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
\r
378 mf = havocbot_ctf_find_flag(self);
\r
379 ef = havocbot_ctf_find_enemy_flag(self);
\r
382 if(mf.cnt!=FLAG_BASE)
\r
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))
\r
392 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
\r
397 // Escort flag carrier
398 if(ef.cnt!=FLAG_BASE)
401 pos = ef.tag_entity.origin;
405 if(vlen(pos-mf.dropped_origin)>700)
\r
407 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT);
\r
412 // About to fail, switch to middlefield
\r
415 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE);
\r
419 // Set the role timeout if necessary
\r
420 if (!self.havocbot_role_timeout)
\r
421 self.havocbot_role_timeout = time + 120;
\r
423 if (time > self.havocbot_role_timeout)
\r
425 havocbot_ctf_reset_role(self);
\r
429 if (self.bot_strategytime < time)
\r
431 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
\r
432 navigation_goalrating_start();
\r
433 havocbot_goalrating_ctf_ourstolenflag(50000);
\r
434 havocbot_goalrating_ctf_enemybase(20000);
\r
435 havocbot_goalrating_items(5000, self.origin, 1000);
436 havocbot_goalrating_items(1000, self.origin, 10000);
\r
437 navigation_goalrating_end();
\r
441 // Retriever (temporary role):
\r
442 void havocbot_role_ctf_retriever()
\r
446 if(self.deadflag != DEAD_NO)
448 havocbot_ctf_reset_role(self);
\r
452 if (self.flagcarried)
\r
454 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
\r
458 // If flag is back on the base switch to previous role
\r
459 mf = havocbot_ctf_find_flag(self);
460 if(mf.cnt==FLAG_BASE)
\r
462 havocbot_ctf_reset_role(self);
\r
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)
\r
477 local float radius;
\r
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);
\r
486 navigation_goalrating_end();
490 void havocbot_role_ctf_middle()
\r
494 if(self.deadflag != DEAD_NO)
496 havocbot_ctf_reset_role(self);
\r
500 if (self.flagcarried)
\r
502 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
\r
506 mf = havocbot_ctf_find_flag(self);
\r
507 if(mf.cnt!=FLAG_BASE)
\r
509 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
\r
513 if (!self.havocbot_role_timeout)
\r
514 self.havocbot_role_timeout = time + 10;
\r
516 if (time > self.havocbot_role_timeout)
\r
518 havocbot_ctf_reset_role(self);
\r
522 if (self.bot_strategytime < time)
\r
526 org = havocbot_ctf_middlepoint;
527 org_z = self.origin_z;
529 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
\r
530 navigation_goalrating_start();
\r
531 havocbot_goalrating_ctf_ourstolenflag(50000);
\r
532 havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000);
\r
533 havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5);
\r
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();
\r
541 void havocbot_role_ctf_defense()
\r
545 if(self.deadflag != DEAD_NO)
547 havocbot_ctf_reset_role(self);
\r
551 if (self.flagcarried)
\r
553 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
\r
557 // If own flag was captured
\r
558 mf = havocbot_ctf_find_flag(self);
\r
559 if(mf.cnt!=FLAG_BASE)
\r
561 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
\r
565 if (!self.havocbot_role_timeout)
\r
566 self.havocbot_role_timeout = time + 30;
\r
568 if (time > self.havocbot_role_timeout)
\r
570 havocbot_ctf_reset_role(self);
\r
573 if (self.bot_strategytime < time)
\r
578 org = mf.dropped_origin;
579 radius = havocbot_ctf_middlepoint_radius;
\r
581 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
\r
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);
\r
608 havocbot_goalrating_ctf_droppedflags(20000, org, radius);
\r
609 havocbot_goalrating_enemyplayers(15000, org, radius);
610 havocbot_goalrating_items(10000, org, radius);
611 havocbot_goalrating_items(5000, self.origin, 10000);
\r
612 navigation_goalrating_end();
\r
616 void havocbot_calculate_middlepoint()
\r
621 f = ctf_worldflaglist;
\r
629 f = f.ctf_worldflagnext;
\r
631 havocbot_ctf_middlepoint = p1 + ((p2-p1) * 0.5);
632 havocbot_ctf_middlepoint_radius = vlen(p2-p1) * 0.5;
\r
635 void havocbot_ctf_reset_role(entity bot)
\r
637 local float cdefense, cmiddle, coffense;
\r
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);
\r
655 ef = havocbot_ctf_find_enemy_flag(bot);
\r
657 // Retrieve stolen flag
\r
658 if(mf.cnt!=FLAG_BASE)
\r
660 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER);
\r
664 // If enemy flag is taken go to the middle to intercept pursuers
\r
665 if(ef.cnt!=FLAG_BASE)
\r
667 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
\r
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
\r
684 // Count mates on middle position
\r
685 cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5);
\r
687 // Count mates on defense position
\r
688 cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5);
\r
690 // Count mates on offense position
\r
691 coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius);
\r
693 if(cdefense<=coffense)
\r
694 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE);
\r
695 else if(coffense<=cmiddle)
\r
696 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
\r
698 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
\r
701 void havocbot_chooserole_ctf()
\r
703 havocbot_ctf_reset_role(self);
\r