]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/gamec/urrebot_ai_main.c
message for players that get moved during team balancing
[divverent/nexuiz.git] / data / qcsrc / server / gamec / urrebot_ai_main.c
1 /* --- UrreBotInfront ---\r
2 I could've used id's infront, but as it wasn't in LordHavoc's multiplayer\r
3 only mod, I had to add a new one, named something else to not mess with people's\r
4 possible/eventual plugin-attempts*/\r
5 \r
6 float(entity targ) UrreBotInfront =\r
7 {\r
8         local float dot;\r
9         local vector vec;\r
10 \r
11         makevectors (self.angles);\r
12         vec = normalize (targ.origin - self.origin);\r
13         dot = vec * v_forward;\r
14 \r
15         if (dot > 0.3)\r
16                 return TRUE;\r
17         return FALSE;\r
18 };\r
19 \r
20 /* --- UrreBotEvalTargets ---\r
21 Enemies are found and lost using this function\r
22 If the bot can't see his enemy for 3 seconds, it is dropped*/\r
23 \r
24 void() UrreBotEvalTargets =\r
25 {\r
26         local float old, new;\r
27         local vector v1, v2;\r
28         local entity e;\r
29 \r
30         v1 = self.origin + self.view_ofs;\r
31 \r
32         if (self.enemy)\r
33         {\r
34                 if (self.enemy.health >= 1 && !self.enemy.deadflag)\r
35                 {\r
36                         if (self.evaltime <= time)\r
37                         {\r
38                                 self.evaltime = time + 3;\r
39                                 v2 = (self.enemy.absmin + self.enemy.absmax) * 0.5;\r
40                                 traceline(v1, v2, TRUE, self);\r
41                                 if (trace_fraction < 1)\r
42                                         self.enemy = world;\r
43                         }\r
44                 }\r
45                 else\r
46                         self.enemy = world;\r
47         }\r
48         e = findradius(v1, 1500);\r
49         while (e)\r
50         {\r
51                 if (!(e.flags & FL_NOTARGET))\r
52                 if (!(cvar("teamplay") && self.team == e.team))      // don't target teammates\r
53                 if (e.flags & FL_CLIENT)\r
54                 if (e != self)\r
55                 if (!e.deadflag)\r
56                 if (UrreBotInfront(e))\r
57                 if (e.health >= 1)\r
58                 {\r
59                         v2 = (e.absmin + e.absmax) * 0.5;\r
60                         traceline(v1, v2, TRUE, self);\r
61                         if (trace_fraction == 1 || trace_ent == e)\r
62                         {\r
63                                 if (self.enemy)\r
64                                 {\r
65                                         old = vlen(self.origin - (self.enemy.absmin + self.enemy.absmax)*0.5);\r
66                                         new = vlen(self.origin - v2);\r
67                                         if (new < old)\r
68                                                 self.enemy = e;\r
69                                 }\r
70                                 else\r
71                                         self.enemy = e;\r
72                         }\r
73                 }\r
74                 e = e.chain;\r
75         }\r
76 \r
77         e = world;\r
78         if (self.goalcurrent.sflags & S_DOOR)\r
79                 e = self.goalcurrent.goalentity;\r
80         else if (self.link0.sflags & S_DOOR)\r
81                 e = self.link0.goalentity;\r
82         if (e.health >= 1)\r
83                 self.enemy = e;\r
84 };\r
85 \r
86 /* --- UrreBotAim ---\r
87 Very crude and simple aiming, with some leading capability*/\r
88 \r
89 void() UrreBotAim =\r
90 {\r
91         local float dist, skeel;\r
92         local vector v, desiredang, testang, diffang;\r
93 \r
94         skeel = bound(1, skill, 10);\r
95 \r
96         // get the desired angles to aim at\r
97         if (self.enemy)\r
98         {\r
99                 v = (self.enemy.absmin + self.enemy.absmax) * 0.5;\r
100                 if (self.enemy.classname == "door")\r
101                         self.aimpoint = v;\r
102                 else if (self.aimtime <= time)\r
103                 {\r
104                         self.aimtime = time + 0.3;\r
105                         traceline(self.origin + self.view_ofs, (self.enemy.absmin + self.enemy.absmax) * 0.5, TRUE, self);\r
106                         if (trace_fraction == 1)\r
107                         {\r
108                                 self.aimpoint = v + self.enemy.velocity*vlen(self.origin - v)*self.lead;\r
109                                 self.aimpoint = self.aimpoint + randomvec()*max(0, 120 - skeel*12);\r
110                         }\r
111                 }\r
112                 desiredang = vectoangles(self.aimpoint - (self.origin + self.view_ofs));\r
113         }\r
114         else\r
115                 desiredang = vectoangles(self.velocity);\r
116         desiredang_x = 0-desiredang_x;\r
117 \r
118         if (desiredang_y <= -180)\r
119                 desiredang_y = desiredang_y + 360;\r
120         else if (desiredang_y >= 180)\r
121                 desiredang_y = desiredang_y - 360;\r
122         if (desiredang_x <= -180)\r
123                 desiredang_x = desiredang_x + 360;\r
124         else if (desiredang_x >= 180)\r
125                 desiredang_x = desiredang_x - 360;\r
126 \r
127         // calculate turn angles\r
128         testang = desiredang - self.v_angle;\r
129         testang_z = 0;\r
130 \r
131         // turn\r
132         dist = vlen(testang * ((skeel + 1) * frametime));\r
133         if (vlen(normalize(testang) * skeel) > dist)\r
134         {\r
135                 diffang = normalize(testang) * skeel;\r
136                 dist = vlen(normalize(testang) * skeel);\r
137         }\r
138         else\r
139                 diffang = testang * ((skeel + 1) * frametime);\r
140         if (dist > vlen(testang))\r
141                 diffang = testang;\r
142 \r
143         self.v_angle = self.v_angle + diffang;\r
144         self.angles_y = self.v_angle_y;\r
145 };\r
146 \r
147 /* --- UrreBotMove ---\r
148 Moves towards the closest point on the next goal in the bots list,\r
149 which can be a navnode, item or domination point*/\r
150 \r
151 void() UrreBotMove =\r
152 {\r
153         local float f, bad;\r
154         local vector dir, tvec;\r
155         local entity plane, optpoint;\r
156 \r
157         if (self.link0)\r
158         {\r
159                 if (boxesoverlap(self.origin + self.mins, self.origin + self.maxs, self.link0.origin + self.link0.mins, self.link0.origin + self.link0.maxs))\r
160                 {\r
161                         optpoint = world;\r
162                         plane = self.link0.plane_chain;\r
163                         while (plane)\r
164                         {\r
165                                 tvec = self.maxs;\r
166                                 if (plane.mangle_x < 0)\r
167                                         tvec_x = self.mins_x;\r
168                                 if (plane.mangle_y < 0)\r
169                                         tvec_y = self.mins_y;\r
170                                 if (plane.mangle_z < 0)\r
171                                         tvec_z = self.mins_z;\r
172                                 tvec += self.origin;\r
173                                 f = tvec*plane.mangle - self.link0.origin*plane.mangle-plane.delay;\r
174                                 if (f > 0)\r
175                                         bad = TRUE;\r
176                                 plane = plane.list;\r
177                         }\r
178                         if (!bad)\r
179                         {\r
180                                 PopRoute();\r
181                                 if (self.goalcurrent.sflags & S_TELEPORT)\r
182                                         self.movepoint = self.goalcurrent.origin;\r
183                                 else\r
184                                 {\r
185                                         if (urrebots_navopt)\r
186                                                 optpoint = MatchOptPoint(self.goalcurrent, self.goallist, self.link0);\r
187                                         if (optpoint)\r
188                                                 self.movepoint = optpoint.origin;\r
189                                         else\r
190                                                 self.movepoint = ClampPointToSpace(self.origin, self.goalcurrent, self.link0);\r
191                                 }\r
192                         }\r
193                 }\r
194                 else if (((self.goalcurrent.sflags & S_TOUCH) && boxesoverlap(self.origin + self.mins, self.origin + self.maxs, self.goalcurrent.origin + self.goalcurrent.mins, self.goalcurrent.origin + self.goalcurrent.maxs)) || boxenclosed(self.origin + self.mins, self.origin + self.maxs, self.goalcurrent.origin + self.goalcurrent.mins, self.goalcurrent.origin + self.goalcurrent.maxs))\r
195                 {\r
196                         optpoint = world;\r
197                         plane = self.goalcurrent.plane_chain;\r
198                         while (plane)\r
199                         {\r
200                                 tvec = self.maxs;\r
201                                 if (plane.mangle_x < 0)\r
202                                         tvec_x = self.mins_x;\r
203                                 if (plane.mangle_y < 0)\r
204                                         tvec_y = self.mins_y;\r
205                                 if (plane.mangle_z < 0)\r
206                                         tvec_z = self.mins_z;\r
207                                 tvec += self.origin;\r
208                                 f = tvec*plane.mangle - self.goalcurrent.origin*plane.mangle-plane.delay;\r
209                                 if (f > 0)\r
210                                         bad = TRUE;\r
211                                 plane = plane.list;\r
212                         }\r
213                         if (!bad)\r
214                         {\r
215                                 PopRoute();\r
216                                 if (self.goalcurrent.sflags & S_TELEPORT)\r
217                                         self.movepoint = self.goalcurrent.origin;\r
218                                 else\r
219                                 {\r
220                                         if (urrebots_navopt)\r
221                                                 optpoint = MatchOptPoint(self.goalcurrent, self.goallist, self.goalcurrent);\r
222                                         if (optpoint)\r
223                                                 self.movepoint = optpoint.origin;\r
224                                         else\r
225                                                 self.movepoint = ClampPointToSpace(self.origin, self.goalcurrent, self.goalcurrent);                                    \r
226                                 }\r
227                                 if (self.movepoint == '0 0 0')\r
228                                 {\r
229                                         self.strat_me = TRUE;\r
230                                         UrreBotPath(minisearch_distance);\r
231                                 }\r
232                         }\r
233                 }\r
234         }\r
235         else\r
236         {\r
237                 optpoint = world;\r
238                 if (!self.goalcurrent || ((self.goalcurrent.flags & FL_ITEM) && !self.goalcurrent.solid) || (self.goalcurrent.classname == "dom_controlpoint" && self.goalcurrent.enemy.team == self.team))\r
239                 {\r
240                         self.strat_me = TRUE;\r
241                         UrreBotPath(minisearch_distance);\r
242                 }\r
243                 if (urrebots_navopt)\r
244                         optpoint = MatchOptPoint(self.goalcurrent, self.goallist, self.goalcurrent);\r
245                 if (optpoint)\r
246                         self.movepoint = optpoint.origin;\r
247                 else\r
248                         self.movepoint = ClampPointToSpace(self.origin, self.goalcurrent, self.goalcurrent);\r
249         }\r
250         dir = normalize(ToPointInSpace(self.goalcurrent, self.movepoint));\r
251         dir = dir * sv_maxspeed;\r
252         makevectors(self.v_angle);\r
253         self.movement_x = dir * v_forward;\r
254         self.movement_y = dir * v_right;\r
255         self.movement_z = dir * v_up;\r
256 };\r
257 \r
258 /* --- UrreBotImpulses ---\r
259 Returns the impulse for the best weapon in the given situation*/\r
260 \r
261 float() UrreBotImpulses =\r
262 {\r
263         local float dist;\r
264         local float cells, rockets, nails, shells;\r
265         local vector v;\r
266 \r
267         if (random() < 0.5)\r
268                 return 0;\r
269 \r
270         dist = 400; // we like nex and mortar\r
271         if (self.enemy)\r
272         {\r
273                 v = (self.enemy.absmin + self.enemy.absmax) * 0.5;\r
274                 dist = vlen(v - self.origin);\r
275         }\r
276 \r
277         cells = self.ammo_cells;\r
278         rockets = self.ammo_rockets;\r
279         nails = self.ammo_nails;\r
280         shells = self.ammo_shells;\r
281 \r
282         if (dist > 300 && cells && (self.items & IT_NEX))\r
283         {\r
284                 self.lead = 0;\r
285                 return WEP_NEX;\r
286         }\r
287         else if (rockets)\r
288         {\r
289                 if (dist < 500)\r
290                 if (self.items & IT_GRENADE_LAUNCHER)\r
291                 {\r
292                         self.lead = 1 / cvar("g_balance_grenadelauncher_speed");\r
293                         return WEP_GRENADE_LAUNCHER;\r
294                 }\r
295                 if (self.items & IT_HAGAR)\r
296                 {\r
297                         self.lead = 1 / cvar("g_balance_hagar_speed");\r
298                         return WEP_HAGAR;\r
299                 }\r
300                 else if (self.items & IT_ROCKET_LAUNCHER)\r
301                 {\r
302                         self.lead = 1 / cvar("g_balance_rocketlauncher_speed");\r
303                         return WEP_ROCKET_LAUNCHER;\r
304                 }\r
305         }\r
306         else if (cells)\r
307         {\r
308                 if (self.items & IT_ELECTRO)\r
309                 {\r
310                         self.lead = 1 / cvar("g_balance_electro_speed");\r
311                         return WEP_ELECTRO;\r
312                 }\r
313                 else if (self.items & IT_CRYLINK)\r
314                 {\r
315                         self.lead = 1 / cvar("g_balance_crylink_speed");\r
316                         return WEP_CRYLINK;\r
317                 }\r
318         }\r
319         else if (nails)\r
320         {\r
321                 if (self.items & IT_UZI)\r
322                 {\r
323                         self.lead = 0;\r
324                         return WEP_UZI;\r
325                 }\r
326         }\r
327         else if (shells)\r
328         {\r
329                 if (self.items & IT_SHOTGUN)\r
330                 {\r
331                         self.lead = 0;\r
332                         return WEP_SHOTGUN;\r
333                 }\r
334         }\r
335         self.lead = 1 / cvar("g_balance_laser_speed");\r
336         return WEP_LASER;\r
337 };\r
338 \r
339 /* --- BeamBox ---\r
340 Used for some debugging, occasionally*/\r
341 \r
342 float BT_LIGHTNING = 0;\r
343 float BT_BEAM = 1;\r
344 void(float beamtype, vector bmins, vector bmaxs) BeamBox =\r
345 {\r
346         local vector v1, v2;\r
347 \r
348         v1 = bmaxs;\r
349         v2 = bmaxs;\r
350         v2_x = bmins_x;\r
351         if (beamtype == BT_LIGHTNING)\r
352                 te_lightning1(world, v1, v2);\r
353         else\r
354                 te_beam(world, v1, v2);\r
355         v1 = bmaxs;\r
356         v2 = bmaxs;\r
357         v2_y = bmins_y;\r
358         if (beamtype == BT_LIGHTNING)\r
359                 te_lightning1(world, v1, v2);\r
360         else\r
361                 te_beam(world, v1, v2);\r
362         v1 = bmaxs;\r
363         v2 = bmaxs;\r
364         v2_z = bmins_z;\r
365         if (beamtype == BT_LIGHTNING)\r
366                 te_lightning1(world, v1, v2);\r
367         else\r
368                 te_beam(world, v1, v2);\r
369         v1 = bmins;\r
370         v2 = bmins;\r
371         v2_x = bmaxs_x;\r
372         if (beamtype == BT_LIGHTNING)\r
373                 te_lightning1(world, v1, v2);\r
374         else\r
375                 te_beam(world, v1, v2);\r
376         v1 = bmins;\r
377         v2 = bmins;\r
378         v2_y = bmaxs_y;\r
379         if (beamtype == BT_LIGHTNING)\r
380                 te_lightning1(world, v1, v2);\r
381         else\r
382                 te_beam(world, v1, v2);\r
383         v1 = bmins;\r
384         v2 = bmins;\r
385         v2_z = bmaxs_z;\r
386         if (beamtype == BT_LIGHTNING)\r
387                 te_lightning1(world, v1, v2);\r
388         else\r
389                 te_beam(world, v1, v2);\r
390         v1 = bmins;\r
391         v1_z = bmaxs_z;\r
392         v2 = bmins;\r
393         v2_x = bmaxs_x;\r
394         v2_z = bmaxs_z;\r
395         if (beamtype == BT_LIGHTNING)\r
396                 te_lightning1(world, v1, v2);\r
397         else\r
398                 te_beam(world, v1, v2);\r
399         v1 = bmins;\r
400         v1_z = bmaxs_z;\r
401         v2 = bmins;\r
402         v2_y = bmaxs_y;\r
403         v2_z = bmaxs_z;\r
404         if (beamtype == BT_LIGHTNING)\r
405                 te_lightning1(world, v1, v2);\r
406         else\r
407                 te_beam(world, v1, v2);\r
408         v1 = bmaxs;\r
409         v1_z = bmins_z;\r
410         v2 = bmaxs;\r
411         v2_x = bmins_x;\r
412         v2_z = bmins_z;\r
413         if (beamtype == BT_LIGHTNING)\r
414                 te_lightning1(world, v1, v2);\r
415         else\r
416                 te_beam(world, v1, v2);\r
417         v1 = bmaxs;\r
418         v1_z = bmins_z;\r
419         v2 = bmaxs;\r
420         v2_y = bmins_y;\r
421         v2_z = bmins_z;\r
422         if (beamtype == BT_LIGHTNING)\r
423                 te_lightning1(world, v1, v2);\r
424         else\r
425                 te_beam(world, v1, v2);\r
426         v1 = bmins;\r
427         v1_x = bmaxs_x;\r
428         v2 = bmaxs;\r
429         v2_y = bmins_y;\r
430         if (beamtype == BT_LIGHTNING)\r
431                 te_lightning1(world, v1, v2);\r
432         else\r
433                 te_beam(world, v1, v2);\r
434         v1 = bmins;\r
435         v1_y = bmaxs_y;\r
436         v2 = bmaxs;\r
437         v2_x = bmins_x;\r
438         if (beamtype == BT_LIGHTNING)\r
439                 te_lightning1(world, v1, v2);\r
440         else\r
441                 te_beam(world, v1, v2);\r
442 };\r
443 \r
444 /* --- UrreBotPath ---\r
445 Marks route and determines which goal is the most useful to head for*/\r
446 \r
447 void(float sdist) UrreBotPath =\r
448 {\r
449         local float f, f2, f3;\r
450         local entity e, best;\r
451 \r
452         ClearRoute();\r
453         MarkRoute(sdist);\r
454         DistEvalItems();\r
455         f2 = 10000000;\r
456         e = findchainflags(flags, FL_ITEM);\r
457         while (e)\r
458         {\r
459                 f = 0;\r
460                 if (e.evalfunc)\r
461                         f = e.evalfunc(e);\r
462                 if (f > 0)\r
463                 if (e.goallist)\r
464                 {\r
465                         f = f + e.costl;\r
466                         if (f < f2)\r
467                         if (e.solid)\r
468                         {\r
469                                 best = e;\r
470                                 f2 = e.costl;\r
471                         }\r
472                 }\r
473                 e = e.chain;\r
474         }\r
475         if (best)\r
476         {\r
477                 while (best)\r
478                 {\r
479                         PushRoute(best);\r
480                         best = best.goallist;\r
481                 }\r
482         }\r
483         else\r
484         {\r
485                 f3 = 0;\r
486                 while (f3 < 3)\r
487                 {\r
488                         f = 0;\r
489                         f2 = 0;\r
490                         f = ceil(random() * navnodes);\r
491                         e = navnode_chain;\r
492                         while (e)\r
493                         {\r
494                                 if (f == f2)\r
495                                 if (e.goallist)\r
496                                 {\r
497                                         best = e;\r
498                                         f3 = 3;\r
499                                 }\r
500                                 f2 = f2 + 1;\r
501                                 e = e.list;\r
502                         }\r
503                         f3 = f3 + 1;\r
504                 }\r
505                 while (best)\r
506                 {\r
507                         PushRoute(best);\r
508                         best = best.goallist;\r
509                 }\r
510         }\r
511 };\r
512 \r
513 /* --- UrreBotThink ---\r
514 In this version, UrreBot does a path search based on timer and turn\r
515 Then it aims, moves, checks if it's stuck, finds/loses stuff to shoot at,\r
516 picks best weapon, shoots, and optionally displays debug stuff, in that\r
517 order\r
518 Aiming must happen before movement, because movement depends on angles\r
519 He does not yet have any combat movement*/\r
520 \r
521 void() UrreBotThink =\r
522 {\r
523         local float f;\r
524         local vector v;\r
525 \r
526         self.movement = '0 0 0';\r
527         self.button0 = 0;\r
528         self.button2 = 0;\r
529         self.impulse = 0;\r
530 \r
531         if (cvar("teamplay") && self.team == self.enemy.team) // don't return fire if hit by a teammate\r
532                 self.enemy = world;\r
533 \r
534         if (self.deadflag)\r
535         {\r
536                 ClearRoute();\r
537                 if (random() < 0.2)\r
538                         self.button0 = 1;\r
539                 return;\r
540         }\r
541 \r
542         if (strategytime <= time)\r
543         if (strategytoken == self)\r
544         {\r
545                 strategytime = time + urrebots_strategytime;\r
546                 strategytoken = self.list;\r
547                 if (!strategytoken)\r
548                         strategytoken = urrebot_chain;\r
549                 if (self.strat_me)\r
550                 {\r
551                         self.strat_me = FALSE;\r
552                         UrreBotPath(stratsearch_distance);\r
553                 }\r
554         }\r
555 \r
556         UrreBotAim();\r
557         UrreBotMove();\r
558 \r
559         if (self.camptime <= time)\r
560         {\r
561                 if (vlen(self.origin - self.campcheck) < 200) // stuckage avoidage\r
562                 {\r
563                         self.camptime = time + urrebots;\r
564                         self.strat_me = TRUE;\r
565                         UrreBotPath(minisearch_distance);\r
566                 }\r
567                 else\r
568                 {\r
569                         self.campcheck = self.origin;\r
570                         self.camptime = time + 2;\r
571                 }\r
572         }\r
573 \r
574         if (self.combattime <= time)\r
575         {\r
576                 self.combattime = time + urrebots_combattime;\r
577                 UrreBotEvalTargets();\r
578                 self.impulse = UrreBotImpulses();\r
579         }\r
580 \r
581         if (self.enemy)\r
582         {\r
583                 makevectors (self.v_angle);\r
584                 v = self.origin + '0 0 16';\r
585                 f = vlen(v - self.aimpoint);\r
586                 traceline (v, v + v_forward*f, FALSE, self);\r
587                 if (vlen(trace_endpos - self.aimpoint) < 150)\r
588                 {\r
589                         if (skill < 5)\r
590                         {\r
591                                 f = skill*0.1;\r
592                                 if (random() < f)\r
593                                         self.button0 = 1;\r
594                         }\r
595                         else\r
596                                 self.button0 = 1;\r
597                 }\r
598         }\r
599         if (cvar("urrebots_debug"))\r
600         {\r
601                 te_lightning1(self, self.origin, self.movepoint);\r
602                 if (!self.goalcurrent)\r
603                 {\r
604                         bprint(self.netname);\r
605                         bprint(" has no goalcurrent\n");\r
606                 }\r
607         }\r
608 };\r