]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/bot/bot_move.qc
commit 1.2.1 game media
[divverent/nexuiz.git] / data / qcsrc / bot / bot_move.qc
1 /***********************************************
2 *                                              *
3 *            FrikBot Movement AI               *
4 *     "The slightly better movement AI"        *
5 *                                              *
6 ***********************************************/
7
8 /*
9
10 This program is in the Public Domain. My crack legal
11 team would like to add:
12
13 RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS"
14 AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE
15 ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR
16 FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN
17 NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY
18 GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL,
19 EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC"
20 SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
21 DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES. 
22
23 You accept this software on the condition that you
24 indemnify and hold harmless Ryan "FrikaC" Smith from
25 any and all liability or damages to third parties,
26 including attorney fees, court costs, and other
27 related costs and expenses, arising out of your use
28 of this software irrespective of the cause of said
29 liability. 
30
31 The export from the United States or the subsequent
32 reexport of this software is subject to compliance
33 with United States export control and munitions
34 control restrictions. You agree that in the event you
35 seek to export this software, you assume full
36 responsibility for obtaining all necessary export
37 licenses and approvals and for assuring compliance
38 with applicable reexport restrictions. 
39
40 Any reproduction of this software must contain
41 this notice in its entirety. 
42
43 */
44
45 void() bot_jump =
46 {
47         // TODO check for precision, etc.
48         self.button2 = TRUE;
49 };
50
51 float(entity e) bot_can_rj =
52 {
53         // this returns true of the bot can rocket/superjump/hook
54         // if your mod doesn't have an RL you can just return FALSE all the time
55         // if it has a hook or some other means for the bot to get to high places
56         // you can check here for that capability
57
58         // am I dumb?
59         if (e.b_skill == 0)
60                 return FALSE;
61
62         // quad = bad
63         if (e.items & 4194304)
64                 return FALSE;
65
66         // do I have rockets & RL?
67         if (!((e.items & 32) && (e.ammo_rockets > 0)))
68                 return FALSE;
69
70         // do I have pent?
71         if (e.items & 1048576)
72                 return TRUE;
73
74         if (e.health > 50)
75                 return TRUE;
76         else
77                 return FALSE;
78 };
79
80 float(float flag) frik_recognize_plat =
81 {
82         if ((self.classname != "waypoint") && !(self.flags & FL_ONGROUND))
83                 return FALSE;
84         traceline(self.origin, self.origin - '0 0 64', TRUE, self);
85         if (trace_ent != world)
86         {
87                 if (flag) // afect bot movement too
88                 {
89                         if (self.keys & KEY_MOVEUP)
90                         {
91                                 if (trace_ent.velocity_z > 0)
92                                         self.keys = self.keys & 960; // 960 is all view keys
93                         }
94                         else if (self.keys & KEY_MOVEDOWN)
95                         {
96                                 if (trace_ent.velocity_z < 0)
97                                         self.keys = self.keys & 960;
98                         }
99                 }
100                 return TRUE;
101         }
102         else
103                 return FALSE;
104 };
105
106 float(vector sdir) frik_KeysForDir =
107 {
108
109         local vector keydir;
110         local float outkeys, tang;
111         outkeys = 0;
112         if (sdir_x || sdir_y)
113         {
114                 // Everything is tested against 60 degrees,
115                 // this allows the bot to overlap the keys
116                 // 30 degrees on each diagonal  45 degrees 
117                 // might look more realistic
118
119                 keydir = vectoangles(sdir);
120                 tang = angcomp(keydir_y, self.v_angle_y);
121                 if ((tang <= 150) && (tang >= 30))
122                         outkeys = outkeys + KEY_MOVELEFT;
123                 else if ((tang >= -150) && (tang <= -30))
124                         outkeys = outkeys + KEY_MOVERIGHT;
125                 if (fabs(tang) <= 60)
126                         outkeys = outkeys + KEY_MOVEFORWARD;
127                 else if (fabs(tang) >= 120)
128                         outkeys = outkeys + KEY_MOVEBACK;
129         }
130         if (sdir_z > 0.7)
131                 outkeys = outkeys + KEY_MOVEUP;
132         else if (sdir_z < 0.7)
133                 outkeys = outkeys + KEY_MOVEDOWN;
134         return outkeys;
135
136 };
137
138 /*
139 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
140
141 frik_obstructed
142
143 Bot has hit a ledge or wall that he should
144 manuever around.
145
146 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
147 */
148
149 void(vector whichway, float danger) frik_obstructed =
150 {
151         local float dist;
152         local vector disway, org;
153 // TODO: something
154         if (self.b_aiflags & AI_BLIND)
155                 return;
156         org = realorigin(self.target1);
157
158         if (danger)
159         {
160                 self.b_aiflags = self.b_aiflags | AI_DANGER;
161                 self.keys = frik_KeysForDir('0 0 0' - whichway);
162         }
163         if (self.b_aiflags & AI_PRECISION)
164                 return;
165
166
167         if (self.target1)
168         {
169                 if (self.b_aiflags & AI_OBSTRUCTED)
170                 {
171                         if (!(self.b_aiflags & AI_DANGER))
172                         {
173                                 self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED;
174                                 return;
175                         }
176                         else if (!danger)
177                                 return;
178                 }
179                 self.obs_dir = whichway;
180                 disway_x = whichway_y * -1;
181                 disway_y = whichway_x;
182                 dist = vlen(org - (self.origin + disway));
183                 disway_x = whichway_y;
184                 disway_y = whichway_x * -1;
185                 self.wallhug = vlen(org - (self.origin + disway)) > dist;
186                 self.b_aiflags = self.b_aiflags | AI_OBSTRUCTED;
187                 
188         }
189         else
190         {
191                 disway_x = whichway_y * -1;
192                 disway_y = whichway_x;
193                 dist = vlen(disway - self.obs_dir);
194                 disway_x = whichway_y;
195                 disway_y = whichway_x * -1;
196                 self.wallhug = vlen(disway - self.obs_dir) < dist;
197                 self.obs_dir = whichway;
198
199                 self.b_aiflags = self.b_aiflags | AI_OBSTRUCTED;
200         }
201 };
202
203 /*
204 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
205
206 frik_obstacles
207
208 Detects small bumps the bot needs to jump over
209 or ledges the bot should avoid falling in.
210
211 Also responsible for jumping gaps.
212
213 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
214 */
215
216 void() frik_obstacles =
217 {
218         local vector start, stop, ang;
219         local float test, conts, dist, hgt;
220
221         if (!(self.flags & FL_ONGROUND))
222                 return;
223         if (self.b_aiflags & AI_BLIND)
224                 return;
225
226         ang = normalize(self.velocity);
227         ang_z = 0;
228         start = self.origin + ang * 32; // ahem
229         start_z = self.origin_z + self.maxs_z;
230         stop = start;
231         stop_z = self.origin_z + self.mins_z;
232         traceline(start, stop - '0 0 256', TRUE, self);
233         if (trace_allsolid || trace_startsolid)
234                 return;
235         hgt = trace_endpos_z - stop_z;
236
237         if (hgt > 18)
238         {
239                 bot_jump();
240                 return;
241         }
242         if (hgt >= 0)
243                 return;
244
245         conts = pointcontents(trace_endpos + '0 0 4');
246         start = stop - '0 0 8';
247         stop = start + ang *  256;
248         traceline(start, stop, TRUE, self);
249         test = vlen(trace_endpos - start);
250         if (test <= 20)
251                 return; // it's a walkable gap, do nothing
252         ang_x = self.velocity_y * -1;
253         ang_y = self.velocity_x;
254         ang = normalize(ang);
255         traceline(start - (ang * 10), start + (ang * 10), TRUE, self);
256         if ((trace_fraction != 1) || trace_startsolid)
257                 return; // gap is only 20 wide, walkable        
258         ang = self.velocity;
259         ang_z = 0;
260         dist = ((540 / sv_gravity) * vlen(ang))/* + 32*/;
261         if (test > dist) // I can't make it
262         {
263                 if (conts < -3) // bad stuff down dare
264                 {
265                         frik_obstructed(ang, TRUE);
266                         return;
267                 }
268                 else
269                 {
270                         if (self.target1)
271                         {
272                                 stop = realorigin(self.target1);
273                                 if ((stop_z - self.origin_z) < -32)
274                                         return; // safe to fall
275                         }
276                         frik_obstructed(ang, FALSE);
277                         return;
278                 }
279         }
280         else
281         {
282                 ang = normalize(ang);
283                 //look for a ledge
284                 traceline(self.origin, self.origin + (ang * (test + 20)), TRUE, self); 
285                 if (trace_fraction != 1)
286                 {
287                         if (conts < -3) // bad stuff down dare
288                         {
289                                 frik_obstructed(ang, TRUE);
290                                 return;
291                         }
292                         else
293                         {
294                                 if (self.target1)
295                                 {
296                                         stop = realorigin(self.target1);
297                                         if ((stop_z - self.origin_z) < -32)
298                                                 return; // safe to fall
299                                 }
300                                 frik_obstructed(ang, FALSE);
301                                 return;
302                         }
303                 }
304
305                 if (self.target1)
306                 {
307                         // getting furter away from my target?
308                         test = vlen(self.target1.origin - (ang + self.origin));
309                         if (test > vlen(self.target1.origin - self.origin))
310                         {
311                                 if (conts < -3) // bad stuff down dare
312                                 {
313                                         frik_obstructed(ang, TRUE);
314                                         return;
315                                 }
316                                 else
317                                 {
318                                         frik_obstructed(ang, FALSE);
319                                         return;
320                                 }
321                         }
322                 }
323         }
324         if (hgt < -18)
325         {
326                 if (self.target1)
327                 {
328                         stop = realorigin(self.target1);
329                         if ((stop_z - self.origin_z) < -32)
330                                 return; // safe to fall
331                 }
332                 bot_jump();
333         }
334         // go for it
335
336 };
337
338 /*
339 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
340
341 After frik_obstructed, the bot uses the 
342 following funtion to move "around" the obstacle
343
344 I have no idea how well it will work
345
346 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
347 */
348
349 void() frik_dodge_obstruction =
350 {
351         local vector way, org;
352         local float oflags, yaw;
353
354         if (!(self.b_aiflags & AI_OBSTRUCTED))
355                 return;
356         if ((self.b_aiflags & (AI_BLIND | AI_PRECISION)) || !(self.flags & FL_ONGROUND))
357         {
358                 self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED;
359                 return;
360         }
361
362         // perform a walkmove check to see if the obs_dir is still obstructed
363         // walkmove is less forgiving than frik_obstacles, so I dunno 
364         // how well this will work
365
366         oflags = self.flags;
367         org = self.origin;
368
369         yaw = vectoyaw(self.obs_dir);
370         if (walkmove(yaw, 32))
371                 self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED;
372         else
373         {
374                 if (self.b_aiflags & AI_DANGER)
375                 {
376                         way = '0 0 0' - self.obs_dir;
377                 }
378                 else if (self.wallhug)
379                 {
380                         way_x = self.obs_dir_y * -1;
381                         way_y = self.obs_dir_x;
382                 }
383                 else
384                 {
385                         way_x = self.obs_dir_y;
386                         way_y = self.obs_dir_x * -1;
387                 }
388                 self.keys = self.keys & 960 + frik_KeysForDir(way);
389         }
390         
391         // fix the bot
392
393         self.origin = org;
394         self.flags = oflags;
395 };
396
397 /*
398 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
399
400 movetogoal and walkmove replacements
401
402 blah
403
404 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
405 */
406
407 void() frik_movetogoal =
408 {
409         local vector way/*, start, stop, ang*/;
410         local float g;
411
412         if (self.target1 == world)
413         {
414                 makevectors(self.v_angle);
415                 frik_walkmove(v_forward);
416                 return;
417         }
418         way = realorigin(self.target1) - self.origin;
419         if (vlen(way) < 25)
420         {
421                 self.keys = self.keys & 960;
422                 return;
423         }
424
425         way = normalize(way);
426         self.keys = self.keys & 960 + frik_KeysForDir(way);
427
428         frik_dodge_obstruction();
429         frik_recognize_plat(TRUE);
430
431         if (self.b_aiflags & AI_PRECISION)
432         {
433                 g = angcomp(self.v_angle_x, self.b_angle_x);
434                 if (fabs(g) > 10)
435                         self.keys = self.keys & 960;
436                 g = angcomp(self.v_angle_y, self.b_angle_y);
437                 if (fabs(g) > 10)
438                         self.keys = self.keys & 960;
439         }
440 };
441
442 float(vector weird) frik_walkmove =
443 {
444         // okay so it's not walkmove
445         // sue me
446         self.keys = self.keys & 960 + frik_KeysForDir(weird);
447
448         frik_dodge_obstruction();
449         frik_recognize_plat(TRUE);
450         if (self.b_aiflags & AI_OBSTRUCTED)
451                 return FALSE;
452         else
453                 return TRUE;
454 };
455
456
457 /*
458 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
459
460 The "hook" method of navigation. This nav
461 system is copyrighted 1999 by Ryan "Frika C" 
462 Smith, keep that in mind when you steal it.
463
464 I brought this back because normal roaming
465 won't work - the bot gets distracted by it's
466 own waypoints.
467
468 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
469 */
470
471 void() frik_bot_roam =
472 {
473         local vector org, ang, org1;
474         local float loopcount, flag, dist;
475
476         loopcount = 26;
477         flag = FALSE;
478         while((loopcount > 0) && !flag)
479         {
480                 loopcount = loopcount - 1;
481                 org = self.origin + self.view_ofs;
482                 ang = self.angles;
483                 ang_y = frik_anglemod(ang_y - 90 + (random() * 180));
484                 ang_x = 0; // avoid upward sloping
485                 makevectors(ang);
486                 traceline(org, org + v_forward * 2300, TRUE, self);
487                 if (trace_fraction != 1)
488                 {
489                         org1 = trace_endpos;
490                         ang = normalize(trace_plane_normal);
491                         ang_z = 0; // avoid upward sloping
492                         traceline(org1, org1 + (ang * 2300), TRUE, self);
493                         if ((trace_fraction != 1) && (vlen(trace_endpos - org1) >= 64))
494                         {
495                                 org = trace_endpos;
496                                 traceline(org, self.origin + self.view_ofs, TRUE, self);
497                                 if (trace_fraction != 1)
498                                 {
499                                         dist = vlen(org1 - org) /2;
500                                         org = org1 + (ang * dist);
501                                         traceline(org, org - '0 0 48', TRUE, self);
502                                         if (trace_fraction != 1)
503                                         {
504                                                 SpawnTempWaypoint(org);
505                                                 flag = TRUE;
506                                         }
507                                 }
508                         }
509                 }
510         }
511         self.b_angle_y = self.v_angle_y + 10;
512 };