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