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