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