centerprints now are managed independently per line
[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;
72         head = findchain(classname, "player");
73         while (head)
74         {
75                 if (head.health > 0)
76                 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
77                 if (vlen(head.origin - org) < sradius)
78                 {
79                         t = head.frags + 25;
80                         if (t < 1)
81                                 t = 1;
82                         t = t / (head.health + head.armortype * head.armorvalue);
83                         if (t > 0)
84                                 navigation_routerating(head, t * ratingscale);
85                 }
86                 head = head.chain;
87         }
88 };
89
90
91 void() havocbot_role_ctf_middle;
92 void() havocbot_role_ctf_defense;
93 void() havocbot_role_ctf_offense;
94 void() havocbot_role_ctf_interceptor;
95
96 void(float ratingscale, vector org, float sradius) havocbot_goalrating_ctf_carrieritems =
97 {
98         local entity head;
99         local float t;
100         head = findchainfloat(bot_pickup, TRUE);
101         while (head)
102         {
103                 // look for health and armor only
104                 if (head.solid) // must be possible to pick up (respawning items don't count)
105                 if (head.health || head.armorvalue)
106                 if (vlen(head.origin - org) < sradius)
107                 {
108                         // debugging
109                         //if (!head.bot_pickupevalfunc || head.model == "")
110                         //      eprint(head);
111                         // get the value of the item
112                         t = head.bot_pickupevalfunc(self, head) * 0.0001;
113                         if (t > 0)
114                                 navigation_routerating(head, t * ratingscale);
115                 }
116                 head = head.chain;
117         }
118 };
119
120 void(float ratingscale) havocbot_goalrating_ctf_ourflag =
121 {
122         local entity head;
123         if (self.team == 5) // red
124                 head = find(world, classname, "item_flag_team1"); // red flag
125         else // blue
126                 head = find(world, classname, "item_flag_team2"); // blue flag
127         navigation_routerating(head, ratingscale);
128 };
129
130 void(float ratingscale) havocbot_goalrating_ctf_enemyflag =
131 {
132         local entity head;
133         if (self.team == 5) // red
134                 head = find(world, classname, "item_flag_team2"); // blue flag
135         else // blue
136                 head = find(world, classname, "item_flag_team1"); // red flag
137         navigation_routerating(head, ratingscale);
138 };
139
140 void(float ratingscale) havocbot_goalrating_ctf_enemybase =
141 {
142         // div0: needs a change in the CTF code
143 };
144
145 void(float ratingscale) havocbot_goalrating_ctf_ourstolenflag =
146 {
147         local entity head;
148         if (self.team == 5) // red
149                 head = find(world, classname, "item_flag_team1"); // red flag
150         else // blue
151                 head = find(world, classname, "item_flag_team2"); // blue flag
152         if (head.cnt != FLAG_BASE)
153                 navigation_routerating(head, ratingscale);
154 };
155
156 void(float ratingscale) havocbot_goalrating_ctf_droppedflags =
157 {
158         local entity redflag, blueflag;
159
160         redflag = find(world, classname, "item_flag_team1");
161         blueflag = find(world, classname, "item_flag_team2");
162
163         if (redflag == world)
164                 error("havocbot: item_flag_team1 missing\n");
165         if (blueflag == world)
166                 error("havocbot: item_flag_team2 missing\n");
167
168         if (redflag.cnt != FLAG_BASE) // red flag is carried or out in the field
169                 navigation_routerating(redflag, ratingscale);
170         if (blueflag.cnt != FLAG_BASE) // blue flag is carried or out in the field
171                 navigation_routerating(blueflag, ratingscale);
172 };
173
174 // CTF: (always teamplay)
175
176 //role rogue: (is this used?)
177 //pick up items and dropped flags (with big rating boost to dropped flags)
178 void() havocbot_role_ctf_rogue =
179 {
180         if (self.bot_strategytime < time)
181         {
182                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
183                 navigation_goalrating_start();
184                 havocbot_goalrating_ctf_droppedflags(5000);
185                 //havocbot_goalrating_enemyplayers(3000, self.origin, 3000);
186                 havocbot_goalrating_items(10000, self.origin, 10000);
187                 navigation_goalrating_end();
188         }
189 }
190
191 //role flag carrier:
192 //pick up armor and health
193 //go to our flag spot
194 void() havocbot_role_ctf_carrier =
195 {
196         if (self.flagcarried == world)
197         {
198                 dprint("changing role to middle\n");
199                 self.havocbot_role = havocbot_role_ctf_middle;
200                 self.havocbot_role_timeout = 0;
201                 return;
202         }
203         if (self.bot_strategytime < time)
204         {
205                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
206                 navigation_goalrating_start();
207                 havocbot_goalrating_ctf_ourflag(5000);
208                 havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
209                 navigation_goalrating_end();
210         }
211 };
212
213 //role offense:
214 //pick up armor and health
215 //if rockets < 25 || health < 100, change role to middle
216 //if carrying flag, change role to flag carrier
217 //if our flag taken, change role to interceptor
218 //(60-90 second timer) change role to middle
219 //go to enemy flag
220 void() havocbot_role_ctf_offense =
221 {
222         local entity f;
223         if (self.flagcarried)
224         {
225                 dprint("changing role to carrier\n");
226                 self.havocbot_role = havocbot_role_ctf_carrier;
227                 self.havocbot_role_timeout = 0;
228                 return;
229         }
230         // check our flag
231         if (self.team == 5) // red
232                 f = find(world, classname, "item_flag_team1");
233         else // blue
234                 f = find(world, classname, "item_flag_team2");
235         if (f.cnt != FLAG_BASE && canreach(f))
236         {
237                 dprint("changing role to interceptor\n");
238                 self.havocbot_previous_role = self.havocbot_role;
239                 self.havocbot_role = havocbot_role_ctf_interceptor;
240                 self.havocbot_role_timeout = 0;
241                 return;
242         }
243         if (!self.havocbot_role_timeout)
244                 self.havocbot_role_timeout = time + random() * 30 + 60;
245         if (self.ammo_rockets < 15 || time > self.havocbot_role_timeout)
246         {
247                 dprint("changing role to middle\n");
248                 self.havocbot_role = havocbot_role_ctf_middle;
249                 self.havocbot_role_timeout = 0;
250                 return;
251         }
252         if (self.bot_strategytime < time)
253         {
254                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
255                 navigation_goalrating_start();
256                 havocbot_goalrating_ctf_ourstolenflag(5000);
257                 havocbot_goalrating_ctf_enemyflag(3000);
258                 havocbot_goalrating_ctf_enemybase(2000);
259                 havocbot_goalrating_items(10000, self.origin, 10000);
260                 navigation_goalrating_end();
261         }
262 };
263
264 //role interceptor (temporary role):
265 //pick up items
266 //if carrying flag, change role to flag carrier
267 //if our flag is back, change role to previous role
268 //follow our flag
269 //go to least recently visited area
270 void() havocbot_role_ctf_interceptor =
271 {
272         local entity f;
273         if (self.flagcarried)
274         {
275                 dprint("changing role to carrier\n");
276                 self.havocbot_role = havocbot_role_ctf_carrier;
277                 self.havocbot_role_timeout = 0;
278                 return;
279         }
280         // check our flag
281         if (self.team == 5) // red
282                 f = find(world, classname, "item_flag_team1");
283         else // blue
284                 f = find(world, classname, "item_flag_team2");
285         if (f.cnt == FLAG_BASE)
286         {
287                 dprint("changing role back\n");
288                 self.havocbot_role = self.havocbot_previous_role;
289                 self.havocbot_role_timeout = 0;
290                 return;
291         }
292
293         if (self.bot_strategytime < time)
294         {
295                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
296                 navigation_goalrating_start();
297                 havocbot_goalrating_ctf_ourstolenflag(5000);
298                 havocbot_goalrating_ctf_droppedflags(5000);
299                 havocbot_goalrating_items(10000, self.origin, 10000);
300                 navigation_goalrating_end();
301         }
302 };
303
304 //role middle:
305 //pick up items
306 //if carrying flag, change role to flag carrier
307 //if our flag taken, change role to interceptor
308 //if see flag (of either team) follow it (this has many implications)
309 //(10-20 second timer) change role to defense or offense
310 //go to least recently visited area
311 void() havocbot_role_ctf_middle =
312 {
313         local entity f;
314         if (self.flagcarried)
315         {
316                 dprint("changing role to carrier\n");
317                 self.havocbot_role = havocbot_role_ctf_carrier;
318                 self.havocbot_role_timeout = 0;
319                 return;
320         }
321         // check our flag
322         if (self.team == 5) // red
323                 f = find(world, classname, "item_flag_team1");
324         else // blue
325                 f = find(world, classname, "item_flag_team2");
326         if (f.cnt != FLAG_BASE && canreach(f))
327         {
328                 dprint("changing role to interceptor\n");
329                 self.havocbot_previous_role = self.havocbot_role;
330                 self.havocbot_role = havocbot_role_ctf_interceptor;
331                 self.havocbot_role_timeout = 0;
332                 return;
333         }
334         if (!self.havocbot_role_timeout)
335                 self.havocbot_role_timeout = time + random() * 10 + 10;
336         if (time > self.havocbot_role_timeout)
337         if (self.ammo_rockets >= 25)
338         {
339                 if (random() < 0.5)
340                 {
341                         dprint("changing role to offense\n");
342                         self.havocbot_role = havocbot_role_ctf_offense;
343                 }
344                 else
345                 {
346                         dprint("changing role to defense\n");
347                         self.havocbot_role = havocbot_role_ctf_defense;
348                 }
349                 self.havocbot_role_timeout = 0;
350                 return;
351         }
352
353         if (self.bot_strategytime < time)
354         {
355                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
356                 navigation_goalrating_start();
357                 havocbot_goalrating_ctf_ourstolenflag(5000);
358                 havocbot_goalrating_ctf_droppedflags(3000);
359                 //havocbot_goalrating_enemyplayers(1000, self.origin, 1000);
360                 havocbot_goalrating_items(10000, self.origin, 10000);
361                 navigation_goalrating_end();
362         }
363 };
364
365 //role defense:
366 //if rockets < 25 || health < 100, change role to middle
367 //if carrying flag, change role to flag carrier
368 //if our flag taken, change role to interceptor
369 //(30-50 second timer) change role to middle
370 //move to nearest unclaimed defense spot
371 void() havocbot_role_ctf_defense =
372 {
373         local entity f;
374         if (self.flagcarried)
375         {
376                 dprint("changing role to carrier\n");
377                 self.havocbot_role = havocbot_role_ctf_carrier;
378                 self.havocbot_role_timeout = 0;
379                 return;
380         }
381         // check our flag
382         if (self.team == 5) // red
383                 f = find(world, classname, "item_flag_team1");
384         else // blue
385                 f = find(world, classname, "item_flag_team2");
386         if (f.cnt != FLAG_BASE && canreach(f))
387         {
388                 dprint("changing role to interceptor\n");
389                 self.havocbot_previous_role = self.havocbot_role;
390                 self.havocbot_role = havocbot_role_ctf_interceptor;
391                 self.havocbot_role_timeout = 0;
392                 return;
393         }
394         if (!self.havocbot_role_timeout)
395                 self.havocbot_role_timeout = time + random() * 20 + 30;
396         if (self.ammo_rockets < 15 || time > self.havocbot_role_timeout)
397         {
398                 dprint("changing role to middle\n");
399                 self.havocbot_role = havocbot_role_ctf_middle;
400                 self.havocbot_role_timeout = 0;
401                 return;
402         }
403         if (self.bot_strategytime < time)
404         {
405                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
406                 navigation_goalrating_start();
407                 havocbot_goalrating_ctf_ourstolenflag(20000);
408                 havocbot_goalrating_ctf_droppedflags(500);
409                 havocbot_goalrating_items(10000, f.origin, 10000);
410                 navigation_goalrating_end();
411         }
412         /*
413         // FIXME: place info_ctf_defensepoint entities in CTF maps and use them
414         // change position occasionally
415         if (time > self.bot_strategytime || self.goalentity.classname != "info_ctf_defensepoint")
416         {
417                 self.bot_strategytime = time + random() * 45 + 15;
418                 self.goalentity = world;
419                 head = findchain(classname, "info_ctf_defensepoint");
420                 while (head)
421                 {
422                         if (time > head.count)
423                         {
424                                 self.goalentity = head;
425                                 head.chain = world;
426                         }
427                         head = head.chain;
428                 }
429                 // if there are no defensepoints defined, switch to middle
430                 if (self.goalentity == world)
431                 {
432                         dprint("changing role to middle\n");
433                         self.havocbot_role = havocbot_role_ctf_middle;
434                         self.havocbot_role_timeout = 0;
435                         return;
436                 }
437         }
438         // keep anyone else from taking this spot
439         if (self.goalentity != world)
440                 self.goalentity.count = time + 0.5;
441         */
442 };
443
444 // CTF:
445 // choose a role according to the situation
446 void() havocbot_chooserole_ctf =
447 {
448         local float r;
449         dprint("choose CTF role...\n");
450         if (self.team == 13)
451                 self.havocbot_role = havocbot_role_ctf_rogue;
452         else
453         {
454                 r = random() * 3;
455                 if (r < 1)
456                         self.havocbot_role = havocbot_role_ctf_offense;
457                 else if (r < 2)
458                         self.havocbot_role = havocbot_role_ctf_middle;
459                 else
460                         self.havocbot_role = havocbot_role_ctf_defense;
461         }
462 };
463
464 //DOM:
465 //go to best items, or control points you don't own
466 void() havocbot_role_dom =
467 {
468         if (self.bot_strategytime < time)
469         {
470                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
471                 navigation_goalrating_start();
472                 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
473                 havocbot_goalrating_items(8000, self.origin, 8000);
474                 //havocbot_goalrating_enemyplayers(1, self.origin, 2000);
475                 //havocbot_goalrating_waypoints(1, self.origin, 1000);
476                 navigation_goalrating_end();
477         }
478 };
479
480 //DM:
481 //go to best items
482 void() havocbot_role_dm =
483 {
484         if (self.bot_strategytime < time)
485         {
486                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
487                 navigation_goalrating_start();
488                 havocbot_goalrating_items(10000, self.origin, 10000);
489                 havocbot_goalrating_enemyplayers(1, self.origin, 20000);
490                 //havocbot_goalrating_waypoints(1, self.origin, 1000);
491                 navigation_goalrating_end();
492         }
493 };
494
495 void() havocbot_chooserole_dm =
496 {
497         self.havocbot_role = havocbot_role_dm;
498 };
499
500 void() havocbot_chooserole_dom =
501 {
502         self.havocbot_role = havocbot_role_dom;
503 };
504
505 void() havocbot_chooserole =
506 {
507         dprint("choose a role...\n");
508         navigation_routetogoal(world);
509         self.bot_strategytime = -1;
510         if (cvar("g_ctf"))
511                 havocbot_chooserole_ctf();
512         else if (cvar("g_domination"))
513                 havocbot_chooserole_dom();
514         else // assume anything else is deathmatch
515                 havocbot_chooserole_dm();
516 };
517