]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/havocbot_ctf.qc
actually read sv_clforceplayermodels
[divverent/nexuiz.git] / data / qcsrc / server / havocbot_ctf.qc
1 #define HAVOCBOT_CTF_ROLE_NONE          0\r
2 #define HAVOCBOT_CTF_ROLE_DEFENSE       2\r
3 #define HAVOCBOT_CTF_ROLE_MIDDLE        4\r
4 #define HAVOCBOT_CTF_ROLE_OFFENSE       8\r
5 #define HAVOCBOT_CTF_ROLE_CARRIER       16\r
6 #define HAVOCBOT_CTF_ROLE_RETRIEVER     32\r
7 #define HAVOCBOT_CTF_ROLE_ESCORT        64\r
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 \r
57 float havocbot_ctf_teamcount(entity bot, vector org, float radius)\r
58 {\r
59         if not(teams_matter)\r
60                 return 0;\r
61 \r
62         float c;\r
63         entity head;\r
64 \r
65         FOR_EACH_PLAYER(head)\r
66         {\r
67                 if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot)\r
68                         continue;\r
69 \r
70                 if(vlen(head.origin - org) < radius)\r
71                         ++c;\r
72         }\r
73 \r
74         return c;\r
75 };\r
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 \r
220 void havocbot_role_ctf_setrole(entity bot, float role)\r
221 {\r
222         dprint(strcat(bot.netname," switched to "));\r
223         switch(role)\r
224         {\r
225                 case HAVOCBOT_CTF_ROLE_CARRIER:\r
226                         dprint("carrier");\r
227                         bot.havocbot_role = havocbot_role_ctf_carrier;\r
228                         bot.havocbot_role_timeout = 0;\r
229                         bot.havocbot_cantfindflag = time + 10;\r
230                         break;\r
231                 case HAVOCBOT_CTF_ROLE_DEFENSE:\r
232                         dprint("defense");\r
233                         bot.havocbot_role = havocbot_role_ctf_defense;\r
234                         bot.havocbot_role_timeout = 0;\r
235                         break;\r
236                 case HAVOCBOT_CTF_ROLE_MIDDLE:\r
237                         dprint("middle");\r
238                         bot.havocbot_role = havocbot_role_ctf_middle;\r
239                         bot.havocbot_role_timeout = 0;\r
240                         break;\r
241                 case HAVOCBOT_CTF_ROLE_OFFENSE:\r
242                         dprint("offense");\r
243                         bot.havocbot_role = havocbot_role_ctf_offense;\r
244                         bot.havocbot_role_timeout = 0;\r
245                         break;\r
246                 case HAVOCBOT_CTF_ROLE_RETRIEVER:\r
247                         dprint("retriever");
248                         bot.havocbot_previous_role = bot.havocbot_role;\r
249                         bot.havocbot_role = havocbot_role_ctf_retriever;\r
250                         bot.havocbot_role_timeout = time + 10;\r
251                         break;\r
252                 case HAVOCBOT_CTF_ROLE_ESCORT:\r
253                         dprint("escort");
254                         bot.havocbot_previous_role = bot.havocbot_role;\r
255                         bot.havocbot_role = havocbot_role_ctf_escort;\r
256                         bot.havocbot_role_timeout = time + 30;\r
257                         break;\r
258         }\r
259         dprint("\n");\r
260 };\r
261 \r
262 void havocbot_role_ctf_carrier()\r
263 {\r
264         if(self.deadflag != DEAD_NO)
265         {\r
266                 havocbot_ctf_reset_role(self);\r
267                 return;\r
268         }\r
269 \r
270         if (self.flagcarried == world)\r
271         {\r
272                 havocbot_ctf_reset_role(self);\r
273                 return;\r
274         }\r
275 \r
276         if (self.bot_strategytime < time)\r
277         {\r
278                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");\r
279 \r
280                 navigation_goalrating_start();\r
281                 havocbot_goalrating_ctf_ourbase(50000);\r
282 \r
283                 if(self.health<100)\r
284                         havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);\r
285 \r
286                 navigation_goalrating_end();\r
287 \r
288                 if (self.navigation_hasgoals)\r
289                         self.havocbot_cantfindflag = time + 10;\r
290                 else if (time > self.havocbot_cantfindflag)\r
291                 {\r
292                         // Can't navigate to my own base, suicide!\r
293                         // TODO: drop it and wander around\r
294                         Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');\r
295                         return;\r
296                 }\r
297         }\r
298 };\r
299 \r
300 void havocbot_role_ctf_escort()\r
301 {\r
302         local entity mf, ef;\r
303 \r
304         if(self.deadflag != DEAD_NO)
305         {\r
306                 havocbot_ctf_reset_role(self);\r
307                 return;\r
308         }\r
309 \r
310         if (self.flagcarried)\r
311         {\r
312                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);\r
313                 return;\r
314         }\r
315 \r
316         // If enemy flag is back on the base switch to previous role\r
317         ef = havocbot_ctf_find_enemy_flag(self);\r
318         if(ef.cnt==FLAG_BASE)\r
319         {\r
320                 self.havocbot_role = self.havocbot_previous_role;\r
321                 self.havocbot_role_timeout = 0;\r
322                 return;\r
323         }\r
324 \r
325         // If the flag carrier reached the base switch to defense\r
326         mf = havocbot_ctf_find_flag(self);\r
327         if(mf.cnt!=FLAG_BASE)\r
328         if(vlen(ef.origin - mf.dropped_origin) < 300)\r
329         {\r
330                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE);\r
331                 return;\r
332         }\r
333 \r
334         // Set the role timeout if necessary\r
335         if (!self.havocbot_role_timeout)
336         {\r
337                 self.havocbot_role_timeout = time + random() * 30 + 60;
338         }\r
339 \r
340         // If nothing happened just switch to previous role\r
341         if (time > self.havocbot_role_timeout)\r
342         {\r
343                 self.havocbot_role = self.havocbot_previous_role;\r
344                 self.havocbot_role_timeout = 0;\r
345                 return;\r
346         }\r
347 \r
348         // Chase the flag carrier\r
349         if (self.bot_strategytime < time)\r
350         {\r
351                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");\r
352                 navigation_goalrating_start();\r
353                 havocbot_goalrating_ctf_enemyflag(30000);\r
354                 havocbot_goalrating_ctf_ourstolenflag(40000);\r
355                 havocbot_goalrating_items(10000, self.origin, 10000);\r
356                 navigation_goalrating_end();\r
357         }\r
358 };\r
359 \r
360 void havocbot_role_ctf_offense()\r
361 {\r
362         local entity mf, ef;
363         local vector pos;\r
364 \r
365         if(self.deadflag != DEAD_NO)
366         {\r
367                 havocbot_ctf_reset_role(self);\r
368                 return;\r
369         }\r
370 \r
371         if (self.flagcarried)\r
372         {\r
373                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);\r
374                 return;\r
375         }\r
376 \r
377         // Check flags\r
378         mf = havocbot_ctf_find_flag(self);\r
379         ef = havocbot_ctf_find_enemy_flag(self);\r
380
381         // Own flag stolen
382         if(mf.cnt!=FLAG_BASE)\r
383         {\r
384                 if(mf.tag_entity)
385                         pos = mf.tag_entity.origin;
386                 else
387                         pos = mf.origin;
388 \r
389                 // Try to get it if closer than the enemy base
390                 if(vlen(self.origin-ef.dropped_origin)>vlen(self.origin-pos))\r
391                 {\r
392                         havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);\r
393                         return;\r
394                 }\r
395         }\r
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)\r
406                 {\r
407                         havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT);\r
408                         return;\r
409                 }\r
410         }
411 \r
412         // About to fail, switch to middlefield\r
413         if(self.health<50)\r
414         {\r
415                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE);\r
416                 return;\r
417         }\r
418 \r
419         // Set the role timeout if necessary\r
420         if (!self.havocbot_role_timeout)\r
421                 self.havocbot_role_timeout = time + 120;\r
422 \r
423         if (time > self.havocbot_role_timeout)\r
424         {\r
425                 havocbot_ctf_reset_role(self);\r
426                 return;\r
427         }\r
428 \r
429         if (self.bot_strategytime < time)\r
430         {\r
431                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");\r
432                 navigation_goalrating_start();\r
433                 havocbot_goalrating_ctf_ourstolenflag(50000);\r
434                 havocbot_goalrating_ctf_enemybase(20000);\r
435                 havocbot_goalrating_items(5000, self.origin, 1000);
436                 havocbot_goalrating_items(1000, self.origin, 10000);\r
437                 navigation_goalrating_end();\r
438         }\r
439 };\r
440 \r
441 // Retriever (temporary role):\r
442 void havocbot_role_ctf_retriever()\r
443 {\r
444         local entity mf;\r
445 \r
446         if(self.deadflag != DEAD_NO)
447         {\r
448                 havocbot_ctf_reset_role(self);\r
449                 return;\r
450         }\r
451 \r
452         if (self.flagcarried)\r
453         {\r
454                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);\r
455                 return;\r
456         }\r
457 \r
458         // If flag is back on the base switch to previous role\r
459         mf = havocbot_ctf_find_flag(self);
460         if(mf.cnt==FLAG_BASE)\r
461         {\r
462                 havocbot_ctf_reset_role(self);\r
463                 return;\r
464         }\r
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 \r
475         if (self.bot_strategytime < time)\r
476         {\r
477                 local float radius;\r
478                 radius = 10000;\r
479 \r
480                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
481                 navigation_goalrating_start();
482                 havocbot_goalrating_ctf_ourstolenflag(50000);
483                 havocbot_goalrating_ctf_droppedflags(40000, self.origin, radius);
484                 havocbot_goalrating_ctf_enemybase(30000);
485                 havocbot_goalrating_items(500, self.origin, radius);\r
486                 navigation_goalrating_end();
487         }\r
488 };\r
489 \r
490 void havocbot_role_ctf_middle()\r
491 {\r
492         local entity mf;\r
493 \r
494         if(self.deadflag != DEAD_NO)
495         {\r
496                 havocbot_ctf_reset_role(self);\r
497                 return;\r
498         }\r
499 \r
500         if (self.flagcarried)\r
501         {\r
502                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);\r
503                 return;\r
504         }\r
505 \r
506         mf = havocbot_ctf_find_flag(self);\r
507         if(mf.cnt!=FLAG_BASE)\r
508         {\r
509                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);\r
510                 return;\r
511         }\r
512 \r
513         if (!self.havocbot_role_timeout)\r
514                 self.havocbot_role_timeout = time + 10;\r
515 \r
516         if (time > self.havocbot_role_timeout)\r
517         {\r
518                 havocbot_ctf_reset_role(self);\r
519                 return;\r
520         }\r
521 \r
522         if (self.bot_strategytime < time)\r
523         {
524                 local vector org;
525
526                 org = havocbot_ctf_middlepoint;
527                 org_z = self.origin_z;
528 \r
529                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");\r
530                 navigation_goalrating_start();\r
531                 havocbot_goalrating_ctf_ourstolenflag(50000);\r
532                 havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000);\r
533                 havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5);\r
534                 havocbot_goalrating_items(5000, org, havocbot_ctf_middlepoint_radius * 0.5);
535                 havocbot_goalrating_items(2500, self.origin, 10000);
536                 havocbot_goalrating_ctf_enemybase(2500);
537                 navigation_goalrating_end();\r
538         }\r
539 };\r
540 \r
541 void havocbot_role_ctf_defense()\r
542 {\r
543         local entity mf;\r
544 \r
545         if(self.deadflag != DEAD_NO)
546         {\r
547                 havocbot_ctf_reset_role(self);\r
548                 return;\r
549         }\r
550 \r
551         if (self.flagcarried)\r
552         {\r
553                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);\r
554                 return;\r
555         }\r
556 \r
557         // If own flag was captured\r
558         mf = havocbot_ctf_find_flag(self);\r
559         if(mf.cnt!=FLAG_BASE)\r
560         {\r
561                 havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);\r
562                 return;\r
563         }\r
564 \r
565         if (!self.havocbot_role_timeout)\r
566                 self.havocbot_role_timeout = time + 30;\r
567 \r
568         if (time > self.havocbot_role_timeout)\r
569         {\r
570                 havocbot_ctf_reset_role(self);\r
571                 return;\r
572         }\r
573         if (self.bot_strategytime < time)\r
574         {\r
575                 local float radius;
576                 local vector org;\r
577 \r
578                 org = mf.dropped_origin;
579                 radius = havocbot_ctf_middlepoint_radius;\r
580 \r
581                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");\r
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 \r
607                 havocbot_goalrating_ctf_ourstolenflag(20000);\r
608                 havocbot_goalrating_ctf_droppedflags(20000, org, radius);\r
609                 havocbot_goalrating_enemyplayers(15000, org, radius);
610                 havocbot_goalrating_items(10000, org, radius);
611                 havocbot_goalrating_items(5000, self.origin, 10000);\r
612                 navigation_goalrating_end();\r
613         }\r
614 };\r
615 \r
616 void havocbot_calculate_middlepoint()\r
617 {\r
618         entity f;\r
619         vector p1, p2;\r
620 \r
621         f = ctf_worldflaglist;\r
622         while (f)\r
623         {\r
624                 if(p1)\r
625                         p2 = f.origin;\r
626                 else\r
627                         p1 = f.origin;\r
628
629                 f = f.ctf_worldflagnext;\r
630         }\r
631         havocbot_ctf_middlepoint = p1 + ((p2-p1) * 0.5);
632         havocbot_ctf_middlepoint_radius  = vlen(p2-p1) * 0.5;\r
633 };\r
634 \r
635 void havocbot_ctf_reset_role(entity bot)\r
636 {\r
637         local float cdefense, cmiddle, coffense;\r
638         local entity mf, ef, head;
639         local float c;\r
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\r
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);\r
655         ef = havocbot_ctf_find_enemy_flag(bot);\r
656 \r
657         // Retrieve stolen flag\r
658         if(mf.cnt!=FLAG_BASE)\r
659         {\r
660                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER);\r
661                 return;\r
662         }\r
663 \r
664         // If enemy flag is taken go to the middle to intercept pursuers\r
665         if(ef.cnt!=FLAG_BASE)\r
666         {\r
667                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);\r
668                 return;\r
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 \r
683         // Evaluate best position to take\r
684         // Count mates on middle position\r
685         cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5);\r
686 \r
687         // Count mates on defense position\r
688         cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5);\r
689 \r
690         // Count mates on offense position\r
691         coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius);\r
692 \r
693         if(cdefense<=coffense)\r
694                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE);\r
695         else if(coffense<=cmiddle)\r
696                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);\r
697         else\r
698                 havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);\r
699 };\r
700 \r
701 void havocbot_chooserole_ctf()\r
702 {\r
703         havocbot_ctf_reset_role(self);\r
704 };