/* * 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 "hud/hudreticle.h" #include "hud/hudtargetbox.h" #include "io/timer.h" #include "localization/localize.h" #include "network/multi.h" #include "playerman/player.h" #include "ship/ship.h" #include "weapon/emp.h" #include "weapon/weapon.h" #include "globalincs/alphacolors.h" #define NUM_RETICLE_ANIS 11 // keep up to date when modifying the number of reticle ani files #define RETICLE_TOP_ARC 0 #define RETICLE_LASER_WARN 1 #define RETICLE_LOCK_WARN 2 #define RETICLE_LEFT_ARC 3 #define RETICLE_RIGHT_ARC 4 #define RETICLE_ONE_PRIMARY 5 #define RETICLE_TWO_PRIMARY 6 #define RETICLE_ONE_SECONDARY 7 #define RETICLE_TWO_SECONDARY 8 #define RETICLE_THREE_SECONDARY 9 //#define RETICLE_LAUNCH_LABEL 5 #define RETICLE_CENTER 10 int Hud_throttle_frame_w[GR_NUM_RESOLUTIONS] = { 49, 78 }; int Hud_throttle_h[GR_NUM_RESOLUTIONS] = { 50, 80 }; int Hud_throttle_bottom_y[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS] = { { 309, 494 }, { 307, 491 } }; int Hud_throttle_aburn_h[GR_NUM_RESOLUTIONS] = { 17, 27 }; int Hud_throttle_aburn_button[GR_NUM_RESOLUTIONS] = { 259, 414 }; int Outer_circle_radius[GR_NUM_RESOLUTIONS] = { 104, 166 }; int Hud_reticle_center[GR_NUM_RESOLUTIONS][2] = { { // GR_640 320, 242 }, { // GR_1024 512, 387 } }; char Reticle_frame_names[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS][NUM_RETICLE_ANIS][MAX_FILENAME_LEN] = { //XSTR:OFF { { // GR_640 "toparc1_fs1", "toparc2_fs1", "toparc3_fs1", "leftarc_fs1", "rightarc1_fs1", "rightarc2_fs1", "rightarc3_fs1", "rightarc4_fs1", "rightarc5_fs1", "rightarc6_fs1", // "toparc4_fs1", "reticle1_fs1", }, { // GR_1024 "2_toparc1_fs1", "2_toparc2_fs1", "2_toparc3_fs1", "2_leftarc_fs1", "2_rightarc1_fs1", "2_rightarc2_fs1", "2_rightarc3_fs1", "2_rightarc4_fs1", "2_rightarc5_fs1", "2_rightarc6_fs1", // "2_toparc4_fs1", "2_reticle1_fs1", } }, { { // GR_640 "toparc1", "toparc2", "toparc3", "leftarc", "rightarc1", "", "", "", "", "", // "", "reticle1", }, { // GR_1024 "2_toparc1", "2_toparc2", "2_toparc3", "2_leftarc", "2_rightarc1", "", "", "", "", "", // "", "2_reticle1", } } //XSTR:ON }; // reticle frame coords int Reticle_frame_coords[NUM_HUD_RETICLE_STYLES][GR_NUM_RESOLUTIONS][NUM_RETICLE_ANIS][2] = { { { // GR_640 {241, 137}, {300, 137}, {320, 137}, {217, 244}, {374, 242}, {406, 253}, {406, 253}, {391, 276}, {391, 276}, {391, 276}, // {297, 162}, {308, 235} }, { // GR_1024 {386, 219}, {480, 219}, {512, 219}, {347, 390}, {598, 387}, {650, 405}, {650, 405}, {626, 442}, {626, 442}, {626, 442}, // {475, 259}, {493, 376} }, }, { { // GR_640 {241, 137}, {400, 245}, {394, 261}, {216, 168}, {359, 168}, {406, 253}, {406, 253}, {391, 276}, {391, 276}, {391, 276}, // {297, 161}, {308, 235} }, { // GR_1024 {386, 219}, {640, 393}, {631, 419}, {346, 269}, {574, 269}, {649, 401}, {649, 401}, {625, 438}, {625, 438}, {625, 438}, // {475, 258}, {493, 370} } } }; // "launch" gauge coords int Reticle_launch_coords[GR_NUM_RESOLUTIONS][2] = { { // GR_640 297, 161 }, { // GR_1024 475, 258 } }; #define THREAT_DUMBFIRE (1<<0) #define THREAT_ATTEMPT_LOCK (1<<1) #define THREAT_LOCK (1<<2) #define THREAT_UPDATE_DUMBFIRE_TIME 1000 // time between checking for dumbfire threats #define THREAT_UPDATE_LOCK_TIME 500 // time between checking for lock threats #define THREAT_DUMBFIRE_FLASH 180 #define THREAT_LOCK_FLASH 180 static int Threat_lock_timer; // timestamp for when to show next flashing frame for lock threat static int Threat_lock_frame; // frame offset of current lock flashing warning HudGaugeReticle::HudGaugeReticle(): HudGauge(HUD_OBJECT_CENTER_RETICLE, HUD_CENTER_RETICLE, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_TOPDOWN | VM_OTHER_SHIP), 255, 255, 255) { has_autoaim_lock = false; } void HudGaugeReticle::initBitmaps(char *fname) { crosshair.first_frame = bm_load_animation(fname, &crosshair.num_frames); if (crosshair.first_frame < 0) { mprintf(("Cannot load hud ani: %s\n", fname)); } } void HudGaugeReticle::initFirepointDisplay(bool firepoint, int scaleX, int scaleY, int size) { firepoint_display = firepoint; firepoint_scale_x = scaleX; firepoint_scale_y = scaleY; firepoint_size = size; } void HudGaugeReticle::render(float /*frametime*/) { ship_info *sip = &Ship_info[Player_ship->ship_info_index]; if (autoaim_frame_offset > 0 || sip->autoaim_lock_snd.isValid() || sip->autoaim_lost_snd.isValid()) { ship *shipp = &Ships[Objects[Player->objnum].instance]; ship_weapon *swp = &shipp->weapons; ai_info *aip = &Ai_info[shipp->ai_index]; if (aip->target_objnum != -1) { bool autoaiming = false; autoaiming = in_autoaim_fov(shipp, swp->current_primary_bank, &Objects[aip->target_objnum]); if (autoaiming) { if (!has_autoaim_lock && sip->autoaim_lock_snd.isValid()) { snd_play(gamesnd_get_game_sound(sip->autoaim_lock_snd)); //snd_play( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::THREAT_FLASH))); } has_autoaim_lock = true; } else { if (has_autoaim_lock && sip->autoaim_lost_snd.isValid()) { snd_play(gamesnd_get_game_sound(sip->autoaim_lost_snd)); } has_autoaim_lock = false; } } } setGaugeColor(HUD_C_BRIGHT); if (has_autoaim_lock) renderBitmap(crosshair.first_frame + autoaim_frame_offset, position[0], position[1]); else renderBitmap(crosshair.first_frame, position[0], position[1]); if (firepoint_display) { fp.clear(); getFirepointStatus(); if (!fp.empty()) { int ax, ay; bm_get_info(crosshair.first_frame, &ax, &ay); int centerX = position[0] + (ax / 2); int centerY = position[1] + (ay / 2); for (SCP_vector::iterator fpi = fp.begin(); fpi != fp.end(); ++fpi) { if (fpi->active == 2) setGaugeColor(HUD_C_BRIGHT); else if (fpi->active == 1) setGaugeColor(HUD_C_NORMAL); else setGaugeColor(HUD_C_DIM); renderCircle((int) (centerX + (fpi->xy.x * firepoint_scale_x)), (int) (centerY + (fpi->xy.y * firepoint_scale_y)), firepoint_size); } } } } void HudGaugeReticle::getFirepointStatus() { //First, get the player ship ship_info* sip; ship* shipp; polymodel* pm; Assert(Objects[Player->objnum].type == OBJ_SHIP); if (Objects[Player->objnum].type == OBJ_SHIP) { shipp = &Ships[Objects[Player->objnum].instance]; sip = &Ship_info[shipp->ship_info_index]; //Get the player eyepoint pm = model_get(sip->model_num); if (pm->n_view_positions == 0) { mprintf(("Model %s does not have a defined eyepoint. Firepoint display could not be generated\n", pm->filename)); } else { if (pm->n_guns > 0) { eye eyepoint = pm->view_positions[shipp->current_viewpoint]; vec2d ep = { eyepoint.pnt.xyz.x, eyepoint.pnt.xyz.y }; for (int i = 0; i < pm->n_guns; i++) { int bankactive = 0; ship_weapon *swp = &shipp->weapons; if (!timestamp_elapsed(shipp->weapons.next_primary_fire_stamp[i])) bankactive = 1; else if (timestamp_elapsed(shipp->weapons.primary_animation_done_time[i])) bankactive = 1; else if (i == shipp->weapons.current_primary_bank || shipp->flags[Ship::Ship_Flags::Primary_linked]) bankactive = 2; int num_slots = pm->gun_banks[i].num_slots; for (int j = 0; j < num_slots; j++) { int fpactive = bankactive; if (sip->flags[Ship::Info_Flags::Dyn_primary_linking]) { // If this firepoint is not among the next shot(s) to be fired, dim it one step if ( !( (j >= (shipp->last_fired_point[i]+1) % num_slots) && (j <= (shipp->last_fired_point[i]+swp->primary_bank_slot_count[i]) % num_slots) ) ) { fpactive--; } } else if (Weapon_info[swp->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Cycle]) { // If this firepoint is not the next one to be fired, dim it one step if (j != (shipp->last_fired_point[i]+1) % num_slots) { fpactive--; } } vec3d fpfromeye; matrix eye_orient, player_transpose; vm_copy_transpose(&player_transpose, &Objects[Player->objnum].orient); vm_matrix_x_matrix(&eye_orient, &player_transpose, &Eye_matrix); vm_vec_rotate(&fpfromeye, &pm->gun_banks[i].pnt[j], &eye_orient); firepoint tmp = { { fpfromeye.xyz.x - ep.x, ep.y - fpfromeye.xyz.y }, fpactive }; fp.push_back(tmp); } } } } } } void HudGaugeReticle::setAutoaimFrame(int framenum) { if (framenum < 0 || framenum > crosshair.num_frames - 1) autoaim_frame_offset = 0; else autoaim_frame_offset = framenum; } void HudGaugeReticle::pageIn() { bm_page_in_aabitmap( crosshair.first_frame, crosshair.num_frames); } HudGaugeThrottle::HudGaugeThrottle(): HudGauge(HUD_OBJECT_THROTTLE, HUD_THROTTLE_GAUGE, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255) { } void HudGaugeThrottle::initThrottleStartY(int y) { Bottom_offset_y = y; } void HudGaugeThrottle::initThrottleSizes(int w, int h) { throttle_w = w; throttle_h = h; } void HudGaugeThrottle::initAburnHeight(int h) { throttle_aburn_h = h; } void HudGaugeThrottle::initMaxSpeedOffsets(int x, int y, bool show) { Max_speed_offsets[0] = x; Max_speed_offsets[1] = y; Show_max_speed = show; } void HudGaugeThrottle::initZeroSpeedOffsets(int x, int y, bool show) { Zero_speed_offsets[0] = x; Zero_speed_offsets[1] = y; Show_min_speed = show; } void HudGaugeThrottle::initOrbitCenterOffsets(int x, int y, bool orbiting) { Orbit_center_offsets[0] = x; Orbit_center_offsets[1] = y; orbit = orbiting; } void HudGaugeThrottle::initOrbitRadius(int radius) { orbit_radius = radius; } void HudGaugeThrottle::initTargetSpeedOffsets(int x, int y, bool show, bool percent) { Target_speed_offsets[0] = x; Target_speed_offsets[1] = y; Show_target_speed = show; Show_percent = percent; } void HudGaugeThrottle::showBackground(bool show) { Show_background = show; } void HudGaugeThrottle::initGlideOffsets(int x, int y, bool custom) { Glide_offsets[0] = x; Glide_offsets[1] = y; Use_custom_glide = custom; } void HudGaugeThrottle::initMatchSpeedOffsets(int x, int y, bool custom) { Match_speed_offsets[0] = x; Match_speed_offsets[1] = y; Use_custom_match_speed = custom; font::FSFont* fsFont = font::get_font(font_num); if (fsFont->getType() == font::VFNT_FONT) { ubyte sc = lcl_get_font_index(font_num); // NOTE: default to normal m because either // a) the german font has no special m (its an a) // b) the font has no special characters if (sc == 0 || Lcl_gr) { Match_speed_icon = 'm'; } else { Match_speed_icon = sc + 3; } Match_speed_draw_background = false; } else { // Default version for other fonts, draw a black character on a rectangle Match_speed_icon = 'm'; Match_speed_draw_background = true; } SCP_stringstream stream; stream << static_cast(Match_speed_icon); const SCP_string& iconStr = stream.str(); gr_get_string_size(&Match_speed_icon_width, nullptr, iconStr.c_str()); } void HudGaugeThrottle::initBitmaps(char *fname) { throttle_frames.first_frame = bm_load_animation(fname, &throttle_frames.num_frames); if (throttle_frames.first_frame < 0) { mprintf(("Cannot load hud ani: %s\n", fname)); } } void HudGaugeThrottle::pageIn() { bm_page_in_aabitmap( throttle_frames.first_frame, throttle_frames.num_frames); } void HudGaugeThrottle::render(float /*frametime*/) { float desired_speed, max_speed, current_speed, absolute_speed, absolute_displayed_speed, max_displayed_speed, percent_max, percent_aburn_max; int desired_y_pos, y_end; ship_info *sip; sip = &Ship_info[Player_ship->ship_info_index]; current_speed = Player_obj->phys_info.fspeed; if ( current_speed < 0.0f){ current_speed = 0.0f; } max_speed = Ships[Player_obj->instance].current_max_speed; if ( max_speed <= 0 ) { max_speed = sip->max_vel.xyz.z; } absolute_speed = Player_obj->phys_info.speed; // scale by distance modifier from hud_guages.tbl for display purposes absolute_displayed_speed = absolute_speed * Hud_speed_multiplier; max_displayed_speed = max_speed * Hud_speed_multiplier; desired_speed = Player->ci.forward * max_speed; if ( desired_speed < 0.0f ){ // so ships that go backwards don't force the indicators below where they can go desired_speed = 0.0f; } desired_y_pos = position[1] + Bottom_offset_y - (int)std::lround(throttle_h*desired_speed/max_speed) - 1; if (max_speed <= 0) { percent_max = 0.0f; } else { percent_max = current_speed / max_speed; } percent_aburn_max = 0.0f; if ( percent_max > 1 ) { percent_max = 1.0f; percent_aburn_max = (current_speed - max_speed) / (sip->afterburner_max_vel.xyz.z - max_speed); if ( percent_aburn_max > 1.0f ) { percent_aburn_max = 1.0f; } if ( percent_aburn_max < 0 ) { percent_aburn_max = 0.0f; } } y_end = position[1] + Bottom_offset_y - (int)std::lround(throttle_h*percent_max); if ( percent_aburn_max > 0 ) { y_end -= (int)std::lround(percent_aburn_max * throttle_aburn_h); } if ( Player_obj->phys_info.flags & PF_AFTERBURNER_ON ) { // default value is 240 when afterburner is on. //I'm assuming that this value is basically Bottom_offset_y - throttle_aburn_h - throttle_h desired_y_pos = position[1] + Bottom_offset_y - throttle_aburn_h - throttle_h; } setGaugeColor(); if(Show_background) { renderThrottleBackground(y_end); } else { renderBitmap(throttle_frames.first_frame, position[0], position[1]); } // draw throttle speed number //hud_render_throttle_speed(current_speed, y_end); // Absolute speed, not forward speed, for hud speed reticle - fixes the guage for sliding -- kazan renderThrottleSpeed(absolute_displayed_speed, y_end); // draw target speed if necessary if ( Show_target_speed ) { char buf[32]; int w, h; if ( Show_percent ) { if ( Player_obj->phys_info.flags & PF_AFTERBURNER_ON ) { strcpy_s(buf, "A/B"); } else { sprintf(buf, XSTR( "%d%%", 326), (int)std::lround( (desired_speed/max_speed)*100 )); } } else { sprintf(buf, "%d", (int)std::lround(desired_speed * Hud_speed_multiplier)); } hud_num_make_mono(buf, font_num); gr_get_string_size(&w, &h, buf); renderString(position[0] + Target_speed_offsets[0] - w, position[1] + Target_speed_offsets[1], buf); } // draw the "desired speed" bar on the throttle renderThrottleLine(desired_y_pos); // draw left arc (the bright portion of the throttle gauge) renderThrottleForeground(y_end); if ( Show_max_speed ) { renderPrintf(position[0] + Max_speed_offsets[0], position[1] + Max_speed_offsets[1], "%d", (int)std::lround(max_displayed_speed)); } if ( Show_min_speed ) { renderPrintf(position[0] + Zero_speed_offsets[0], position[1] + Zero_speed_offsets[1], "%s", XSTR( "0", 292)); } } void HudGaugeThrottle::renderThrottleSpeed(float current_speed, int y_end) { char buf[32]; int sx, sy, x_pos, y_pos, w, h; //setGaugeColor(); sprintf(buf, "%d", (int)std::lround(current_speed)); hud_num_make_mono(buf, font_num); gr_get_string_size(&w, &h, buf); if ( orbit ) { // y_end is the y-coordinate of the current throttle setting, calc x-coordinate for edge of // circle (x^2 + y^2 = r^2) y_pos = position[1] + Orbit_center_offsets[1] - y_end; x_pos = (int)sqrt(double(orbit_radius * orbit_radius - y_pos * y_pos) ); x_pos = position[0] + Orbit_center_offsets[0] - x_pos; // draw current speed at (x_pos, y_end); sx = x_pos - w - 2; sy = fl2i(y_end - h/2.0f + 1.5); } else { sx = position[0] + Orbit_center_offsets[0] - w; sy = position[1] + Orbit_center_offsets[1]; } renderPrintf(sx, sy, "%s", buf); if ( object_get_gliding(Player_obj) ) { if ( Use_custom_glide ) { renderString(position[0] + Glide_offsets[0], position[1] + Glide_offsets[1], "GLIDE"); } else { int offset; if ( current_speed <= 9.5 ) { offset = -31; } else if ( current_speed <= 99.5 ) { offset = -22; } else { offset = -13; } renderString(sx+offset, sy + h, "GLIDE"); } } else if ( Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET ) { if ( Use_custom_match_speed ) { renderMatchSpeedIcon(position[0] + Match_speed_offsets[0], position[1] + Match_speed_offsets[1]); } else { int offset; if ( current_speed <= 9.5 ) { offset = 0; } else { offset = 3; } renderMatchSpeedIcon(sx+offset, sy + h); } } } void HudGaugeThrottle::renderThrottleLine(int y_line) { // hud_set_bright_color(); //setGaugeColor(HUD_C_BRIGHT); renderBitmapEx(throttle_frames.first_frame+3, position[0], y_line, throttle_w, 1, 0, y_line-position[1]); } void HudGaugeThrottle::renderThrottleForeground(int y_end) { int w,h; //setGaugeColor(); bm_get_info(throttle_frames.first_frame+1,&w,&h); if ( y_end < (position[1] + h - 1) ) { renderBitmapEx(throttle_frames.first_frame + 2, position[0], y_end, w, h - (y_end - position[1]), 0, y_end - position[1]); } } void HudGaugeThrottle::renderThrottleBackground(int y_end) { int w,h; //setGaugeColor(); bm_get_info( throttle_frames.first_frame+1,&w,&h); if ( y_end > position[1] ) { renderBitmapEx(throttle_frames.first_frame+1, position[0], position[1], w, y_end-position[1]+1, 0, 0); } } void HudGaugeThrottle::renderMatchSpeedIcon(int x, int y) { if (Match_speed_draw_background) { // One pixel boundary renderRect(x, y, Match_speed_icon_width + 2, gr_get_font_height() + 2); gr_set_color_fast(&Color_black); renderPrintf(x + 1, y + 1, "%c", Match_speed_icon); setGaugeColor(); } else { renderPrintf(x, y, "%c", Match_speed_icon); } } HudGaugeThreatIndicator::HudGaugeThreatIndicator(): HudGauge(HUD_OBJECT_THREAT, HUD_THREAT_GAUGE, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255) { } void HudGaugeThreatIndicator::initLaserWarnOffsets(int x, int y) { Laser_warn_offsets[0] = x; Laser_warn_offsets[1] = y; } void HudGaugeThreatIndicator::initLockWarnOffsets(int x, int y) { Lock_warn_offsets[0] = x; Lock_warn_offsets[1] = y; } void HudGaugeThreatIndicator::initBitmaps(char *fname_arc, char *fname_laser, char *fname_lock) { threat_arc.first_frame = bm_load_animation(fname_arc, &threat_arc.num_frames); if (threat_arc.first_frame < 0) { mprintf(("Cannot load hud ani: %s\n", fname_arc)); } laser_warn.first_frame = bm_load_animation(fname_laser, &laser_warn.num_frames); if (laser_warn.first_frame < 0) { mprintf(("Cannot load hud ani: %s\n", fname_laser)); } lock_warn.first_frame = bm_load_animation(fname_lock, &lock_warn.num_frames); if (lock_warn.first_frame < 0) { mprintf(("Cannot load hud ani: %s\n", fname_lock)); } } void HudGaugeThreatIndicator::initialize() { laser_warn_timer = timestamp(1); laser_warn_frame = 0; lock_warn_timer = timestamp(1); lock_warn_frame = 0; HudGauge::initialize(); } void HudGaugeThreatIndicator::pageIn() { bm_page_in_aabitmap(threat_arc.first_frame, threat_arc.num_frames); bm_page_in_aabitmap(laser_warn.first_frame, laser_warn.num_frames); bm_page_in_aabitmap(lock_warn.first_frame, lock_warn.num_frames); } void HudGaugeThreatIndicator::render(float /*frametime*/) { setGaugeColor(); renderBitmap(threat_arc.first_frame+1, position[0], position[1]); renderLaserThreat(); renderLockThreat(); } void HudGaugeThreatIndicator::renderLaserThreat() { int frame_offset, num_frames; //Check how many frames the ani actually has num_frames = laser_warn.num_frames; //We need at least two frames here Assert( num_frames >= 2 ); if ( Player->threat_flags & THREAT_DUMBFIRE ) { if ( timestamp_elapsed(laser_warn_timer) ) { laser_warn_timer = timestamp(THREAT_DUMBFIRE_FLASH); laser_warn_frame++; if ( laser_warn_frame > (num_frames - 1) ) { //The first frame being the default "off" setting, we need to cycle through all the other frames laser_warn_frame = 1; } } frame_offset = laser_warn_frame; } else { frame_offset = 0; } renderBitmap(laser_warn.first_frame + frame_offset, position[0] + Laser_warn_offsets[0], position[1] + Laser_warn_offsets[1]); } void HudGaugeThreatIndicator::renderLockThreat() { int frame_offset, num_frames; //Let's find out how many frames our ani has, and adjust accordingly num_frames = lock_warn.num_frames; //We need at least two frames here Assert( num_frames >= 2 ); if ( Player->threat_flags & (THREAT_LOCK | THREAT_ATTEMPT_LOCK) ) { if ( timestamp_elapsed(lock_warn_timer) ) { if ( Player->threat_flags & THREAT_LOCK ) { lock_warn_timer = timestamp(fl2i(THREAT_LOCK_FLASH/2.0f)); } else { lock_warn_timer = timestamp(THREAT_LOCK_FLASH); } lock_warn_frame++; if ( lock_warn_frame > (num_frames - 1) ) { //The first frame being the default "off" setting, we need to cycle through all the other frames lock_warn_frame = 1; } } frame_offset = lock_warn_frame; } else { frame_offset = 0; } renderBitmap(lock_warn.first_frame+frame_offset, position[0] + Lock_warn_offsets[0], position[1] + Lock_warn_offsets[1]); } HudGaugeWeaponLinking::HudGaugeWeaponLinking(): HudGauge(HUD_OBJECT_WEAPON_LINKING, HUD_THREAT_GAUGE, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255) { } void HudGaugeWeaponLinking::init1PrimaryOffsets(int x, int y) { Weapon_link_offsets[LINK_ONE_PRIMARY][0] = x; Weapon_link_offsets[LINK_ONE_PRIMARY][1] = y; } void HudGaugeWeaponLinking::init2PrimaryOffsets(int x, int y) { Weapon_link_offsets[LINK_TWO_PRIMARY][0] = x; Weapon_link_offsets[LINK_TWO_PRIMARY][1] = y; } void HudGaugeWeaponLinking::init1SecondaryOffsets(int x, int y) { Weapon_link_offsets[LINK_ONE_SECONDARY][0] = x; Weapon_link_offsets[LINK_ONE_SECONDARY][1] = y; } void HudGaugeWeaponLinking::init2SecondaryOffsets(int x, int y) { Weapon_link_offsets[LINK_TWO_SECONDARY][0] = x; Weapon_link_offsets[LINK_TWO_SECONDARY][1] = y; } void HudGaugeWeaponLinking::init3SecondaryOffsets(int x, int y) { Weapon_link_offsets[LINK_THREE_SECONDARY][0] = x; Weapon_link_offsets[LINK_THREE_SECONDARY][1] = y; } void HudGaugeWeaponLinking::initBitmaps(char *fname_arc, char *fname_primary_link_1, char *fname_primary_link_2, char *fname_secondary_link_1, char *fname_secondary_link_2, char *fname_secondary_link_3) { arc.first_frame = bm_load_animation(fname_arc, &arc.num_frames); if (arc.first_frame < 0) { Warning(LOCATION, "Cannot load hud ani: %s\n", fname_arc); } weapon_linking_modes[LINK_ONE_PRIMARY].first_frame = bm_load_animation(fname_primary_link_1, &weapon_linking_modes[LINK_ONE_PRIMARY].num_frames); if (weapon_linking_modes[LINK_ONE_PRIMARY].first_frame < 0) { Warning(LOCATION, "Cannot load hud ani: %s\n", fname_primary_link_1); } weapon_linking_modes[LINK_TWO_PRIMARY].first_frame = bm_load_animation(fname_primary_link_2, &weapon_linking_modes[LINK_TWO_PRIMARY].num_frames); if (weapon_linking_modes[LINK_TWO_PRIMARY].first_frame < 0) { Warning(LOCATION, "Cannot load hud ani: %s\n", fname_primary_link_2); } weapon_linking_modes[LINK_ONE_SECONDARY].first_frame = bm_load_animation(fname_secondary_link_1, &weapon_linking_modes[LINK_ONE_SECONDARY].num_frames); if (weapon_linking_modes[LINK_ONE_SECONDARY].first_frame < 0) { Warning(LOCATION, "Cannot load hud ani: %s\n", fname_secondary_link_1); } weapon_linking_modes[LINK_TWO_SECONDARY].first_frame = bm_load_animation(fname_secondary_link_2, &weapon_linking_modes[LINK_TWO_SECONDARY].num_frames); if (weapon_linking_modes[LINK_TWO_SECONDARY].first_frame < 0) { Warning(LOCATION, "Cannot load hud ani: %s\n", fname_secondary_link_2); } weapon_linking_modes[LINK_THREE_SECONDARY].first_frame = bm_load_animation(fname_secondary_link_3, &weapon_linking_modes[LINK_THREE_SECONDARY].num_frames); if (weapon_linking_modes[LINK_THREE_SECONDARY].first_frame < 0) { Warning(LOCATION, "Cannot load hud ani: %s\n", fname_secondary_link_3); } } void HudGaugeWeaponLinking::pageIn() { bm_page_in_aabitmap(arc.first_frame, arc.num_frames); for(int i = 0; i < NUM_WEAPON_LINK_MODES; i++) { bm_page_in_aabitmap(weapon_linking_modes[i].first_frame, weapon_linking_modes[i].num_frames); } } void HudGaugeWeaponLinking::render(float /*frametime*/) { int gauge_index=0, frame_offset=0; ship_weapon *swp; setGaugeColor(); if (arc.first_frame >= 0) { renderBitmap(arc.first_frame+1, position[0], position[1]); } swp = &Player_ship->weapons; switch( swp->num_primary_banks ) { case 0: gauge_index = -1; break; case 1: gauge_index = LINK_ONE_PRIMARY; if ( Player_ship->weapons.current_primary_bank == -1 ) { frame_offset = 0; } else { frame_offset = 1; } break; case 2: gauge_index = LINK_TWO_PRIMARY; if ( swp->current_primary_bank == -1 ) { frame_offset = 0; } else { if ( Player_ship->flags[Ship::Ship_Flags::Primary_linked] ) { frame_offset = 3; } else { if ( swp->current_primary_bank == 0 ) { frame_offset = 1; } else { frame_offset = 2; } } } break; default: Int3(); // shouldn't happen (get Alan if it does) return; break; } if ( gauge_index != -1 ) { if (weapon_linking_modes[gauge_index].first_frame >= 0) { renderBitmap(weapon_linking_modes[gauge_index].first_frame+frame_offset, position[0] + Weapon_link_offsets[gauge_index][0], position[1] + Weapon_link_offsets[gauge_index][1]); } } int num_banks = swp->num_secondary_banks; if ( num_banks <= 0 ) { num_banks = Ship_info[Player_ship->ship_info_index].num_secondary_banks; } switch( num_banks ) { case 0: gauge_index = -1; break; case 1: gauge_index = LINK_ONE_SECONDARY; break; case 2: gauge_index = LINK_TWO_SECONDARY; break; case 3: gauge_index = LINK_THREE_SECONDARY; break; default: Int3(); // shouldn't happen (get Alan if it does) return; break; } if ( gauge_index != -1 ) { if ( swp->num_secondary_banks <= 0 ) { frame_offset = 0; } else { frame_offset = swp->current_secondary_bank+1; } if (weapon_linking_modes[gauge_index].first_frame >= 0) { renderBitmap(weapon_linking_modes[gauge_index].first_frame+frame_offset, position[0] + Weapon_link_offsets[gauge_index][0], position[1] + Weapon_link_offsets[gauge_index][1]); } } } // Called at the start of each level.. use Reticle_inited so we only load frames once void hud_init_reticle() { Threat_lock_timer = timestamp(0); Threat_lock_frame = 1; Player->threat_flags = 0; Player->update_dumbfire_time = timestamp(0); Player->update_lock_time = timestamp(0); } // called once per frame to update the reticle gauges. Makes calls to // ship_dumbfire_threat() and ship_lock_threat() and updates Threat_flags. void hud_update_reticle( player *pp ) { int rval; ship *shipp; // multiplayer clients won't call this routine if ( MULTIPLAYER_CLIENT || MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])) return; shipp = &Ships[Objects[pp->objnum].instance]; if ( ship_dumbfire_threat(shipp) ) { pp->threat_flags |= THREAT_DUMBFIRE; pp->update_dumbfire_time = timestamp(THREAT_UPDATE_DUMBFIRE_TIME); } if ( timestamp_elapsed(pp->update_dumbfire_time) ) { pp->update_dumbfire_time = timestamp(THREAT_UPDATE_DUMBFIRE_TIME); pp->threat_flags &= ~THREAT_DUMBFIRE; } if ( timestamp_elapsed(pp->update_lock_time) ) { pp->threat_flags &= ~(THREAT_LOCK | THREAT_ATTEMPT_LOCK); pp->update_lock_time = timestamp(THREAT_UPDATE_LOCK_TIME); rval = ship_lock_threat(shipp); if ( rval == 1 ) { pp->threat_flags |= THREAT_ATTEMPT_LOCK; } else if ( rval == 2 ) { pp->threat_flags |= THREAT_LOCK; } } if(Player->threat_flags & THREAT_LOCK ) { // a less hacked up version of the missile launch warning hud_start_text_flash(XSTR("Launch", 1507), THREAT_LOCK_FLASH, fl2i(THREAT_LOCK_FLASH/2.0f)); } if ( Player->threat_flags & (THREAT_ATTEMPT_LOCK) ) { if ( timestamp_elapsed(Threat_lock_timer) ) { Threat_lock_timer = timestamp(THREAT_LOCK_FLASH); Threat_lock_frame++; if ( Threat_lock_frame > 2 ) { Threat_lock_frame = 1; } if ( (Threat_lock_frame == 2) && (Player->threat_flags & THREAT_ATTEMPT_LOCK ) ) { snd_play( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::THREAT_FLASH))); } } } }