/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include "gamesnd/gamesnd.h" #include "globalincs/alphacolors.h" #include "globalincs/linklist.h" #include "globalincs/systemvars.h" #include "hud/hudescort.h" #include "hud/hudmessage.h" #include "hud/hudparse.h" #include "hud/hudshield.h" #include "hud/hudtargetbox.h" #include "iff_defs/iff_defs.h" #include "io/timer.h" #include "network/multi.h" #include "network/multiutil.h" #include "object/object.h" #include "parse/parselo.h" #include "playerman/player.h" #include "ship/ship.h" #include "weapon/emp.h" static int Show_escort_view; typedef struct escort_info { int objnum; int obj_signature; // so we are sure we have a valid objnum int priority; // higher priority is higher in the list short np_id; // netplayer id (for multiplayer dogfight mode) // These parallel the way the shield_hit_info struct works; intentionally // not using it here because this is a lot simpler and less error-prone int escort_hit_timer; int escort_hit_next_flash; bool escort_show_bright; escort_info() : objnum(-1), obj_signature(-1), priority(-1), np_id(-1), escort_hit_timer(0), escort_hit_next_flash(0), escort_show_bright(false) {} } escort_info; static SCP_list Escort_ships; int Max_escort_ships = 3; static int Last_target_index; // index into Escort_gauges for last targeted via 'Next Escort Target' key static SCP_list::iterator get_escort_entry_from_index(int index) { int e_idx = 0; auto it = Escort_ships.begin(); while ( (e_idx != index) && (it != Escort_ships.end()) ) { ++it; ++e_idx; } return it; } HudGaugeEscort::HudGaugeEscort(): HudGauge(HUD_OBJECT_ESCORT, HUD_ESCORT_VIEW, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255) { } void HudGaugeEscort::initHeaderText(const char *text) { strcpy_s(header_text, text); } void HudGaugeEscort::initHeaderTextOffsets(int x, int y) { header_text_offsets[0] = x; header_text_offsets[1] = y; } void HudGaugeEscort::initListStartOffsets(int x, int y) { list_start_offsets[0] = x; list_start_offsets[1] = y; } void HudGaugeEscort::initEntryHeight(int h) { entry_h = h; } void HudGaugeEscort::initEntryStaggerWidth(int w) { entry_stagger_w = w; } void HudGaugeEscort::initBottomBgOffset(int offset) { bottom_bg_offset = offset; } void HudGaugeEscort::initShipNameOffsets(int x, int y) { ship_name_offsets[0] = x; ship_name_offsets[1] = y; } void HudGaugeEscort::initShipIntegrityOffsets(int x, int y) { ship_integrity_offsets[0] = x; ship_integrity_offsets[1] = y; } void HudGaugeEscort::initShipStatusOffsets(int x, int y) { ship_status_offsets[0] = x; ship_status_offsets[1] = y; } void HudGaugeEscort::initShipNameMaxWidth(int w) { ship_name_max_width = w; } void HudGaugeEscort::initRightAlignNames(bool align) { right_align_names = align; } void HudGaugeEscort::initBitmaps(const char *fname_top, const char *fname_middle, const char *fname_bottom) { Escort_gauges[0].first_frame = bm_load_animation(fname_top, &Escort_gauges[0].num_frames); if (Escort_gauges[0].first_frame == -1) { Warning(LOCATION, "Could not load in ani: %s\n", fname_top); return; } Escort_gauges[1].first_frame = bm_load_animation(fname_middle, &Escort_gauges[1].num_frames); if (Escort_gauges[1].first_frame == -1) { Warning(LOCATION, "Could not load in ani: %s\n", fname_middle); return; } Escort_gauges[2].first_frame = bm_load_animation(fname_bottom, &Escort_gauges[2].num_frames); if (Escort_gauges[2].first_frame == -1) { Warning(LOCATION, "Could not load in ani: %s\n", fname_bottom); return; } } void HudGaugeEscort::pageIn() { int i; for ( i = 0; i < NUM_ESCORT_FRAMES; i++ ) { bm_page_in_aabitmap( Escort_gauges[i].first_frame, Escort_gauges[i].num_frames); } } int HudGaugeEscort::setGaugeColorEscort(int index, int team) { int is_flashing = 0; int is_bright = 0; int seen_from_team = (Player_ship != NULL) ? Player_ship->team : -1; int objnum = -1; // multiplayer dogfight if(MULTI_DOGFIGHT) { setGaugeColor(); return 0; } auto eship = get_escort_entry_from_index(index); if (eship == Escort_ships.end()) { return 0; } if ( (Game_mode & GM_MULTIPLAYER) && (eship->np_id >= 0) ) { int np_index = find_player_index(eship->np_id); if ( (np_index >= 0) && (Net_players[np_index].m_player != nullptr) ) { objnum = Net_players[np_index].m_player->objnum; } } else { objnum = eship->objnum; } if (objnum < 0) { return 0; } // set flashing color if (!timestamp_elapsed(eship->escort_hit_timer)) { is_flashing = 1; if (eship->escort_show_bright) { is_bright = 1; } } // Goober5000 - now base this on team color gr_set_color_fast(iff_get_color_by_team_and_object(team, seen_from_team, is_bright, &Objects[objnum])); // Goober5000 - an alternative; same as original but incorporating teams for non-friendlies /* if ((seen_from_team == team) || (seen_from_team < 0)) // :V: sez assume friendly if Player_ship is NULL hud_set_gauge_color(HUD_ESCORT_VIEW, is_bright ? HUD_C_BRIGHT : HUD_C_DIM); else gr_set_color_fast(iff_get_color_by_team(team, seen_from_team, is_bright)); */ // Goober5000 - original color logic /* if ((seen_from_team == team) || (seen_from_team < 0)) // :V: sez assume friendly if Player_ship is NULL hud_set_gauge_color(HUD_ESCORT_VIEW, is_bright ? HUD_C_BRIGHT : HUD_C_DIM); else gr_set_color_fast(is_bright ? &Color_bright_red : &Color_red); */ return is_flashing; } void HudGaugeEscort::render(float /*frametime*/, bool config) { if (Escort_gauges[0].first_frame < 0) return; if (!config && !Show_escort_view ) { return; } if (!config && Escort_ships.empty() ) { return; } int x = position[0]; int y = position[1]; float scale = 1.0; if (config) { std::tie(x, y, scale) = hud_config_convert_coord_sys(position[0], position[1], base_w, base_h); } // hud_set_default_color(); setGaugeColor(HUD_C_NONE, config); // draw the top of the escort view renderBitmap(Escort_gauges[0].first_frame, x, y, scale, config); renderString(x + fl2i(header_text_offsets[0] * scale), y + fl2i(header_text_offsets[1] * scale), header_text, scale, config); int lx = x + fl2i(list_start_offsets[0] * scale); int ly = y + fl2i(list_start_offsets[1] * scale); //This is temporary. Oh really? int num_escort_ships = config ? 2 : std::min(hud_escort_num_ships_on_list(), Max_escort_ships) - 1; if (num_escort_ships > 0) { for (int i = 0; i < num_escort_ships; i++) { renderBitmap(Escort_gauges[1].first_frame, lx, ly, scale, config); renderIcon(lx, ly, i, scale, config); // Apply offset for next entry lx += fl2i(entry_stagger_w * scale); ly += fl2i(entry_h * scale); } } // Show the last escort entry (already positioned correctly) renderBitmap(Escort_gauges[2].first_frame, lx, ly + fl2i(bottom_bg_offset * scale), scale, config); renderIcon(lx, ly, num_escort_ships, scale, config); // Now that we know the height of the list, we can send the coords to hud config if (config) { int bmw, bmh; bm_get_info(Escort_gauges[2].first_frame, &bmw, &bmh); hud_config_set_mouse_coords(gauge_config_id, x, lx + fl2i(bmw * scale), y, ly + fl2i(bmh * scale)); } } // draw the shield icon and integrity for the escort ship void HudGaugeEscort::renderIcon(int x, int y, int index, float scale, bool config) { if(!config && MULTI_DOGFIGHT && index <= 2) { renderIconDogfight(x, y, index); return; } auto eship = get_escort_entry_from_index(index); if (!config && eship == Escort_ships.end()) { return; } int objnum = -1; if (!config) { if ((Game_mode & GM_MULTIPLAYER) && (eship->np_id >= 0)) { int np_index = find_player_index(eship->np_id); if (np_index >= 0) { objnum = Net_players[np_index].m_player->objnum; // this can occassionally happen in multi when a player still needs to respawn. if (objnum < 0 || Objects[objnum].type != OBJ_SHIP) { return; } } } else { objnum = eship->objnum; } if (objnum < 0) { return; } } object* objp = nullptr; ship* sp = nullptr; if (!config) { objp = &Objects[objnum]; sp = &Ships[objp->instance]; // determine if its "friendly" or not // Goober5000 - changed in favor of just passing the team setGaugeColorEscort(index, sp->team); } else { // For config we do friendly, friendly disable, and hostile in that order // But try to find defined IFFs that match, else just use red and green bool found; switch (index) { case 2: // Try to find hostile found = false; for (iff_info& iff : Iff_info) { if (!stricmp(iff.iff_name, "hostile")) { gr_set_color_fast(iff_get_color(iff.color_index, false)); found = true; break; } } if (!found) { gr_set_color_fast(&Color_bright_red); } break; default: // Try to find friendly found = false; for (iff_info& iff : Iff_info) { if (!stricmp(iff.iff_name, "friendly")) { gr_set_color_fast(iff_get_color(iff.color_index, false)); found = true; break; } } if (!found) { gr_set_color_fast(&Color_bright_green); } break; } } // draw a 'D' if a ship is disabled if ((config && index == 1) || (!config && (( (sp->flags[Ship::Ship_Flags::Disabled]) || (ship_subsys_disrupted(sp, SUBSYSTEM_ENGINE)) )))) { renderString( x + fl2i(ship_status_offsets[0] * scale), y + fl2i(ship_status_offsets[1] * scale), EG_NULL, XSTR( "D", 284), scale, config); } // print out ship name // original behavior replaced with similar logic to hudtargetbox.cpp, except // if the name is hidden, it's replaced with the class name. char buf[256]; if (!config) { if (((Iff_info[sp->team].flags & IFFF_WING_NAME_HIDDEN) && (sp->wingnum != -1)) || (sp->flags[Ship::Ship_Flags::Hide_ship_name])) { // If we're hiding the ship name, we probably shouldn't append the callsign either hud_stuff_ship_class(buf, sp); } else { hud_stuff_ship_name(buf, sp); if (!Dont_show_callsigns_in_escort_list) { // maybe concatenate the callsign if (*buf) { char callsign[NAME_LENGTH]; hud_stuff_ship_callsign(callsign, sp); if (*callsign) sprintf(&buf[strlen(buf)], " (%s)", callsign); } // maybe substitute the callsign else { hud_stuff_ship_callsign(buf, sp); } } } } else { switch (index) { case 0: strcpy(buf, "Friendly Ship"); break; case 1: strcpy(buf, "Friendly Disabled Ship"); break; case 2: strcpy(buf, "Hostile Ship"); break; default: strcpy(buf, "Escort Ship"); break; } } const int w = font::force_fit_string(buf, 255, fl2i(ship_name_max_width * scale), scale); if (right_align_names) { renderString( x + fl2i(ship_name_offsets[0] * scale) + fl2i(ship_name_max_width * scale) - w, y + fl2i(ship_name_offsets[1] * scale), EG_ESCORT1 + index, buf, scale, config); } else { renderString( x + fl2i(ship_name_offsets[0] * scale), y + fl2i(ship_name_offsets[1] * scale), EG_ESCORT1 + index, buf, scale, config); } int screen_integrity; // show ship integrity float integrity = 0.0f; if (!config) { float shields; hud_get_target_strength(objp, &shields, &integrity); screen_integrity = fl2i(std::lround(integrity * 100)); } else { switch (index) { case 1: screen_integrity = 25; break; case 2: screen_integrity = 50; break; default: screen_integrity = 100; break; } } // set offsets based on integrity int offset = 0; if (screen_integrity < 100) { offset = 2; if (screen_integrity == 0) { if (integrity > 0) { screen_integrity = 1; } } } renderPrintfWithGauge( x+fl2i(ship_integrity_offsets[0] * scale) + offset, y+fl2i(ship_integrity_offsets[1] * scale), EG_NULL, scale, config, "%d", screen_integrity); //Let's be nice. setGaugeColor(HUD_C_NONE, config); } // multiplayer dogfight void HudGaugeEscort::renderIconDogfight(int x, int y, int index) { int hull_integrity = 100; char buf[256]; int np_index; object *objp; int stat_shift = 40; // always use the standard color to avoid confusion setGaugeColor(); auto eship = get_escort_entry_from_index(index); if (eship == Escort_ships.end()) { return; } // netplayer index np_index = find_player_index(eship->np_id); if((np_index < 0) || (np_index >= MAX_PLAYERS) || (Net_players[np_index].m_player == NULL)){ return; } // print out player name strcpy_s(buf, Net_players[np_index].m_player->callsign); font::force_fit_string(buf, 255, 100 - stat_shift); renderString( x + ship_name_offsets[0], y + ship_name_offsets[1], EG_ESCORT1 + index, buf); // can we get the player object? objp = NULL; if((Net_players[np_index].m_player->objnum >= 0) && (Net_players[np_index].m_player->objnum < MAX_OBJECTS) && (Objects[Net_players[np_index].m_player->objnum].type == OBJ_SHIP)){ objp = &Objects[Net_players[np_index].m_player->objnum]; if((objp->instance >= 0) && (objp->instance < MAX_SHIPS) && (Ships[objp->instance].ship_info_index >= 0) && (Ships[objp->instance].ship_info_index < MAX_SHIPS)){ // } else { return; } hull_integrity = (int)(((float)objp->hull_strength / (float)Ships[objp->instance].ship_max_hull_strength) * 100.0f); if(hull_integrity < 0){ hull_integrity = 0; } } // show ship integrity if(objp == NULL){ renderPrintfWithGauge( x+ship_integrity_offsets[0] - stat_shift, y+ship_integrity_offsets[1], EG_NULL, 1.0f, false, "%d", Net_players[np_index].m_player->stats.m_kill_count_ok); } else { renderPrintfWithGauge( x+ship_integrity_offsets[0] - stat_shift, y+ship_integrity_offsets[1], EG_NULL, 1.0f, false, "(%d%%) %d", hull_integrity, Net_players[np_index].m_player->stats.m_kill_count_ok); } } void hud_escort_update_list() { for (auto &es : Escort_ships) { if ( !timestamp_elapsed(es.escort_hit_timer) ) { if (timestamp_elapsed(es.escort_hit_next_flash)) { es.escort_hit_next_flash = timestamp(SHIELD_FLASH_INTERVAL); es.escort_show_bright = !es.escort_show_bright; // toggle between default and bright frames } } } } // called from HUD init, loads the bitmap data in once, and resets any data for each level void hud_escort_init() { Last_target_index = -1; if (Max_escort_ships > MAX_COMPLETE_ESCORT_LIST) { Max_escort_ships = MAX_COMPLETE_ESCORT_LIST; } } // ---------------------------------------------------------------------- // hud_escort_clear_all() // void hud_escort_clear_all(bool clear_flags) { if (clear_flags) { for (auto &es : Escort_ships) { if ( (es.objnum >= 0) && (Objects[es.objnum].type == OBJ_SHIP) && (Objects[es.objnum].instance >= 0) ) { Ships[Objects[es.objnum].instance].flags.remove(Ship::Ship_Flags::Escort); } } } Escort_ships.clear(); } // internal helper function for sort. // sorts first by priority number and then alphabetically static bool escort_compare(const escort_info &escort1, const escort_info &escort2) { if (MULTI_DOGFIGHT) { int n1 = find_player_index(escort1.np_id); int n2 = find_player_index(escort2.np_id); if ( (n1 < 0) || (n2 < 0) || (Net_players[n1].m_player == nullptr) || (Net_players[n2].m_player == nullptr) ) { return false; } return (Net_players[n1].m_player->stats.m_kill_count_ok >= Net_players[n2].m_player->stats.m_kill_count_ok); } else { // in multi, players go to the top if (Game_mode & GM_MULTIPLAYER) { if ( (escort1.np_id >= 0) && (escort2.np_id < 0) ) { return true; } else if ( (escort1.np_id < 0) && (escort2.np_id >= 0) ) { return false; } } if (escort1.priority != escort2.priority) { return (escort1.priority > escort2.priority); } else if ( (escort1.objnum >= 0) && (Objects[escort1.objnum].instance >= 0) && (escort2.objnum >= 0) && (Objects[escort2.objnum].instance >= 0) ) { const char *name1 = Ships[Objects[escort1.objnum].instance].ship_name; const char *name2 = Ships[Objects[escort2.objnum].instance].ship_name; return (stricmp(name1, name2) > 0); } } return false; } // create complete priority sorted escort list for all active ships // escorts - array of escort info // num_escorts - number of escorts requests in field of active ships // This will be culled to MAX_ESCORTS, selecting the top set from escorts void hud_create_complete_escort_list() { ship_obj *so; object *objp; int idx; // multiplayer dogfight if (MULTI_DOGFIGHT) { for (idx = 0; idx < MAX_PLAYERS; idx++) { // is this a valid player if ( MULTI_CONNECTED(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) ) { // add the ship escort_info einfo; einfo.np_id = Net_players[idx].player_id; Escort_ships.push_back(einfo); } } } // all others else { for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { Assert( so->objnum >= 0 && so->objnum < MAX_OBJECTS); if((so->objnum < 0) || (so->objnum >= MAX_OBJECTS)){ continue; } objp = &Objects[so->objnum]; // don't process objects that should be dead if (objp->flags[Object::Object_Flags::Should_be_dead]) { continue; } Assert( objp->type == OBJ_SHIP ); if(objp->type != OBJ_SHIP){ continue; } // only process ships that might be on the list if ( !(Ships[objp->instance].flags[Ship::Ship_Flags::Escort]) ) { continue; } // only process ships that can be seen by sensors if ( (Ships[objp->instance].flags[Ship::Ship_Flags::Hidden_from_sensors]) ) { continue; } // don't process most stealth ships if ( (Ships[objp->instance].flags[Ship::Ship_Flags::Stealth]) ) { if ( Ships[objp->instance].team == Player_ship->team ) { // friendly stealths are only not seen when explicitly specified if ( Ships[objp->instance].flags[Ship::Ship_Flags::Friendly_stealth_invis] ) { continue; } } // non-friendly stealths are never seen else { continue; } } // add the ship escort_info einfo; einfo.objnum = so->objnum; einfo.obj_signature = objp->signature; einfo.priority = Ships[objp->instance].escort_priority; Escort_ships.push_back(einfo); } } } // ---------------------------------------------------------------------- // hud_init_escort_info() // // Set up the initial escort list // void hud_setup_escort_list(int level) { // this should never be called while in a mission if (Game_mode & GM_IN_MISSION) { return; } Escort_ships.clear(); // build escort list hud_create_complete_escort_list(); // and sort it by priority Escort_ships.sort(escort_compare); // then resize the list to fit max if (hud_escort_num_ships_on_list() > MAX_COMPLETE_ESCORT_LIST) { Escort_ships.resize(MAX_COMPLETE_ESCORT_LIST); } if(level){ Show_escort_view = 1; } } // re-sort the escort list void hud_resort_escort_list() { Escort_ships.sort(escort_compare); } // called once per frame to refresh the escort list if important flags changed void hud_escort_cull_list() { if ( Escort_ships.empty() ) { return; } int np_index; auto it = Escort_ships.begin(); // multiplayer dogfight if(MULTI_DOGFIGHT){ while (it != Escort_ships.end()) { np_index = find_player_index(it->np_id); // maybe remove him if he left if (np_index < 0) { it = Escort_ships.erase(it); } else { ++it; } } } // everything else else { int objnum; bool remove; while (it != Escort_ships.end()) { remove = false; objnum = it->objnum; // player ships should stay so long as they are valid if ( (Game_mode & GM_MULTIPLAYER) && (it->np_id >= 0) ) { np_index = find_player_index(it->np_id); // maybe remove him if he left if (np_index < 0) { remove = true; } } else if (Objects[objnum].flags[Object::Object_Flags::Should_be_dead]) { remove = true; } else if (Objects[objnum].type == OBJ_SHIP) { int shipnum = Objects[objnum].instance; Assert( shipnum >= 0 && shipnum < MAX_SHIPS ); if ( !(Ships[shipnum].flags[Ship::Ship_Flags::Escort]) ) { remove = true; } else if (Ships[shipnum].flags[Ship::Ship_Flags::Hidden_from_sensors]) { remove = true; } else if (Ships[shipnum].flags[Ship::Ship_Flags::Stealth]) { if (Ships[shipnum].team == Player_ship->team) { // friendly stealths are only not seen when explicitly specified if ( Ships[shipnum].flags[Ship::Ship_Flags::Friendly_stealth_invis]) { remove = true; } } // non-friendly stealths are never seen else { remove = true; } } } if (remove) { it = Escort_ships.erase(it); } else { ++it; } } } } // ---------------------------------------------------------------------- // hud_escort_view_toggle() // void hud_escort_view_toggle() { Show_escort_view ^= 1; if ( Show_escort_view ) { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Escort view enabled", 286)); } else { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Escort view disabled", 287)); } } // try to add a ship to the escort list, if slot available void hud_add_ship_to_escort(int objnum, int supress_feedback) { // no ships on the escort list in multiplayer dogfight if (MULTI_DOGFIGHT) { return; } Assertion(objnum >= 0, "Invalid objnum passed to hud_add_ship_to_escort()!"); if (objnum < 0) { return; } if (Objects[objnum].type != OBJ_SHIP) { if ( !supress_feedback ) { snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL)); } return; } // check if ship is already on complete escort list bool found = false; for (auto &es : Escort_ships) { if (es.obj_signature == Objects[objnum].signature) { found = true; break; } } // add new ship into complete list if ( !found ) { escort_info einfo; einfo.objnum = objnum; einfo.obj_signature = Objects[objnum].signature; einfo.priority = Ships[Objects[objnum].instance].escort_priority; // add him to escort list and re-sort Escort_ships.push_back(einfo); Escort_ships.sort(escort_compare); Ships[Objects[objnum].instance].flags.set(Ship::Ship_Flags::Escort); } if (hud_escort_num_ships_on_list() > MAX_COMPLETE_ESCORT_LIST) { Escort_ships.resize(MAX_COMPLETE_ESCORT_LIST); // maybe do feedback if ( !supress_feedback ) { found = false; // search thru list for objnum for (auto &es : Escort_ships) { if (es.objnum == objnum) { found = true; break; } } if ( !found ) { HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Escort list is full with %d ships", 288), hud_escort_num_ships_on_list()); snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL)); } } } hud_gauge_popup_start(HUD_ESCORT_VIEW); } // ---------------------------------------------------------------------- // hud_add_remove_ship_escort() // void hud_add_remove_ship_escort(int objnum, int supress_feedback) { // no ships on the escort list in multiplayer dogfight if(MULTI_DOGFIGHT){ return; } if ( objnum < 0 ) { Int3(); return; } if ( Objects[objnum].type != OBJ_SHIP ) { if ( !supress_feedback ) { snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL)); } return; } // if it's in the list already just remove it for (auto it = Escort_ships.begin(); it != Escort_ships.end(); ++it) { if (it->obj_signature == Objects[objnum].signature) { if ( (Objects[objnum].type == OBJ_SHIP) && (Objects[objnum].instance >= 0) ) { Ships[Objects[objnum].instance].flags.remove(Ship::Ship_Flags::Escort); } Escort_ships.erase(it); hud_gauge_popup_start(HUD_ESCORT_VIEW); return; } } // otherwise add ship to list hud_add_ship_to_escort(objnum, supress_feedback); } void hud_remove_ship_from_escort(int objnum) { // no ships on the escort list in multiplayer dogfight if(MULTI_DOGFIGHT){ return; } if ( objnum < 0 ) { Int3(); return; } for (auto it = Escort_ships.begin(); it != Escort_ships.end(); ++it) { if (it->obj_signature == Objects[objnum].signature) { if ( (Objects[objnum].type == OBJ_SHIP) && (Objects[objnum].instance >= 0) ) { Ships[Objects[objnum].instance].flags.remove(Ship::Ship_Flags::Escort); } Escort_ships.erase(it); hud_gauge_popup_start(HUD_ESCORT_VIEW); break; } } } /** * Called whenever a ship is hit to determine if that ship is in the escort list. If it * is, then start timers to flash the name hull/shield icon for that ship. * * @param objp The object hit * @param quadrant Shield quadrant on the object that was hit, alternatively -1 if no shield */ void hud_escort_ship_hit(const object *objp, int /*quadrant*/) { // no ships on the escort list in multiplayer dogfight if(MULTI_DOGFIGHT){ return; } int objnum = OBJ_INDEX(objp); for (auto &es : Escort_ships) { if (es.objnum == objnum) { hud_gauge_popup_start(HUD_ESCORT_VIEW); es.escort_hit_timer = timestamp(SHIELD_HIT_FLASH_DURATION); es.escort_hit_next_flash = timestamp(SHIELD_FLASH_INTERVAL); break; } } } // target the next ship in the escort list void hud_escort_target_next() { int objnum; if ( Escort_ships.empty() ) { snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f ); return; } Last_target_index++; if ( Last_target_index >= hud_escort_num_ships_on_list() ) { Last_target_index = 0; } auto eship = get_escort_entry_from_index(Last_target_index); if ( (eship == Escort_ships.end()) || (eship->objnum < 0) ) { return; } objnum = eship->objnum; set_target_objnum( Player_ai, objnum); hud_restore_subsystem_target(&Ships[Objects[objnum].instance]); } // return the number of ships currently on the escort list int hud_escort_num_ships_on_list() { return static_cast(Escort_ships.size()); } // Return the object number for the ship at index position in the escort list int hud_escort_return_objnum(int index) { int escort_objnum, escort_sig; if (index < 0) { return -1; } auto eship = get_escort_entry_from_index(index); if ( (eship == Escort_ships.end()) || (eship->objnum < 0) ) { return -1; } escort_objnum = eship->objnum; escort_sig = eship->obj_signature; // ensure this is still a valid index if (Objects[escort_objnum].signature != escort_sig) { return -1; } return escort_objnum; } void hud_escort_add_player(short id) { Assert(Game_mode & GM_MULTIPLAYER); if(!(Game_mode & GM_MULTIPLAYER)){ return; } // just go through and add as long as its not a duplicate for (auto it = Escort_ships.begin(); it != Escort_ships.end(); ++it) { if (it->np_id == id) { return; } } escort_info einfo; einfo.np_id = id; Escort_ships.push_back(einfo); Escort_ships.sort(escort_compare); // resize the list to fit if (hud_escort_num_ships_on_list() > MAX_COMPLETE_ESCORT_LIST) { Escort_ships.resize(MAX_COMPLETE_ESCORT_LIST); } } void hud_escort_remove_player(short id) { Assert(Game_mode & GM_MULTIPLAYER); if(!(Game_mode & GM_MULTIPLAYER)){ return; } // find the instance and remove it if possible for (auto it = Escort_ships.begin(); it != Escort_ships.end(); ++it) { if (it->np_id == id) { Escort_ships.erase(it); return; } } }