]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/bot/bot_way.qc
commit 1.2.1 game media
[divverent/nexuiz.git] / data / qcsrc / bot / bot_way.qc
1  /***********************************************
2 *                                              *
3 *             FrikBot Waypoints                *
4 *         "The better than roaming 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
46
47 /*
48 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
49
50 Waypoint Linking code
51
52 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
53 */
54
55
56 float (entity e1, entity e2) CheckLinked =
57 {
58         if ((e1 == e2) || (e2 == world) || (e1 == world))
59                 return FALSE;
60         else if (e1.target1 == e2)
61         {
62                 if (e1.b_aiflags & AI_TELELINK_1)
63                         return 2;
64                 else return TRUE;
65         }
66         else if (e1.target2 == e2)
67         {
68                 if (e1.b_aiflags & AI_TELELINK_2)
69                         return 2;
70                 else return TRUE;
71         }
72         else if (e1.target3 == e2)
73         {
74                 if (e1.b_aiflags & AI_TELELINK_3)
75                         return 2;
76                 else return TRUE;
77         }
78         else if (e1.target4 == e2)
79         {
80                 if (e1.b_aiflags & AI_TELELINK_4)
81                         return 2;
82                 else return TRUE;
83         }
84
85         else return FALSE;
86 };
87
88
89 float (entity e1, entity e2) LinkWays =
90 {
91         if ((e1 == e2) || (e2 == world) || (e1 == world))
92                 return FALSE;
93         else if (CheckLinked(e1, e2))
94                 return FALSE; // already linked!!!
95
96         if (e1.target1 == world)
97         {
98                 e1.target1 = e2;
99                 return TRUE;
100         }
101         else if (e1.target2 == world)
102         {
103                 e1.target2 = e2;
104                 return TRUE;
105         }
106         else if (e1.target3 == world)
107         {
108                 e1.target3 = e2;
109                 return TRUE;
110         }
111         else if (e1.target4 == world)
112         {
113                 e1.target4 = e2;
114                 return TRUE;
115         }
116         else return FALSE;
117
118 };
119 // Link Ways part 2, used only for teleporters
120
121 float (entity e1, entity e2) TeleLinkWays =
122 {
123         if ((e1 == e2) || (e2 == world) || (e1 == world))
124                 return FALSE;
125         else if (CheckLinked(e1, e2))
126                 return FALSE; // already linked!!!
127
128         if (e1.target1 == world)
129         {
130                 e1.target1 = e2;
131                 e1.b_aiflags = e1.b_aiflags | AI_TELELINK_1;
132                 return TRUE;
133         }
134         else if (e1.target2 == world)
135         {
136                 e1.target2 = e2;
137                 e1.b_aiflags = e1.b_aiflags | AI_TELELINK_2;
138                 return TRUE;
139         }
140         else if (e1.target3 == world)
141         {
142                 e1.target3 = e2;
143                 e1.b_aiflags = e1.b_aiflags | AI_TELELINK_3;
144             return TRUE;
145         }
146         else if (e1.target4 == world)
147         {
148                 e1.target4 = e2;
149                 e1.b_aiflags = e1.b_aiflags | AI_TELELINK_4;
150                 return TRUE;
151         }
152         else
153                 return FALSE;
154
155 };
156
157 void (entity e1, entity e2) UnlinkWays =
158 {
159         if ((e1 == e2) || (e2 == world) || (e1 == world))
160                 return;
161         else if (!CheckLinked(e1, e2))
162                 return;
163
164         if (e1.target1 == e2)
165         {
166                 e1.b_aiflags = e1.b_aiflags - (e1.b_aiflags & AI_TELELINK_1);
167                 e1.target1 = world;
168         }
169         if (e1.target2 == e2)
170         {
171                 e1.b_aiflags = e1.b_aiflags - (e1.b_aiflags & AI_TELELINK_2);
172                 e1.target2 = world;
173         }
174         if (e1.target3 == e2)
175         {
176                 e1.b_aiflags = e1.b_aiflags - (e1.b_aiflags & AI_TELELINK_3);
177                 e1.target3 = world;
178         }
179         if (e1.target4 == e2)
180         {
181                 e1.b_aiflags = e1.b_aiflags - (e1.b_aiflags & AI_TELELINK_4);
182                 e1.target4 = world;
183         }
184
185 };
186
187 /*
188 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
189
190 FindWaypoint
191
192 This is used quite a bit, by many different
193 functions big lag causer
194
195 Finds the closest, fisible, waypoint to e
196
197 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
198 */
199
200 entity(entity start) FindWayPoint =
201 {
202         local entity t;
203         local vector org;
204         local float dst, tdst;
205         local entity best;
206
207         org = realorigin(self);
208
209         t = way_head; 
210         if (start != world)
211         {
212                 dst = vlen(start.origin - org);
213                 best = start;
214         }
215         else
216         {
217                 dst = 100000;
218                 best = world;
219         }
220         while(t)
221         {
222                 // real players cut through ignore types
223                 if (dst < 20)
224                         return best;
225                 if (!(t.b_aiflags & AI_IGNORE_TYPES) || self.ishuman)
226                 {
227                         tdst = vlen(t.origin - org);
228                         if (tdst < dst)
229                         {
230                                 if (sisible(t))
231                                 {
232                                         dst = tdst;
233                                         best = t;
234                                 }
235                         }
236                 }
237                 t = t._next;
238         } 
239         return best;
240 };
241
242 /*
243 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
244
245 Waypoint Spawning Code
246
247 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
248 */
249
250 entity way_foot; // Ugh. Do I need a foot for this or not?
251
252 entity(vector org) make_waypoint = 
253 {
254         local entity point;
255         point = spawn();
256         point.classname = "waypoint";
257         point.search_time = time; // don't double back for me;
258         point.solid = SOLID_TRIGGER;
259         point.movetype = MOVETYPE_NONE;
260         point.items = -1;
261         setorigin(point, org);
262         
263         setsize(point, PL_MIN, PL_MAX);
264         waypoints = waypoints + 1;
265         if (!way_head)
266         {
267                 way_head = point;
268                 way_foot = point;
269         }
270         else
271         {
272                 way_foot._next = point;
273                 point._last = way_foot;
274                 way_foot = point;
275         }
276
277         point.count = waypoints;
278         if (waypoint_mode > WM_LOADED) // editor modes
279                 setmodel(point, "progs/s_bubble.spr"); 
280         return point;
281 };
282
283 /*
284 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
285
286 Dynamic Waypoint spawning and linking. Not
287 very good all things considered.
288
289 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
290 */
291
292 void() DynamicWaypoint =
293 {
294         local entity t;
295         local float dist, dynlink, dynpoint, editor;
296
297         if (self.teleport_time > self.portal_time)
298         {
299                 if (!self.flags & FL_WATERJUMP)
300                 {
301                         self.dyn_flags = 2;
302                         if (!self.ishuman)
303                         {
304                                 bot_lost(self.target1, TRUE);
305                                 self.enemy = world;
306                         }
307                 }
308                 self.portal_time = self.teleport_time;
309         }
310 // stacking everything on waypoint_mode might've been good for the editor,
311 // but it sucks to beat hell for this code.
312
313
314 // convert waypoint_mode to something more usable..
315         if (waypoint_mode > WM_LOADED)
316         {
317                 if (self.ishuman)
318                 {
319                         if (waypoint_mode == WM_EDITOR_DYNLINK)
320                                 dynlink = 1;
321                         else if (waypoint_mode == WM_EDITOR_DYNAMIC)
322                                 dynlink = dynpoint = 1;
323                         editor = 1;
324                 }
325         }       
326         else if (waypoint_mode == WM_DYNAMIC)
327                 dynlink = dynpoint = 1;
328
329 // if there's nothing for dynamic to do..
330         if (!dynpoint)
331         {
332                 if (!editor)
333                         return;
334         }
335 // for speed sake, I won't have bots dynamic waypoint in coop
336         if (!self.ishuman)
337                 if (coop)
338                         return;
339
340 // don't waypoint in single player
341         if (max_clients < 2)
342                 return; 
343 // if you're dead
344         else if (self.health <= 0)
345         {
346                 if (dynpoint)
347                 {
348                         if (self.current_way)
349                         {
350                                 if (pointcontents(self.origin) < -4)
351                                 {
352                                         if (self.current_way.b_aiflags & AI_BLIND)
353                                                 self.current_way.b_aiflags = self.current_way.b_aiflags | AI_PRECISION;
354                                         else
355                                                 self.current_way.b_aiflags = self.current_way.b_aiflags | AI_BLIND;
356                                 }
357                         }
358                 }
359                 self.dyn_dest = '0 0 0';
360                 self.current_way = world;
361                 self.dyn_flags = 0;
362                 return;
363         }
364
365 // you shouldn't be making waypoints mid air
366         if (dynpoint)
367         {
368                 if (!((self.flags & FL_ONGROUND) || self.waterlevel == 3))
369                 {
370                         if (self.dyn_flags != 2)
371                         {
372                                 self.dyn_flags = 1;
373                         }
374                         return;
375                 }
376         }
377 // keep from doing the rest of this every frame
378         if (self.dyn_time > time)
379                 return;
380         self.dyn_time = time + 0.2;
381
382 // display the links for editor mode
383         if (editor)
384         {
385                 if (self.current_way)
386                 {
387                         if (self.current_way.target1)
388                                 DeveloperLightning(self.current_way, self.current_way.target1, self.current_way.b_aiflags & AI_TELELINK_1);
389                         if (self.current_way.target2)
390                                 DeveloperLightning(self.current_way, self.current_way.target2, self.current_way.b_aiflags & AI_TELELINK_2);
391                         if (self.current_way.target3)
392                                 DeveloperLightning(self.current_way, self.current_way.target3, self.current_way.b_aiflags & AI_TELELINK_3);
393                         if (self.current_way.target4)
394                                 DeveloperLightning(self.current_way, self.current_way.target4, self.current_way.b_aiflags & AI_TELELINK_4);
395                 }
396                 if (self.b_aiflags & AI_HOLD_SELECT)
397                         return;
398         }
399
400         t = FindWayPoint(self.current_way);
401         if (t)
402         {
403                 dist = vlen(self.origin - t.origin);
404                 if (dist < 192)
405                 {
406                         if (dist < 64)
407                         {
408
409                                 if (t != self.current_way)
410                                 {
411                                         if (dynlink)
412                                         {
413                                                 if (!self.dyn_flags)
414                                                 {
415                                                         if (wisible(t, self.current_way))
416                                                                 LinkWays(t, self.current_way);
417                                                 }
418                                                 if (self.dyn_flags == 2)
419                                                         TeleLinkWays(self.current_way, t);
420                                                 else if (wisible(t, self.current_way))
421                                                         LinkWays(self.current_way, t);
422                                         }
423                                         if (editor)
424                                         {
425                                                 setmodel(t, "progs/s_light.spr");
426                                                 if (self.current_way)
427                                                         setmodel(self.current_way, "progs/s_bubble.spr");
428                                         }
429                                 }
430                                 self.current_way = t;
431                                 self.dyn_flags = 0;
432                         }
433                         self.dyn_dest = self.origin + self.view_ofs;
434                         return;
435                 }
436         }
437
438         if (frik_recognize_plat(FALSE)) 
439         {
440                 if (vlen(trace_ent.velocity) > 0)
441                 {
442                         if (self.dyn_plat)
443                                 return;
444                         self.dyn_plat = TRUE;
445                         if (!self.dyn_flags)
446                                 self.dyn_flags = 1;
447                         //bprint("on a plat!!!!!\n");
448                 }
449                 else
450                         self.dyn_plat = FALSE;
451         }
452         else
453                 self.dyn_plat = FALSE;
454
455         if (self.dyn_flags == 2)
456                 self.dyn_dest = self.origin + self.view_ofs;
457         else if (self.dyn_dest == '0 0 0')
458                 self.dyn_dest = self.origin + self.view_ofs;
459         if (!dynpoint)
460                 return;
461         t = make_waypoint(self.dyn_dest);
462
463         if (!self.dyn_flags)
464         {
465                 if (wisible(t, self.current_way))
466                         LinkWays(t, self.current_way);
467         }
468         if (self.dyn_flags == 2)
469                 TeleLinkWays(self.current_way, t);
470         else if (wisible(t, self.current_way))
471                 LinkWays(self.current_way, t);
472
473         if (editor)
474         {
475                 setmodel(t, "progs/s_light.spr");
476                 if (self.current_way)
477                         setmodel(self.current_way, "progs/s_bubble.spr");
478         }
479         self.current_way = t;
480         self.dyn_flags = 0;
481         
482         self.dyn_dest = self.origin + self.view_ofs;
483
484         if (frik_recognize_plat(FALSE))
485         {
486                 if (trace_ent.classname == "door")
487                         t.b_aiflags = t.b_aiflags | AI_DOORFLAG;
488         }
489 };
490
491 /*
492 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
493
494 Waypoint Loading from file
495
496 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
497 */
498
499 void() ClearAllWays =
500 {
501
502         local entity t, n;
503         t = way_head;
504         while(t)
505         {
506                 n = t._next;
507                 remove(t);
508                 t = n;
509         }
510         way_head = world;
511         way_foot = world;
512         waypoints = 0;
513 };
514
515 entity(float num) WaypointForNum =
516 {
517         local entity t;
518         if (!num)
519                 return world;
520
521         t = way_head;
522         while (t)
523         {
524                 if (t.count == num)
525                         return t;
526                 t = t._next;
527         }
528         return world;
529 };
530
531 void() FixThisWaypoint = 
532 {
533         self.enemy.target1 = WaypointForNum(self.enemy.b_pants);
534         self.enemy.target2 = WaypointForNum(self.enemy.b_skill);
535         self.enemy.target3 = WaypointForNum(self.enemy.b_shirt);
536         self.enemy.target4 = WaypointForNum(self.enemy.b_frags);
537         self.enemy = self.enemy._next;
538         self.nextthink = time;
539         if (self.enemy == world)
540         {
541                 remove(self);
542                 fixer = world;
543         }
544 };
545
546 void() FixWaypoints =
547 {
548         if (!fixer)
549                 fixer = spawn();
550         fixer.nextthink = time;
551         fixer.think = FixThisWaypoint;
552         fixer.enemy = way_head;
553 };
554
555
556
557 void(entity what) delete_waypoint =
558 {
559         local entity t;
560
561         if (way_head == what)
562                 way_head = what._next;
563         if (way_foot == what)
564                 way_foot = what._last;
565         if (what._last)
566                 what._last._next = what._next;
567         if (what._next)
568                 what._next._last = what._last;
569         waypoints = 0;
570         t = way_head;
571         while(t)
572         {
573                 t.count = waypoints = waypoints + 1;
574                 if (CheckLinked(t, what))
575                         UnlinkWays(t, what);
576                 t = t._next;
577         }
578         if (self.current_way == what)
579                 self.current_way = world;
580         remove(what);
581 };
582
583 /*
584 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
585
586 FindRoute & FindThing used by the pathing code
587 in bot_ai.qc
588
589 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
590 */
591
592
593 entity(string s) FindThing =
594 {
595         local entity t;
596         local float tdst, dst;
597         local entity best;
598         dst = 100000;
599         best = world;
600         t = find (world, classname, s);
601         while (t != world)
602         {
603                 tdst = vlen(((t.absmin + t.absmax) * 0.5) - self.origin);
604             if (tdst < dst)
605                 {
606                         dst = tdst;
607                         best = t;
608                 } 
609                 t = find(t, classname, s);
610         }
611         return best;
612 };
613
614 /*
615 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
616
617 FindRoute, this is a key function in the
618 pathing. The name is a bit misleading, this
619 code finds the closest waypoint that is part
620 of a route calculated by the begin_route and
621 end_route routines This is a definite path to
622 an object.
623
624 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
625 */
626
627 entity(entity lastone) FindRoute =
628 {
629         // kinda like FindWaypoint, only of this bots route though
630         local entity t, best;
631         local float dst, tdst, flag;
632         flag = ClientBitFlag(self.b_clientno);
633         t = way_head;
634         dst = 100000;
635         best = world;
636         while(t)
637         {
638                 tdst = vlen(t.origin - self.origin);
639             if ((tdst < dst) &&  (t.b_sound & flag))
640                 {
641                         if ((lastone == world) || (CheckLinked(lastone, t)))
642                         {
643                                 dst = tdst;
644                                 best = t;
645                         }
646                 } 
647                 t = t._next;
648         } 
649         return best;
650 };
651 /*
652 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
653
654 Route & path table management
655
656 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
657 */
658
659 void() ClearRouteTable =
660 {
661         // cleans up route table
662
663         local entity t;
664         t = way_head;
665         while (t)
666         {
667                 t. keys = FALSE;
668                 t.enemy = world;
669                 t.items = -1; // not in table
670                 t = t._next;
671         }
672 };
673
674 void() ClearMyRoute =
675 {
676         local float flag;
677         local entity t;
678
679         flag = ClientBitFlag(self.b_clientno);
680
681         t = way_head;
682         while (t)
683         {
684                 t.b_sound = t.b_sound - (t.b_sound & flag);
685                 t = t._next;
686         }
687 };
688
689
690 /*
691 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
692
693 Mark_path
694
695 After the route has been found, mark it with
696 bitflags  so the table can be used for a
697 different bot.
698
699 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
700 */
701
702
703 void(entity this) mark_path =
704 {
705         local entity t, oself;
706         local float flag;
707
708         ClearMyRoute();
709
710         oself = self;
711         self = this;
712         t = FindWayPoint(this.current_way);
713         self = oself;
714         // FIXME
715         // ugh, better way to find players please!!!
716         if (this.classname != "player")
717                 this.current_way = t;
718                 
719         if (t.enemy == world)
720         {
721                 bot_lost(this, FALSE);
722                 if (waypoint_mode == WM_DYNAMIC)
723                         self.route_failed = TRUE;
724                 return;
725         }
726
727         flag = ClientBitFlag(self.b_clientno);
728
729         while(t)
730         {
731                 if (t.b_sound & flag)
732                         return;
733                 if (t == self.last_way)
734                         return;
735                 t.b_sound = t.b_sound | flag;
736                 t = t.enemy;    
737         }
738 };
739
740 /*
741 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
742
743 WaypointThink
744
745 Calculates the routes. We use thinks to avoid
746 tripping the runaway loop counter
747
748 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
749 */
750
751 void(entity e2, float b_bit) FollowLink =
752 {
753         local float dist;
754         
755         if (self.b_aiflags & b_bit)
756                 dist = self.items;
757         else
758                 dist = vlen(self.origin - e2.origin) + self.items;
759
760         // check if this is an RJ link
761         if (e2.b_aiflags & AI_SUPER_JUMP)
762         {
763                 if (!bot_can_rj(route_table))
764                         return;
765         }
766         if (e2.b_aiflags & AI_DIFFICULT)
767                 dist = dist + 1000;
768
769         dist = dist + random() * 100; // add a little chaos
770
771         if ((dist < e2.items) || (e2.items == -1))
772         {
773                 if (!e2.keys)
774                         busy_waypoints = busy_waypoints + 1;
775                 e2.keys = TRUE;
776                 e2.items = dist;
777                 e2.think = WaypointThink;
778                 e2.nextthink = time;
779                 e2.enemy = self;
780         }
781 };
782
783 void() WaypointThink =
784 {
785         local entity oself;
786
787         if (self.items == -1)
788                 return;
789         // can you say ugly?
790         if (self.b_aiflags & AI_TRACE_TEST)
791         {
792                 if (self.target1)
793                 {
794                         traceline(self.origin, self.target1.origin, TRUE, self);
795                         if (trace_fraction == 1)
796                                 FollowLink(self.target1, AI_TELELINK_1);
797                 }
798                 if (self.target2)
799                 {
800                         traceline(self.origin, self.target2.origin, TRUE, self);
801                         if (trace_fraction == 1)
802                                 FollowLink(self.target2, AI_TELELINK_2);
803                 }
804                 if (self.target3)
805                 {
806                         traceline(self.origin, self.target3.origin, TRUE, self);
807                         if (trace_fraction == 1)
808                         FollowLink(self.target3, AI_TELELINK_3);
809                 }
810                 if (self.target4)
811                 {
812                         traceline(self.origin, self.target4.origin, TRUE, self);
813                         if (trace_fraction == 1)
814                                 FollowLink(self.target4, AI_TELELINK_4);
815                 }
816         }
817         else
818         {
819                 if (self.target1)
820                         FollowLink(self.target1, AI_TELELINK_1);
821                 if (self.target2)
822                         FollowLink(self.target2, AI_TELELINK_2);
823                 if (self.target3)
824                         FollowLink(self.target3, AI_TELELINK_3);
825                 if (self.target4)
826                         FollowLink(self.target4, AI_TELELINK_4);
827         }
828
829         busy_waypoints = busy_waypoints - 1;
830         self.keys = FALSE;
831
832         if (busy_waypoints <= 0)
833         {
834                 if (direct_route)
835                 {
836                         oself = self;
837                         self = route_table;
838                         bot_get_path(self.target1, FALSE);
839                         self = oself;
840                         direct_route = FALSE;
841                 }
842         }
843 };
844
845 /*
846 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
847
848 begin_route and bot_get_path
849
850 PLEASE NOTE: bot_get_path replaces the old
851 calls to begin_route. 
852
853 Routing isn't done all at once now, but in two
854 stages, the bot will calc a route *THEN*
855 choose a target, *THEN* mark a path.
856
857 Boy it's confusing.
858
859 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
860 */
861
862 float() begin_route =
863 {
864         if (busy_waypoints > 0)
865                 return FALSE;
866         
867         if (route_table != world)
868         {
869                 if (!route_table.ishuman)
870                 {
871                         if (route_table.b_clientno != -1)
872                                 return FALSE;
873                 }
874         }
875
876         route_table = self;
877         ClearRouteTable();
878         self.last_way = FindWayPoint(self.current_way);
879
880         if (self.last_way != world)
881         {
882                 self.last_way.items = vlen(self.last_way.origin - self.origin);
883                 self.last_way.nextthink = time;
884                 self.last_way.think = WaypointThink;
885                 self.last_way.keys = TRUE;
886                 busy_waypoints = 1;
887                 return TRUE;
888         }
889         else 
890         {
891                 route_table = world;
892                 busy_waypoints = 0;
893                 return FALSE;
894         }
895 };
896
897 void(entity this, float direct) bot_get_path =
898 {
899         if (this == world)
900                 return;
901
902         if (route_table == self)
903         {
904                 if (busy_waypoints <= 0)
905                 {
906                         route_table = world;
907                         mark_path(this);
908                 }
909                 return;
910         }
911         if (direct)
912         {
913                 if(begin_route())
914                         direct_route = TRUE;
915                 else
916                         bot_lost(this, FALSE);
917                 return;
918         }
919 };
920
921 /*
922 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
923
924 BSP/QC Waypoint loading
925
926 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
927 */
928
929 void() waypoint =
930 {       
931         self.search_time = time;
932         self.solid = SOLID_TRIGGER;
933         self.movetype = MOVETYPE_NONE;
934         setorigin(self, self.origin);
935         
936         setsize(self, PL_MIN, PL_MAX);
937         waypoints = waypoints + 1;
938         if (!way_head)
939         {
940                 way_head = self;
941                 way_foot = self;
942         }
943         else
944         {
945                 way_foot._next = self;
946                 self._last = way_foot;
947                 way_foot = self;
948         }
949
950         self.count = waypoints;
951         waypoint_mode = WM_LOADED;
952         if (self.count == 1)
953         {
954                 self.think = FixWaypoints; // wait until all bsp loaded points are spawned
955                 self.nextthink = time;
956         }
957 };
958
959 void(vector org, vector bit1, float bit4, float flargs) make_way =
960 {
961         local entity y;
962         waypoint_mode = WM_LOADED;
963         y = make_waypoint(org);
964         y.b_aiflags = flargs;
965         y.b_pants = bit1_x;
966         y.b_skill = bit1_y;
967         y.b_shirt = bit1_z;
968         y.b_frags = bit4;
969         if (y.count == 1)
970         {
971                 y.think = FixWaypoints; // wait until all qc loaded points are spawned
972                 y.nextthink = time;
973         }
974 };
975
976
977 /*
978 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
979
980 Temporary Marker code
981
982 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
983 */
984
985 void(vector org) SpawnTempWaypoint =
986 {       
987         local entity tep;
988
989         if (!self.temp_way)
990                 self.temp_way = tep = spawn();
991         else
992                 tep = self.temp_way;
993
994         tep.classname = "temp_waypoint";
995         tep.search_time = 0;
996         tep.solid = SOLID_TRIGGER;
997         tep.movetype = MOVETYPE_NOCLIP;
998         setorigin(tep, org);
999         target_add(tep);
1000         setsize(tep, PL_MIN, PL_MAX); // FIXME: convert these to numerical
1001 };