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