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