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