/* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ #pragma off (unreferenced) static char rcsid[] = "$Id: escort.c 1.88 1996/09/12 12:11:42 matt Exp $"; #pragma on (unreferenced) #ifdef WINDOWS #include "desw.h" #endif #include // for printf() #include // for rand() and qsort() #include // for memset() #include #include "inferno.h" #include "mono.h" #include "3d.h" #include "palette.h" #include "object.h" #include "error.h" #include "ai.h" #include "robot.h" #include "fvi.h" #include "physics.h" #include "wall.h" #include "player.h" #include "fireball.h" #include "game.h" #include "powerup.h" #include "cntrlcen.h" #include "gauges.h" #include "key.h" #include "fuelcen.h" #include "sounds.h" #include "screens.h" #include "text.h" #include "gamefont.h" #include "newmenu.h" #include "playsave.h" #include "gameseq.h" #include "automap.h" #include "laser.h" #include "pa_enabl.h" #ifdef EDITOR #include "editor\editor.h" #endif extern void multi_send_stolen_items(); char *Escort_goal_text[MAX_ESCORT_GOALS] = { { "BLUE KEY"}, { "YELLOW KEY"}, { "RED KEY"}, { "REACTOR"}, { "EXIT"}, { "ENERGY"}, { "ENERGYCEN"}, { "SHIELD"}, { "POWERUP"}, { "ROBOT"}, { "HOSTAGES"}, { "SPEW"}, { "SCRAM"}, { "EXIT"}, { "BOSS"}, { "MARKER 1"}, { "MARKER 2"}, { "MARKER 3"}, { "MARKER 4"}, { "MARKER 5"}, { "MARKER 6"}, { "MARKER 7"}, { "MARKER 8"}, { "MARKER 9"}, // -- too much work -- "KAMIKAZE " }; int Max_escort_length = 200; int Escort_kill_object = -1; ubyte Stolen_items[MAX_STOLEN_ITEMS]; int Stolen_item_index; fix Escort_last_path_created = 0; int Escort_goal_object = ESCORT_GOAL_UNSPECIFIED, Escort_special_goal = -1, Escort_goal_index = -1, Buddy_messages_suppressed = 0; fix Buddy_sorry_time; int Buddy_objnum, Buddy_allowed_to_talk; int Looking_for_marker; int Last_buddy_key; fix Last_buddy_message_time; //if change this length, change in playsave.c also #define GUIDEBOT_NAME_LEN 9 char guidebot_name[GUIDEBOT_NAME_LEN+1] = "GUIDE-BOT"; char real_guidebot_name[GUIDEBOT_NAME_LEN+1] = "GUIDE-BOT"; void init_buddy_for_level(void) { int i; Buddy_allowed_to_talk = 0; Buddy_objnum = -1; Escort_goal_object = ESCORT_GOAL_UNSPECIFIED; Escort_special_goal = -1; Escort_goal_index = -1; Buddy_messages_suppressed = 0; for (i=0; i<=Highest_object_index; i++) if (Robot_info[Objects[i].id].companion) break; if (i <= Highest_object_index) Buddy_objnum = i; Buddy_sorry_time = -F1_0; Looking_for_marker = -1; Last_buddy_key = -1; } // ----------------------------------------------------------------------------- // See if segment from curseg through sidenum is reachable. // Return true if it is reachable, else return false. int segment_is_reachable(int curseg, int sidenum) { int wall_num, rval; segment *segp = &Segments[curseg]; if (!IS_CHILD(segp->children[sidenum])) return 0; wall_num = segp->sides[sidenum].wall_num; // If no wall, then it is reachable if (wall_num == -1) return 1; rval = ai_door_is_openable(NULL, segp, sidenum); return rval; // -- MK, 10/17/95 -- // -- MK, 10/17/95 -- // Hmm, a closed wall. I think this mean not reachable. // -- MK, 10/17/95 -- if (Walls[wall_num].type == WALL_CLOSED) // -- MK, 10/17/95 -- return 0; // -- MK, 10/17/95 -- // -- MK, 10/17/95 -- if (Walls[wall_num].type == WALL_DOOR) { // -- MK, 10/17/95 -- if (Walls[wall_num].keys == KEY_NONE) { // -- MK, 10/17/95 -- return 1; // @MK, 10/17/95: Be consistent with ai_door_is_openable // -- MK, 10/17/95 -- // -- if (Walls[wall_num].flags & WALL_DOOR_LOCKED) // -- MK, 10/17/95 -- // -- return 0; // -- MK, 10/17/95 -- // -- else // -- MK, 10/17/95 -- // -- return 1; // -- MK, 10/17/95 -- } else if (Walls[wall_num].keys == KEY_BLUE) // -- MK, 10/17/95 -- return (Players[Player_num].flags & PLAYER_FLAGS_BLUE_KEY); // -- MK, 10/17/95 -- else if (Walls[wall_num].keys == KEY_GOLD) // -- MK, 10/17/95 -- return (Players[Player_num].flags & PLAYER_FLAGS_GOLD_KEY); // -- MK, 10/17/95 -- else if (Walls[wall_num].keys == KEY_RED) // -- MK, 10/17/95 -- return (Players[Player_num].flags & PLAYER_FLAGS_RED_KEY); // -- MK, 10/17/95 -- else // -- MK, 10/17/95 -- Int3(); // Impossible! Doesn't have no key, but doesn't have any key! // -- MK, 10/17/95 -- } else // -- MK, 10/17/95 -- return 1; // -- MK, 10/17/95 -- // -- MK, 10/17/95 -- Int3(); // Hmm, thought 'if' above had to return! // -- MK, 10/17/95 -- return 0; } // ----------------------------------------------------------------------------- // Create a breadth-first list of segments reachable from current segment. // max_segs is maximum number of segments to search. Use MAX_SEGMENTS to search all. // On exit, *length <= max_segs. // Input: // start_seg // Output: // bfs_list: array of shorts, each reachable segment. Includes start segment. // length: number of elements in bfs_list void create_bfs_list(int start_seg, short bfs_list[], int *length, int max_segs) { int i, head, tail; byte visited[MAX_SEGMENTS]; for (i=0; ichildren[i]; if (IS_CHILD(connected_seg) && (visited[connected_seg] == 0)) { if (segment_is_reachable(curseg, i)) { bfs_list[head++] = connected_seg; if (head >= max_segs) break; visited[connected_seg] = 1; Assert(head < MAX_SEGMENTS); } } } } *length = head; } // ----------------------------------------------------------------------------- // Return true if ok for buddy to talk, else return false. // Buddy is allowed to talk if the segment he is in does not contain a blastable wall that has not been blasted // AND he has never yet, since being initialized for level, been allowed to talk. int ok_for_buddy_to_talk(void) { int i; segment *segp; if (Objects[Buddy_objnum].type != OBJ_ROBOT) { Buddy_allowed_to_talk = 0; return 0; } if (Buddy_allowed_to_talk) return 1; if ((Objects[Buddy_objnum].type == OBJ_ROBOT) && (Buddy_objnum <= Highest_object_index) && !Robot_info[Objects[Buddy_objnum].id].companion) { for (i=0; i<=Highest_object_index; i++) if (Robot_info[Objects[i].id].companion) break; if (i > Highest_object_index) return 0; else Buddy_objnum = i; } segp = &Segments[Objects[Buddy_objnum].segnum]; for (i=0; isides[i].wall_num; if (wall_num != -1) { if ((Walls[wall_num].type == WALL_BLASTABLE) && !(Walls[wall_num].flags & WALL_BLASTED)) return 0; } // Check one level deeper. if (IS_CHILD(segp->children[i])) { int j; segment *csegp = &Segments[segp->children[i]]; for (j=0; jsides[j].wall_num; if (wall2 != -1) { if ((Walls[wall2].type == WALL_BLASTABLE) && !(Walls[wall2].flags & WALL_BLASTED)) return 0; } } } } Buddy_allowed_to_talk = 1; return 1; } // -------------------------------------------------------------------------------------------- void detect_escort_goal_accomplished(int index) { int i,j; int detected = 0; if (!Buddy_allowed_to_talk) return; // If goal is to go away, how can it be achieved? if (Escort_special_goal == ESCORT_GOAL_SCRAM) return; // See if goal found was a key. Need to handle default goals differently. // Note, no buddy_met_goal sound when blow up reactor or exit. Not great, but ok // since for reactor, noisy, for exit, buddy is disappearing. if ((Escort_special_goal == -1) && (Escort_goal_index == index)) { detected = 1; goto dega_ok; } if ((Escort_goal_index <= ESCORT_GOAL_RED_KEY) && (index >= 0)) { if (Objects[index].type == OBJ_POWERUP) { if (Objects[index].id == POW_KEY_BLUE) { if (Escort_goal_index == ESCORT_GOAL_BLUE_KEY) { detected = 1; goto dega_ok; } } else if (Objects[index].id == POW_KEY_GOLD) { if (Escort_goal_index == ESCORT_GOAL_GOLD_KEY) { detected = 1; goto dega_ok; } } else if (Objects[index].id == POW_KEY_RED) { if (Escort_goal_index == ESCORT_GOAL_RED_KEY) { detected = 1; goto dega_ok; } } } } if (Escort_special_goal != -1) if (Escort_special_goal == ESCORT_GOAL_ENERGYCEN) { if (index == -4) detected = 1; else { for (i=0; i GameTime)) { if (ok_for_buddy_to_talk()) { char gb_str[16], new_format[128]; va_list args; int t; va_start(args, format ); vsprintf(new_format, format, args); va_end(args); gb_str[0] = 1; gb_str[1] = BM_XRGB(28, 0, 0); strcpy(&gb_str[2], guidebot_name); t = strlen(gb_str); gb_str[t] = ':'; gb_str[t+1] = 1; gb_str[t+2] = BM_XRGB(0, 31, 0); gb_str[t+3] = 0; HUD_init_message("%s %s", gb_str, new_format); Last_buddy_message_time = GameTime; } } } // ----------------------------------------------------------------------------- void thief_message(char * format, ... ) { char gb_str[16], new_format[128]; va_list args; va_start(args, format ); vsprintf(new_format, format, args); va_end(args); gb_str[0] = 1; gb_str[1] = BM_XRGB(28, 0, 0); strcpy(&gb_str[2], "THIEF:"); gb_str[8] = 1; gb_str[9] = BM_XRGB(0, 31, 0); gb_str[10] = 0; HUD_init_message("%s %s", gb_str, new_format); } // ----------------------------------------------------------------------------- // Return true if marker #id has been placed. int marker_exists_in_mine(int id) { int i; for (i=0; i<=Highest_object_index; i++) if (Objects[i].type == OBJ_MARKER) if (Objects[i].id == id) return 1; return 0; } // ----------------------------------------------------------------------------- void set_escort_special_goal(int special_key) { int marker_key; Buddy_messages_suppressed = 0; if (!Buddy_allowed_to_talk) { ok_for_buddy_to_talk(); if (!Buddy_allowed_to_talk) { int i; for (i=0; i<=Highest_object_index; i++) if ((Objects[i].type == OBJ_ROBOT) && Robot_info[Objects[i].id].companion) { HUD_init_message("%s has not been released.",guidebot_name); break; } if (i == Highest_object_index+1) HUD_init_message("No Guide-Bot in mine."); return; } } special_key = special_key & (~KEY_SHIFTED); marker_key = special_key; #ifdef MACINTOSH switch(special_key) { case KEY_5: marker_key = KEY_1+4; break; case KEY_6: marker_key = KEY_1+5; break; case KEY_7: marker_key = KEY_1+6; break; case KEY_8: marker_key = KEY_1+7; break; case KEY_9: marker_key = KEY_1+8; break; case KEY_0: marker_key = KEY_1+9; break; } #endif if (Last_buddy_key == special_key) if ((Looking_for_marker == -1) && (special_key != KEY_0)) { if (marker_exists_in_mine(marker_key - KEY_1)) Looking_for_marker = marker_key - KEY_1; else { Last_buddy_message_time = 0; // Force this message to get through. buddy_message("Marker %i not placed.", marker_key - KEY_1 + 1); Looking_for_marker = -1; } } else Looking_for_marker = -1; Last_buddy_key = special_key; if (special_key == KEY_0) Looking_for_marker = -1; if ( Looking_for_marker != -1 ) { Escort_special_goal = ESCORT_GOAL_MARKER1 + marker_key - KEY_1; } else { switch (special_key) { case KEY_1: Escort_special_goal = ESCORT_GOAL_ENERGY; break; case KEY_2: Escort_special_goal = ESCORT_GOAL_ENERGYCEN; break; case KEY_3: Escort_special_goal = ESCORT_GOAL_SHIELD; break; case KEY_4: Escort_special_goal = ESCORT_GOAL_POWERUP; break; case KEY_5: Escort_special_goal = ESCORT_GOAL_ROBOT; break; case KEY_6: Escort_special_goal = ESCORT_GOAL_HOSTAGE; break; case KEY_7: Escort_special_goal = ESCORT_GOAL_SCRAM; break; case KEY_8: Escort_special_goal = ESCORT_GOAL_PLAYER_SPEW; break; case KEY_9: Escort_special_goal = ESCORT_GOAL_EXIT; break; case KEY_0: Escort_special_goal = -1; break; default: Int3(); // Oops, called with illegal key value. } } Last_buddy_message_time = GameTime - 2*F1_0; // Allow next message to come through. say_escort_goal(Escort_special_goal); // -- Escort_goal_object = escort_set_goal_object(); Escort_goal_object = ESCORT_GOAL_UNSPECIFIED; } // -- old, pre-bfs, way -- // ----------------------------------------------------------------------------- // -- old, pre-bfs, way -- // Return object of interest. // -- old, pre-bfs, way -- int exists_in_mine(int objtype, int objid) // -- old, pre-bfs, way -- { // -- old, pre-bfs, way -- int i; // -- old, pre-bfs, way -- // -- old, pre-bfs, way -- mprintf((0, "exists_in_mine, type == %i, id == %i\n", objtype, objid)); // -- old, pre-bfs, way -- // -- old, pre-bfs, way -- if (objtype == FUELCEN_CHECK) { // -- old, pre-bfs, way -- for (i=0; i<=Highest_segment_index; i++) // -- old, pre-bfs, way -- if (Segments[i].special == SEGMENT_IS_FUELCEN) // -- old, pre-bfs, way -- return i; // -- old, pre-bfs, way -- } else { // -- old, pre-bfs, way -- for (i=0; i<=Highest_object_index; i++) { // -- old, pre-bfs, way -- if (Objects[i].type == objtype) { // -- old, pre-bfs, way -- // Don't find escort robots if looking for robot! // -- old, pre-bfs, way -- if ((Objects[i].type == OBJ_ROBOT) && (Robot_info[Objects[i].id].companion)) // -- old, pre-bfs, way -- continue; // -- old, pre-bfs, way -- // -- old, pre-bfs, way -- if (objid == -1) { // -- 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)) // -- old, pre-bfs, way -- return i; // -- old, pre-bfs, way -- else // -- old, pre-bfs, way -- return i; // -- old, pre-bfs, way -- } else if (Objects[i].id == objid) // -- old, pre-bfs, way -- return i; // -- old, pre-bfs, way -- } // -- old, pre-bfs, way -- } // -- old, pre-bfs, way -- } // -- old, pre-bfs, way -- // -- old, pre-bfs, way -- return -1; // -- old, pre-bfs, way -- // -- old, pre-bfs, way -- } // ----------------------------------------------------------------------------- // Return id of boss. int get_boss_id(void) { int i; for (i=0; i<=Highest_object_index; i++) if (Objects[i].type == OBJ_ROBOT) if (Robot_info[Objects[i].id].boss_flag) return Objects[i].id; return -1; } // ----------------------------------------------------------------------------- // Return object index if object of objtype, objid exists in mine, else return -1 // "special" is used to find objects spewed by player which is hacked into flags field of powerup. int exists_in_mine_2(int segnum, int objtype, int objid, int special) { if (Segments[segnum].objects != -1) { int objnum = Segments[segnum].objects; while (objnum != -1) { object *curobjp = &Objects[objnum]; if (special == ESCORT_GOAL_PLAYER_SPEW) { if (curobjp->flags & OF_PLAYER_DROPPED) return objnum; } if (curobjp->type == objtype) { // Don't find escort robots if looking for robot! if ((curobjp->type == OBJ_ROBOT) && (Robot_info[curobjp->id].companion)) ; else if (objid == -1) { if ((objtype == OBJ_POWERUP) && (curobjp->id != POW_KEY_BLUE) && (curobjp->id != POW_KEY_GOLD) && (curobjp->id != POW_KEY_RED)) return objnum; else return objnum; } else if (curobjp->id == objid) return objnum; } if (objtype == OBJ_POWERUP) if (curobjp->contains_count) if (curobjp->contains_type == OBJ_POWERUP) if (curobjp->contains_id == objid) return objnum; objnum = curobjp->next; } } return -1; } // ----------------------------------------------------------------------------- // Return nearest object of interest. // If special == ESCORT_GOAL_PLAYER_SPEW, then looking for any object spewed by player. // -1 means object does not exist in mine. // -2 means object does exist in mine, but buddy-bot can't reach it (eg, behind triggered wall) int exists_in_mine(int start_seg, int objtype, int objid, int special) { int segindex, segnum; short bfs_list[MAX_SEGMENTS]; int length; // mprintf((0, "exists_in_mine, type == %i, id == %i\n", objtype, objid)); create_bfs_list(start_seg, bfs_list, &length, MAX_SEGMENTS); if (objtype == FUELCEN_CHECK) { for (segindex=0; segindex