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