1 /***********************************************
\r
3 * FrikBot Movement AI *
\r
4 * "The slightly better movement AI" *
\r
6 ***********************************************/
\r
10 This program is in the Public Domain. My crack legal
\r
11 team would like to add:
\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
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
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
40 Any reproduction of this software must contain
\r
41 this notice in its entirety.
\r
47 // TODO check for precision, etc.
\r
48 self.button2 = TRUE;
\r
51 float(entity e) bot_can_rj =
\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
63 if (e.items & 4194304)
\r
66 // do I have rockets & RL?
\r
67 if (!((e.items & 32) && (e.ammo_rockets > 0)))
\r
71 if (e.items & 1048576)
\r
80 float(float flag) frik_recognize_plat =
\r
82 if ((self.classname != "waypoint") && !(self.flags & FL_ONGROUND))
\r
84 traceline(self.origin, self.origin - '0 0 64', TRUE, self);
\r
85 if (trace_ent != world)
\r
87 if (flag) // afect bot movement too
\r
89 if (self.keys & KEY_MOVEUP)
\r
91 if (trace_ent.velocity_z > 0)
\r
92 self.keys = self.keys & 960; // 960 is all view keys
\r
94 else if (self.keys & KEY_MOVEDOWN)
\r
96 if (trace_ent.velocity_z < 0)
\r
97 self.keys = self.keys & 960;
\r
106 float(vector sdir) frik_KeysForDir =
\r
109 local vector keydir;
\r
110 local float outkeys, tang;
\r
112 if (sdir_x || sdir_y)
\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
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
131 outkeys = outkeys + KEY_MOVEUP;
\r
132 else if (sdir_z < 0.7)
\r
133 outkeys = outkeys + KEY_MOVEDOWN;
\r
139 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
\r
143 Bot has hit a ledge or wall that he should
\r
146 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
\r
149 void(vector whichway, float danger) frik_obstructed =
\r
152 local vector disway, org;
\r
154 if (self.b_aiflags & AI_BLIND)
\r
156 org = realorigin(self.target1);
\r
160 self.b_aiflags = self.b_aiflags | AI_DANGER;
\r
161 self.keys = frik_KeysForDir('0 0 0' - whichway);
\r
163 if (self.b_aiflags & AI_PRECISION)
\r
169 if (self.b_aiflags & AI_OBSTRUCTED)
\r
171 if (!(self.b_aiflags & AI_DANGER))
\r
173 self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED;
\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
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
199 self.b_aiflags = self.b_aiflags | AI_OBSTRUCTED;
\r
204 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
\r
208 Detects small bumps the bot needs to jump over
\r
209 or ledges the bot should avoid falling in.
\r
211 Also responsible for jumping gaps.
\r
213 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
\r
216 void() frik_obstacles =
\r
218 local vector start, stop, ang;
\r
219 local float test, conts, dist, hgt;
\r
221 if (!(self.flags & FL_ONGROUND))
\r
223 if (self.b_aiflags & AI_BLIND)
\r
226 ang = normalize(self.velocity);
\r
228 start = self.origin + ang * 32; // ahem
\r
229 start_z = self.origin_z + self.maxs_z;
\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
235 hgt = trace_endpos_z - stop_z;
\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
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
260 dist = ((540 / sv_gravity) * vlen(ang))/* + 32*/;
\r
261 if (test > dist) // I can't make it
\r
263 if (conts < -3) // bad stuff down dare
\r
265 frik_obstructed(ang, TRUE);
\r
272 stop = realorigin(self.target1);
\r
273 if ((stop_z - self.origin_z) < -32)
\r
274 return; // safe to fall
\r
276 frik_obstructed(ang, FALSE);
\r
282 ang = normalize(ang);
\r
284 traceline(self.origin, self.origin + (ang * (test + 20)), TRUE, self);
\r
285 if (trace_fraction != 1)
\r
287 if (conts < -3) // bad stuff down dare
\r
289 frik_obstructed(ang, TRUE);
\r
296 stop = realorigin(self.target1);
\r
297 if ((stop_z - self.origin_z) < -32)
\r
298 return; // safe to fall
\r
300 frik_obstructed(ang, FALSE);
\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
311 if (conts < -3) // bad stuff down dare
\r
313 frik_obstructed(ang, TRUE);
\r
318 frik_obstructed(ang, FALSE);
\r
328 stop = realorigin(self.target1);
\r
329 if ((stop_z - self.origin_z) < -32)
\r
330 return; // safe to fall
\r
339 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
\r
341 After frik_obstructed, the bot uses the
\r
342 following funtion to move "around" the obstacle
\r
344 I have no idea how well it will work
\r
346 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
\r
349 void() frik_dodge_obstruction =
\r
351 local vector way, org;
\r
352 local float oflags, yaw;
\r
354 if (!(self.b_aiflags & AI_OBSTRUCTED))
\r
356 if ((self.b_aiflags & (AI_BLIND | AI_PRECISION)) || !(self.flags & FL_ONGROUND))
\r
358 self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED;
\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
366 oflags = self.flags;
\r
369 yaw = vectoyaw(self.obs_dir);
\r
370 if (walkmove(yaw, 32))
\r
371 self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED;
\r
374 if (self.b_aiflags & AI_DANGER)
\r
376 way = '0 0 0' - self.obs_dir;
\r
378 else if (self.wallhug)
\r
380 way_x = self.obs_dir_y * -1;
\r
381 way_y = self.obs_dir_x;
\r
385 way_x = self.obs_dir_y;
\r
386 way_y = self.obs_dir_x * -1;
\r
388 self.keys = self.keys & 960 + frik_KeysForDir(way);
\r
394 self.flags = oflags;
\r
398 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
\r
400 movetogoal and walkmove replacements
\r
404 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
\r
407 void() frik_movetogoal =
\r
412 if (self.target1 == world)
\r
414 makevectors(self.v_angle);
\r
415 frik_walkmove(v_forward);
\r
418 way = realorigin(self.target1) - self.origin;
\r
419 if (vlen(way) < 25)
\r
421 self.keys = self.keys & 960;
\r
425 way = normalize(way);
\r
426 self.keys = self.keys & 960 + frik_KeysForDir(way);
\r
428 frik_dodge_obstruction();
\r
429 frik_recognize_plat(TRUE);
\r
431 if (self.b_aiflags & AI_PRECISION)
\r
433 g = angcomp(self.v_angle_x, self.b_angle_x);
\r
435 self.keys = self.keys & 960;
\r
436 g = angcomp(self.v_angle_y, self.b_angle_y);
\r
438 self.keys = self.keys & 960;
\r
442 float(vector weird) frik_walkmove =
\r
444 // okay so it's not walkmove
\r
446 self.keys = self.keys & 960 + frik_KeysForDir(weird);
\r
448 frik_dodge_obstruction();
\r
449 frik_recognize_plat(TRUE);
\r
450 if (self.b_aiflags & AI_OBSTRUCTED)
\r
458 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
\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
464 I brought this back because normal roaming
\r
465 won't work - the bot gets distracted by it's
\r
468 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
\r
471 void() frik_bot_roam =
\r
473 local vector org, ang, org1;
\r
474 local float loopcount, flag, dist;
\r
478 while((loopcount > 0) && !flag)
\r
480 loopcount = loopcount - 1;
\r
481 org = self.origin + self.view_ofs;
\r
483 ang_y = frik_anglemod(ang_y - 90 + (random() * 180));
\r
484 ang_x = 0; // avoid upward sloping
\r
486 traceline(org, org + v_forward * 2300, TRUE, self);
\r
487 if (trace_fraction != 1)
\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
495 org = trace_endpos;
\r
496 traceline(org, self.origin + self.view_ofs, TRUE, self);
\r
497 if (trace_fraction != 1)
\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
504 SpawnTempWaypoint(org);
\r
511 self.b_angle_y = self.v_angle_y + 10;
\r