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