]> icculus.org git repositories - btb/d2x.git/blob - main/escort.c
changed args_find to FindArg
[btb/d2x.git] / main / escort.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14
15 #include <conf.h>
16 #include <stdio.h>              //      for printf()
17 #include <stdlib.h>             // for rand() and qsort()
18 #include <string.h>             // for memset()
19
20 #include "inferno.h"
21 #include "mono.h"
22 #include "fix.h"
23 #include "vecmat.h"
24 #include "gr.h"
25 #include "3d.h"
26 #include "palette.h"
27
28 #include "object.h"
29 #include "error.h"
30 #include "ai.h"
31 #include "robot.h"
32 #include "fvi.h"
33 #include "physics.h"
34 #include "wall.h"
35 #include "player.h"
36 #include "fireball.h"
37 #include "game.h"
38 #include "powerup.h"
39 #include "cntrlcen.h"
40 #include "gauges.h"
41 #include "key.h"
42 #include "fuelcen.h"
43 #include "sounds.h"
44 #include "screens.h"
45 #include "text.h"
46 #include "gamefont.h"
47 #include "newmenu.h"
48 #include "playsave.h"
49 #include "gameseq.h"
50 #include "automap.h"
51 #include "laser.h"
52 #include "pa_enabl.h"
53
54 #ifdef EDITOR
55 #include "editor\editor.h"
56 #endif
57
58 extern void multi_send_stolen_items();
59 void say_escort_goal(int goal_num);
60 void show_escort_menu(char *msg);
61
62
63 char *Escort_goal_text[MAX_ESCORT_GOALS] = {
64         "BLUE KEY",
65         "YELLOW KEY",
66         "RED KEY",
67         "REACTOR",
68         "EXIT",
69         "ENERGY",
70         "ENERGYCEN",
71         "SHIELD",
72         "POWERUP",
73         "ROBOT",
74         "HOSTAGES",
75         "SPEW",
76         "SCRAM",
77         "EXIT",
78         "BOSS",
79         "MARKER 1",
80         "MARKER 2",
81         "MARKER 3",
82         "MARKER 4",
83         "MARKER 5",
84         "MARKER 6",
85         "MARKER 7",
86         "MARKER 8",
87         "MARKER 9",
88 // -- too much work --  "KAMIKAZE  "
89 };
90
91 int     Max_escort_length = 200;
92 int     Escort_kill_object = -1;
93 ubyte Stolen_items[MAX_STOLEN_ITEMS];
94 int     Stolen_item_index;
95 fix     Escort_last_path_created = 0;
96 int     Escort_goal_object = ESCORT_GOAL_UNSPECIFIED, Escort_special_goal = -1, Escort_goal_index = -1, Buddy_messages_suppressed = 0;
97 fix     Buddy_sorry_time;
98 int     Buddy_objnum, Buddy_allowed_to_talk;
99 int     Looking_for_marker;
100 int     Last_buddy_key;
101
102 fix     Last_buddy_message_time;
103
104 //if change this length, change in playsave.c also
105 #define GUIDEBOT_NAME_LEN 9
106 char guidebot_name[GUIDEBOT_NAME_LEN+1] = "GUIDE-BOT";
107 char real_guidebot_name[GUIDEBOT_NAME_LEN+1] = "GUIDE-BOT";
108
109 void init_buddy_for_level(void)
110 {
111         int     i;
112
113         Buddy_allowed_to_talk = 0;
114         Buddy_objnum = -1;
115         Escort_goal_object = ESCORT_GOAL_UNSPECIFIED;
116         Escort_special_goal = -1;
117         Escort_goal_index = -1;
118         Buddy_messages_suppressed = 0;
119
120         for (i=0; i<=Highest_object_index; i++)
121                 if (Robot_info[Objects[i].id].companion)
122                         break;
123         if (i <= Highest_object_index)
124                 Buddy_objnum = i;
125
126         Buddy_sorry_time = -F1_0;
127
128         Looking_for_marker = -1;
129         Last_buddy_key = -1;
130 }
131
132 //      -----------------------------------------------------------------------------
133 //      See if segment from curseg through sidenum is reachable.
134 //      Return true if it is reachable, else return false.
135 int segment_is_reachable(int curseg, int sidenum)
136 {
137         int             wall_num, rval;
138         segment *segp = &Segments[curseg];
139
140         if (!IS_CHILD(segp->children[sidenum]))
141                 return 0;
142
143         wall_num = segp->sides[sidenum].wall_num;
144
145         //      If no wall, then it is reachable
146         if (wall_num == -1)
147                 return 1;
148
149         rval = ai_door_is_openable(NULL, segp, sidenum);
150
151         return rval;
152
153 // -- MK, 10/17/95 -- 
154 // -- MK, 10/17/95 --   //      Hmm, a closed wall.  I think this mean not reachable.
155 // -- MK, 10/17/95 --   if (Walls[wall_num].type == WALL_CLOSED)
156 // -- MK, 10/17/95 --           return 0;
157 // -- MK, 10/17/95 -- 
158 // -- MK, 10/17/95 --   if (Walls[wall_num].type == WALL_DOOR) {
159 // -- MK, 10/17/95 --           if (Walls[wall_num].keys == KEY_NONE) {
160 // -- MK, 10/17/95 --                   return 1;               //      @MK, 10/17/95: Be consistent with ai_door_is_openable
161 // -- MK, 10/17/95 -- // --                     if (Walls[wall_num].flags & WALL_DOOR_LOCKED)
162 // -- MK, 10/17/95 -- // --                             return 0;
163 // -- MK, 10/17/95 -- // --                     else
164 // -- MK, 10/17/95 -- // --                             return 1;
165 // -- MK, 10/17/95 --           } else if (Walls[wall_num].keys == KEY_BLUE)
166 // -- MK, 10/17/95 --                   return (Players[Player_num].flags & PLAYER_FLAGS_BLUE_KEY);
167 // -- MK, 10/17/95 --           else if (Walls[wall_num].keys == KEY_GOLD)
168 // -- MK, 10/17/95 --                   return (Players[Player_num].flags & PLAYER_FLAGS_GOLD_KEY);
169 // -- MK, 10/17/95 --           else if (Walls[wall_num].keys == KEY_RED)
170 // -- MK, 10/17/95 --                   return (Players[Player_num].flags & PLAYER_FLAGS_RED_KEY);
171 // -- MK, 10/17/95 --           else
172 // -- MK, 10/17/95 --                   Int3(); //      Impossible!  Doesn't have no key, but doesn't have any key!
173 // -- MK, 10/17/95 --   } else
174 // -- MK, 10/17/95 --           return 1;
175 // -- MK, 10/17/95 -- 
176 // -- MK, 10/17/95 --   Int3(); //      Hmm, thought 'if' above had to return!
177 // -- MK, 10/17/95 --   return 0;
178
179 }
180
181
182 //      -----------------------------------------------------------------------------
183 //      Create a breadth-first list of segments reachable from current segment.
184 //      max_segs is maximum number of segments to search.  Use MAX_SEGMENTS to search all.
185 //      On exit, *length <= max_segs.
186 //      Input:
187 //              start_seg
188 //      Output:
189 //              bfs_list:       array of shorts, each reachable segment.  Includes start segment.
190 //              length:         number of elements in bfs_list
191 void create_bfs_list(int start_seg, short bfs_list[], int *length, int max_segs)
192 {
193         int     i, head, tail;
194         byte    visited[MAX_SEGMENTS];
195
196         for (i=0; i<MAX_SEGMENTS; i++)
197                 visited[i] = 0;
198
199         head = 0;
200         tail = 0;
201
202         bfs_list[head++] = start_seg;
203         visited[start_seg] = 1;
204
205         while ((head != tail) && (head < max_segs)) {
206                 int             i;
207                 int             curseg;
208                 segment *cursegp;
209
210                 curseg = bfs_list[tail++];
211                 cursegp = &Segments[curseg];
212
213                 for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
214                         int     connected_seg;
215
216                         connected_seg = cursegp->children[i];
217
218                         if (IS_CHILD(connected_seg) && (visited[connected_seg] == 0)) {
219                                 if (segment_is_reachable(curseg, i)) {
220                                         bfs_list[head++] = connected_seg;
221                                         if (head >= max_segs)
222                                                 break;
223                                         visited[connected_seg] = 1;
224                                         Assert(head < MAX_SEGMENTS);
225                                 }
226                         }
227                 }
228         }
229
230         *length = head;
231         
232 }
233
234 //      -----------------------------------------------------------------------------
235 //      Return true if ok for buddy to talk, else return false.
236 //      Buddy is allowed to talk if the segment he is in does not contain a blastable wall that has not been blasted
237 //      AND he has never yet, since being initialized for level, been allowed to talk.
238 int ok_for_buddy_to_talk(void)
239 {
240         int             i;
241         segment *segp;
242
243         if (Objects[Buddy_objnum].type != OBJ_ROBOT) {
244                 Buddy_allowed_to_talk = 0;
245                 return 0;
246         }
247
248         if (Buddy_allowed_to_talk)
249                 return 1;
250
251         if ((Objects[Buddy_objnum].type == OBJ_ROBOT) && (Buddy_objnum <= Highest_object_index) && !Robot_info[Objects[Buddy_objnum].id].companion) {
252                 for (i=0; i<=Highest_object_index; i++)
253                         if (Robot_info[Objects[i].id].companion)
254                                 break;
255                 if (i > Highest_object_index)
256                         return 0;
257                 else
258                         Buddy_objnum = i;
259         }
260
261         segp = &Segments[Objects[Buddy_objnum].segnum];
262
263         for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
264                 int     wall_num = segp->sides[i].wall_num;
265
266                 if (wall_num != -1) {
267                         if ((Walls[wall_num].type == WALL_BLASTABLE) && !(Walls[wall_num].flags & WALL_BLASTED))
268                                 return 0;
269                 }
270
271                 //      Check one level deeper.
272                 if (IS_CHILD(segp->children[i])) {
273                         int             j;
274                         segment *csegp = &Segments[segp->children[i]];
275
276                         for (j=0; j<MAX_SIDES_PER_SEGMENT; j++) {
277                                 int     wall2 = csegp->sides[j].wall_num;
278
279                                 if (wall2 != -1) {
280                                         if ((Walls[wall2].type == WALL_BLASTABLE) && !(Walls[wall2].flags & WALL_BLASTED))
281                                                 return 0;
282                                 }
283                         }
284                 }
285         }
286
287         Buddy_allowed_to_talk = 1;
288         return 1;
289 }
290
291 //      --------------------------------------------------------------------------------------------
292 void detect_escort_goal_accomplished(int index)
293 {
294         int     i,j;
295         int     detected = 0;
296
297         if (!Buddy_allowed_to_talk)
298                 return;
299
300         //      If goal is to go away, how can it be achieved?
301         if (Escort_special_goal == ESCORT_GOAL_SCRAM)
302                 return;
303
304 //      See if goal found was a key.  Need to handle default goals differently.
305 //      Note, no buddy_met_goal sound when blow up reactor or exit.  Not great, but ok
306 //      since for reactor, noisy, for exit, buddy is disappearing.
307 if ((Escort_special_goal == -1) && (Escort_goal_index == index)) {
308         detected = 1;
309         goto dega_ok;
310 }
311
312 if ((Escort_goal_index <= ESCORT_GOAL_RED_KEY) && (index >= 0)) {
313         if (Objects[index].type == OBJ_POWERUP)  {
314                 if (Objects[index].id == POW_KEY_BLUE) {
315                         if (Escort_goal_index == ESCORT_GOAL_BLUE_KEY) {
316                                 detected = 1;
317                                 goto dega_ok;
318                         }
319                 } else if (Objects[index].id == POW_KEY_GOLD) {
320                         if (Escort_goal_index == ESCORT_GOAL_GOLD_KEY) {
321                                 detected = 1;
322                                 goto dega_ok;
323                         }
324                 } else if (Objects[index].id == POW_KEY_RED) {
325                         if (Escort_goal_index == ESCORT_GOAL_RED_KEY) {
326                                 detected = 1;
327                                 goto dega_ok;
328                         }
329                 }
330         }
331 }
332         if (Escort_special_goal != -1)
333         {
334                 if (Escort_special_goal == ESCORT_GOAL_ENERGYCEN) {
335                         if (index == -4)
336                                 detected = 1;
337                         else {
338                                 for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
339                                         if (Segments[index].children[i] == Escort_goal_index) {
340                                                 detected = 1;
341                                                 goto dega_ok;
342                                         } else {
343                                                 for (j=0; j<MAX_SIDES_PER_SEGMENT; j++)
344                                                         if (Segments[i].children[j] == Escort_goal_index) {
345                                                                 detected = 1;
346                                                                 goto dega_ok;
347                                                         }
348                                         }
349                         }
350                 } else if ((Objects[index].type == OBJ_POWERUP) && (Escort_special_goal == ESCORT_GOAL_POWERUP))
351                         detected = 1;   //      Any type of powerup picked up will do.
352                 else if ((Objects[index].type == Objects[Escort_goal_index].type) && (Objects[index].id == Objects[Escort_goal_index].id)) {
353                         //      Note: This will help a little bit in making the buddy believe a goal is satisfied.  Won't work for a general goal like "find any powerup"
354                         // because of the insistence of both type and id matching.
355                         detected = 1;
356                 }
357         }
358
359 dega_ok: ;
360         if (detected && ok_for_buddy_to_talk()) {
361                 digi_play_sample_once(SOUND_BUDDY_MET_GOAL, F1_0);
362                 Escort_goal_index = -1;
363                 Escort_goal_object = ESCORT_GOAL_UNSPECIFIED;
364                 Escort_special_goal = -1;
365                 Looking_for_marker = -1;
366         }
367
368 }
369
370 void change_guidebot_name()
371 {
372         newmenu_item m;
373         char text[GUIDEBOT_NAME_LEN+1]="";
374         int item;
375
376         strcpy(text,guidebot_name);
377
378         m.type=NM_TYPE_INPUT; m.text_len = GUIDEBOT_NAME_LEN; m.text = text;
379         item = newmenu_do( NULL, "Enter Guide-bot name:", 1, &m, NULL );
380
381         if (item != -1) {
382                 strcpy(guidebot_name,text);
383                 strcpy(real_guidebot_name,text);
384                 write_player_file();
385         }
386 }
387
388 //      -----------------------------------------------------------------------------
389 void buddy_message(char * format, ... )
390 {
391         if (Buddy_messages_suppressed)
392                 return;
393
394         if (Game_mode & GM_MULTI)
395                 return;
396
397         if ((Last_buddy_message_time + F1_0 < GameTime) || (Last_buddy_message_time > GameTime)) {
398                 if (ok_for_buddy_to_talk()) {
399                         char    gb_str[16], new_format[128];
400                         va_list args;
401                         int t;
402
403                         va_start(args, format );
404                         vsprintf(new_format, format, args);
405                         va_end(args);
406
407                         gb_str[0] = 1;
408                         gb_str[1] = BM_XRGB(28, 0, 0);
409                         strcpy(&gb_str[2], guidebot_name);
410                         t = strlen(gb_str);
411                         gb_str[t] = ':';
412                         gb_str[t+1] = 1;
413                         gb_str[t+2] = BM_XRGB(0, 31, 0);
414                         gb_str[t+3] = 0;
415
416                         HUD_init_message("%s %s", gb_str, new_format);
417
418                         Last_buddy_message_time = GameTime;
419                 }
420         }
421
422 }
423
424 //      -----------------------------------------------------------------------------
425 void thief_message(char * format, ... )
426 {
427
428         char    gb_str[16], new_format[128];
429         va_list args;
430
431         va_start(args, format );
432         vsprintf(new_format, format, args);
433         va_end(args);
434
435         gb_str[0] = 1;
436         gb_str[1] = BM_XRGB(28, 0, 0);
437         strcpy(&gb_str[2], "THIEF:");
438         gb_str[8] = 1;
439         gb_str[9] = BM_XRGB(0, 31, 0);
440         gb_str[10] = 0;
441
442         HUD_init_message("%s %s", gb_str, new_format);
443
444 }
445
446 //      -----------------------------------------------------------------------------
447 //      Return true if marker #id has been placed.
448 int marker_exists_in_mine(int id)
449 {
450         int     i;
451
452         for (i=0; i<=Highest_object_index; i++)
453                 if (Objects[i].type == OBJ_MARKER)
454                         if (Objects[i].id == id)
455                                 return 1;
456
457         return 0;
458 }
459
460 //      -----------------------------------------------------------------------------
461 void set_escort_special_goal(int special_key)
462 {
463         int marker_key;
464
465         Buddy_messages_suppressed = 0;
466
467         if (!Buddy_allowed_to_talk) {
468                 ok_for_buddy_to_talk();
469                 if (!Buddy_allowed_to_talk) {
470                         int     i;
471
472                         for (i=0; i<=Highest_object_index; i++)
473                                 if ((Objects[i].type == OBJ_ROBOT) && Robot_info[Objects[i].id].companion) {
474                                         HUD_init_message("%s has not been released.",guidebot_name);
475                                         break;
476                                 }
477                         if (i == Highest_object_index+1)
478                                 HUD_init_message("No Guide-Bot in mine.");
479
480                         return;
481                 }
482         }
483
484         special_key = special_key & (~KEY_SHIFTED);
485
486         marker_key = special_key;
487         
488         #ifdef MACINTOSH
489         switch(special_key) {
490                 case KEY_5:
491                         marker_key = KEY_1+4;
492                         break;
493                 case KEY_6:
494                         marker_key = KEY_1+5;
495                         break;
496                 case KEY_7:
497                         marker_key = KEY_1+6;
498                         break;
499                 case KEY_8:
500                         marker_key = KEY_1+7;
501                         break;
502                 case KEY_9:
503                         marker_key = KEY_1+8;
504                         break;
505                 case KEY_0:
506                         marker_key = KEY_1+9;
507                         break;
508         }
509         #endif
510
511         if (Last_buddy_key == special_key)
512         {
513                 if ((Looking_for_marker == -1) && (special_key != KEY_0)) {
514                         if (marker_exists_in_mine(marker_key - KEY_1))
515                                 Looking_for_marker = marker_key - KEY_1;
516                         else {
517                                 Last_buddy_message_time = 0;    //      Force this message to get through.
518                                 buddy_message("Marker %i not placed.", marker_key - KEY_1 + 1);
519                                 Looking_for_marker = -1;
520                         }
521                 } else {
522                         Looking_for_marker = -1;
523                 }
524         }
525
526         Last_buddy_key = special_key;
527
528         if (special_key == KEY_0)
529                 Looking_for_marker = -1;
530                 
531         if ( Looking_for_marker != -1 ) {
532                 Escort_special_goal = ESCORT_GOAL_MARKER1 + marker_key - KEY_1;
533         } else {
534                 switch (special_key) {
535                         case KEY_1:     Escort_special_goal = ESCORT_GOAL_ENERGY;                       break;
536                         case KEY_2:     Escort_special_goal = ESCORT_GOAL_ENERGYCEN;            break;
537                         case KEY_3:     Escort_special_goal = ESCORT_GOAL_SHIELD;                       break;
538                         case KEY_4:     Escort_special_goal = ESCORT_GOAL_POWERUP;              break;
539                         case KEY_5:     Escort_special_goal = ESCORT_GOAL_ROBOT;                        break;
540                         case KEY_6:     Escort_special_goal = ESCORT_GOAL_HOSTAGE;              break;
541                         case KEY_7:     Escort_special_goal = ESCORT_GOAL_SCRAM;                        break;
542                         case KEY_8:     Escort_special_goal = ESCORT_GOAL_PLAYER_SPEW;  break;
543                         case KEY_9:     Escort_special_goal = ESCORT_GOAL_EXIT;                 break;
544                         case KEY_0:     Escort_special_goal = -1;                                                               break;
545                         default:
546                                 Int3();         //      Oops, called with illegal key value.
547                 }
548         }
549
550         Last_buddy_message_time = GameTime - 2*F1_0;    //      Allow next message to come through.
551
552         say_escort_goal(Escort_special_goal);
553         // -- Escort_goal_object = escort_set_goal_object();
554
555         Escort_goal_object = ESCORT_GOAL_UNSPECIFIED;
556 }
557
558 // -- old, pre-bfs, way -- //   -----------------------------------------------------------------------------
559 // -- old, pre-bfs, way -- //   Return object of interest.
560 // -- old, pre-bfs, way -- int exists_in_mine(int objtype, int objid)
561 // -- old, pre-bfs, way -- {
562 // -- old, pre-bfs, way --      int     i;
563 // -- old, pre-bfs, way -- 
564 // -- old, pre-bfs, way --      mprintf((0, "exists_in_mine, type == %i, id == %i\n", objtype, objid));
565 // -- old, pre-bfs, way -- 
566 // -- old, pre-bfs, way --      if (objtype == FUELCEN_CHECK) {
567 // -- old, pre-bfs, way --              for (i=0; i<=Highest_segment_index; i++)
568 // -- old, pre-bfs, way --                      if (Segments[i].special == SEGMENT_IS_FUELCEN)
569 // -- old, pre-bfs, way --                              return i;
570 // -- old, pre-bfs, way --      } else {
571 // -- old, pre-bfs, way --              for (i=0; i<=Highest_object_index; i++) {
572 // -- old, pre-bfs, way --                      if (Objects[i].type == objtype) {
573 // -- old, pre-bfs, way --                              //      Don't find escort robots if looking for robot!
574 // -- old, pre-bfs, way --                              if ((Objects[i].type == OBJ_ROBOT) && (Robot_info[Objects[i].id].companion))
575 // -- old, pre-bfs, way --                                      continue;
576 // -- old, pre-bfs, way -- 
577 // -- old, pre-bfs, way --                              if (objid == -1) {
578 // -- old, pre-bfs, way --                                      if ((objtype == OBJ_POWERUP) && (Objects[i].id != POW_KEY_BLUE) && (Objects[i].id != POW_KEY_GOLD) && (Objects[i].id != POW_KEY_RED))
579 // -- old, pre-bfs, way --                                              return i;
580 // -- old, pre-bfs, way --                                      else
581 // -- old, pre-bfs, way --                                              return i;
582 // -- old, pre-bfs, way --                              } else if (Objects[i].id == objid)
583 // -- old, pre-bfs, way --                                      return i;
584 // -- old, pre-bfs, way --                      }
585 // -- old, pre-bfs, way --              }
586 // -- old, pre-bfs, way --      }
587 // -- old, pre-bfs, way -- 
588 // -- old, pre-bfs, way --      return -1;
589 // -- old, pre-bfs, way -- 
590 // -- old, pre-bfs, way -- }
591
592 //      -----------------------------------------------------------------------------
593 //      Return id of boss.
594 int get_boss_id(void)
595 {
596         int     i;
597
598         for (i=0; i<=Highest_object_index; i++)
599                 if (Objects[i].type == OBJ_ROBOT)
600                         if (Robot_info[Objects[i].id].boss_flag)
601                                 return Objects[i].id;
602
603         return -1;
604 }
605
606 //      -----------------------------------------------------------------------------
607 //      Return object index if object of objtype, objid exists in mine, else return -1
608 //      "special" is used to find objects spewed by player which is hacked into flags field of powerup.
609 int exists_in_mine_2(int segnum, int objtype, int objid, int special)
610 {
611         if (Segments[segnum].objects != -1) {
612                 int             objnum = Segments[segnum].objects;
613
614                 while (objnum != -1) {
615                         object  *curobjp = &Objects[objnum];
616
617                         if (special == ESCORT_GOAL_PLAYER_SPEW) {
618                                 if (curobjp->flags & OF_PLAYER_DROPPED)
619                                         return objnum;
620                         }
621
622                         if (curobjp->type == objtype) {
623                                 //      Don't find escort robots if looking for robot!
624                                 if ((curobjp->type == OBJ_ROBOT) && (Robot_info[curobjp->id].companion))
625                                         ;
626                                 else if (objid == -1) {
627                                         if ((objtype == OBJ_POWERUP) && (curobjp->id != POW_KEY_BLUE) && (curobjp->id != POW_KEY_GOLD) && (curobjp->id != POW_KEY_RED))
628                                                 return objnum;
629                                         else
630                                                 return objnum;
631                                 } else if (curobjp->id == objid)
632                                         return objnum;
633                         }
634
635                         if (objtype == OBJ_POWERUP)
636                                 if (curobjp->contains_count)
637                                         if (curobjp->contains_type == OBJ_POWERUP)
638                                                 if (curobjp->contains_id == objid)
639                                                         return objnum;
640
641                         objnum = curobjp->next;
642                 }
643         }
644
645         return -1;
646 }
647
648 //      -----------------------------------------------------------------------------
649 //      Return nearest object of interest.
650 //      If special == ESCORT_GOAL_PLAYER_SPEW, then looking for any object spewed by player.
651 //      -1 means object does not exist in mine.
652 //      -2 means object does exist in mine, but buddy-bot can't reach it (eg, behind triggered wall)
653 int exists_in_mine(int start_seg, int objtype, int objid, int special)
654 {
655         int     segindex, segnum;
656         short   bfs_list[MAX_SEGMENTS];
657         int     length;
658
659 //      mprintf((0, "exists_in_mine, type == %i, id == %i\n", objtype, objid));
660
661         create_bfs_list(start_seg, bfs_list, &length, MAX_SEGMENTS);
662
663         if (objtype == FUELCEN_CHECK) {
664                 for (segindex=0; segindex<length; segindex++) {
665                         segnum = bfs_list[segindex];
666                         if (Segment2s[segnum].special == SEGMENT_IS_FUELCEN)
667                                 return segnum;
668                 }
669         } else {
670                 for (segindex=0; segindex<length; segindex++) {
671                         int     objnum;
672
673                         segnum = bfs_list[segindex];
674
675                         objnum = exists_in_mine_2(segnum, objtype, objid, special);
676                         if (objnum != -1)
677                                 return objnum;
678
679                 }
680         }
681
682         //      Couldn't find what we're looking for by looking at connectivity.
683         //      See if it's in the mine.  It could be hidden behind a trigger or switch
684         //      which the buddybot doesn't understand.
685         if (objtype == FUELCEN_CHECK) {
686                 for (segnum=0; segnum<=Highest_segment_index; segnum++)
687                         if (Segment2s[segnum].special == SEGMENT_IS_FUELCEN)
688                                 return -2;
689         } else {
690                 for (segnum=0; segnum<=Highest_segment_index; segnum++) {
691                         int     objnum;
692
693                         objnum = exists_in_mine_2(segnum, objtype, objid, special);
694                         if (objnum != -1)
695                                 return -2;
696                 }
697         }
698
699         return -1;
700 }
701
702 //      -----------------------------------------------------------------------------
703 //      Return true if it happened, else return false.
704 int find_exit_segment(void)
705 {
706         int     i,j;
707
708         //      ---------- Find exit doors ----------
709         for (i=0; i<=Highest_segment_index; i++)
710                 for (j=0; j<MAX_SIDES_PER_SEGMENT; j++)
711                         if (Segments[i].children[j] == -2) {
712                                 return i;
713                         }
714
715         return -1;
716 }
717
718 #define BUDDY_MARKER_TEXT_LEN   25
719
720 //      -----------------------------------------------------------------------------
721 void say_escort_goal(int goal_num)
722 {
723         if (Player_is_dead)
724                 return;
725
726         switch (goal_num) {
727                 case ESCORT_GOAL_BLUE_KEY:              buddy_message("Finding BLUE KEY");                      break;
728                 case ESCORT_GOAL_GOLD_KEY:              buddy_message("Finding YELLOW KEY");            break;
729                 case ESCORT_GOAL_RED_KEY:               buddy_message("Finding RED KEY");                       break;
730                 case ESCORT_GOAL_CONTROLCEN:    buddy_message("Finding REACTOR");                       break;
731                 case ESCORT_GOAL_EXIT:                  buddy_message("Finding EXIT");                          break;
732                 case ESCORT_GOAL_ENERGY:                buddy_message("Finding ENERGY");                                break;
733                 case ESCORT_GOAL_ENERGYCEN:     buddy_message("Finding ENERGY CENTER"); break;
734                 case ESCORT_GOAL_SHIELD:                buddy_message("Finding a SHIELD");                      break;
735                 case ESCORT_GOAL_POWERUP:               buddy_message("Finding a POWERUP");                     break;
736                 case ESCORT_GOAL_ROBOT:                 buddy_message("Finding a ROBOT");                       break;
737                 case ESCORT_GOAL_HOSTAGE:               buddy_message("Finding a HOSTAGE");                     break;
738                 case ESCORT_GOAL_SCRAM:                 buddy_message("Staying away...");                       break;
739                 case ESCORT_GOAL_BOSS:                  buddy_message("Finding BOSS robot");            break;
740                 case ESCORT_GOAL_PLAYER_SPEW:   buddy_message("Finding your powerups"); break;
741                 case ESCORT_GOAL_MARKER1:
742                 case ESCORT_GOAL_MARKER2:
743                 case ESCORT_GOAL_MARKER3:
744                 case ESCORT_GOAL_MARKER4:
745                 case ESCORT_GOAL_MARKER5:
746                 case ESCORT_GOAL_MARKER6:
747                 case ESCORT_GOAL_MARKER7:
748                 case ESCORT_GOAL_MARKER8:
749                 case ESCORT_GOAL_MARKER9:
750                         { char marker_text[BUDDY_MARKER_TEXT_LEN];
751                         strncpy(marker_text, MarkerMessage[goal_num-ESCORT_GOAL_MARKER1], BUDDY_MARKER_TEXT_LEN-1);
752                         marker_text[BUDDY_MARKER_TEXT_LEN-1] = 0;
753                         buddy_message("Finding marker %i: '%s'", goal_num-ESCORT_GOAL_MARKER1+1, marker_text);
754                         break;
755                         }
756         }
757 }
758
759 //      -----------------------------------------------------------------------------
760 void escort_create_path_to_goal(object *objp)
761 {
762         int     goal_seg = -1;
763         int                     objnum = objp-Objects;
764         ai_static       *aip = &objp->ctype.ai_info;
765         ai_local                *ailp = &Ai_local_info[objnum];
766
767         if (Escort_special_goal != -1)
768                 Escort_goal_object = Escort_special_goal;
769
770         Escort_kill_object = -1;
771
772         if (Looking_for_marker != -1) {
773
774                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_MARKER, Escort_goal_object-ESCORT_GOAL_MARKER1, -1);
775                 if (Escort_goal_index > -1)
776                         goal_seg = Objects[Escort_goal_index].segnum;
777         } else {
778                 switch (Escort_goal_object) {
779                         case ESCORT_GOAL_BLUE_KEY:
780                                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_POWERUP, POW_KEY_BLUE, -1);
781                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
782                                 break;
783                         case ESCORT_GOAL_GOLD_KEY:
784                                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_POWERUP, POW_KEY_GOLD, -1);
785                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
786                                 break;
787                         case ESCORT_GOAL_RED_KEY:
788                                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_POWERUP, POW_KEY_RED, -1);
789                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
790                                 break;
791                         case ESCORT_GOAL_CONTROLCEN:
792                                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_CNTRLCEN, -1, -1);
793                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
794                                 break;
795                         case ESCORT_GOAL_EXIT:
796                         case ESCORT_GOAL_EXIT2:
797                                 goal_seg = find_exit_segment();
798                                 Escort_goal_index = goal_seg;
799                                 break;
800                         case ESCORT_GOAL_ENERGY:
801                                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_POWERUP, POW_ENERGY, -1);
802                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
803                                 break;
804                         case ESCORT_GOAL_ENERGYCEN:
805                                 goal_seg = exists_in_mine(objp->segnum, FUELCEN_CHECK, -1, -1);
806                                 Escort_goal_index = goal_seg;
807                                 break;
808                         case ESCORT_GOAL_SHIELD:
809                                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_POWERUP, POW_SHIELD_BOOST, -1);
810                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
811                                 break;
812                         case ESCORT_GOAL_POWERUP:
813                                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_POWERUP, -1, -1);
814                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
815                                 break;
816                         case ESCORT_GOAL_ROBOT:
817                                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_ROBOT, -1, -1);
818                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
819                                 break;
820                         case ESCORT_GOAL_HOSTAGE:
821                                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_HOSTAGE, -1, -1);
822                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
823                                 break;
824                         case ESCORT_GOAL_PLAYER_SPEW:
825                                 Escort_goal_index = exists_in_mine(objp->segnum, -1, -1, ESCORT_GOAL_PLAYER_SPEW);
826                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
827                                 break;
828                         case ESCORT_GOAL_SCRAM:
829                                 goal_seg = -3;          //      Kinda a hack.
830                                 Escort_goal_index = goal_seg;
831                                 break;
832                         case ESCORT_GOAL_BOSS: {
833                                 int     boss_id;
834         
835                                 boss_id = get_boss_id();
836                                 Assert(boss_id != -1);
837                                 Escort_goal_index = exists_in_mine(objp->segnum, OBJ_ROBOT, boss_id, -1);
838                                 if (Escort_goal_index > -1) goal_seg = Objects[Escort_goal_index].segnum;
839                                 break;
840                         }
841                         default:
842                                 Int3(); //      Oops, Illegal value in Escort_goal_object.
843                                 goal_seg = 0;
844                                 break;
845                 }
846         }
847
848         // -- mprintf((0, "Creating path from escort to goal #%i in segment #%i.\n", Escort_goal_object, goal_seg));
849         if ((Escort_goal_index < 0) && (Escort_goal_index != -3)) {     //      I apologize for this statement -- MK, 09/22/95
850                 if (Escort_goal_index == -1) {
851                         Last_buddy_message_time = 0;    //      Force this message to get through.
852                         buddy_message("No %s in mine.", Escort_goal_text[Escort_goal_object-1]);
853                         Looking_for_marker = -1;
854                 } else if (Escort_goal_index == -2) {
855                         Last_buddy_message_time = 0;    //      Force this message to get through.
856                         buddy_message("Can't reach %s.", Escort_goal_text[Escort_goal_object-1]);
857                         Looking_for_marker = -1;
858                 } else
859                         Int3();
860
861                 Escort_goal_object = ESCORT_GOAL_UNSPECIFIED;
862                 Escort_special_goal = -1;
863         } else {
864                 if (goal_seg == -3) {
865                         create_n_segment_path(objp, 16 + d_rand() * 16, -1);
866                         aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
867                 } else {
868                         create_path_to_segment(objp, goal_seg, Max_escort_length, 1);   //      MK!: Last parm (safety_flag) used to be 1!!
869                         if (aip->path_length > 3)
870                                 aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
871                         if ((aip->path_length > 0) && (Point_segs[aip->hide_index + aip->path_length - 1].segnum != goal_seg)) {
872                                 fix     dist_to_player;
873                                 Last_buddy_message_time = 0;    //      Force this message to get through.
874                                 buddy_message("Can't reach %s.", Escort_goal_text[Escort_goal_object-1]);
875                                 Looking_for_marker = -1;
876                                 Escort_goal_object = ESCORT_GOAL_SCRAM;
877                                 dist_to_player = find_connected_distance(&objp->pos, objp->segnum, &Believed_player_pos, Believed_player_seg, 100, WID_FLY_FLAG);
878                                 if (dist_to_player > MIN_ESCORT_DISTANCE)
879                                         create_path_to_player(objp, Max_escort_length, 1);      //      MK!: Last parm used to be 1!
880                                 else {
881                                         create_n_segment_path(objp, 8 + d_rand() * 8, -1);
882                                         aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
883                                 }
884                         }
885                 }
886
887                 ailp->mode = AIM_GOTO_OBJECT;
888
889                 say_escort_goal(Escort_goal_object);
890         }
891
892 }
893
894 //      -----------------------------------------------------------------------------
895 //      Escort robot chooses goal object based on player's keys, location.
896 //      Returns goal object.
897 int escort_set_goal_object(void)
898 {
899         if (Escort_special_goal != -1)
900                 return ESCORT_GOAL_UNSPECIFIED;
901         else if (!(ConsoleObject->flags & PLAYER_FLAGS_BLUE_KEY) && (exists_in_mine(ConsoleObject->segnum, OBJ_POWERUP, POW_KEY_BLUE, -1) != -1))
902                 return ESCORT_GOAL_BLUE_KEY;
903         else if (!(ConsoleObject->flags & PLAYER_FLAGS_GOLD_KEY) && (exists_in_mine(ConsoleObject->segnum, OBJ_POWERUP, POW_KEY_GOLD, -1) != -1))
904                 return ESCORT_GOAL_GOLD_KEY;
905         else if (!(ConsoleObject->flags & PLAYER_FLAGS_RED_KEY) && (exists_in_mine(ConsoleObject->segnum, OBJ_POWERUP, POW_KEY_RED, -1) != -1))
906                 return ESCORT_GOAL_RED_KEY;
907         else if (Control_center_destroyed == 0) {
908                 if (Num_boss_teleport_segs)
909                         return ESCORT_GOAL_BOSS;
910                 else
911                         return ESCORT_GOAL_CONTROLCEN;
912         } else
913                 return ESCORT_GOAL_EXIT;
914         
915 }
916
917 #define MAX_ESCORT_TIME_AWAY            (F1_0*4)
918
919 fix     Buddy_last_seen_player = 0, Buddy_last_player_path_created;
920
921 //      -----------------------------------------------------------------------------
922 int time_to_visit_player(object *objp, ai_local *ailp, ai_static *aip)
923 {
924         //      Note: This one has highest priority because, even if already going towards player,
925         //      might be necessary to create a new path, as player can move.
926         if (GameTime - Buddy_last_seen_player > MAX_ESCORT_TIME_AWAY)
927                 if (GameTime - Buddy_last_player_path_created > F1_0)
928                         return 1;
929
930         if (ailp->mode == AIM_GOTO_PLAYER)
931                 return 0;
932
933         if (objp->segnum == ConsoleObject->segnum)
934                 return 0;
935
936         if (aip->cur_path_index < aip->path_length/2)
937                 return 0;
938         
939         return 1;
940 }
941
942 int     Buddy_objnum;
943 int Buddy_dude_cheat;
944 fix     Last_come_back_message_time = 0;
945
946 fix     Buddy_last_missile_time;
947
948 //      -----------------------------------------------------------------------------
949 void bash_buddy_weapon_info(int weapon_objnum)
950 {
951         object  *objp = &Objects[weapon_objnum];
952
953         objp->ctype.laser_info.parent_num = ConsoleObject-Objects;
954         objp->ctype.laser_info.parent_type = OBJ_PLAYER;
955         objp->ctype.laser_info.parent_signature = ConsoleObject->signature;
956 }
957
958 //      -----------------------------------------------------------------------------
959 int maybe_buddy_fire_mega(int objnum)
960 {
961         object  *objp = &Objects[objnum];
962         object  *buddy_objp = &Objects[Buddy_objnum];
963         fix             dist, dot;
964         vms_vector      vec_to_robot;
965         int             weapon_objnum;
966
967         vm_vec_sub(&vec_to_robot, &buddy_objp->pos, &objp->pos);
968         dist = vm_vec_normalize_quick(&vec_to_robot);
969
970         if (dist > F1_0*100)
971                 return 0;
972
973         dot = vm_vec_dot(&vec_to_robot, &buddy_objp->orient.fvec);
974
975         if (dot < F1_0/2)
976                 return 0;
977
978         if (!object_to_object_visibility(buddy_objp, objp, FQ_TRANSWALL))
979                 return 0;
980
981         mprintf((0, "Buddy firing mega in frame %i\n", FrameCount));
982
983         buddy_message("GAHOOGA!");
984
985         weapon_objnum = Laser_create_new_easy( &buddy_objp->orient.fvec, &buddy_objp->pos, objnum, MEGA_ID, 1);
986
987         if (weapon_objnum != -1)
988                 bash_buddy_weapon_info(weapon_objnum);
989
990         return 1;
991 }
992
993 //      -----------------------------------------------------------------------------
994 int maybe_buddy_fire_smart(int objnum)
995 {
996         object  *objp = &Objects[objnum];
997         object  *buddy_objp = &Objects[Buddy_objnum];
998         fix             dist;
999         int             weapon_objnum;
1000
1001         dist = vm_vec_dist_quick(&buddy_objp->pos, &objp->pos);
1002
1003         if (dist > F1_0*80)
1004                 return 0;
1005
1006         if (!object_to_object_visibility(buddy_objp, objp, FQ_TRANSWALL))
1007                 return 0;
1008
1009         mprintf((0, "Buddy firing smart missile in frame %i\n", FrameCount));
1010
1011         buddy_message("WHAMMO!");
1012
1013         weapon_objnum = Laser_create_new_easy( &buddy_objp->orient.fvec, &buddy_objp->pos, objnum, SMART_ID, 1);
1014
1015         if (weapon_objnum != -1)
1016                 bash_buddy_weapon_info(weapon_objnum);
1017
1018         return 1;
1019 }
1020
1021 //      -----------------------------------------------------------------------------
1022 void do_buddy_dude_stuff(void)
1023 {
1024         int     i;
1025
1026         if (!ok_for_buddy_to_talk())
1027                 return;
1028
1029         if (Buddy_last_missile_time > GameTime)
1030                 Buddy_last_missile_time = 0;
1031
1032         if (Buddy_last_missile_time + F1_0*2 < GameTime) {
1033                 //      See if a robot potentially in view cone
1034                 for (i=0; i<=Highest_object_index; i++)
1035                         if ((Objects[i].type == OBJ_ROBOT) && !Robot_info[Objects[i].id].companion)
1036                                 if (maybe_buddy_fire_mega(i)) {
1037                                         Buddy_last_missile_time = GameTime;
1038                                         return;
1039                                 }
1040
1041                 //      See if a robot near enough that buddy should fire smart missile
1042                 for (i=0; i<=Highest_object_index; i++)
1043                         if ((Objects[i].type == OBJ_ROBOT) && !Robot_info[Objects[i].id].companion)
1044                                 if (maybe_buddy_fire_smart(i)) {
1045                                         Buddy_last_missile_time = GameTime;
1046                                         return;
1047                                 }
1048
1049         }
1050 }
1051
1052 //      -----------------------------------------------------------------------------
1053 //      Called every frame (or something).
1054 void do_escort_frame(object *objp, fix dist_to_player, int player_visibility)
1055 {
1056         int                     objnum = objp-Objects;
1057         ai_static       *aip = &objp->ctype.ai_info;
1058         ai_local                *ailp = &Ai_local_info[objnum];
1059
1060         Buddy_objnum = objp-Objects;
1061
1062         if (player_visibility) {
1063                 Buddy_last_seen_player = GameTime;
1064                 if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT_ON)      //      DAMN! MK, stupid bug, fixed 12/08/95, changed PLAYER_FLAGS_HEADLIGHT to PLAYER_FLAGS_HEADLIGHT_ON
1065                         if (f2i(Players[Player_num].energy) < 40)
1066                                 if ((f2i(Players[Player_num].energy)/2) & 2)
1067                                         if (!Player_is_dead)
1068                                                 buddy_message("Hey, your headlight's on!");
1069
1070         }
1071
1072         if (Buddy_dude_cheat)
1073                 do_buddy_dude_stuff();
1074
1075         if (Buddy_sorry_time + F1_0 > GameTime) {
1076                 Last_buddy_message_time = 0;    //      Force this message to get through.
1077                 if (Buddy_sorry_time < GameTime + F1_0*2)
1078                         buddy_message("Oops, sorry 'bout that...");
1079                 Buddy_sorry_time = -F1_0*2;
1080         }
1081
1082         //      If buddy not allowed to talk, then he is locked in his room.  Make him mostly do nothing unless you're nearby.
1083         if (!Buddy_allowed_to_talk)
1084                 if (dist_to_player > F1_0*100)
1085                         aip->SKIP_AI_COUNT = (F1_0/4)/FrameTime;
1086
1087         // -- mprintf((0, "%10s: Dist to player = %7.3f, segnum = %4i\n", mode_text[ailp->mode], f2fl(dist_to_player), objp->segnum));
1088
1089         //      AIM_WANDER has been co-opted for buddy behavior (didn't want to modify aistruct.h)
1090         //      It means the object has been told to get lost and has come to the end of its path.
1091         //      If the player is now visible, then create a path.
1092         if (ailp->mode == AIM_WANDER)
1093                 if (player_visibility) {
1094                         // -- mprintf((0, "Buddy: Going from wander to path following!\n"));
1095                         create_n_segment_path(objp, 16 + d_rand() * 16, -1);
1096                         aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
1097                 }
1098
1099         if (Escort_special_goal == ESCORT_GOAL_SCRAM) {
1100                 if (player_visibility)
1101                         if (Escort_last_path_created + F1_0*3 < GameTime) {
1102                                 mprintf((0, "Frame %i: Buddy creating new scram path.\n", FrameCount));
1103                                 create_n_segment_path(objp, 10 + d_rand() * 16, ConsoleObject->segnum);
1104                                 Escort_last_path_created = GameTime;
1105                         }
1106
1107                 // -- Int3();
1108                 // -- mprintf((0, "Buddy: Seg = %3i, dist = %7.3f\n", objp->segnum, f2fl(dist_to_player)));
1109                 return;
1110         }
1111
1112         //      Force checking for new goal every 5 seconds, and create new path, if necessary.
1113         if (((Escort_special_goal != ESCORT_GOAL_SCRAM) && ((Escort_last_path_created + F1_0*5) < GameTime)) ||
1114                 ((Escort_special_goal == ESCORT_GOAL_SCRAM) && ((Escort_last_path_created + F1_0*15) < GameTime))) {
1115                 Escort_goal_object = ESCORT_GOAL_UNSPECIFIED;
1116                 Escort_last_path_created = GameTime;
1117         }
1118
1119         if ((Escort_special_goal != ESCORT_GOAL_SCRAM) && time_to_visit_player(objp, ailp, aip)) {
1120                 int     max_len;
1121
1122                 Buddy_last_player_path_created = GameTime;
1123                 ailp->mode = AIM_GOTO_PLAYER;
1124                 if (!player_visibility) {
1125                         if ((Last_come_back_message_time + F1_0 < GameTime) || (Last_come_back_message_time > GameTime)) {
1126                                 buddy_message("Coming back to get you.");
1127                                 Last_come_back_message_time = GameTime;
1128                         }
1129                 }
1130                 //      No point in Buddy creating very long path if he's not allowed to talk.  Really kills framerate.
1131                 max_len = Max_escort_length;
1132                 if (!Buddy_allowed_to_talk)
1133                         max_len = 3;
1134                 create_path_to_player(objp, max_len, 1);        //      MK!: Last parm used to be 1!
1135                 aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
1136                 // -- mprintf((0, "Creating path to player, length = %i\n", aip->path_length));
1137                 ailp->mode = AIM_GOTO_PLAYER;
1138         }       else if (GameTime - Buddy_last_seen_player > MAX_ESCORT_TIME_AWAY) {
1139                 //      This is to prevent buddy from looking for a goal, which he will do because we only allow path creation once/second.
1140                 return;
1141         } else if ((ailp->mode == AIM_GOTO_PLAYER) && (dist_to_player < MIN_ESCORT_DISTANCE)) {
1142                 Escort_goal_object = escort_set_goal_object();
1143                 ailp->mode = AIM_GOTO_OBJECT;           //      May look stupid to be before path creation, but ai_door_is_openable uses mode to determine what doors can be got through
1144                 escort_create_path_to_goal(objp);
1145                 aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
1146                 // mprintf((0, "Creating path to goal, length = %i\n", aip->path_length));
1147                 if (aip->path_length < 3) {
1148                         create_n_segment_path(objp, 5, Believed_player_seg);
1149                         // mprintf((0, "Path to goal has length %i, just wandering...\n", aip->path_length));
1150                 }
1151                 ailp->mode = AIM_GOTO_OBJECT;
1152         } else if (Escort_goal_object == ESCORT_GOAL_UNSPECIFIED) {
1153                 if ((ailp->mode != AIM_GOTO_PLAYER) || (dist_to_player < MIN_ESCORT_DISTANCE)) {
1154                         Escort_goal_object = escort_set_goal_object();
1155                         ailp->mode = AIM_GOTO_OBJECT;           //      May look stupid to be before path creation, but ai_door_is_openable uses mode to determine what doors can be got through
1156                         escort_create_path_to_goal(objp);
1157                         aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
1158                         // mprintf((0, "Creating path to goal, length = %i\n", aip->path_length));
1159                         if (aip->path_length < 3) {
1160                                 create_n_segment_path(objp, 5, Believed_player_seg);
1161                                 // mprintf((0, "Path to goal has length %i, just wandering...\n", aip->path_length));
1162                         }
1163                         ailp->mode = AIM_GOTO_OBJECT;
1164                 }
1165         } else
1166                 ; // mprintf((0, "!"));
1167
1168 }
1169
1170 void invalidate_escort_goal(void)
1171 {
1172         Escort_goal_object = -1;
1173 }
1174
1175 //      -------------------------------------------------------------------------------------------------
1176 void do_snipe_frame(object *objp, fix dist_to_player, int player_visibility, vms_vector *vec_to_player)
1177 {
1178         int                     objnum = objp-Objects;
1179         ai_local                *ailp = &Ai_local_info[objnum];
1180         fix                     connected_distance;
1181
1182         if (dist_to_player > F1_0*500)
1183                 return;
1184
1185         // -- mprintf((0, "Mode: %10s, Dist: %7.3f\n", mode_text[ailp->mode], f2fl(dist_to_player)));
1186
1187         switch (ailp->mode) {
1188                 case AIM_SNIPE_WAIT:
1189                         if ((dist_to_player > F1_0*50) && (ailp->next_action_time > 0))
1190                                 return;
1191
1192                         ailp->next_action_time = SNIPE_WAIT_TIME;
1193
1194                         connected_distance = find_connected_distance(&objp->pos, objp->segnum, &Believed_player_pos, Believed_player_seg, 30, WID_FLY_FLAG);
1195                         if (connected_distance < F1_0*500) {
1196                                 // -- mprintf((0, "Object #%i entering attack mode.\n", objnum));
1197                                 create_path_to_player(objp, 30, 1);
1198                                 ailp->mode = AIM_SNIPE_ATTACK;
1199                                 ailp->next_action_time = SNIPE_ATTACK_TIME;     //      have up to 10 seconds to find player.
1200                         }
1201                         break;
1202
1203                 case AIM_SNIPE_RETREAT:
1204                 case AIM_SNIPE_RETREAT_BACKWARDS:
1205                         if (ailp->next_action_time < 0) {
1206                                 ailp->mode = AIM_SNIPE_WAIT;
1207                                 ailp->next_action_time = SNIPE_WAIT_TIME;
1208                                 // -- mprintf((0, "Object #%i going from retreat to wait.\n", objnum));
1209                         } else if ((player_visibility == 0) || (ailp->next_action_time > SNIPE_ABORT_RETREAT_TIME)) {
1210                                 ai_follow_path(objp, player_visibility, player_visibility, vec_to_player);
1211                                 ailp->mode = AIM_SNIPE_RETREAT_BACKWARDS;
1212                         } else {
1213                                 // -- mprintf((0, "Object #%i going from retreat to fire.\n", objnum));
1214                                 ailp->mode = AIM_SNIPE_FIRE;
1215                                 ailp->next_action_time = SNIPE_FIRE_TIME/2;
1216                         }
1217                         break;
1218
1219                 case AIM_SNIPE_ATTACK:
1220                         if (ailp->next_action_time < 0) {
1221                                 // -- mprintf((0, "Object #%i timed out from attack to retreat mode.\n", objnum));
1222                                 ailp->mode = AIM_SNIPE_RETREAT;
1223                                 ailp->next_action_time = SNIPE_WAIT_TIME;
1224                         } else {
1225                                 // -- mprintf((0, "Object #%i attacking: visibility = %i\n", player_visibility));
1226                                 ai_follow_path(objp, player_visibility, player_visibility, vec_to_player);
1227                                 if (player_visibility) {
1228                                         ailp->mode = AIM_SNIPE_FIRE;
1229                                         ailp->next_action_time = SNIPE_FIRE_TIME;
1230                                 } else
1231                                         ailp->mode = AIM_SNIPE_ATTACK;
1232                         }
1233                         break;
1234
1235                 case AIM_SNIPE_FIRE:
1236                         if (ailp->next_action_time < 0) {
1237                                 ai_static       *aip = &objp->ctype.ai_info;
1238                                 // -- mprintf((0, "Object #%i going from fire to retreat.\n", objnum));
1239                                 create_n_segment_path(objp, 10 + d_rand()/2048, ConsoleObject->segnum);
1240                                 aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
1241                                 if (d_rand() < 8192)
1242                                         ailp->mode = AIM_SNIPE_RETREAT_BACKWARDS;
1243                                 else
1244                                         ailp->mode = AIM_SNIPE_RETREAT;
1245                                 ailp->next_action_time = SNIPE_RETREAT_TIME;
1246                         } else {
1247                         }
1248                         break;
1249
1250                 default:
1251                         Int3(); //      Oops, illegal mode for snipe behavior.
1252                         ailp->mode = AIM_SNIPE_ATTACK;
1253                         ailp->next_action_time = F1_0;
1254                         break;
1255         }
1256
1257 }
1258
1259 #define THIEF_DEPTH     20
1260
1261 extern int pick_connected_segment(object *objp, int max_depth);
1262
1263 //      ------------------------------------------------------------------------------------------------------
1264 //      Choose segment to recreate thief in.
1265 int choose_thief_recreation_segment(void)
1266 {
1267         int     segnum = -1;
1268         int     cur_drop_depth;
1269
1270         cur_drop_depth = THIEF_DEPTH;
1271
1272         while ((segnum == -1) && (cur_drop_depth > THIEF_DEPTH/2)) {
1273                 segnum = pick_connected_segment(&Objects[Players[Player_num].objnum], cur_drop_depth);
1274                 if (Segment2s[segnum].special == SEGMENT_IS_CONTROLCEN)
1275                         segnum = -1;
1276                 cur_drop_depth--;
1277         }
1278
1279         if (segnum == -1) {
1280                 mprintf((1, "Warning: Unable to find a connected segment for thief recreation.\n"));
1281                 return (d_rand() * Highest_segment_index) >> 15;
1282         } else
1283                 return segnum;
1284
1285 }
1286
1287 extern object * create_morph_robot( segment *segp, vms_vector *object_pos, int object_id);
1288
1289 fix     Re_init_thief_time = 0x3f000000;
1290
1291 //      ----------------------------------------------------------------------
1292 void recreate_thief(object *objp)
1293 {
1294         int                     segnum;
1295         vms_vector      center_point;
1296         object          *new_obj;
1297
1298         segnum = choose_thief_recreation_segment();
1299         compute_segment_center(&center_point, &Segments[segnum]);
1300
1301         new_obj = create_morph_robot( &Segments[segnum], &center_point, objp->id);
1302         init_ai_object(new_obj-Objects, AIB_SNIPE, -1);
1303         Re_init_thief_time = GameTime + F1_0*10;                //      In 10 seconds, re-initialize thief.
1304 }
1305
1306 //      ----------------------------------------------------------------------------
1307 #define THIEF_ATTACK_TIME               (F1_0*10)
1308
1309 fix     Thief_wait_times[NDL] = {F1_0*30, F1_0*25, F1_0*20, F1_0*15, F1_0*10};
1310
1311 //      -------------------------------------------------------------------------------------------------
1312 void do_thief_frame(object *objp, fix dist_to_player, int player_visibility, vms_vector *vec_to_player)
1313 {
1314         int                     objnum = objp-Objects;
1315         ai_local                *ailp = &Ai_local_info[objnum];
1316         fix                     connected_distance;
1317
1318         // -- mprintf((0, "%10s: Action Time: %7.3f\n", mode_text[ailp->mode], f2fl(ailp->next_action_time)));
1319
1320         if ((Current_level_num < 0) && (Re_init_thief_time < GameTime)) {
1321                 if (Re_init_thief_time > GameTime - F1_0*2)
1322                         init_thief_for_level();
1323                 Re_init_thief_time = 0x3f000000;
1324         }
1325
1326         if ((dist_to_player > F1_0*500) && (ailp->next_action_time > 0))
1327                 return;
1328
1329         if (Player_is_dead)
1330                 ailp->mode = AIM_THIEF_RETREAT;
1331
1332         switch (ailp->mode) {
1333                 case AIM_THIEF_WAIT:
1334                         // -- mprintf((0, "WAIT\n"));
1335
1336                         if (ailp->player_awareness_type >= PA_PLAYER_COLLISION) {
1337                                 ailp->player_awareness_type = 0;
1338                                 // -- mprintf((0, "Thief: Awareness = %i ", ailp->player_awareness_type));
1339
1340                                 // -- mprintf((0, "ATTACK\n"));
1341                                 create_path_to_player(objp, 30, 1);
1342                                 ailp->mode = AIM_THIEF_ATTACK;
1343                                 ailp->next_action_time = THIEF_ATTACK_TIME/2;
1344                                 return;
1345                         } else if (player_visibility) {
1346                                 // -- mprintf((0, "RETREAT\n"));
1347                                 create_n_segment_path(objp, 15, ConsoleObject->segnum);
1348                                 ailp->mode = AIM_THIEF_RETREAT;
1349                                 return;
1350                         }
1351
1352                         if ((dist_to_player > F1_0*50) && (ailp->next_action_time > 0))
1353                                 return;
1354
1355                         ailp->next_action_time = Thief_wait_times[Difficulty_level]/2;
1356
1357                         connected_distance = find_connected_distance(&objp->pos, objp->segnum, &Believed_player_pos, Believed_player_seg, 30, WID_FLY_FLAG);
1358                         if (connected_distance < F1_0*500) {
1359                                 // -- mprintf((0, "Thief creating path to player.\n", objnum));
1360                                 create_path_to_player(objp, 30, 1);
1361                                 ailp->mode = AIM_THIEF_ATTACK;
1362                                 ailp->next_action_time = THIEF_ATTACK_TIME;     //      have up to 10 seconds to find player.
1363                         }
1364
1365                         break;
1366
1367                 case AIM_THIEF_RETREAT:
1368                         // -- mprintf((0, "RETREAT\n"));
1369
1370                         if (ailp->next_action_time < 0) {
1371                                 ailp->mode = AIM_THIEF_WAIT;
1372                                 ailp->next_action_time = Thief_wait_times[Difficulty_level];
1373                         } else if ((dist_to_player < F1_0*100) || player_visibility || (ailp->player_awareness_type >= PA_PLAYER_COLLISION)) {
1374                                 ai_follow_path(objp, player_visibility, player_visibility, vec_to_player);
1375                                 if ((dist_to_player < F1_0*100) || (ailp->player_awareness_type >= PA_PLAYER_COLLISION)) {
1376                                         ai_static       *aip = &objp->ctype.ai_info;
1377                                         if (((aip->cur_path_index <=1) && (aip->PATH_DIR == -1)) || ((aip->cur_path_index >= aip->path_length-1) && (aip->PATH_DIR == 1))) {
1378                                                 ailp->player_awareness_type = 0;
1379                                                 create_n_segment_path(objp, 10, ConsoleObject->segnum);
1380
1381                                                 //      If path is real short, try again, allowing to go through player's segment
1382                                                 if (aip->path_length < 4) {
1383                                                         // -- mprintf((0, "Thief is cornered.  Willing to fly through player.\n"));
1384                                                         create_n_segment_path(objp, 10, -1);
1385                                                 } else if (objp->shields* 4 < Robot_info[objp->id].strength) {
1386                                                         //      If robot really low on hits, will run through player with even longer path
1387                                                         if (aip->path_length < 8) {
1388                                                                 create_n_segment_path(objp, 10, -1);
1389                                                         }
1390                                                 }
1391
1392                                                 ailp->mode = AIM_THIEF_RETREAT;
1393                                                 // -- mprintf((0, "Thief creating new RETREAT path.\n"));
1394                                         }
1395                                 } else
1396                                         ailp->mode = AIM_THIEF_RETREAT;
1397
1398                         }
1399
1400                         break;
1401
1402                 //      This means the thief goes from wherever he is to the player.
1403                 //      Note: When thief successfully steals something, his action time is forced negative and his mode is changed
1404                 //                      to retreat to get him out of attack mode.
1405                 case AIM_THIEF_ATTACK:
1406                         // -- mprintf((0, "ATTACK\n"));
1407
1408                         if (ailp->player_awareness_type >= PA_PLAYER_COLLISION) {
1409                                 ailp->player_awareness_type = 0;
1410                                 if (d_rand() > 8192) {
1411                                         // --- mprintf((0, "RETREAT!!\n"));
1412                                         create_n_segment_path(objp, 10, ConsoleObject->segnum);
1413                                         Ai_local_info[objp-Objects].next_action_time = Thief_wait_times[Difficulty_level]/2;
1414                                         Ai_local_info[objp-Objects].mode = AIM_THIEF_RETREAT;
1415                                 }
1416                         } else if (ailp->next_action_time < 0) {
1417                                 //      This forces him to create a new path every second.
1418                                 ailp->next_action_time = F1_0;
1419                                 create_path_to_player(objp, 100, 0);
1420                                 ailp->mode = AIM_THIEF_ATTACK;
1421                                 // -- mprintf((0, "Creating path to player.\n"));
1422                         } else {
1423                                 if (player_visibility && (dist_to_player < F1_0*100)) {
1424                                         //      If the player is close to looking at the thief, thief shall run away.
1425                                         //      No more stupid thief trying to sneak up on you when you're looking right at him!
1426                                         if (dist_to_player > F1_0*60) {
1427                                                 fix     dot = vm_vec_dot(vec_to_player, &ConsoleObject->orient.fvec);
1428                                                 if (dot < -F1_0/2) {    //      Looking at least towards thief, so thief will run!
1429                                                         create_n_segment_path(objp, 10, ConsoleObject->segnum);
1430                                                         Ai_local_info[objp-Objects].next_action_time = Thief_wait_times[Difficulty_level]/2;
1431                                                         Ai_local_info[objp-Objects].mode = AIM_THIEF_RETREAT;
1432                                                 }
1433                                         } 
1434                                         ai_turn_towards_vector(vec_to_player, objp, F1_0/4);
1435                                         move_towards_player(objp, vec_to_player);
1436                                 } else {
1437                                         ai_static       *aip = &objp->ctype.ai_info;
1438                                         //      If path length == 0, then he will keep trying to create path, but he is probably stuck in his closet.
1439                                         if ((aip->path_length > 1) || ((FrameCount & 0x0f) == 0)) {
1440                                                 ai_follow_path(objp, player_visibility, player_visibility, vec_to_player);
1441                                                 ailp->mode = AIM_THIEF_ATTACK;
1442                                         }
1443                                 }
1444                         }
1445                         break;
1446
1447                 default:
1448                         mprintf ((0,"Thief mode (broken) = %d\n",ailp->mode));
1449                         // -- Int3();   //      Oops, illegal mode for thief behavior.
1450                         ailp->mode = AIM_THIEF_ATTACK;
1451                         ailp->next_action_time = F1_0;
1452                         break;
1453         }
1454
1455 }
1456
1457 //      ----------------------------------------------------------------------------
1458 //      Return true if this item (whose presence is indicated by Players[player_num].flags) gets stolen.
1459 int maybe_steal_flag_item(int player_num, int flagval)
1460 {
1461         if (Players[player_num].flags & flagval) {
1462                 if (d_rand() < THIEF_PROBABILITY) {
1463                         int     powerup_index=-1;
1464                         Players[player_num].flags &= (~flagval);
1465                         // -- mprintf((0, "You lost your %4x capability!\n", flagval));
1466                         switch (flagval) {
1467                                 case PLAYER_FLAGS_INVULNERABLE:
1468                                         powerup_index = POW_INVULNERABILITY;
1469                                         thief_message("Invulnerability stolen!");
1470                                         break;
1471                                 case PLAYER_FLAGS_CLOAKED:
1472                                         powerup_index = POW_CLOAK;
1473                                         thief_message("Cloak stolen!");
1474                                         break;
1475                                 case PLAYER_FLAGS_MAP_ALL:
1476                                         powerup_index = POW_FULL_MAP;
1477                                         thief_message("Full map stolen!");
1478                                         break;
1479                                 case PLAYER_FLAGS_QUAD_LASERS:
1480                                         powerup_index = POW_QUAD_FIRE;
1481                                         thief_message("Quad lasers stolen!");
1482                                         break;
1483                                 case PLAYER_FLAGS_AFTERBURNER:
1484                                         powerup_index = POW_AFTERBURNER;
1485                                         thief_message("Afterburner stolen!");
1486                                         break;
1487 // --                           case PLAYER_FLAGS_AMMO_RACK:
1488 // --                                   powerup_index = POW_AMMO_RACK;
1489 // --                                   thief_message("Ammo Rack stolen!");
1490 // --                                   break;
1491                                 case PLAYER_FLAGS_CONVERTER:
1492                                         powerup_index = POW_CONVERTER;
1493                                         thief_message("Converter stolen!");
1494                                         break;
1495                                 case PLAYER_FLAGS_HEADLIGHT:
1496                                         powerup_index = POW_HEADLIGHT;
1497                                         thief_message("Headlight stolen!");
1498                                    Players[Player_num].flags &= ~PLAYER_FLAGS_HEADLIGHT_ON;
1499                                         break;
1500                         }
1501                         Assert(powerup_index != -1);
1502                         Stolen_items[Stolen_item_index] = powerup_index;
1503
1504                         digi_play_sample_once(SOUND_WEAPON_STOLEN, F1_0);
1505                         return 1;
1506                 }
1507         }
1508
1509         return 0;
1510 }
1511
1512 //      ----------------------------------------------------------------------------
1513 int maybe_steal_secondary_weapon(int player_num, int weapon_num)
1514 {
1515         if ((Players[player_num].secondary_weapon_flags & HAS_FLAG(weapon_num)) && Players[player_num].secondary_ammo[weapon_num])
1516                 if (d_rand() < THIEF_PROBABILITY) {
1517                         if (weapon_num == PROXIMITY_INDEX)
1518                                 if (d_rand() > 8192)            //      Come in groups of 4, only add 1/4 of time.
1519                                         return 0;
1520                         Players[player_num].secondary_ammo[weapon_num]--;
1521
1522                         //      Smart mines and proxbombs don't get dropped because they only come in 4 packs.
1523                         if ((weapon_num != PROXIMITY_INDEX) && (weapon_num != SMART_MINE_INDEX)) {
1524                                 Stolen_items[Stolen_item_index] = Secondary_weapon_to_powerup[weapon_num];
1525                         }
1526
1527                         thief_message("%s stolen!", Text_string[114+weapon_num]);               //      Danger! Danger! Use of literal!  Danger!
1528                         if (Players[Player_num].secondary_ammo[weapon_num] == 0)
1529                                 auto_select_weapon(1);
1530
1531                         // -- compress_stolen_items();
1532                         digi_play_sample_once(SOUND_WEAPON_STOLEN, F1_0);
1533                         return 1;
1534                 }
1535
1536         return 0;
1537 }
1538
1539 //      ----------------------------------------------------------------------------
1540 int maybe_steal_primary_weapon(int player_num, int weapon_num)
1541 {
1542         if ((Players[player_num].primary_weapon_flags & HAS_FLAG(weapon_num)) && Players[player_num].primary_ammo[weapon_num]) {
1543                 if (d_rand() < THIEF_PROBABILITY) {
1544                         if (weapon_num == 0) {
1545                                 if (Players[player_num].laser_level > 0) {
1546                                         if (Players[player_num].laser_level > 3) {
1547                                                 Stolen_items[Stolen_item_index] = POW_SUPER_LASER;
1548                                         } else {
1549                                                 Stolen_items[Stolen_item_index] = Primary_weapon_to_powerup[weapon_num];
1550                                         }
1551                                         thief_message("%s level decreased!", Text_string[104+weapon_num]);              //      Danger! Danger! Use of literal!  Danger!
1552                                         Players[player_num].laser_level--;
1553                                         digi_play_sample_once(SOUND_WEAPON_STOLEN, F1_0);
1554                                         return 1;
1555                                 }
1556                         } else if (Players[player_num].primary_weapon_flags & (1 << weapon_num)) {
1557                                 Players[player_num].primary_weapon_flags &= ~(1 << weapon_num);
1558                                 Stolen_items[Stolen_item_index] = Primary_weapon_to_powerup[weapon_num];
1559
1560                                 thief_message("%s stolen!", Text_string[104+weapon_num]);               //      Danger! Danger! Use of literal!  Danger!
1561                                 auto_select_weapon(0);
1562                                 digi_play_sample_once(SOUND_WEAPON_STOLEN, F1_0);
1563                                 return 1;
1564                         }
1565                 }
1566         }
1567
1568         return 0;
1569 }
1570
1571
1572
1573 //      ----------------------------------------------------------------------------
1574 //      Called for a thief-type robot.
1575 //      If a item successfully stolen, returns true, else returns false.
1576 //      If a wapon successfully stolen, do everything, removing it from player,
1577 //      updating Stolen_items information, deselecting, etc.
1578 int attempt_to_steal_item_3(object *objp, int player_num)
1579 {
1580         int     i;
1581
1582         if (Ai_local_info[objp-Objects].mode != AIM_THIEF_ATTACK)
1583                 return 0;
1584
1585         //      First, try to steal equipped items.
1586
1587         if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_INVULNERABLE))
1588                 return 1;
1589
1590         //      If primary weapon = laser, first try to rip away those nasty quad lasers!
1591         if (Primary_weapon == 0)
1592                 if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_QUAD_LASERS))
1593                         return 1;
1594
1595         //      Makes it more likely to steal primary than secondary.
1596         for (i=0; i<2; i++)
1597                 if (maybe_steal_primary_weapon(player_num, Primary_weapon))
1598                         return 1;
1599
1600         if (maybe_steal_secondary_weapon(player_num, Secondary_weapon))
1601                 return 1;
1602
1603         //      See what the player has and try to snag something.
1604         //      Try best things first.
1605         if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_INVULNERABLE))
1606                 return 1;
1607         if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_CLOAKED))
1608                 return 1;
1609         if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_QUAD_LASERS))
1610                 return 1;
1611         if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_AFTERBURNER))
1612                 return 1;
1613         if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_CONVERTER))
1614                 return 1;
1615 // --   if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_AMMO_RACK))  //      Can't steal because what if have too many items, say 15 homing missiles?
1616 // --           return 1;
1617         if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_HEADLIGHT))
1618                 return 1;
1619         if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_MAP_ALL))
1620                 return 1;
1621
1622         for (i=MAX_SECONDARY_WEAPONS-1; i>=0; i--) {
1623                 if (maybe_steal_primary_weapon(player_num, i))
1624                         return 1;
1625                 if (maybe_steal_secondary_weapon(player_num, i))
1626                         return 1;
1627         }
1628
1629         return 0;
1630 }
1631
1632 //      ----------------------------------------------------------------------------
1633 int attempt_to_steal_item_2(object *objp, int player_num)
1634 {
1635         int     rval;
1636
1637         rval = attempt_to_steal_item_3(objp, player_num);
1638
1639         if (rval) {
1640                 Stolen_item_index = (Stolen_item_index+1) % MAX_STOLEN_ITEMS;
1641                 if (d_rand() > 20000)   //      Occasionally, boost the value again
1642                         Stolen_item_index = (Stolen_item_index+1) % MAX_STOLEN_ITEMS;
1643         }
1644
1645         return rval;
1646 }
1647
1648 //      ----------------------------------------------------------------------------
1649 //      Called for a thief-type robot.
1650 //      If a item successfully stolen, returns true, else returns false.
1651 //      If a wapon successfully stolen, do everything, removing it from player,
1652 //      updating Stolen_items information, deselecting, etc.
1653 int attempt_to_steal_item(object *objp, int player_num)
1654 {
1655         int     i;
1656         int     rval = 0;
1657
1658         if (objp->ctype.ai_info.dying_start_time)
1659                 return 0;
1660
1661         rval += attempt_to_steal_item_2(objp, player_num);
1662
1663         for (i=0; i<3; i++) {
1664                 if (!rval || (d_rand() < 11000)) {      //      about 1/3 of time, steal another item
1665                         rval += attempt_to_steal_item_2(objp, player_num);
1666                 } else
1667                         break;
1668         }
1669         // -- mprintf((0, "%i items were stolen!\n", rval));
1670
1671         create_n_segment_path(objp, 10, ConsoleObject->segnum);
1672         Ai_local_info[objp-Objects].next_action_time = Thief_wait_times[Difficulty_level]/2;
1673         Ai_local_info[objp-Objects].mode = AIM_THIEF_RETREAT;
1674         if (rval) {
1675                 PALETTE_FLASH_ADD(30, 15, -20);
1676                 update_laser_weapon_info();
1677 //              digi_link_sound_to_pos( SOUND_NASTY_ROBOT_HIT_1, objp->segnum, 0, &objp->pos, 0 , DEFAULT_ROBOT_SOUND_VOLUME);
1678 //      I removed this to make the "steal sound" more obvious -AP
1679 #ifdef NETWORK
1680                 if (Game_mode & GM_NETWORK)
1681                  multi_send_stolen_items();
1682 #endif
1683         }
1684         return rval;
1685 }
1686
1687 // --------------------------------------------------------------------------------------------------------------
1688 //      Indicate no items have been stolen.
1689 void init_thief_for_level(void)
1690 {
1691         int     i;
1692
1693         for (i=0; i<MAX_STOLEN_ITEMS; i++)
1694                 Stolen_items[i] = 255;
1695
1696         Assert (MAX_STOLEN_ITEMS >= 3*2);       //      Oops!  Loop below will overwrite memory!
1697   
1698    if (!(Game_mode & GM_MULTI))    
1699                 for (i=0; i<3; i++) {
1700                         Stolen_items[2*i] = POW_SHIELD_BOOST;
1701                         Stolen_items[2*i+1] = POW_ENERGY;
1702                 }
1703
1704         Stolen_item_index = 0;
1705 }
1706
1707 // --------------------------------------------------------------------------------------------------------------
1708 void drop_stolen_items(object *objp)
1709 {
1710         int     i;
1711
1712         mprintf ((0,"Dropping thief items!\n"));
1713
1714         // -- compress_stolen_items();
1715
1716         for (i=0; i<MAX_STOLEN_ITEMS; i++) {
1717                 if (Stolen_items[i] != 255)
1718                         drop_powerup(OBJ_POWERUP, Stolen_items[i], 1, &objp->mtype.phys_info.velocity, &objp->pos, objp->segnum);
1719                 Stolen_items[i] = 255;
1720         }
1721
1722 }
1723
1724 // --------------------------------------------------------------------------------------------------------------
1725 void do_escort_menu(void)
1726 {
1727         int     i;
1728         char    msg[300];
1729         int     paused;
1730         int     key;
1731         int     next_goal;
1732         char    goal_str[32], tstr[32];
1733
1734         if (Game_mode & GM_MULTI) {
1735                 HUD_init_message("No Guide-Bot in Multiplayer!");
1736                 return;
1737         }
1738
1739         for (i=0; i<=Highest_object_index; i++) {
1740                 if (Objects[i].type == OBJ_ROBOT)
1741                         if (Robot_info[Objects[i].id].companion)
1742                                 break;
1743         }
1744
1745         if (i > Highest_object_index) {
1746
1747                 HUD_init_message("No Guide-Bot present in mine!");
1748
1749                 #ifndef NDEBUG
1750                 //      If no buddy bot, create one!
1751                 HUD_init_message("Debug Version: Creating Guide-Bot!");
1752                 create_buddy_bot();
1753                 #else
1754                 return;
1755                 #endif
1756         }
1757
1758         ok_for_buddy_to_talk(); //      Needed here or we might not know buddy can talk when he can.
1759
1760         if (!Buddy_allowed_to_talk) {
1761                 HUD_init_message("%s has not been released",guidebot_name);
1762                 return;
1763         }
1764
1765         digi_pause_digi_sounds();
1766         stop_time();
1767
1768         palette_save();
1769         apply_modified_palette();
1770         reset_palette_add();
1771
1772         game_flush_inputs();
1773
1774         paused = 1;
1775
1776 //      set_screen_mode( SCREEN_MENU );
1777         set_popup_screen();
1778         gr_palette_load( gr_palette );
1779
1780         //      This prevents the buddy from coming back if you've told him to scram.
1781         //      If we don't set next_goal, we get garbage there.
1782         if (Escort_special_goal == ESCORT_GOAL_SCRAM) {
1783                 Escort_special_goal = -1;       //      Else setting next goal might fail.
1784                 next_goal = escort_set_goal_object();
1785                 Escort_special_goal = ESCORT_GOAL_SCRAM;
1786         } else {
1787                 Escort_special_goal = -1;       //      Else setting next goal might fail.
1788                 next_goal = escort_set_goal_object();
1789         }
1790
1791         switch (next_goal) {
1792         #ifndef NDEBUG
1793                 case ESCORT_GOAL_UNSPECIFIED:
1794                         Int3();
1795                         sprintf(goal_str, "ERROR");
1796                         break;
1797         #endif
1798                         
1799                 case ESCORT_GOAL_BLUE_KEY:
1800                         sprintf(goal_str, "blue key");
1801                         break;
1802                 case ESCORT_GOAL_GOLD_KEY:
1803                         sprintf(goal_str, "yellow key");
1804                         break;
1805                 case ESCORT_GOAL_RED_KEY:
1806                         sprintf(goal_str, "red key");
1807                         break;
1808                 case ESCORT_GOAL_CONTROLCEN:
1809                         sprintf(goal_str, "reactor");
1810                         break;
1811                 case ESCORT_GOAL_BOSS:
1812                         sprintf(goal_str, "boss");
1813                         break;
1814                 case ESCORT_GOAL_EXIT:
1815                         sprintf(goal_str, "exit");
1816                         break;
1817                 case ESCORT_GOAL_MARKER1:
1818                 case ESCORT_GOAL_MARKER2:
1819                 case ESCORT_GOAL_MARKER3:
1820                 case ESCORT_GOAL_MARKER4:
1821                 case ESCORT_GOAL_MARKER5:
1822                 case ESCORT_GOAL_MARKER6:
1823                 case ESCORT_GOAL_MARKER7:
1824                 case ESCORT_GOAL_MARKER8:
1825                 case ESCORT_GOAL_MARKER9:
1826                         sprintf(goal_str, "marker %i", next_goal-ESCORT_GOAL_MARKER1+1);
1827                         break;
1828
1829         }
1830                         
1831         if (!Buddy_messages_suppressed)
1832                 sprintf(tstr, "Suppress");
1833         else
1834                 sprintf(tstr, "Enable");
1835
1836         sprintf(msg,    "Select Guide-Bot Command:\n\n"
1837                                                 "0.  Next Goal: %s" CC_LSPACING_S "3\n"
1838                                                 "\x84.  Find Energy Powerup" CC_LSPACING_S "3\n"
1839                                                 "2.  Find Energy Center" CC_LSPACING_S "3\n"
1840                                                 "3.  Find Shield Powerup" CC_LSPACING_S "3\n"
1841                                                 "4.  Find Any Powerup" CC_LSPACING_S "3\n"
1842                                                 "5.  Find a Robot" CC_LSPACING_S "3\n"
1843                                                 "6.  Find a Hostage" CC_LSPACING_S "3\n"
1844                                                 "7.  Stay Away From Me" CC_LSPACING_S "3\n"
1845                                                 "8.  Find My Powerups" CC_LSPACING_S "3\n"
1846                                                 "9.  Find the exit\n\n"
1847                                                 "T.  %s Messages\n"
1848                                                 // -- "9.       Find the exit" CC_LSPACING_S "3\n"
1849                                 , goal_str, tstr);
1850
1851         show_escort_menu(msg);          //TXT_PAUSE);
1852
1853         while (paused) {
1854         #ifdef WINDOWS
1855                 while (!(key = key_inkey()))
1856                 {
1857                         MSG wmsg;
1858                         DoMessageStuff(&wmsg);
1859                 }
1860         #else
1861                 key = key_getch();
1862         #endif
1863
1864                 switch (key) {
1865                         case KEY_0:
1866                         case KEY_1:
1867                         case KEY_2:
1868                         case KEY_3:
1869                         case KEY_4:
1870                         case KEY_5:
1871                         case KEY_6:
1872                         case KEY_7:
1873                         case KEY_8:
1874                         case KEY_9:
1875                                 Looking_for_marker = -1;
1876                                 Last_buddy_key = -1;
1877                                 set_escort_special_goal(key);
1878                                 Last_buddy_key = -1;
1879                                 paused = 0;
1880                                 break;
1881
1882                         case KEY_ESC:
1883                         case KEY_ENTER:
1884                                 clear_boxed_message();
1885                                 paused=0;
1886                                 break;
1887
1888 //--10/08/95-- Screwed up font, background.  Why needed, anyway?
1889 //--10/08/95--                  case KEY_F1:
1890 //--10/08/95--                          clear_boxed_message();
1891 //--10/08/95--                          do_show_help();
1892 //--10/08/95--                          show_boxed_message(msg);
1893 //--10/08/95--                          break;
1894
1895                         case KEY_PRINT_SCREEN:
1896                                 save_screen_shot(0);
1897                                 break;
1898
1899                         #ifndef RELEASE
1900                         case KEY_BACKSP: Int3(); break;
1901                         #endif
1902
1903                         case KEY_T: {
1904                                 char    msg[32];
1905                                 int     temp;
1906
1907                                 temp = !Buddy_messages_suppressed;
1908
1909                                 if (temp)
1910                                         strcpy(msg, "suppressed");
1911                                 else
1912                                         strcpy(msg, "enabled");
1913
1914                                 Buddy_messages_suppressed = 1;
1915                                 buddy_message("Messages %s.", msg);
1916
1917                                 Buddy_messages_suppressed = temp;
1918
1919                                 paused = 0;
1920                                 break;
1921                         }
1922
1923                         default:
1924                                 break;
1925
1926                 }
1927
1928         }
1929
1930         game_flush_inputs();
1931
1932         palette_restore();
1933
1934         start_time();
1935         digi_resume_digi_sounds();
1936
1937 }
1938
1939 //      -------------------------------------------------------------------------------
1940 //      Show the Buddy menu!
1941 void show_escort_menu(char *msg)
1942 {       
1943         int     w,h,aw;
1944         int     x,y;
1945
1946
1947         WINDOS(
1948                 dd_gr_set_current_canvas(&dd_VR_screen_pages[0]),
1949                 gr_set_current_canvas(&VR_screen_pages[0])
1950         );
1951
1952         gr_set_curfont( GAME_FONT );
1953
1954         gr_get_string_size(msg,&w,&h,&aw);
1955
1956         x = (grd_curscreen->sc_w-w)/2;
1957         y = (grd_curscreen->sc_h-h)/4;
1958
1959         gr_set_fontcolor( gr_getcolor(0, 28, 0), -1 );
1960    
1961    PA_DFX (pa_set_frontbuffer_current());
1962         PA_DFX (nm_draw_background(x-15,y-15,x+w+15-1,y+h+15-1));
1963    PA_DFX (pa_set_backbuffer_current());
1964    nm_draw_background(x-15,y-15,x+w+15-1,y+h+15-1);
1965
1966 WIN(DDGRLOCK(dd_grd_curcanv));\
1967         PA_DFX (pa_set_frontbuffer_current());
1968         PA_DFX (gr_ustring( x, y, msg ));
1969         PA_DFX (pa_set_backbuffer_current());
1970         gr_ustring( x, y, msg );
1971 WIN(DDGRUNLOCK(dd_grd_curcanv));
1972         gr_update();
1973
1974         reset_cockpit();
1975 }
1976