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