]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/havocbot_roles.qc
Make multiple turrets a bit less of a cpuhog by spreading the thinks over different...
[divverent/nexuiz.git] / data / qcsrc / server / havocbot_roles.qc
1
2 .float havocbot_role_timeout;
3 .void() havocbot_previous_role;
4 .void() havocbot_role;
5 float bot_ignore_bots;
6
7 .float max_armorvalue;
8
9 void havocbot_goalrating_items(float ratingscale, vector org, float sradius)
10 {
11         local entity head;
12         local entity player;
13         local float rating, d, discard, distance, friend_distance, enemy_distance;
14         ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
15         head = findchainfloat(bot_pickup, TRUE);
16
17         while (head)
18         {
19                 distance = vlen(head.origin - org);
20                 friend_distance = 10000; enemy_distance = 10000;
21                 rating = 0;
22
23                 if(!head.solid || distance > sradius || (head == self.ignoregoal && time < self.ignoregoaltime) )
24                 {
25                         head = head.chain;
26                         continue;
27                 }
28
29                 // Check if the item can be picked up safely
30                 if(head.classname == "droppedweapon")
31                 {
32                         traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
33
34                         d = pointcontents(trace_endpos + '0 0 1');
35                         if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
36                         {
37                                 head = head.chain;
38                                 continue;
39                         }
40                         if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
41                         {
42                                 head = head.chain;
43                                 continue;
44                         }
45                 }
46                 else
47                 {
48                         // Ignore items under water
49                         traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
50                         if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
51                         {
52                                 head = head.chain;
53                                 continue;
54                         }
55                 }
56
57                 if(teams_matter)
58                 {
59                         discard = FALSE;
60
61                         FOR_EACH_PLAYER(player)
62                         {
63
64                                 if ( self == player || player.deadflag )
65                                         continue;
66
67                                 d = vlen(player.origin - head.origin); // distance between player and item
68
69                                 if ( player.team == self.team )
70                                 {
71                                         if ( clienttype(player) != CLIENTTYPE_REAL || discard )
72                                                 continue;
73
74                                         if( d > friend_distance)
75                                                 continue;
76
77                                         friend_distance = d;
78
79                                         discard = TRUE;
80
81                                         if( head.health && player.health > self.health )
82                                                 continue;
83
84                                         if( head.armorvalue && player.armorvalue > self.armorvalue)
85                                                 continue;
86
87                                         if( head.weapons )
88                                         if( (player.weapons & head.weapons) != head.weapons)
89                                                 continue;
90
91                                         if (head.ammo_shells && player.ammo_shells > self.ammo_shells)
92                                                 continue;
93
94                                         if (head.ammo_nails && player.ammo_nails > self.ammo_nails)
95                                                 continue;
96
97                                         if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets)
98                                                 continue;
99
100                                         if (head.ammo_cells && player.ammo_cells > self.ammo_cells )
101                                                 continue;
102
103                                         discard = FALSE;
104                                 }
105                                 else
106                                 {
107                                         // If enemy only track distances
108                                         // TODO: track only if visible ?
109                                         if( d < enemy_distance )
110                                                 enemy_distance = d;
111                                 }
112                         }
113
114                         // Rate the item only if no one needs it, or if an enemy is closer to it
115                         if ( (enemy_distance < friend_distance && distance < enemy_distance) ||
116                                 (friend_distance > cvar("bot_ai_friends_aware_pickup_radius") ) || !discard )
117                                 rating = head.bot_pickupevalfunc(self, head);
118
119                 }
120                 else
121                         rating = head.bot_pickupevalfunc(self, head);
122
123                 if(rating > 0)
124                         navigation_routerating(head, rating * ratingscale, 2000);
125                 head = head.chain;
126         }
127 };
128
129 void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius)
130 {
131         local entity head;
132         head = findchain(classname, "dom_controlpoint");
133         while (head)
134         {
135                 if (vlen(head.origin - org) < sradius)
136                 {
137                         if(head.cnt > -1) // this is just being fought for
138                                 navigation_routerating(head, ratingscale, 5000);
139                         else if(head.goalentity.cnt == 0) // unclaimed point
140                                 navigation_routerating(head, ratingscale * 0.5, 5000);
141                         else if(head.goalentity.team != self.team) // other team's point
142                                 navigation_routerating(head, ratingscale * 0.2, 5000);
143                 }
144                 head = head.chain;
145         }
146 };
147
148 /*
149 // LordHavoc: this function was already unused, but for waypoints to be a
150 // useful goal the bots would have to seek out the least-recently-visited
151 // ones, not the closest
152 void havocbot_goalrating_waypoints(float ratingscale, vector org, float sradius)
153 {
154         local entity head;
155         head = findchain(classname, "waypoint");
156         while (head)
157         {
158                 if (vlen(head.origin - org) < sradius && vlen(head.origin - org) > 100)
159                         navigation_routerating(head, ratingscale, 2000);
160                 head = head.chain;
161         }
162 };
163 */
164
165 void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius)
166 {
167         local entity head;
168         local float t, noteam, distance;
169         noteam = ((self.team == 0) || !teams_matter); // fteqcc sucks
170
171         if (cvar("bot_nofire"))
172                 return;
173
174         // don't chase players if we're under water
175         if(self.waterlevel>WATERLEVEL_WETFEET)
176                 return;
177
178         FOR_EACH_PLAYER(head)
179         {
180                 // TODO: Merge this logic with the bot_shouldattack function
181                 if (self != head)
182                 if (head.health > 0)
183                 if ((noteam && (!bot_ignore_bots || clienttype(head) == CLIENTTYPE_REAL)) || head.team != self.team)
184                 {
185                         distance = vlen(head.origin - org);
186                         if (distance < 100 || distance > sradius)
187                                 continue;
188
189                         if(g_minstagib)
190                         if(head.items & IT_STRENGTH)
191                                 continue;
192
193                         // rate only visible enemies
194                         /*
195                         traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
196                         if (trace_fraction < 1 || trace_ent != head)
197                                 continue;
198                         */
199
200                         if(head.flags & FL_INWATER || head.flags & FL_PARTIALGROUND)
201                                 continue;
202
203                         // not falling
204                         if(head.flags & FL_ONGROUND == 0)
205                         {
206                                 traceline(head.origin, head.origin + '0 0 -1500', TRUE, world);
207                                 t = pointcontents(trace_endpos + '0 0 1');
208                                 if( t != CONTENT_SOLID )
209                                 if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
210                                         continue;
211                                 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
212                                         continue;
213                         }
214
215                         t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
216                         navigation_routerating(head, t * ratingscale, 2000);
217                 }
218         }
219 };
220
221 // choose a role according to the situation
222 void() havocbot_role_dm;
223
224 //DOM:
225 //go to best items, or control points you don't own
226 void havocbot_role_dom()
227 {
228         if(self.deadflag != DEAD_NO)
229                 return;
230
231         if (self.bot_strategytime < time)
232         {
233                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
234                 navigation_goalrating_start();
235                 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
236                 havocbot_goalrating_items(8000, self.origin, 8000);
237                 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
238                 //havocbot_goalrating_waypoints(1, self.origin, 1000);
239                 navigation_goalrating_end();
240         }
241 };
242
243 //DM:
244 //go to best items
245 void havocbot_role_dm()
246 {
247         if(self.deadflag != DEAD_NO)
248                 return;
249
250         if (self.bot_strategytime < time)
251         {
252                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
253                 navigation_goalrating_start();
254                 havocbot_goalrating_items(10000, self.origin, 10000);
255                 havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
256                 //havocbot_goalrating_waypoints(1, self.origin, 1000);
257                 navigation_goalrating_end();
258         }
259 };
260
261 //Race:
262 //go to next checkpoint, and annoy enemies
263 .float race_checkpoint;
264 void havocbot_role_race()
265 {
266         if(self.deadflag != DEAD_NO)
267                 return;
268
269         entity e;
270         if (self.bot_strategytime < time)
271         {
272                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
273                 navigation_goalrating_start();
274                 /*
275                 havocbot_goalrating_items(100, self.origin, 10000);
276                 havocbot_goalrating_enemyplayers(500, self.origin, 20000);
277                 */
278
279                 for(e = world; (e = find(e, classname, "trigger_race_checkpoint")) != world; )
280                 {
281                         if(e.cnt == self.race_checkpoint)
282                         {
283                                 navigation_routerating(e, 1000000, 5000);
284                         }
285                         else if(self.race_checkpoint == -1)
286                         {
287                                 navigation_routerating(e, 1000000, 5000);
288                         }
289                 }
290
291                 navigation_goalrating_end();
292         }
293 };
294
295 void havocbot_chooserole_dm()
296 {
297         self.havocbot_role = havocbot_role_dm;
298 };
299
300 void havocbot_chooserole_race()
301 {
302         self.havocbot_role = havocbot_role_race;
303 };
304
305 void havocbot_chooserole_dom()
306 {
307         self.havocbot_role = havocbot_role_dom;
308 };
309
310
311
312
313
314
315 entity kh_worldkeylist;
316 .entity kh_worldkeynext;
317 void havocbot_goalrating_kh(float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy)
318 {
319         local entity head;
320         for (head = kh_worldkeylist; head; head = head.kh_worldkeynext)
321         {
322                 if(head.owner == self)
323                         continue;
324                 if(!kh_tracking_enabled)
325                 {
326                         // if it's carried by our team we know about it
327                         // otherwise we have to see it to know about it
328                         if(!head.owner || head.team != self.team)
329                         {
330                                 traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
331                                 if (trace_fraction < 1 && trace_ent != head)
332                                         continue; // skip what I can't see
333                         }
334                 }
335                 if(!head.owner)
336                         navigation_routerating(head, ratingscale_dropped * BOT_PICKUP_RATING_HIGH, 100000);
337                 else if(head.team == self.team)
338                         navigation_routerating(head.owner, ratingscale_team * BOT_PICKUP_RATING_HIGH, 100000);
339                 else
340                         navigation_routerating(head.owner, ratingscale_enemy * BOT_PICKUP_RATING_HIGH, 100000);
341         }
342
343         havocbot_goalrating_items(1, self.origin, 10000);
344 };
345
346 void() havocbot_role_kh_carrier;
347 void() havocbot_role_kh_defense;
348 void() havocbot_role_kh_offense;
349 void() havocbot_role_kh_freelancer;
350 void havocbot_role_kh_carrier()
351 {
352         if(self.deadflag != DEAD_NO)
353                 return;
354
355         if (!(self.kh_next))
356         {
357                 dprint("changing role to freelancer\n");
358                 self.havocbot_role = havocbot_role_kh_freelancer;
359                 self.havocbot_role_timeout = 0;
360                 return;
361         }
362
363         if (self.bot_strategytime < time)
364         {
365                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
366                 navigation_goalrating_start();
367
368                 if(kh_Key_AllOwnedByWhichTeam() == self.team)
369                         havocbot_goalrating_kh(10, 0.1, 0.1); // bring home
370                 else
371                         havocbot_goalrating_kh(4, 4, 1); // play defensively
372
373                 navigation_goalrating_end();
374         }
375 }
376
377 void havocbot_role_kh_defense()
378 {
379         if(self.deadflag != DEAD_NO)
380                 return;
381
382         if (self.kh_next)
383         {
384                 dprint("changing role to carrier\n");
385                 self.havocbot_role = havocbot_role_kh_carrier;
386                 self.havocbot_role_timeout = 0;
387                 return;
388         }
389
390         if (!self.havocbot_role_timeout)
391                 self.havocbot_role_timeout = time + random() * 10 + 20;
392         if (time > self.havocbot_role_timeout)
393         {
394                 dprint("changing role to freelancer\n");
395                 self.havocbot_role = havocbot_role_kh_freelancer;
396                 self.havocbot_role_timeout = 0;
397                 return;
398         }
399
400         if (self.bot_strategytime < time)
401         {
402                 float key_owner_team;
403                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
404                 navigation_goalrating_start();
405
406                 key_owner_team = kh_Key_AllOwnedByWhichTeam();
407                 if(key_owner_team == self.team)
408                         havocbot_goalrating_kh(10, 0.1, 0.1); // defend key carriers
409                 else if(key_owner_team == -1)
410                         havocbot_goalrating_kh(4, 1, 0.1); // play defensively
411                 else
412                         havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY
413
414                 navigation_goalrating_end();
415         }
416 };
417
418 void havocbot_role_kh_offense()
419 {
420         if(self.deadflag != DEAD_NO)
421                 return;
422
423         if (self.kh_next)
424         {
425                 dprint("changing role to carrier\n");
426                 self.havocbot_role = havocbot_role_kh_carrier;
427                 self.havocbot_role_timeout = 0;
428                 return;
429         }
430
431         if (!self.havocbot_role_timeout)
432                 self.havocbot_role_timeout = time + random() * 10 + 20;
433         if (time > self.havocbot_role_timeout)
434         {
435                 dprint("changing role to freelancer\n");
436                 self.havocbot_role = havocbot_role_kh_freelancer;
437                 self.havocbot_role_timeout = 0;
438                 return;
439         }
440
441         if (self.bot_strategytime < time)
442         {
443                 float key_owner_team;
444
445                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
446                 navigation_goalrating_start();
447
448                 key_owner_team = kh_Key_AllOwnedByWhichTeam();
449                 if(key_owner_team == self.team)
450                         havocbot_goalrating_kh(10, 0.1, 0.1); // defend anyway
451                 else if(key_owner_team == -1)
452                         havocbot_goalrating_kh(0.1, 1, 4); // play offensively
453                 else
454                         havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK! EMERGENCY!
455
456                 navigation_goalrating_end();
457         }
458 };
459
460 void havocbot_role_kh_freelancer()
461 {
462         if(self.deadflag != DEAD_NO)
463                 return;
464
465         if (self.kh_next)
466         {
467                 dprint("changing role to carrier\n");
468                 self.havocbot_role = havocbot_role_kh_carrier;
469                 self.havocbot_role_timeout = 0;
470                 return;
471         }
472
473         if (!self.havocbot_role_timeout)
474                 self.havocbot_role_timeout = time + random() * 10 + 10;
475         if (time > self.havocbot_role_timeout)
476         {
477                 if (random() < 0.5)
478                 {
479                         dprint("changing role to offense\n");
480                         self.havocbot_role = havocbot_role_kh_offense;
481                 }
482                 else
483                 {
484                         dprint("changing role to defense\n");
485                         self.havocbot_role = havocbot_role_kh_defense;
486                 }
487                 self.havocbot_role_timeout = 0;
488                 return;
489         }
490
491         if (self.bot_strategytime < time)
492         {
493                 float key_owner_team;
494
495                 self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
496                 navigation_goalrating_start();
497
498                 key_owner_team = kh_Key_AllOwnedByWhichTeam();
499                 if(key_owner_team == self.team)
500                         havocbot_goalrating_kh(10, 0.1, 0.1); // defend anyway
501                 else if(key_owner_team == -1)
502                         havocbot_goalrating_kh(1, 10, 4); // prefer dropped keys
503                 else
504                         havocbot_goalrating_kh(0.1, 0.1, 10); // ATTACK ANYWAY
505
506                 navigation_goalrating_end();
507         }
508 };
509
510 void havocbot_chooserole_kh()
511 {
512         local float r;
513
514         if(self.deadflag != DEAD_NO)
515                 return;
516
517         r = random() * 3;
518         if (r < 1)
519                 self.havocbot_role = havocbot_role_kh_offense;
520         else if (r < 2)
521                 self.havocbot_role = havocbot_role_kh_defense;
522         else
523                 self.havocbot_role = havocbot_role_kh_freelancer;
524 };
525
526 void havocbot_chooserole()
527 {
528         dprint("choosing a role...\n");
529         navigation_clearroute();
530         self.bot_strategytime = 0;
531         if (g_ctf)
532                 havocbot_chooserole_ctf();
533         else if (g_domination)
534                 havocbot_chooserole_dom();
535         else if (g_keyhunt)
536                 havocbot_chooserole_kh();
537         else if (g_race || g_cts)
538                 havocbot_chooserole_race();
539         else if (g_onslaught)
540                 havocbot_chooserole_ons();
541         else // assume anything else is deathmatch
542                 havocbot_chooserole_dm();
543 };
544