]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/bot/havocbot/role_ctf.qc
f85f2ef64c3e8a0c0964d5d344eb6977ef1c300d
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / havocbot / role_ctf.qc
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
8
9 .void() havocbot_role;
10 .void() havocbot_previous_role;
11
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;
18
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;
22
23 .float havocbot_cantfindflag;
24 .float havocbot_role_timeout;
25 .entity ctf_worldflagnext;
26 .entity basewaypoint;
27
28 entity ctf_worldflaglist;
29 vector havocbot_ctf_middlepoint;
30 float havocbot_ctf_middlepoint_radius;
31
32 entity havocbot_ctf_find_flag(entity bot)
33 {
34         entity f;
35         f = ctf_worldflaglist;
36         while (f)
37         {
38                 if (bot.team == f.team)
39                         return f;
40                 f = f.ctf_worldflagnext;
41         }
42         return world;
43 };
44
45 entity havocbot_ctf_find_enemy_flag(entity bot)
46 {
47         entity f;
48         f = ctf_worldflaglist;
49         while (f)
50         {
51                 if (bot.team != f.team)
52                         return f;
53                 f = f.ctf_worldflagnext;
54         }
55         return world;
56 };
57
58 float havocbot_ctf_teamcount(entity bot, vector org, float radius)
59 {
60         if not(teamplay)
61                 return 0;
62
63         float c;
64         entity head;
65
66         FOR_EACH_PLAYER(head)
67         {
68                 if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot)
69                         continue;
70
71                 if(vlen(head.origin - org) < radius)
72                         ++c;
73         }
74
75         return c;
76 };
77
78 void havocbot_goalrating_ctf_ourflag(float ratingscale)
79 {
80         local entity head;
81         head = ctf_worldflaglist;
82         while (head)
83         {
84                 if (self.team == head.team)
85                         break;
86                 head = head.ctf_worldflagnext;
87         }
88         if (head)
89                 navigation_routerating(head, ratingscale, 10000);
90 };
91
92 void havocbot_goalrating_ctf_ourbase(float ratingscale)
93 {
94         local entity head;
95         head = ctf_worldflaglist;
96         while (head)
97         {
98                 if (self.team == head.team)
99                         break;
100                 head = head.ctf_worldflagnext;
101         }
102         if not(head)
103                 return;
104
105         navigation_routerating(head.basewaypoint, ratingscale, 10000);
106 };
107
108 void havocbot_goalrating_ctf_enemyflag(float ratingscale)
109 {
110         local entity head;
111         head = ctf_worldflaglist;
112         while (head)
113         {
114                 if (self.team != head.team)
115                         break;
116                 head = head.ctf_worldflagnext;
117         }
118         if (head)
119                 navigation_routerating(head, ratingscale, 10000);
120 };
121
122 void havocbot_goalrating_ctf_enemybase(float ratingscale)
123 {
124         if not(bot_waypoints_for_items)
125         {
126                 havocbot_goalrating_ctf_enemyflag(ratingscale);
127                 return;
128         }
129
130         local entity head;
131
132         head = havocbot_ctf_find_enemy_flag(self);
133
134         if not(head)
135                 return;
136
137         navigation_routerating(head.basewaypoint, ratingscale, 10000);
138 };
139
140 void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
141 {
142         local entity mf;
143
144         mf = havocbot_ctf_find_flag(self);
145
146         if(mf.cnt == FLAG_BASE)
147                 return;
148
149         if(mf.tag_entity)
150                 navigation_routerating(mf.tag_entity, ratingscale, 10000);
151 };
152
153 void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float radius)
154 {
155         local entity head;
156         head = ctf_worldflaglist;
157         while (head)
158         {
159                 // flag is out in the field
160                 if(head.cnt != FLAG_BASE)
161                 if(head.tag_entity==world)      // dropped
162                 {
163                         if(radius)
164                         {
165                                 if(vlen(org-head.origin)<radius)
166                                         navigation_routerating(head, ratingscale, 10000);
167                         }
168                         else
169                                 navigation_routerating(head, ratingscale, 10000);
170                 }
171
172                 head = head.ctf_worldflagnext;
173         }
174 };
175
176 void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
177 {
178         local entity head;
179         local float t;
180         head = findchainfloat(bot_pickup, TRUE);
181         while (head)
182         {
183                 // gather health and armor only
184                 if (head.solid)
185                 if (head.health || head.armorvalue)
186                 if (vlen(head.origin - org) < sradius)
187                 {
188                         // get the value of the item
189                         t = head.bot_pickupevalfunc(self, head) * 0.0001;
190                         if (t > 0)
191                                 navigation_routerating(head, t * ratingscale, 500);
192                 }
193                 head = head.chain;
194         }
195 };
196
197 void havocbot_role_ctf_setrole(entity bot, float role)
198 {
199         dprint(strcat(bot.netname," switched to "));
200         switch(role)
201         {
202                 case HAVOCBOT_CTF_ROLE_CARRIER:
203                         dprint("carrier");
204                         bot.havocbot_role = havocbot_role_ctf_carrier;
205                         bot.havocbot_role_timeout = 0;
206                         bot.havocbot_cantfindflag = time + 10;
207                         bot.bot_strategytime = 0;
208                         break;
209                 case HAVOCBOT_CTF_ROLE_DEFENSE:
210                         dprint("defense");
211                         bot.havocbot_role = havocbot_role_ctf_defense;
212                         bot.havocbot_role_timeout = 0;
213                         break;
214                 case HAVOCBOT_CTF_ROLE_MIDDLE:
215                         dprint("middle");
216                         bot.havocbot_role = havocbot_role_ctf_middle;
217                         bot.havocbot_role_timeout = 0;
218                         break;
219                 case HAVOCBOT_CTF_ROLE_OFFENSE:
220                         dprint("offense");
221                         bot.havocbot_role = havocbot_role_ctf_offense;
222                         bot.havocbot_role_timeout = 0;
223                         break;
224                 case HAVOCBOT_CTF_ROLE_RETRIEVER:
225                         dprint("retriever");
226                         bot.havocbot_previous_role = bot.havocbot_role;
227                         bot.havocbot_role = havocbot_role_ctf_retriever;
228                         bot.havocbot_role_timeout = time + 10;
229                         bot.bot_strategytime = 0;
230                         break;
231                 case HAVOCBOT_CTF_ROLE_ESCORT:
232                         dprint("escort");
233                         bot.havocbot_previous_role = bot.havocbot_role;
234                         bot.havocbot_role = havocbot_role_ctf_escort;
235                         bot.havocbot_role_timeout = time + 30;
236                         bot.bot_strategytime = 0;
237                         break;
238         }
239         dprint("\n");
240 };
241
242 void havocbot_role_ctf_carrier()
243 {
244         if(self.deadflag != DEAD_NO)
245         {
246                 havocbot_ctf_reset_role(self);
247                 return;
248         }
249
250         if (self.flagcarried == world)
251         {
252                 havocbot_ctf_reset_role(self);
253                 return;
254         }
255
256         if (self.bot_strategytime < time)
257         {
258                 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
259
260                 navigation_goalrating_start();
261                 havocbot_goalrating_ctf_ourbase(50000);
262
263                 if(self.health<100)
264                         havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
265
266                 navigation_goalrating_end();
267
268                 if (self.navigation_hasgoals)
269                         self.havocbot_cantfindflag = time + 10;
270                 else if (time > self.havocbot_cantfindflag)
271                 {
272                         // Can't navigate to my own base, suicide!
273                         // TODO: drop it and wander around
274                         Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
275                         return;
276                 }
277         }
278 };
279
280 void havocbot_role_ctf_escort()
281 {
282         local entity mf, ef;
283
284         if(self.deadflag != DEAD_NO)
285         {
286                 havocbot_ctf_reset_role(self);
287                 return;
288         }
289
290         if (self.flagcarried)
291         {
292                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
293                 return;
294         }
295
296         // If enemy flag is back on the base switch to previous role
297         ef = havocbot_ctf_find_enemy_flag(self);
298         if(ef.cnt==FLAG_BASE)
299         {
300                 self.havocbot_role = self.havocbot_previous_role;
301                 self.havocbot_role_timeout = 0;
302                 return;
303         }
304
305         // If the flag carrier reached the base switch to defense
306         mf = havocbot_ctf_find_flag(self);
307         if(mf.cnt!=FLAG_BASE)
308         if(vlen(ef.origin - mf.dropped_origin) < 300)
309         {
310                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE);
311                 return;
312         }
313
314         // Set the role timeout if necessary
315         if (!self.havocbot_role_timeout)
316         {
317                 self.havocbot_role_timeout = time + random() * 30 + 60;
318         }
319
320         // If nothing happened just switch to previous role
321         if (time > self.havocbot_role_timeout)
322         {
323                 self.havocbot_role = self.havocbot_previous_role;
324                 self.havocbot_role_timeout = 0;
325                 return;
326         }
327
328         // Chase the flag carrier
329         if (self.bot_strategytime < time)
330         {
331                 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
332                 navigation_goalrating_start();
333                 havocbot_goalrating_ctf_enemyflag(30000);
334                 havocbot_goalrating_ctf_ourstolenflag(40000);
335                 havocbot_goalrating_items(10000, self.origin, 10000);
336                 navigation_goalrating_end();
337         }
338 };
339
340 void havocbot_role_ctf_offense()
341 {
342         local entity mf, ef;
343         local vector pos;
344
345         if(self.deadflag != DEAD_NO)
346         {
347                 havocbot_ctf_reset_role(self);
348                 return;
349         }
350
351         if (self.flagcarried)
352         {
353                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
354                 return;
355         }
356
357         // Check flags
358         mf = havocbot_ctf_find_flag(self);
359         ef = havocbot_ctf_find_enemy_flag(self);
360
361         // Own flag stolen
362         if(mf.cnt!=FLAG_BASE)
363         {
364                 if(mf.tag_entity)
365                         pos = mf.tag_entity.origin;
366                 else
367                         pos = mf.origin;
368
369                 // Try to get it if closer than the enemy base
370                 if(vlen(self.origin-ef.dropped_origin)>vlen(self.origin-pos))
371                 {
372                         havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
373                         return;
374                 }
375         }
376
377         // Escort flag carrier
378         if(ef.cnt!=FLAG_BASE)
379         {
380                 if(ef.tag_entity)
381                         pos = ef.tag_entity.origin;
382                 else
383                         pos = ef.origin;
384
385                 if(vlen(pos-mf.dropped_origin)>700)
386                 {
387                         havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT);
388                         return;
389                 }
390         }
391
392         // About to fail, switch to middlefield
393         if(self.health<50)
394         {
395                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE);
396                 return;
397         }
398
399         // Set the role timeout if necessary
400         if (!self.havocbot_role_timeout)
401                 self.havocbot_role_timeout = time + 120;
402
403         if (time > self.havocbot_role_timeout)
404         {
405                 havocbot_ctf_reset_role(self);
406                 return;
407         }
408
409         if (self.bot_strategytime < time)
410         {
411                 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
412                 navigation_goalrating_start();
413                 havocbot_goalrating_ctf_ourstolenflag(50000);
414                 havocbot_goalrating_ctf_enemybase(20000);
415                 havocbot_goalrating_items(5000, self.origin, 1000);
416                 havocbot_goalrating_items(1000, self.origin, 10000);
417                 navigation_goalrating_end();
418         }
419 };
420
421 // Retriever (temporary role):
422 void havocbot_role_ctf_retriever()
423 {
424         local entity mf;
425
426         if(self.deadflag != DEAD_NO)
427         {
428                 havocbot_ctf_reset_role(self);
429                 return;
430         }
431
432         if (self.flagcarried)
433         {
434                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
435                 return;
436         }
437
438         // If flag is back on the base switch to previous role
439         mf = havocbot_ctf_find_flag(self);
440         if(mf.cnt==FLAG_BASE)
441         {
442                 havocbot_ctf_reset_role(self);
443                 return;
444         }
445
446         if (!self.havocbot_role_timeout)
447                 self.havocbot_role_timeout = time + 20;
448
449         if (time > self.havocbot_role_timeout)
450         {
451                 havocbot_ctf_reset_role(self);
452                 return;
453         }
454
455         if (self.bot_strategytime < time)
456         {
457                 local float radius;
458                 radius = 10000;
459
460                 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
461                 navigation_goalrating_start();
462                 havocbot_goalrating_ctf_ourstolenflag(50000);
463                 havocbot_goalrating_ctf_droppedflags(40000, self.origin, radius);
464                 havocbot_goalrating_ctf_enemybase(30000);
465                 havocbot_goalrating_items(500, self.origin, radius);
466                 navigation_goalrating_end();
467         }
468 };
469
470 void havocbot_role_ctf_middle()
471 {
472         local entity mf;
473
474         if(self.deadflag != DEAD_NO)
475         {
476                 havocbot_ctf_reset_role(self);
477                 return;
478         }
479
480         if (self.flagcarried)
481         {
482                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
483                 return;
484         }
485
486         mf = havocbot_ctf_find_flag(self);
487         if(mf.cnt!=FLAG_BASE)
488         {
489                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
490                 return;
491         }
492
493         if (!self.havocbot_role_timeout)
494                 self.havocbot_role_timeout = time + 10;
495
496         if (time > self.havocbot_role_timeout)
497         {
498                 havocbot_ctf_reset_role(self);
499                 return;
500         }
501
502         if (self.bot_strategytime < time)
503         {
504                 local vector org;
505
506                 org = havocbot_ctf_middlepoint;
507                 org_z = self.origin_z;
508
509                 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
510                 navigation_goalrating_start();
511                 havocbot_goalrating_ctf_ourstolenflag(50000);
512                 havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000);
513                 havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5);
514                 havocbot_goalrating_items(5000, org, havocbot_ctf_middlepoint_radius * 0.5);
515                 havocbot_goalrating_items(2500, self.origin, 10000);
516                 havocbot_goalrating_ctf_enemybase(2500);
517                 navigation_goalrating_end();
518         }
519 };
520
521 void havocbot_role_ctf_defense()
522 {
523         local entity mf;
524
525         if(self.deadflag != DEAD_NO)
526         {
527                 havocbot_ctf_reset_role(self);
528                 return;
529         }
530
531         if (self.flagcarried)
532         {
533                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
534                 return;
535         }
536
537         // If own flag was captured
538         mf = havocbot_ctf_find_flag(self);
539         if(mf.cnt!=FLAG_BASE)
540         {
541                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
542                 return;
543         }
544
545         if (!self.havocbot_role_timeout)
546                 self.havocbot_role_timeout = time + 30;
547
548         if (time > self.havocbot_role_timeout)
549         {
550                 havocbot_ctf_reset_role(self);
551                 return;
552         }
553         if (self.bot_strategytime < time)
554         {
555                 local float radius;
556                 local vector org;
557
558                 org = mf.dropped_origin;
559                 radius = havocbot_ctf_middlepoint_radius;
560
561                 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
562                 navigation_goalrating_start();
563
564                 // if enemies are closer to our base, go there
565                 local entity head, closestplayer;
566                 local float distance, bestdistance;
567                 distance = 10000;
568                 FOR_EACH_PLAYER(head)
569                 {
570                         if(head.deadflag!=DEAD_NO)
571                                 continue;
572
573                         distance = vlen(org - head.origin);
574                         if(distance<bestdistance)
575                         {
576                                 closestplayer = head;
577                                 bestdistance = distance;
578                         }
579                 }
580
581                 if(closestplayer)
582                 if(closestplayer.team!=self.team)
583                 if(vlen(org - self.origin)>1000)
584                 if(checkpvs(self.origin,closestplayer)||random()<0.5)
585                         havocbot_goalrating_ctf_ourbase(30000);
586
587                 havocbot_goalrating_ctf_ourstolenflag(20000);
588                 havocbot_goalrating_ctf_droppedflags(20000, org, radius);
589                 havocbot_goalrating_enemyplayers(15000, org, radius);
590                 havocbot_goalrating_items(10000, org, radius);
591                 havocbot_goalrating_items(5000, self.origin, 10000);
592                 navigation_goalrating_end();
593         }
594 };
595
596 void havocbot_calculate_middlepoint()
597 {
598         entity f;
599         vector p1, p2;
600
601         f = ctf_worldflaglist;
602         while (f)
603         {
604                 if(p1)
605                         p2 = f.origin;
606                 else
607                         p1 = f.origin;
608
609                 f = f.ctf_worldflagnext;
610         }
611         havocbot_ctf_middlepoint = p1 + ((p2-p1) * 0.5);
612         havocbot_ctf_middlepoint_radius  = vlen(p2-p1) * 0.5;
613 };
614
615 void havocbot_ctf_reset_role(entity bot)
616 {
617         local float cdefense, cmiddle, coffense;
618         local entity mf, ef, head;
619         local float c;
620
621         if(bot.deadflag != DEAD_NO)
622                 return;
623
624         if(vlen(havocbot_ctf_middlepoint)==0)
625                 havocbot_calculate_middlepoint();
626
627         // Check ctf flags
628         if (bot.flagcarried)
629         {
630                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_CARRIER);
631                 return;
632         }
633
634         mf = havocbot_ctf_find_flag(bot);
635         ef = havocbot_ctf_find_enemy_flag(bot);
636
637         // Retrieve stolen flag
638         if(mf.cnt!=FLAG_BASE)
639         {
640                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER);
641                 return;
642         }
643
644         // If enemy flag is taken go to the middle to intercept pursuers
645         if(ef.cnt!=FLAG_BASE)
646         {
647                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
648                 return;
649         }
650
651         // if there is only me on the team switch to offense
652         c = 0;
653         FOR_EACH_PLAYER(head)
654         if(head.team==bot.team)
655                 ++c;
656
657         if(c==1)
658         {
659                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
660                 return;
661         }
662
663         // Evaluate best position to take
664         // Count mates on middle position
665         cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5);
666
667         // Count mates on defense position
668         cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5);
669
670         // Count mates on offense position
671         coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius);
672
673         if(cdefense<=coffense)
674                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE);
675         else if(coffense<=cmiddle)
676                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
677         else
678                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
679 };
680
681 void havocbot_chooserole_ctf()
682 {
683         havocbot_ctf_reset_role(self);
684 };