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