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