game mode "keyhunt", still in testing
[divverent/nexuiz.git] / data / qcsrc / server / havocbot_roles.qc
1
2 .float havocbot_role_timeout;
3 .void() havocbot_previous_role;
4 .float bot_strategytime;
5 .void() havocbot_role;
6 float bot_ignore_bots;
7
8 float(entity e) canreach =
9 {
10         return vlen(self.origin - e.origin) < 1500;
11 }
12
13 void(float ratingscale, vector org, float sradius) havocbot_goalrating_items =
14 {
15         local entity head;
16         local float t;
17         head = findchainfloat(bot_pickup, TRUE);
18         while (head)
19         {
20                 if (head.solid) // must be possible to pick up (respawning items don't count)
21                 if (vlen(head.origin - org) < sradius)
22                 {
23                         // debugging
24                         //if (!head.bot_pickupevalfunc || head.model == "")
25                         //      eprint(head);
26                         // get the value of the item
27                         t = head.bot_pickupevalfunc(self, head) * 0.0001;
28                         if (t > 0)
29                                 navigation_routerating(head, t * ratingscale);
30                 }
31                 head = head.chain;
32         }
33 };
34
35 void(float ratingscale, vector org, float sradius) havocbot_goalrating_controlpoints =
36 {
37         local entity head;
38         head = findchain(classname, "dom_controlpoint");
39         while (head)
40         {
41                 if (vlen(head.origin - org) < sradius)
42                 {
43                         if(head.cnt > -1) // this is just being fought for
44                                 navigation_routerating(head, ratingscale);
45                         else if(head.goalentity.cnt == 0) // unclaimed point
46                                 navigation_routerating(head, ratingscale * 0.5);
47                         else if(head.goalentity.team != self.team) // other team's point
48                                 navigation_routerating(head, ratingscale * 0.2);
49                 }
50                 head = head.chain;
51         }
52 };
53
54 void(float ratingscale, vector org, float sradius) havocbot_goalrating_waypoints =
55 {
56         local entity head;
57         head = findchain(classname, "waypoint");
58         while (head)
59         {
60                 if (vlen(head.origin - org) < sradius && vlen(head.origin - org) > 100)
61                         navigation_routerating(head, ratingscale);
62                 head = head.chain;
63         }
64 };
65
66 void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers =
67 {
68         local entity head;
69         local float t, noteam;
70         ratingscale = ratingscale * 1200;
71         noteam = ((self.team == 0) || (teamplay == 0)); // fteqcc sucks
72         //dprint("teamplay is "); dprint(ftos(teamplay)); dprint(", own team is ");
73         //dprint(ftos(self.team)); dprint(" -> noteam is "); dprint(ftos(noteam));
74         //dprint("\n");
75
76         FOR_EACH_PLAYER(head)
77         {
78                 if (self != head)
79                 if (head.health > 0)
80                 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
81                 if (vlen(head.origin - org) < sradius)
82                 {
83                         t = head.frags + 25;
84                         if (t < 1)
85                                 t = 1;
86                         t = t / (head.health + head.armortype * head.armorvalue);
87                         if (t > 0)
88                         {
89                                 //dprint("found: "); dprint(head.netname); dprint("\n");
90                                 navigation_routerating(head, t * ratingscale);
91                         }
92                 }
93         }
94 };
95
96
97 void() havocbot_role_ctf_middle;
98 void() havocbot_role_ctf_defense;
99 void() havocbot_role_ctf_offense;
100 void() havocbot_role_ctf_interceptor;
101
102 void(float ratingscale, vector org, float sradius) havocbot_goalrating_ctf_carrieritems =
103 {
104         local entity head;
105         local float t;
106         head = findchainfloat(bot_pickup, TRUE);
107         while (head)
108         {
109                 // look for health and armor only
110                 if (head.solid) // must be possible to pick up (respawning items don't count)
111                 if (head.health || head.armorvalue)
112                 if (vlen(head.origin - org) < sradius)
113                 {
114                         // debugging
115                         //if (!head.bot_pickupevalfunc || head.model == "")
116                         //      eprint(head);
117                         // get the value of the item
118                         t = head.bot_pickupevalfunc(self, head) * 0.0001;
119                         if (t > 0)
120                                 navigation_routerating(head, t * ratingscale);
121                 }
122                 head = head.chain;
123         }
124 };
125
126 void(float ratingscale) havocbot_goalrating_ctf_ourflag =
127 {
128         local entity head;
129         if (self.team == 5) // red
130                 head = find(world, classname, "item_flag_team1"); // red flag
131         else // blue
132                 head = find(world, classname, "item_flag_team2"); // blue flag
133         navigation_routerating(head, ratingscale);
134 };
135
136 void(float ratingscale) havocbot_goalrating_ctf_enemyflag =
137 {
138         local entity head;
139         if (self.team == 5) // red
140                 head = find(world, classname, "item_flag_team2"); // blue flag
141         else // blue
142                 head = find(world, classname, "item_flag_team1"); // red flag
143         navigation_routerating(head, ratingscale);
144 };
145
146 void(float ratingscale) havocbot_goalrating_ctf_enemybase =
147 {
148         // div0: needs a change in the CTF code
149 };
150
151 void(float ratingscale) havocbot_goalrating_ctf_ourstolenflag =
152 {
153         local entity head;
154         if (self.team == 5) // red
155                 head = find(world, classname, "item_flag_team1"); // red flag
156         else // blue
157                 head = find(world, classname, "item_flag_team2"); // blue flag
158         if (head.cnt != FLAG_BASE)
159                 navigation_routerating(head, ratingscale);
160 };
161
162 void(float ratingscale) havocbot_goalrating_ctf_droppedflags =
163 {
164         local entity redflag, blueflag;
165
166         redflag = find(world, classname, "item_flag_team1");
167         blueflag = find(world, classname, "item_flag_team2");
168
169         if (redflag == world)
170                 error("havocbot: item_flag_team1 missing\n");
171         if (blueflag == world)
172                 error("havocbot: item_flag_team2 missing\n");
173
174         if (redflag.cnt != FLAG_BASE) // red flag is carried or out in the field
175                 navigation_routerating(redflag, ratingscale);
176         if (blueflag.cnt != FLAG_BASE) // blue flag is carried or out in the field
177                 navigation_routerating(blueflag, ratingscale);
178 };
179
180 // CTF: (always teamplay)
181
182 //role rogue: (is this used?)
183 //pick up items and dropped flags (with big rating boost to dropped flags)
184 void() havocbot_role_ctf_rogue =
185 {
186         if (self.bot_strategytime < time)
187         {
188                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
189                 navigation_goalrating_start();
190                 havocbot_goalrating_ctf_droppedflags(5000);
191                 //havocbot_goalrating_enemyplayers(3000, self.origin, 3000);
192                 havocbot_goalrating_items(10000, self.origin, 10000);
193                 navigation_goalrating_end();
194         }
195 }
196
197 //role flag carrier:
198 //pick up armor and health
199 //go to our flag spot
200 void() havocbot_role_ctf_carrier =
201 {
202         if (self.flagcarried == world)
203         {
204                 dprint("changing role to middle\n");
205                 self.havocbot_role = havocbot_role_ctf_middle;
206                 self.havocbot_role_timeout = 0;
207                 return;
208         }
209         if (self.bot_strategytime < time)
210         {
211                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
212                 navigation_goalrating_start();
213                 havocbot_goalrating_ctf_ourflag(5000);
214                 havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
215                 navigation_goalrating_end();
216         }
217 };
218
219 //role offense:
220 //pick up armor and health
221 //if rockets < 25 || health < 100, change role to middle
222 //if carrying flag, change role to flag carrier
223 //if our flag taken, change role to interceptor
224 //(60-90 second timer) change role to middle
225 //go to enemy flag
226 void() havocbot_role_ctf_offense =
227 {
228         local entity f;
229         if (self.flagcarried)
230         {
231                 dprint("changing role to carrier\n");
232                 self.havocbot_role = havocbot_role_ctf_carrier;
233                 self.havocbot_role_timeout = 0;
234                 return;
235         }
236         // check our flag
237         if (self.team == 5) // red
238                 f = find(world, classname, "item_flag_team1");
239         else // blue
240                 f = find(world, classname, "item_flag_team2");
241         if (f.cnt != FLAG_BASE && canreach(f))
242         {
243                 dprint("changing role to interceptor\n");
244                 self.havocbot_previous_role = self.havocbot_role;
245                 self.havocbot_role = havocbot_role_ctf_interceptor;
246                 self.havocbot_role_timeout = 0;
247                 return;
248         }
249         if (!self.havocbot_role_timeout)
250                 self.havocbot_role_timeout = time + random() * 30 + 60;
251         if (self.ammo_rockets < 15 || time > self.havocbot_role_timeout)
252         {
253                 dprint("changing role to middle\n");
254                 self.havocbot_role = havocbot_role_ctf_middle;
255                 self.havocbot_role_timeout = 0;
256                 return;
257         }
258         if (self.bot_strategytime < time)
259         {
260                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
261                 navigation_goalrating_start();
262                 havocbot_goalrating_ctf_ourstolenflag(5000);
263                 havocbot_goalrating_ctf_enemyflag(3000);
264                 havocbot_goalrating_ctf_enemybase(2000);
265                 havocbot_goalrating_items(10000, self.origin, 10000);
266                 navigation_goalrating_end();
267         }
268 };
269
270 //role interceptor (temporary role):
271 //pick up items
272 //if carrying flag, change role to flag carrier
273 //if our flag is back, change role to previous role
274 //follow our flag
275 //go to least recently visited area
276 void() havocbot_role_ctf_interceptor =
277 {
278         local entity f;
279         if (self.flagcarried)
280         {
281                 dprint("changing role to carrier\n");
282                 self.havocbot_role = havocbot_role_ctf_carrier;
283                 self.havocbot_role_timeout = 0;
284                 return;
285         }
286         // check our flag
287         if (self.team == 5) // red
288                 f = find(world, classname, "item_flag_team1");
289         else // blue
290                 f = find(world, classname, "item_flag_team2");
291         if (f.cnt == FLAG_BASE)
292         {
293                 dprint("changing role back\n");
294                 self.havocbot_role = self.havocbot_previous_role;
295                 self.havocbot_role_timeout = 0;
296                 return;
297         }
298
299         if (self.bot_strategytime < time)
300         {
301                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
302                 navigation_goalrating_start();
303                 havocbot_goalrating_ctf_ourstolenflag(5000);
304                 havocbot_goalrating_ctf_droppedflags(5000);
305                 havocbot_goalrating_items(10000, self.origin, 10000);
306                 navigation_goalrating_end();
307         }
308 };
309
310 //role middle:
311 //pick up items
312 //if carrying flag, change role to flag carrier
313 //if our flag taken, change role to interceptor
314 //if see flag (of either team) follow it (this has many implications)
315 //(10-20 second timer) change role to defense or offense
316 //go to least recently visited area
317 void() havocbot_role_ctf_middle =
318 {
319         local entity f;
320         if (self.flagcarried)
321         {
322                 dprint("changing role to carrier\n");
323                 self.havocbot_role = havocbot_role_ctf_carrier;
324                 self.havocbot_role_timeout = 0;
325                 return;
326         }
327         // check our flag
328         if (self.team == 5) // red
329                 f = find(world, classname, "item_flag_team1");
330         else // blue
331                 f = find(world, classname, "item_flag_team2");
332         if (f.cnt != FLAG_BASE && canreach(f))
333         {
334                 dprint("changing role to interceptor\n");
335                 self.havocbot_previous_role = self.havocbot_role;
336                 self.havocbot_role = havocbot_role_ctf_interceptor;
337                 self.havocbot_role_timeout = 0;
338                 return;
339         }
340         if (!self.havocbot_role_timeout)
341                 self.havocbot_role_timeout = time + random() * 10 + 10;
342         if (time > self.havocbot_role_timeout)
343         if (self.ammo_rockets >= 25)
344         {
345                 if (random() < 0.5)
346                 {
347                         dprint("changing role to offense\n");
348                         self.havocbot_role = havocbot_role_ctf_offense;
349                 }
350                 else
351                 {
352                         dprint("changing role to defense\n");
353                         self.havocbot_role = havocbot_role_ctf_defense;
354                 }
355                 self.havocbot_role_timeout = 0;
356                 return;
357         }
358
359         if (self.bot_strategytime < time)
360         {
361                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
362                 navigation_goalrating_start();
363                 havocbot_goalrating_ctf_ourstolenflag(5000);
364                 havocbot_goalrating_ctf_droppedflags(3000);
365                 //havocbot_goalrating_enemyplayers(1000, self.origin, 1000);
366                 havocbot_goalrating_items(10000, self.origin, 10000);
367                 navigation_goalrating_end();
368         }
369 };
370
371 //role defense:
372 //if rockets < 25 || health < 100, change role to middle
373 //if carrying flag, change role to flag carrier
374 //if our flag taken, change role to interceptor
375 //(30-50 second timer) change role to middle
376 //move to nearest unclaimed defense spot
377 void() havocbot_role_ctf_defense =
378 {
379         local entity f;
380         if (self.flagcarried)
381         {
382                 dprint("changing role to carrier\n");
383                 self.havocbot_role = havocbot_role_ctf_carrier;
384                 self.havocbot_role_timeout = 0;
385                 return;
386         }
387         // check our flag
388         if (self.team == 5) // red
389                 f = find(world, classname, "item_flag_team1");
390         else // blue
391                 f = find(world, classname, "item_flag_team2");
392         if (f.cnt != FLAG_BASE && canreach(f))
393         {
394                 dprint("changing role to interceptor\n");
395                 self.havocbot_previous_role = self.havocbot_role;
396                 self.havocbot_role = havocbot_role_ctf_interceptor;
397                 self.havocbot_role_timeout = 0;
398                 return;
399         }
400         if (!self.havocbot_role_timeout)
401                 self.havocbot_role_timeout = time + random() * 20 + 30;
402         if (self.ammo_rockets < 15 || time > self.havocbot_role_timeout)
403         {
404                 dprint("changing role to middle\n");
405                 self.havocbot_role = havocbot_role_ctf_middle;
406                 self.havocbot_role_timeout = 0;
407                 return;
408         }
409         if (self.bot_strategytime < time)
410         {
411                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
412                 navigation_goalrating_start();
413                 havocbot_goalrating_ctf_ourstolenflag(20000);
414                 havocbot_goalrating_ctf_droppedflags(500);
415                 havocbot_goalrating_items(10000, f.origin, 10000);
416                 navigation_goalrating_end();
417         }
418         /*
419         // FIXME: place info_ctf_defensepoint entities in CTF maps and use them
420         // change position occasionally
421         if (time > self.bot_strategytime || self.goalentity.classname != "info_ctf_defensepoint")
422         {
423                 self.bot_strategytime = time + random() * 45 + 15;
424                 self.goalentity = world;
425                 head = findchain(classname, "info_ctf_defensepoint");
426                 while (head)
427                 {
428                         if (time > head.count)
429                         {
430                                 self.goalentity = head;
431                                 head.chain = world;
432                         }
433                         head = head.chain;
434                 }
435                 // if there are no defensepoints defined, switch to middle
436                 if (self.goalentity == world)
437                 {
438                         dprint("changing role to middle\n");
439                         self.havocbot_role = havocbot_role_ctf_middle;
440                         self.havocbot_role_timeout = 0;
441                         return;
442                 }
443         }
444         // keep anyone else from taking this spot
445         if (self.goalentity != world)
446                 self.goalentity.count = time + 0.5;
447         */
448 };
449
450 // CTF:
451 // choose a role according to the situation
452 void() havocbot_chooserole_ctf =
453 {
454         local float r;
455         dprint("choose CTF role...\n");
456         if (self.team == 13)
457                 self.havocbot_role = havocbot_role_ctf_rogue;
458         else
459         {
460                 r = random() * 3;
461                 if (r < 1)
462                         self.havocbot_role = havocbot_role_ctf_offense;
463                 else if (r < 2)
464                         self.havocbot_role = havocbot_role_ctf_middle;
465                 else
466                         self.havocbot_role = havocbot_role_ctf_defense;
467         }
468 };
469
470 //DOM:
471 //go to best items, or control points you don't own
472 void() havocbot_role_dom =
473 {
474         if (self.bot_strategytime < time)
475         {
476                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
477                 navigation_goalrating_start();
478                 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
479                 havocbot_goalrating_items(8000, self.origin, 8000);
480                 //havocbot_goalrating_enemyplayers(1, self.origin, 2000);
481                 //havocbot_goalrating_waypoints(1, self.origin, 1000);
482                 navigation_goalrating_end();
483         }
484 };
485
486 //DM:
487 //go to best items
488 void() havocbot_role_dm =
489 {
490         if (self.bot_strategytime < time)
491         {
492                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
493                 navigation_goalrating_start();
494                 havocbot_goalrating_items(10000, self.origin, 10000);
495                 havocbot_goalrating_enemyplayers(1, self.origin, 20000);
496                 //havocbot_goalrating_waypoints(1, self.origin, 1000);
497                 navigation_goalrating_end();
498         }
499 };
500
501 void() havocbot_chooserole_dm =
502 {
503         self.havocbot_role = havocbot_role_dm;
504 };
505
506 void() havocbot_chooserole_dom =
507 {
508         self.havocbot_role = havocbot_role_dom;
509 };
510
511
512
513
514
515
516 void(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy) havocbot_goalrating_kh =
517 {
518         local entity head;
519         for(head = world; (head = find(head, classname, "item_kh_key")); )
520         {
521                 if(head.owner == self)
522                         continue;
523                 if(!head.owner)
524                         navigation_routerating(head, ratingscale_dropped);
525                 else if(head.team == self.team)
526                         navigation_routerating(head, ratingscale_team);
527                 else
528                         navigation_routerating(head, ratingscale_enemy);
529         }
530 };
531
532 void() havocbot_role_kh_carrier;
533 void() havocbot_role_kh_defense;
534 void() havocbot_role_kh_offense;
535 void() havocbot_role_kh_freelancer;
536 void() havocbot_role_kh_carrier =
537 {
538         local entity head;
539         if (!(self.items & IT_KEY1))
540         {
541                 dprint("changing role to freelancer\n");
542                 self.havocbot_role = havocbot_role_kh_freelancer;
543                 self.havocbot_role_timeout = 0;
544                 return;
545         }
546
547         if (self.bot_strategytime < time)
548         {
549                 float enemies_have_keys;
550
551                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
552                 navigation_goalrating_start();
553
554                 enemies_have_keys = FALSE;
555                 for(head = world; (head = find(head, classname, "item_kh_key")); )
556                 {
557                         if(head.owner)
558                         if(head.team != self.team)
559                         {
560                                 enemies_have_keys = TRUE;
561                                 break;
562                         }
563                 }
564
565                 if(enemies_have_keys)
566                         havocbot_goalrating_kh(4000, 4000, 100);
567                 else
568                         havocbot_goalrating_kh(10000, 10000, 1);
569                 havocbot_goalrating_items(10000, self.origin, 10000);
570                 navigation_goalrating_end();
571         }
572 }
573
574 void() havocbot_role_kh_defense =
575 {
576         if (self.items & IT_KEY1)
577         {
578                 dprint("changing role to carrier\n");
579                 self.havocbot_role = havocbot_role_kh_carrier;
580                 self.havocbot_role_timeout = 0;
581                 return;
582         }
583
584         if (!self.havocbot_role_timeout)
585                 self.havocbot_role_timeout = time + random() * 10 + 20;
586         if (time > self.havocbot_role_timeout)
587         {
588                 dprint("changing role to freelancer\n");
589                 self.havocbot_role = havocbot_role_kh_freelancer;
590                 self.havocbot_role_timeout = 0;
591                 return;
592         }
593
594         if (self.bot_strategytime < time)
595         {
596                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
597                 navigation_goalrating_start();
598                 havocbot_goalrating_kh(4000, 1000, 1);
599                 havocbot_goalrating_items(10000, self.origin, 10000);
600                 navigation_goalrating_end();
601         }
602 };
603
604 void() havocbot_role_kh_offense =
605 {
606         if (self.items & IT_KEY1)
607         {
608                 dprint("changing role to carrier\n");
609                 self.havocbot_role = havocbot_role_kh_carrier;
610                 self.havocbot_role_timeout = 0;
611                 return;
612         }
613
614         if (!self.havocbot_role_timeout)
615                 self.havocbot_role_timeout = time + random() * 10 + 20;
616         if (time > self.havocbot_role_timeout)
617         {
618                 dprint("changing role to freelancer\n");
619                 self.havocbot_role = havocbot_role_kh_freelancer;
620                 self.havocbot_role_timeout = 0;
621                 return;
622         }
623
624         if (self.bot_strategytime < time)
625         {
626                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
627                 navigation_goalrating_start();
628                 havocbot_goalrating_kh(1, 1000, 4000);
629                 havocbot_goalrating_items(10000, self.origin, 10000);
630                 navigation_goalrating_end();
631         }
632 };
633
634 void() havocbot_role_kh_freelancer =
635 {
636         if (self.items & IT_KEY1)
637         {
638                 dprint("changing role to carrier\n");
639                 self.havocbot_role = havocbot_role_kh_carrier;
640                 self.havocbot_role_timeout = 0;
641                 return;
642         }
643
644         if (!self.havocbot_role_timeout)
645                 self.havocbot_role_timeout = time + random() * 10 + 10;
646         if (time > self.havocbot_role_timeout)
647         {
648                 if (random() < 0.5)
649                 {
650                         dprint("changing role to offense\n");
651                         self.havocbot_role = havocbot_role_kh_offense;
652                 }
653                 else
654                 {
655                         dprint("changing role to defense\n");
656                         self.havocbot_role = havocbot_role_kh_defense;
657                 }
658                 self.havocbot_role_timeout = 0;
659                 return;
660         }
661
662         if (self.bot_strategytime < time)
663         {
664                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
665                 navigation_goalrating_start();
666                 havocbot_goalrating_kh(1000, 4000, 1000);
667                 havocbot_goalrating_items(10000, self.origin, 10000);
668                 navigation_goalrating_end();
669         }
670 };
671
672
673
674
675
676 void() havocbot_chooserole_kh =
677 {
678         local float r;
679         r = random() * 3;
680         if (r < 1)
681                 self.havocbot_role = havocbot_role_kh_offense;
682         else if (r < 2)
683                 self.havocbot_role = havocbot_role_kh_defense;
684         else
685                 self.havocbot_role = havocbot_role_kh_freelancer;
686 };
687
688 void() havocbot_chooserole =
689 {
690         dprint("choose a role...\n");
691         navigation_routetogoal(world);
692         self.bot_strategytime = -1;
693         if (cvar("g_ctf"))
694                 havocbot_chooserole_ctf();
695         else if (cvar("g_domination"))
696                 havocbot_chooserole_dom();
697         else if (cvar("g_keyhunt"))
698                 havocbot_chooserole_kh();
699         else // assume anything else is deathmatch
700                 havocbot_chooserole_dm();
701 };
702