]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/gamec/urrebot_ai_main.c
Reinstated navigation "optimization" for profiling tests.
[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                         plane = self.link0.plane_chain;\r
162                         while (plane)\r
163                         {\r
164                                 tvec = self.maxs;\r
165                                 if (plane.mangle_x < 0)\r
166                                         tvec_x = self.mins_x;\r
167                                 if (plane.mangle_y < 0)\r
168                                         tvec_y = self.mins_y;\r
169                                 if (plane.mangle_z < 0)\r
170                                         tvec_z = self.mins_z;\r
171                                 tvec += self.origin;\r
172                                 f = tvec*plane.mangle - self.link0.origin*plane.mangle-plane.delay;\r
173                                 if (f > 0)\r
174                                         bad = TRUE;\r
175                                 plane = plane.list;\r
176                         }\r
177                         if (!bad)\r
178                         {\r
179                                 PopRoute();\r
180                                 if (self.goalcurrent.sflags & S_TELEPORT)\r
181                                         self.movepoint = self.goalcurrent.origin;\r
182                                 else\r
183                                 {\r
184                                         optpoint = MatchOptPoint(self.goalcurrent, self.goallist, self.link0);\r
185                                         if (optpoint)\r
186                                                 self.movepoint = optpoint.origin;\r
187                                         else\r
188                                                 self.movepoint = ClampPointToSpace(self.origin, self.goalcurrent, self.link0);\r
189                                 }\r
190                         }\r
191                 }\r
192                 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
193                 {\r
194                         plane = self.goalcurrent.plane_chain;\r
195                         while (plane)\r
196                         {\r
197                                 tvec = self.maxs;\r
198                                 if (plane.mangle_x < 0)\r
199                                         tvec_x = self.mins_x;\r
200                                 if (plane.mangle_y < 0)\r
201                                         tvec_y = self.mins_y;\r
202                                 if (plane.mangle_z < 0)\r
203                                         tvec_z = self.mins_z;\r
204                                 tvec += self.origin;\r
205                                 f = tvec*plane.mangle - self.goalcurrent.origin*plane.mangle-plane.delay;\r
206                                 if (f > 0)\r
207                                         bad = TRUE;\r
208                                 plane = plane.list;\r
209                         }\r
210                         if (!bad)\r
211                         {\r
212                                 PopRoute();\r
213                                 if (self.goalcurrent.sflags & S_TELEPORT)\r
214                                         self.movepoint = self.goalcurrent.origin;\r
215                                 else\r
216                                 {\r
217                                         optpoint = MatchOptPoint(self.goalcurrent, self.goallist, self.goalcurrent);\r
218                                         if (optpoint)\r
219                                                 self.movepoint = optpoint.origin;\r
220                                         else\r
221                                                 self.movepoint = ClampPointToSpace(self.origin, self.goalcurrent, self.goalcurrent);                                    \r
222                                 }\r
223                                 if (self.movepoint == '0 0 0')\r
224                                 {\r
225                                         self.strat_me = TRUE;\r
226                                         UrreBotPath(minisearch_distance);\r
227                                 }\r
228                         }\r
229                 }\r
230         }\r
231         else\r
232         {\r
233                 if (!self.goalcurrent || ((self.goalcurrent.flags & FL_ITEM) && !self.goalcurrent.solid) || (self.goalcurrent.classname == "dom_controlpoint" && self.goalcurrent.enemy.team == self.team))\r
234                 {\r
235                         self.strat_me = TRUE;\r
236                         UrreBotPath(minisearch_distance);\r
237                 }\r
238                 optpoint = MatchOptPoint(self.goalcurrent, self.goallist, self.goalcurrent);\r
239                 if (optpoint)\r
240                         self.movepoint = optpoint.origin;\r
241                 else\r
242                         self.movepoint = ClampPointToSpace(self.origin, self.goalcurrent, self.goalcurrent);\r
243         }\r
244         dir = normalize(ToPointInSpace(self.goalcurrent, self.movepoint));\r
245         dir = dir * sv_maxspeed;\r
246         makevectors(self.v_angle);\r
247         self.movement_x = dir * v_forward;\r
248         self.movement_y = dir * v_right;\r
249         self.movement_z = dir * v_up;\r
250 };\r
251 \r
252 /* --- UrreBotImpulses ---\r
253 Returns the impulse for the best weapon in the given situation*/\r
254 \r
255 float() UrreBotImpulses =\r
256 {\r
257         local float dist;\r
258         local float cells, rockets, nails, shells;\r
259         local vector v;\r
260 \r
261         if (random() < 0.5)\r
262                 return 0;\r
263 \r
264         dist = 400; // we like nex and mortar\r
265         if (self.enemy)\r
266         {\r
267                 v = (self.enemy.absmin + self.enemy.absmax) * 0.5;\r
268                 dist = vlen(v - self.origin);\r
269         }\r
270 \r
271         cells = self.ammo_cells;\r
272         rockets = self.ammo_rockets;\r
273         nails = self.ammo_nails;\r
274         shells = self.ammo_shells;\r
275 \r
276         if (dist > 300 && cells && (self.items & IT_NEX))\r
277         {\r
278                 self.lead = 0;\r
279                 return WEP_NEX;\r
280         }\r
281         else if (rockets)\r
282         {\r
283                 if (dist < 500)\r
284                 if (self.items & IT_GRENADE_LAUNCHER)\r
285                 {\r
286                         self.lead = 1 / cvar("g_balance_grenadelauncher_speed");\r
287                         return WEP_GRENADE_LAUNCHER;\r
288                 }\r
289                 if (self.items & IT_HAGAR)\r
290                 {\r
291                         self.lead = 1 / cvar("g_balance_hagar_speed");\r
292                         return WEP_HAGAR;\r
293                 }\r
294                 else if (self.items & IT_ROCKET_LAUNCHER)\r
295                 {\r
296                         self.lead = 1 / cvar("g_balance_rocketlauncher_speed");\r
297                         return WEP_ROCKET_LAUNCHER;\r
298                 }\r
299         }\r
300         else if (cells)\r
301         {\r
302                 if (self.items & IT_ELECTRO)\r
303                 {\r
304                         self.lead = 1 / cvar("g_balance_electro_speed");\r
305                         return WEP_ELECTRO;\r
306                 }\r
307                 else if (self.items & IT_CRYLINK)\r
308                 {\r
309                         self.lead = 1 / cvar("g_balance_crylink_speed");\r
310                         return WEP_CRYLINK;\r
311                 }\r
312         }\r
313         else if (nails)\r
314         {\r
315                 if (self.items & IT_UZI)\r
316                 {\r
317                         self.lead = 0;\r
318                         return WEP_UZI;\r
319                 }\r
320         }\r
321         else if (shells)\r
322         {\r
323                 if (self.items & IT_SHOTGUN)\r
324                 {\r
325                         self.lead = 0;\r
326                         return WEP_SHOTGUN;\r
327                 }\r
328         }\r
329         self.lead = 1 / cvar("g_balance_laser_speed");\r
330         return WEP_LASER;\r
331 };\r
332 \r
333 /* --- BeamBox ---\r
334 Used for some debugging, occasionally*/\r
335 \r
336 float BT_LIGHTNING = 0;\r
337 float BT_BEAM = 1;\r
338 void(float beamtype, vector bmins, vector bmaxs) BeamBox =\r
339 {\r
340         local vector v1, v2;\r
341 \r
342         v1 = bmaxs;\r
343         v2 = bmaxs;\r
344         v2_x = bmins_x;\r
345         if (beamtype == BT_LIGHTNING)\r
346                 te_lightning1(world, v1, v2);\r
347         else\r
348                 te_beam(world, v1, v2);\r
349         v1 = bmaxs;\r
350         v2 = bmaxs;\r
351         v2_y = bmins_y;\r
352         if (beamtype == BT_LIGHTNING)\r
353                 te_lightning1(world, v1, v2);\r
354         else\r
355                 te_beam(world, v1, v2);\r
356         v1 = bmaxs;\r
357         v2 = bmaxs;\r
358         v2_z = bmins_z;\r
359         if (beamtype == BT_LIGHTNING)\r
360                 te_lightning1(world, v1, v2);\r
361         else\r
362                 te_beam(world, v1, v2);\r
363         v1 = bmins;\r
364         v2 = bmins;\r
365         v2_x = bmaxs_x;\r
366         if (beamtype == BT_LIGHTNING)\r
367                 te_lightning1(world, v1, v2);\r
368         else\r
369                 te_beam(world, v1, v2);\r
370         v1 = bmins;\r
371         v2 = bmins;\r
372         v2_y = bmaxs_y;\r
373         if (beamtype == BT_LIGHTNING)\r
374                 te_lightning1(world, v1, v2);\r
375         else\r
376                 te_beam(world, v1, v2);\r
377         v1 = bmins;\r
378         v2 = bmins;\r
379         v2_z = bmaxs_z;\r
380         if (beamtype == BT_LIGHTNING)\r
381                 te_lightning1(world, v1, v2);\r
382         else\r
383                 te_beam(world, v1, v2);\r
384         v1 = bmins;\r
385         v1_z = bmaxs_z;\r
386         v2 = bmins;\r
387         v2_x = bmaxs_x;\r
388         v2_z = bmaxs_z;\r
389         if (beamtype == BT_LIGHTNING)\r
390                 te_lightning1(world, v1, v2);\r
391         else\r
392                 te_beam(world, v1, v2);\r
393         v1 = bmins;\r
394         v1_z = bmaxs_z;\r
395         v2 = bmins;\r
396         v2_y = bmaxs_y;\r
397         v2_z = bmaxs_z;\r
398         if (beamtype == BT_LIGHTNING)\r
399                 te_lightning1(world, v1, v2);\r
400         else\r
401                 te_beam(world, v1, v2);\r
402         v1 = bmaxs;\r
403         v1_z = bmins_z;\r
404         v2 = bmaxs;\r
405         v2_x = bmins_x;\r
406         v2_z = bmins_z;\r
407         if (beamtype == BT_LIGHTNING)\r
408                 te_lightning1(world, v1, v2);\r
409         else\r
410                 te_beam(world, v1, v2);\r
411         v1 = bmaxs;\r
412         v1_z = bmins_z;\r
413         v2 = bmaxs;\r
414         v2_y = bmins_y;\r
415         v2_z = bmins_z;\r
416         if (beamtype == BT_LIGHTNING)\r
417                 te_lightning1(world, v1, v2);\r
418         else\r
419                 te_beam(world, v1, v2);\r
420         v1 = bmins;\r
421         v1_x = bmaxs_x;\r
422         v2 = bmaxs;\r
423         v2_y = bmins_y;\r
424         if (beamtype == BT_LIGHTNING)\r
425                 te_lightning1(world, v1, v2);\r
426         else\r
427                 te_beam(world, v1, v2);\r
428         v1 = bmins;\r
429         v1_y = bmaxs_y;\r
430         v2 = bmaxs;\r
431         v2_x = bmins_x;\r
432         if (beamtype == BT_LIGHTNING)\r
433                 te_lightning1(world, v1, v2);\r
434         else\r
435                 te_beam(world, v1, v2);\r
436 };\r
437 \r
438 /* --- UrreBotPath ---\r
439 Marks route and determines which goal is the most useful to head for*/\r
440 \r
441 void(float sdist) UrreBotPath =\r
442 {\r
443         local float f, f2, f3;\r
444         local entity e, best;\r
445 \r
446         ClearRoute();\r
447         MarkRoute(sdist);\r
448         DistEvalItems();\r
449         f2 = 10000000;\r
450         e = findchainflags(flags, FL_ITEM);\r
451         while (e)\r
452         {\r
453                 f = 0;\r
454                 if (e.evalfunc)\r
455                         f = e.evalfunc(e);\r
456                 if (f > 0)\r
457                 if (e.goallist)\r
458                 {\r
459                         f = f + e.costl;\r
460                         if (f < f2)\r
461                         if (e.solid)\r
462                         {\r
463                                 best = e;\r
464                                 f2 = e.costl;\r
465                         }\r
466                 }\r
467                 e = e.chain;\r
468         }\r
469         if (best)\r
470         {\r
471                 while (best)\r
472                 {\r
473                         PushRoute(best);\r
474                         best = best.goallist;\r
475                 }\r
476         }\r
477         else\r
478         {\r
479                 f3 = 0;\r
480                 while (f3 < 3)\r
481                 {\r
482                         f = 0;\r
483                         f2 = 0;\r
484                         f = ceil(random() * navnodes);\r
485                         e = navnode_chain;\r
486                         while (e)\r
487                         {\r
488                                 if (f == f2)\r
489                                 if (e.goallist)\r
490                                 {\r
491                                         best = e;\r
492                                         f3 = 3;\r
493                                 }\r
494                                 f2 = f2 + 1;\r
495                                 e = e.list;\r
496                         }\r
497                         f3 = f3 + 1;\r
498                 }\r
499                 while (best)\r
500                 {\r
501                         PushRoute(best);\r
502                         best = best.goallist;\r
503                 }\r
504         }\r
505 };\r
506 \r
507 /* --- UrreBotThink ---\r
508 In this version, UrreBot does a path search based on timer and turn\r
509 Then it aims, moves, checks if it's stuck, finds/loses stuff to shoot at,\r
510 picks best weapon, shoots, and optionally displays debug stuff, in that\r
511 order\r
512 Aiming must happen before movement, because movement depends on angles\r
513 He does not yet have any combat movement*/\r
514 \r
515 void() UrreBotThink =\r
516 {\r
517         local float f;\r
518         local vector v;\r
519 \r
520         self.movement = '0 0 0';\r
521         self.button0 = 0;\r
522         self.button2 = 0;\r
523         self.impulse = 0;\r
524 \r
525         if (cvar("teamplay") && self.team == self.enemy.team) // don't return fire if hit by a teammate\r
526                 self.enemy = world;\r
527 \r
528         if (self.deadflag)\r
529         {\r
530                 ClearRoute();\r
531                 if (random() < 0.2)\r
532                         self.button0 = 1;\r
533                 return;\r
534         }\r
535 \r
536         if (strategytime <= time)\r
537         if (strategytoken == self)\r
538         {\r
539                 strategytime = time + urrebots_strategytime;\r
540                 strategytoken = self.list;\r
541                 if (!strategytoken)\r
542                         strategytoken = urrebot_chain;\r
543                 if (self.strat_me)\r
544                 {\r
545                         self.strat_me = FALSE;\r
546                         UrreBotPath(stratsearch_distance);\r
547                 }\r
548         }\r
549 \r
550         UrreBotAim();\r
551         UrreBotMove();\r
552 \r
553         if (self.camptime <= time)\r
554         {\r
555                 if (vlen(self.origin - self.campcheck) < 200) // stuckage avoidage\r
556                 {\r
557                         self.camptime = time + urrebots;\r
558                         self.strat_me = TRUE;\r
559                         UrreBotPath(minisearch_distance);\r
560                 }\r
561                 else\r
562                 {\r
563                         self.campcheck = self.origin;\r
564                         self.camptime = time + 2;\r
565                 }\r
566         }\r
567 \r
568         if (self.combattime <= time)\r
569         {\r
570                 self.combattime = time + urrebots_combattime;\r
571                 UrreBotEvalTargets();\r
572                 self.impulse = UrreBotImpulses();\r
573         }\r
574 \r
575         if (self.enemy)\r
576         {\r
577                 makevectors (self.v_angle);\r
578                 v = self.origin + '0 0 16';\r
579                 f = vlen(v - self.aimpoint);\r
580                 traceline (v, v + v_forward*f, FALSE, self);\r
581                 if (vlen(trace_endpos - self.aimpoint) < 150)\r
582                 {\r
583                         if (skill < 5)\r
584                         {\r
585                                 f = skill*0.1;\r
586                                 if (random() < f)\r
587                                         self.button0 = 1;\r
588                         }\r
589                         else\r
590                                 self.button0 = 1;\r
591                 }\r
592         }\r
593         if (cvar("urrebots_debug"))\r
594         {\r
595                 te_lightning1(self, self.origin, self.movepoint);\r
596                 if (!self.goalcurrent)\r
597                 {\r
598                         bprint(self.netname);\r
599                         bprint(" has no goalcurrent\n");\r
600                 }\r
601         }\r
602 };\r