diff options
Diffstat (limited to 'Stars45/HUDView.cpp')
-rw-r--r-- | Stars45/HUDView.cpp | 4393 |
1 files changed, 4393 insertions, 0 deletions
diff --git a/Stars45/HUDView.cpp b/Stars45/HUDView.cpp new file mode 100644 index 0000000..392ba3e --- /dev/null +++ b/Stars45/HUDView.cpp @@ -0,0 +1,4393 @@ +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: Stars.exe + FILE: HUDView.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + View class for Heads Up Display +*/ + +#include "MemDebug.h" +#include "HUDView.h" +#include "HUDSounds.h" +#include "Ship.h" +#include "Element.h" +#include "Computer.h" +#include "Drive.h" +#include "Instruction.h" +#include "NavSystem.h" +#include "Power.h" +#include "Shield.h" +#include "Sensor.h" +#include "Contact.h" +#include "ShipDesign.h" +#include "Shot.h" +#include "Drone.h" +#include "Thruster.h" +#include "Weapon.h" +#include "WeaponGroup.h" +#include "FlightDeck.h" +#include "SteerAI.h" +#include "Sim.h" +#include "StarSystem.h" +#include "Starshatter.h" +#include "CameraDirector.h" +#include "MFD.h" +#include "RadioView.h" +#include "FormatUtil.h" +#include "Hoop.h" +#include "QuantumDrive.h" +#include "KeyMap.h" +#include "AudioConfig.h" +#include "Player.h" + +#include "NetGame.h" +#include "NetPlayer.h" + +#include "Color.h" +#include "CameraView.h" +#include "Screen.h" +#include "DataLoader.h" +#include "Scene.h" +#include "FontMgr.h" +#include "Graphic.h" +#include "Sprite.h" +#include "Keyboard.h" +#include "Mouse.h" +#include "MouseController.h" +#include "Polygon.h" +#include "Sound.h" +#include "Game.h" +#include "Window.h" + +static Bitmap hud_left_air; +static Bitmap hud_right_air; +static Bitmap hud_left_fighter; +static Bitmap hud_right_fighter; +static Bitmap hud_left_starship; +static Bitmap hud_right_starship; +static Bitmap instr_left; +static Bitmap instr_right; +static Bitmap warn_left; +static Bitmap warn_right; +static Bitmap lead; +static Bitmap cross; +static Bitmap cross1; +static Bitmap cross2; +static Bitmap cross3; +static Bitmap cross4; +static Bitmap fpm; +static Bitmap hpm; +static Bitmap pitch_ladder_pos; +static Bitmap pitch_ladder_neg; +static Bitmap chase_left; +static Bitmap chase_right; +static Bitmap chase_top; +static Bitmap chase_bottom; +static Bitmap icon_ship; +static Bitmap icon_target; + +static BYTE* hud_left_shade_air = 0; +static BYTE* hud_right_shade_air = 0; +static BYTE* hud_left_shade_fighter = 0; +static BYTE* hud_right_shade_fighter = 0; +static BYTE* hud_left_shade_starship = 0; +static BYTE* hud_right_shade_starship = 0; +static BYTE* instr_left_shade = 0; +static BYTE* instr_right_shade = 0; +static BYTE* warn_left_shade = 0; +static BYTE* warn_right_shade = 0; +static BYTE* lead_shade = 0; +static BYTE* cross_shade = 0; +static BYTE* cross1_shade = 0; +static BYTE* cross2_shade = 0; +static BYTE* cross3_shade = 0; +static BYTE* cross4_shade = 0; +static BYTE* fpm_shade = 0; +static BYTE* hpm_shade = 0; +static BYTE* pitch_ladder_pos_shade = 0; +static BYTE* pitch_ladder_neg_shade = 0; +static BYTE* chase_left_shade = 0; +static BYTE* chase_right_shade = 0; +static BYTE* chase_top_shade = 0; +static BYTE* chase_bottom_shade = 0; +static BYTE* icon_ship_shade = 0; +static BYTE* icon_target_shade = 0; + +static Sprite* hud_left_sprite = 0; +static Sprite* hud_right_sprite = 0; +static Sprite* fpm_sprite = 0; +static Sprite* hpm_sprite = 0; +static Sprite* lead_sprite = 0; +static Sprite* aim_sprite = 0; +static Sprite* tgt1_sprite = 0; +static Sprite* tgt2_sprite = 0; +static Sprite* tgt3_sprite = 0; +static Sprite* tgt4_sprite = 0; +static Sprite* chase_sprite = 0; +static Sprite* instr_left_sprite = 0; +static Sprite* instr_right_sprite = 0; +static Sprite* warn_left_sprite = 0; +static Sprite* warn_right_sprite = 0; +static Sprite* icon_ship_sprite = 0; +static Sprite* icon_target_sprite = 0; + +static Sound* missile_lock_sound; + +const int NUM_HUD_COLORS = 4; + +Color standard_hud_colors[NUM_HUD_COLORS] = { + Color(130,190,140), // green + Color(130,200,220), // cyan + Color(250,170, 80), // orange + // Color(220,220,100), // yellow + Color( 16, 16, 16) // dark gray +}; + +Color standard_txt_colors[NUM_HUD_COLORS] = { + Color(150,200,170), // green w/ green gray + Color(220,220,180), // cyan w/ light yellow + Color(220,220, 80), // orange w/ yellow + // Color(180,200,220), // yellow w/ white + Color( 32, 32, 32) // dark gray +}; + +Color night_vision_colors[NUM_HUD_COLORS] = { + Color( 20, 80, 20), // green + Color( 30, 80, 80), // cyan + Color( 80, 80, 20), // yellow + // Color(180,200,220), // not used + Color( 0, 0, 0) // no night vision +}; + +static Font* hud_font = 0; +static Font* big_font = 0; + +static bool mouse_in = false; +static int mouse_latch = 0; +static int mouse_index = -1; + +static int ship_status = System::NOMINAL; +static int tgt_status = System::NOMINAL; + +// +--------------------------------------------------------------------+ + +static enum TXT { + MAX_CONTACT = 50, + + TXT_CAUTION_TXT = 0, + TXT_LAST_CAUTION = 23, + TXT_CAM_ANGLE, + TXT_CAM_MODE, + TXT_PAUSED, + TXT_GEAR_DOWN, + + TXT_HUD_MODE, + TXT_PRIMARY_WEP, + TXT_SECONDARY_WEP, + TXT_DECOY, + TXT_SHIELD, + TXT_AUTO, + TXT_SHOOT, + TXT_NAV_INDEX, + TXT_NAV_ACTION, + TXT_NAV_FORMATION, + TXT_NAV_SPEED, + TXT_NAV_ETR, + TXT_NAV_HOLD, + + TXT_SPEED, + TXT_RANGE, + TXT_CLOSING_SPEED, + TXT_THREAT_WARN, + TXT_COMPASS, + TXT_HEADING, + TXT_PITCH, + TXT_ALTITUDE, + TXT_GFORCE, + TXT_MISSILE_T1, + TXT_MISSILE_T2, + TXT_ICON_SHIP_TYPE, + TXT_ICON_TARGET_TYPE, + TXT_TARGET_NAME, + TXT_TARGET_DESIGN, + TXT_TARGET_SHIELD, + TXT_TARGET_HULL, + TXT_TARGET_SUB, + TXT_TARGET_ETA, + + TXT_MSG_1, + TXT_MSG_2, + TXT_MSG_3, + TXT_MSG_4, + TXT_MSG_5, + TXT_MSG_6, + + TXT_NAV_PT, + TXT_SELF, + TXT_SELF_NAME, + TXT_CONTACT_NAME, + TXT_CONTACT_INFO = TXT_CONTACT_NAME + MAX_CONTACT, + TXT_LAST = TXT_CONTACT_INFO + MAX_CONTACT, + + TXT_LAST_ACTIVE = TXT_NAV_HOLD, + TXT_INSTR_PAGE = TXT_CAUTION_TXT + 6, +}; + +static HUDText hud_text[TXT_LAST]; + +void +HUDView::DrawHUDText(int index, const char* txt, Rect& rect, int align, int upcase, bool box) +{ + if (index < 0 || index >= TXT_LAST) + return; + + HUDText& ht = hud_text[index]; + Color hc = ht.color; + + char txt_buf[256]; + int n = strlen(txt); + + if (n > 250) n = 250; + + int i; + for (i = 0; i < n; i++) { + if (upcase && islower(txt[i])) + txt_buf[i] = toupper(txt[i]); + else + txt_buf[i] = txt[i]; + } + + txt_buf[i] = 0; + + if (box) { + ht.font->DrawText(txt_buf, n, rect, DT_LEFT | DT_SINGLELINE | DT_CALCRECT); + + if ((align & DT_CENTER) != 0) { + int cx = width/2; + rect.x = cx - rect.w/2; + } + } + + if (!cockpit_hud_texture && rect.Contains(Mouse::X(), Mouse::Y())) { + mouse_in = true; + + if (index <= TXT_LAST_ACTIVE) + hc = Color::White; + + if (Mouse::LButton() && !mouse_latch) { + mouse_latch = 2; + mouse_index = index; + } + } + + if (cockpit_hud_texture && + index >= TXT_HUD_MODE && + index <= TXT_TARGET_ETA && + ht.font != big_font) { + + Sprite* s = hud_sprite[0]; + + int cx = (int) s->Location().x; + int cy = (int) s->Location().y; + int w2 = s->Width() / 2; + int h2 = s->Height() / 2; + + Rect txt_rect(rect); + txt_rect.x -= (cx-w2); + txt_rect.y -= (cy-h2); + + if (index == TXT_ICON_SHIP_TYPE) + txt_rect = Rect(0, 500, 128, 12); + + else if (index == TXT_ICON_TARGET_TYPE) + txt_rect = Rect(128, 500, 128, 12); + + ht.font->SetColor(hc); + ht.font->DrawText(txt_buf, n, txt_rect, align | DT_SINGLELINE, cockpit_hud_texture); + ht.hidden = false; + } + else { + ht.font->SetColor(hc); + ht.font->DrawText(txt_buf, n, rect, align | DT_SINGLELINE); + ht.rect = rect; + ht.hidden = false; + + if (box) { + rect.Inflate(3,2); + rect.h--; + window->DrawRect(rect, hud_color); + } + } +} + +void +HUDView::HideHUDText(int index) +{ + if (index >= TXT_LAST) + return; + + hud_text[index].hidden = true; +} + +// +--------------------------------------------------------------------+ + +HUDView* HUDView::hud_view = 0; +bool HUDView::arcade = false; +bool HUDView::show_fps = false; +int HUDView::def_color_set = 1; +int HUDView::gunsight = 1; + +// +--------------------------------------------------------------------+ + +HUDView::HUDView(Window* c) +: View(c), projector(0), camview(0), +sim(0), ship(0), target(0), mode(HUD_MODE_TAC), +tactical(0), overlay(0), cockpit_hud_texture(0), +threat(0), active_region(0), transition(false), docking(false), +az_ring(0), az_pointer(0), el_ring(0), el_pointer(0), compass_scale(1), +show_warn(false), show_inst(false), inst_page(0) +{ + hud_view = this; + + sim = Sim::GetSim(); + + if (sim) + sim->ShowGrid(false); + + int i; + + width = window->Width(); + height = window->Height(); + xcenter = (width / 2.0) - 0.5; + ycenter = (height / 2.0) + 0.5; + + PrepareBitmap("HUDleftA.pcx", hud_left_air, hud_left_shade_air); + PrepareBitmap("HUDrightA.pcx", hud_right_air, hud_right_shade_air); + PrepareBitmap("HUDleft.pcx", hud_left_fighter, hud_left_shade_fighter); + PrepareBitmap("HUDright.pcx", hud_right_fighter, hud_right_shade_fighter); + PrepareBitmap("HUDleft1.pcx", hud_left_starship, hud_left_shade_starship); + PrepareBitmap("HUDright1.pcx", hud_right_starship, hud_right_shade_starship); + PrepareBitmap("INSTR_left.pcx", instr_left, instr_left_shade); + PrepareBitmap("INSTR_right.pcx", instr_right, instr_right_shade); + PrepareBitmap("CAUTION_left.pcx", warn_left, warn_left_shade); + PrepareBitmap("CAUTION_right.pcx", warn_right, warn_right_shade); + PrepareBitmap("hud_icon.pcx", icon_ship, icon_ship_shade); + PrepareBitmap("hud_icon.pcx", icon_target, icon_target_shade); + + PrepareBitmap("lead.pcx", lead, lead_shade); + PrepareBitmap("cross.pcx", cross, cross_shade); + PrepareBitmap("cross1.pcx", cross1, cross1_shade); + PrepareBitmap("cross2.pcx", cross2, cross2_shade); + PrepareBitmap("cross3.pcx", cross3, cross3_shade); + PrepareBitmap("cross4.pcx", cross4, cross4_shade); + PrepareBitmap("fpm.pcx", fpm, fpm_shade); + PrepareBitmap("hpm.pcx", hpm, hpm_shade); + PrepareBitmap("chase_l.pcx", chase_left, chase_left_shade); + PrepareBitmap("chase_r.pcx", chase_right, chase_right_shade); + PrepareBitmap("chase_t.pcx", chase_top, chase_top_shade); + PrepareBitmap("chase_b.pcx", chase_bottom, chase_bottom_shade); + PrepareBitmap("ladder1.pcx", pitch_ladder_pos, + pitch_ladder_pos_shade); + PrepareBitmap("ladder2.pcx", pitch_ladder_neg, + pitch_ladder_neg_shade); + + hud_left_air.SetType(Bitmap::BMP_TRANSLUCENT); + hud_right_air.SetType(Bitmap::BMP_TRANSLUCENT); + hud_left_fighter.SetType(Bitmap::BMP_TRANSLUCENT); + hud_right_fighter.SetType(Bitmap::BMP_TRANSLUCENT); + hud_left_starship.SetType(Bitmap::BMP_TRANSLUCENT); + hud_right_starship.SetType(Bitmap::BMP_TRANSLUCENT); + instr_left.SetType(Bitmap::BMP_TRANSLUCENT); + instr_right.SetType(Bitmap::BMP_TRANSLUCENT); + warn_left.SetType(Bitmap::BMP_TRANSLUCENT); + warn_right.SetType(Bitmap::BMP_TRANSLUCENT); + icon_ship.SetType(Bitmap::BMP_TRANSLUCENT); + icon_target.SetType(Bitmap::BMP_TRANSLUCENT); + fpm.SetType(Bitmap::BMP_TRANSLUCENT); + hpm.SetType(Bitmap::BMP_TRANSLUCENT); + lead.SetType(Bitmap::BMP_TRANSLUCENT); + cross.SetType(Bitmap::BMP_TRANSLUCENT); + cross1.SetType(Bitmap::BMP_TRANSLUCENT); + cross2.SetType(Bitmap::BMP_TRANSLUCENT); + cross3.SetType(Bitmap::BMP_TRANSLUCENT); + cross4.SetType(Bitmap::BMP_TRANSLUCENT); + chase_left.SetType(Bitmap::BMP_TRANSLUCENT); + chase_right.SetType(Bitmap::BMP_TRANSLUCENT); + chase_top.SetType(Bitmap::BMP_TRANSLUCENT); + chase_bottom.SetType(Bitmap::BMP_TRANSLUCENT); + pitch_ladder_pos.SetType(Bitmap::BMP_TRANSLUCENT); + pitch_ladder_neg.SetType(Bitmap::BMP_TRANSLUCENT); + + hud_left_sprite = new(__FILE__,__LINE__) Sprite(&hud_left_fighter); + hud_right_sprite = new(__FILE__,__LINE__) Sprite(&hud_right_fighter); + instr_left_sprite = new(__FILE__,__LINE__) Sprite(&instr_left); + instr_right_sprite = new(__FILE__,__LINE__) Sprite(&instr_right); + warn_left_sprite = new(__FILE__,__LINE__) Sprite(&warn_left); + warn_right_sprite = new(__FILE__,__LINE__) Sprite(&warn_right); + icon_ship_sprite = new(__FILE__,__LINE__) Sprite(&icon_ship); + icon_target_sprite = new(__FILE__,__LINE__) Sprite(&icon_target); + fpm_sprite = new(__FILE__,__LINE__) Sprite(&fpm); + hpm_sprite = new(__FILE__,__LINE__) Sprite(&hpm); + lead_sprite = new(__FILE__,__LINE__) Sprite(&lead); + aim_sprite = new(__FILE__,__LINE__) Sprite(&cross); + tgt1_sprite = new(__FILE__,__LINE__) Sprite(&cross1); + tgt2_sprite = new(__FILE__,__LINE__) Sprite(&cross2); + tgt3_sprite = new(__FILE__,__LINE__) Sprite(&cross3); + tgt4_sprite = new(__FILE__,__LINE__) Sprite(&cross4); + chase_sprite = new(__FILE__,__LINE__) Sprite(&chase_left); + + ZeroMemory(hud_sprite, sizeof(hud_sprite)); + + hud_sprite[ 0] = hud_left_sprite; + hud_sprite[ 1] = hud_right_sprite; + hud_sprite[ 2] = instr_left_sprite; + hud_sprite[ 3] = instr_right_sprite; + hud_sprite[ 4] = warn_left_sprite; + hud_sprite[ 5] = warn_right_sprite; + hud_sprite[ 6] = icon_ship_sprite; + hud_sprite[ 7] = icon_target_sprite; + hud_sprite[ 8] = fpm_sprite; + hud_sprite[ 9] = hpm_sprite; + hud_sprite[10] = lead_sprite; + hud_sprite[11] = aim_sprite; + hud_sprite[12] = tgt1_sprite; + hud_sprite[13] = tgt2_sprite; + hud_sprite[14] = tgt3_sprite; + hud_sprite[15] = tgt4_sprite; + hud_sprite[16] = chase_sprite; + + double pitch_ladder_UV[8] = { 0.125,0.0625, 0.875,0.0625, 0.875,0, 0.125,0 }; + double UV[8]; + + for (i = 0; i < 15; i++) { + pitch_ladder[i] = new(__FILE__,__LINE__) Sprite(&pitch_ladder_pos); + + CopyMemory(UV, pitch_ladder_UV, sizeof(UV)); + UV[1] = UV[3] = (pitch_ladder_UV[1] * (i )); + UV[5] = UV[7] = (pitch_ladder_UV[1] * (i+1)); + + pitch_ladder[i]->Reshape(192, 16); + pitch_ladder[i]->SetTexCoords(UV); + pitch_ladder[i]->SetBlendMode(2); + pitch_ladder[i]->Hide(); + } + + // zero mark at i=15 + { + pitch_ladder[i] = new(__FILE__,__LINE__) Sprite(&pitch_ladder_pos); + + UV[0] = UV[6] = 0; + UV[2] = UV[4] = 1; + UV[1] = UV[3] = (pitch_ladder_UV[1] * (i+1)); + UV[5] = UV[7] = (pitch_ladder_UV[1] * (i )); + + pitch_ladder[i]->Reshape(256, 16); + pitch_ladder[i]->SetTexCoords(UV); + pitch_ladder[i]->SetBlendMode(2); + pitch_ladder[i]->Hide(); + } + + for (i = 16; i < 31; i++) { + pitch_ladder[i] = new(__FILE__,__LINE__) Sprite(&pitch_ladder_neg); + + CopyMemory(UV, pitch_ladder_UV, sizeof(UV)); + UV[1] = UV[3] = (pitch_ladder_UV[1] * (30 - i )); + UV[5] = UV[7] = (pitch_ladder_UV[1] * (30 - i+1)); + + pitch_ladder[i]->Reshape(192, 16); + pitch_ladder[i]->SetTexCoords(UV); + pitch_ladder[i]->SetBlendMode(2); + pitch_ladder[i]->Hide(); + } + + for (i = 0; i < 3; i++) + mfd[i] = new(__FILE__,__LINE__) MFD(window, i); + + mfd[0]->SetRect(Rect( 8, height - 136, 128, 128)); + mfd[1]->SetRect(Rect(width - 136, height - 136, 128, 128)); + mfd[2]->SetRect(Rect( 8, 8, 128, 128)); + + hud_left_sprite->MoveTo( Point(width/2-128, height/2, 1)); + hud_right_sprite->MoveTo(Point(width/2+128, height/2, 1)); + hud_left_sprite->SetBlendMode(2); + hud_left_sprite->SetFilter(0); + hud_right_sprite->SetBlendMode(2); + hud_right_sprite->SetFilter(0); + + instr_left_sprite->MoveTo( Point(width/2-128, height-128, 1)); + instr_right_sprite->MoveTo(Point(width/2+128, height-128, 1)); + instr_left_sprite->SetBlendMode(2); + instr_left_sprite->SetFilter(0); + instr_right_sprite->SetBlendMode(2); + instr_right_sprite->SetFilter(0); + + warn_left_sprite->MoveTo( Point(width/2-128, height-128, 1)); + warn_right_sprite->MoveTo(Point(width/2+128, height-128, 1)); + warn_left_sprite->SetBlendMode(2); + warn_left_sprite->SetFilter(0); + warn_right_sprite->SetBlendMode(2); + warn_right_sprite->SetFilter(0); + + icon_ship_sprite->MoveTo( Point( 184, height-72, 1)); + icon_target_sprite->MoveTo(Point(width - 184, height-72, 1)); + icon_ship_sprite->SetBlendMode(2); + icon_ship_sprite->SetFilter(0); + icon_target_sprite->SetBlendMode(2); + icon_target_sprite->SetFilter(0); + + fpm_sprite->MoveTo(Point(width/2, height/2, 1)); + hpm_sprite->MoveTo(Point(width/2, height/2, 1)); + lead_sprite->MoveTo(Point(width/2, height/2, 1)); + aim_sprite->MoveTo(Point(width/2, height/2, 1)); + tgt1_sprite->MoveTo(Point(width/2, height/2, 1)); + tgt2_sprite->MoveTo(Point(width/2, height/2, 1)); + tgt3_sprite->MoveTo(Point(width/2, height/2, 1)); + tgt4_sprite->MoveTo(Point(width/2, height/2, 1)); + + fpm_sprite->SetBlendMode(2); + hpm_sprite->SetBlendMode(2); + lead_sprite->SetBlendMode(2); + aim_sprite->SetBlendMode(2); + tgt1_sprite->SetBlendMode(2); + tgt2_sprite->SetBlendMode(2); + tgt3_sprite->SetBlendMode(2); + tgt4_sprite->SetBlendMode(2); + chase_sprite->SetBlendMode(2); + + fpm_sprite->SetFilter(0); + hpm_sprite->SetFilter(0); + lead_sprite->SetFilter(0); + aim_sprite->SetFilter(0); + tgt1_sprite->SetFilter(0); + tgt2_sprite->SetFilter(0); + tgt3_sprite->SetFilter(0); + tgt4_sprite->SetFilter(0); + chase_sprite->SetFilter(0); + + lead_sprite->Hide(); + aim_sprite->Hide(); + tgt1_sprite->Hide(); + tgt2_sprite->Hide(); + tgt3_sprite->Hide(); + tgt4_sprite->Hide(); + chase_sprite->Hide(); + + aw = chase_left.Width() / 2; + ah = chase_left.Height() / 2; + + mfd[0]->SetMode(MFD::MFD_MODE_SHIP); + mfd[1]->SetMode(MFD::MFD_MODE_FOV); + mfd[2]->SetMode(MFD::MFD_MODE_GAME); + + hud_font = FontMgr::Find("HUD"); + big_font = FontMgr::Find("GUI"); + + for (i = 0; i < TXT_LAST; i++) { + hud_text[i].font = hud_font; + } + + hud_text[TXT_THREAT_WARN].font = big_font; + hud_text[TXT_SHOOT].font = big_font; + hud_text[TXT_AUTO].font = big_font; + + SetHUDColorSet(def_color_set); + MFD::SetColor(standard_hud_colors[color]); + + DataLoader* loader = DataLoader::GetLoader(); + loader->SetDataPath("HUD/"); + + az_ring = new(__FILE__,__LINE__) Solid; + az_pointer = new(__FILE__,__LINE__) Solid; + el_ring = new(__FILE__,__LINE__) Solid; + el_pointer = new(__FILE__,__LINE__) Solid; + + az_ring->Load("CompassRing.mag", compass_scale); + az_pointer->Load("CompassPointer.mag", compass_scale); + el_ring->Load("PitchRing.mag", compass_scale); + el_pointer->Load("CompassPointer.mag", compass_scale); + + loader->SetDataPath("Sounds/"); + loader->LoadSound("MissileLock.wav", missile_lock_sound, Sound::LOOP | Sound::LOCKED); + + loader->SetDataPath(0); + + for (i = 0; i < MAX_MSG; i++) + msg_time[i] = 0; +} + +HUDView::~HUDView() +{ + HideCompass(); + + if (missile_lock_sound) { + missile_lock_sound->Stop(); + missile_lock_sound->Release(); + missile_lock_sound = 0; + } + + for (int i = 0; i < 3; i++) { + delete mfd[i]; + mfd[i] = 0; + } + + for (int i = 0; i < 32; i++) { + GRAPHIC_DESTROY(hud_sprite[i]); + } + + fpm.ClearImage(); + hpm.ClearImage(); + lead.ClearImage(); + cross.ClearImage(); + cross1.ClearImage(); + cross2.ClearImage(); + cross3.ClearImage(); + cross4.ClearImage(); + hud_left_air.ClearImage(); + hud_right_air.ClearImage(); + hud_left_fighter.ClearImage(); + hud_right_fighter.ClearImage(); + hud_left_starship.ClearImage(); + hud_right_starship.ClearImage(); + instr_left.ClearImage(); + instr_right.ClearImage(); + warn_left.ClearImage(); + warn_right.ClearImage(); + icon_ship.ClearImage(); + icon_target.ClearImage(); + chase_left.ClearImage(); + chase_right.ClearImage(); + chase_top.ClearImage(); + chase_bottom.ClearImage(); + pitch_ladder_pos.ClearImage(); + pitch_ladder_neg.ClearImage(); + + delete [] fpm_shade; + delete [] hpm_shade; + delete [] lead_shade; + delete [] cross_shade; + delete [] cross1_shade; + delete [] cross2_shade; + delete [] cross3_shade; + delete [] cross4_shade; + delete [] hud_left_shade_air; + delete [] hud_right_shade_air; + delete [] hud_left_shade_fighter; + delete [] hud_right_shade_fighter; + delete [] hud_left_shade_starship; + delete [] hud_right_shade_starship; + delete [] instr_left_shade; + delete [] instr_right_shade; + delete [] warn_left_shade; + delete [] warn_right_shade; + delete [] icon_ship_shade; + delete [] icon_target_shade; + delete [] chase_left_shade; + delete [] chase_right_shade; + delete [] chase_top_shade; + delete [] chase_bottom_shade; + delete [] pitch_ladder_pos_shade; + delete [] pitch_ladder_neg_shade; + + delete az_ring; + delete az_pointer; + delete el_ring; + delete el_pointer; + + fpm_shade = 0; + hpm_shade = 0; + cross_shade = 0; + cross1_shade = 0; + cross2_shade = 0; + cross3_shade = 0; + cross4_shade = 0; + hud_left_shade_air = 0; + hud_right_shade_air = 0; + hud_left_shade_fighter = 0; + hud_right_shade_fighter = 0; + hud_left_shade_starship = 0; + hud_right_shade_starship = 0; + instr_left_shade = 0; + instr_right_shade = 0; + warn_left_shade = 0; + warn_right_shade = 0; + icon_ship_shade = 0; + icon_target_shade = 0; + chase_left_shade = 0; + chase_right_shade = 0; + chase_top_shade = 0; + chase_bottom_shade = 0; + pitch_ladder_pos_shade = 0; + pitch_ladder_neg_shade = 0; + + az_ring = 0; + az_pointer = 0; + el_ring = 0; + el_pointer = 0; + + hud_view = 0; +} + +void +HUDView::OnWindowMove() +{ + width = window->Width(); + height = window->Height(); + xcenter = (width / 2.0) - 0.5; + ycenter = (height / 2.0) + 0.5; + + mfd[0]->SetRect(Rect( 8, height - 136, 128, 128)); + mfd[1]->SetRect(Rect(width - 136, height - 136, 128, 128)); + mfd[2]->SetRect(Rect( 8, 8, 128, 128)); + + hud_left_sprite->MoveTo( Point(width/2-128, height/2, 1)); + hud_right_sprite->MoveTo(Point(width/2+128, height/2, 1)); + + instr_left_sprite->MoveTo( Point(width/2-128, height-128, 1)); + instr_right_sprite->MoveTo(Point(width/2+128, height-128, 1)); + warn_left_sprite->MoveTo( Point(width/2-128, height-128, 1)); + warn_right_sprite->MoveTo(Point(width/2+128, height-128, 1)); + icon_ship_sprite->MoveTo( Point( 184, height-72, 1)); + icon_target_sprite->MoveTo(Point(width - 184, height-72, 1)); + + for (int i = 0; i < TXT_LAST; i++) { + hud_text[i].font = hud_font; + hud_text[i].color = standard_txt_colors[color]; + } + + if (big_font) { + hud_text[TXT_THREAT_WARN].font = big_font; + hud_text[TXT_SHOOT].font = big_font; + hud_text[TXT_AUTO].font = big_font; + } + + MFD::SetColor(standard_hud_colors[color]); + + int cx = width/2; + int cy = height/2; +} + +// +--------------------------------------------------------------------+ + +void +HUDView::SetTacticalMode(int mode) +{ + if (tactical != mode) { + tactical = mode; + + if (tactical) { + hud_left_sprite->Hide(); + hud_right_sprite->Hide(); + + for (int i = 0; i < 31; i++) + pitch_ladder[i]->Hide(); + } + else if (Game::MaxTexSize() > 128) { + hud_left_sprite->Show(); + hud_right_sprite->Show(); + } + } +} + +void +HUDView::SetOverlayMode(int mode) +{ + if (overlay != mode) { + overlay = mode; + } +} + +// +--------------------------------------------------------------------+ + +bool +HUDView::Update(SimObject* obj) +{ + if (obj == ship) { + if (target) + SetTarget(0); + + ship = 0; + + for (int i = 0; i < 3; i++) + mfd[i]->SetShip(ship); + } + + if (obj == target) { + target = 0; + PrepareBitmap("hud_icon.pcx", icon_target, icon_target_shade); + ColorizeBitmap(icon_target, icon_target_shade, txt_color); + } + + return SimObserver::Update(obj); +} + +const char* +HUDView::GetObserverName() const +{ + return "HUDView"; +} + +// +--------------------------------------------------------------------+ + +void +HUDView::UseCameraView(CameraView* v) +{ + if (v && camview != v) { + camview = v; + + for (int i = 0; i < 3; i++) + mfd[i]->UseCameraView(camview); + + projector = camview->GetProjector(); + } +} + +// +--------------------------------------------------------------------+ + +Color +HUDView::MarkerColor(Contact* contact) +{ + Color c(80,80,80); + + if (contact) { + Sim* sim = Sim::GetSim(); + Ship* ship = sim->GetPlayerShip(); + + int c_iff = contact->GetIFF(ship); + + c = Ship::IFFColor(c_iff) * contact->Age(); + + if (contact->GetShot() && contact->Threat(ship)) { + if ((Game::RealTime()/500) & 1) + c = c * 2; + else + c = c * 0.5; + } + } + + return c; +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawContactMarkers() +{ + threat = 0; + + for (int i = 0; i < MAX_CONTACT; i++) { + HideHUDText(TXT_CONTACT_NAME+i); + HideHUDText(TXT_CONTACT_INFO+i); + } + + + if (!ship) + return; + + int index = 0; + ListIter<Contact> contact = ship->ContactList(); + + // draw own sensor contacts: + while (++contact) { + Contact* c = contact.value(); + + // draw track ladder: + if (c->TrackLength() > 0 && c->GetShip() != ship) { + DrawTrack(c); + } + + DrawContact(c, index++); + } + + Color c = ship->MarkerColor(); + + // draw own ship track ladder: + if (CameraDirector::GetCameraMode() == CameraDirector::MODE_ORBIT && ship->TrackLength() > 0) { + int ctl = ship->TrackLength(); + + Point t1 = ship->Location(); + Point t2 = ship->TrackPoint(0); + + if (t1 != t2) + DrawTrackSegment(t1, t2, c); + + for (int i = 0; i < ctl-1; i++) { + t1 = ship->TrackPoint(i); + t2 = ship->TrackPoint(i+1); + + if (t1 != t2) + DrawTrackSegment(t1, t2, c * ((double) (ctl-i)/ (double) ctl)); + } + } + + // draw own ship marker: + Point mark_pt = ship->Location(); + projector->Transform(mark_pt); + + // clip: + if (CameraDirector::GetCameraMode() == CameraDirector::MODE_ORBIT && mark_pt.z > 1.0) { + projector->Project(mark_pt); + + int x = (int) mark_pt.x; + int y = (int) mark_pt.y; + + if (x > 4 && x < width-4 && + y > 4 && y < height-4) { + + DrawDiamond(x,y,5,c); + + if (tactical) { + Rect self_rect(x+8, y-4, 200, 12); + DrawHUDText(TXT_SELF, ship->Name(), self_rect, DT_LEFT, HUD_MIXED_CASE); + + if (NetGame::GetInstance()) { + Player* p = Player::GetCurrentPlayer(); + if (p) { + Rect net_name_rect(x+8, y+6, 120, 12); + DrawHUDText(TXT_SELF_NAME, p->Name(), net_name_rect, DT_LEFT, HUD_MIXED_CASE); + } + } + } + } + } + + // draw life bars on targeted ship: + if (target && target->Type() == SimObject::SIM_SHIP && target->Rep()) { + Ship* tgt_ship = (Ship*) target; + if (tgt_ship == nullptr) { + Print(" Null Pointer in HUDView::DrawContactMarkers(). Please investigate."); + return; + } + Graphic* g = tgt_ship->Rep(); + Rect r = g->ScreenRect(); + + Point mark_pt; + + if (tgt_ship) + mark_pt = tgt_ship->Location(); + + projector->Transform(mark_pt); + + // clip: + if (mark_pt.z > 1.0) { + projector->Project(mark_pt); + + int x = (int) mark_pt.x; + int y = r.y; + + if (y >= 2000) + y = (int) mark_pt.y; + + if (x > 4 && x < width-4 && + y > 4 && y < height-4) { + + const int BAR_LENGTH = 40; + + // life bars: + int sx = x - BAR_LENGTH/2; + int sy = y - 8; + + double hull_strength = tgt_ship->Integrity() / tgt_ship->Design()->integrity; + + int hw = (int) (BAR_LENGTH * hull_strength); + int sw = (int) (BAR_LENGTH * (tgt_ship->ShieldStrength() / 100.0)); + + System::STATUS s = System::NOMINAL; + + if (hull_strength < 0.30) s = System::CRITICAL; + else if (hull_strength < 0.60) s = System::DEGRADED; + + Color hc = GetStatusColor(s); + Color sc = hud_color; + + window->FillRect(sx, sy, sx+hw, sy+1, hc); + window->FillRect(sx, sy+3, sx+sw, sy+4, sc); + } + } + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawContact(Contact* contact, int index) +{ + if (index >= MAX_CONTACT) return; + + Color c = MarkerColor(contact); + int c_iff = contact->GetIFF(ship); + Ship* c_ship = contact->GetShip(); + Shot* c_shot = contact->GetShot(); + Point mark_pt = contact->Location(); + double distance = 0; + + if (!c_ship && !c_shot || c_ship == ship) + return; + + if (c_ship && c_ship->GetFlightPhase() < Ship::ACTIVE) + return; + + if (c_ship) { + mark_pt = c_ship->Location(); + + if (c_ship->IsGroundUnit()) + mark_pt += Point(0,150,0); + } + else { + mark_pt = c_shot->Location(); + } + + projector->Transform(mark_pt); + + // clip: + if (mark_pt.z > 1.0) { + distance = mark_pt.length(); + + projector->Project(mark_pt); + + int x = (int) mark_pt.x; + int y = (int) mark_pt.y; + + if (x > 4 && x < width-4 && + y > 4 && y < height-4) { + + DrawDiamond(x,y,3,c); + + if (contact->Threat(ship)) { + if (c_ship) { + window->DrawEllipse(x-6, y-6, x+6, y+6, c); + } + else { + DrawDiamond(x,y,7,c); + } + } + + bool name_crowded = false; + + if (x < width-8) { + char code = *(Game::GetText("HUDView.symbol.fighter").data()); + + if (c_ship) { + if (c_ship->Class() > Ship::LCA) + code = *(Game::GetText("HUDView.symbol.starship").data()); + } + + else if (c_shot) { + code = *(Game::GetText("HUDView.symbol.torpedo").data()); + } + + Sensor* sensor = ship->GetSensor(); + double limit = 75e3; + + if (sensor) + limit = sensor->GetBeamRange(); + + double range = contact->Range(ship, limit); + + char contact_buf[256]; + Rect contact_rect(x+8, y-4, 120, 12); + + if (range == 0) { + sprintf_s(contact_buf, "%c *", code); + } + else { + bool mega = false; + + if (range > 999e3) { + range /= 1e6; + mega = true; + } + else if (range < 1e3) + range = 1; + else + range /= 1000; + + if (arcade) { + if (c_ship) + strcpy_s(contact_buf, c_ship->Name()); + else if (!mega) + sprintf_s(contact_buf, "%c %d", code, (int) range); + else + sprintf_s(contact_buf, "%c %.1f M", code, range); + } + else { + char closing = '+'; + Point delta_v; + + if (c_ship) + delta_v = ship->Velocity() - c_ship->Velocity(); + else if (c_shot) + delta_v = ship->Velocity() - c_shot->Velocity(); + + if (delta_v * ship->Velocity() < 0) // losing ground + closing = '-'; + + if (!mega) + sprintf_s(contact_buf, "%c %d%c", code, (int) range, closing); + else + sprintf_s(contact_buf, "%c %.1f M", code, range); + } + } + + if (!IsNameCrowded(x, y)) { + DrawHUDText(TXT_CONTACT_INFO+index, contact_buf, contact_rect, DT_LEFT, HUD_MIXED_CASE); + + if (c_shot || (c_ship && (c_ship->IsDropship() || c_ship->IsStatic()))) + name_crowded = distance > 50e3; + } + else { + name_crowded = true; + } + } + + bool name_drawn = false; + if (NetGame::GetInstance() && c_ship) { + NetPlayer* netp = NetGame::GetInstance()->FindPlayerByObjID(c_ship->GetObjID()); + if (netp && strcmp(netp->Name(), "Server A.I. Ship")) { + Rect contact_rect(x+8, y+6, 120, 12); + DrawHUDText(TXT_CONTACT_NAME+index, netp->Name(), contact_rect, DT_LEFT, HUD_MIXED_CASE); + name_drawn = true; + } + } + + if (!name_drawn && !name_crowded && c_ship && c_iff < 10 && !arcade) { + Rect contact_rect(x+8, y+6, 120, 12); + DrawHUDText(TXT_CONTACT_NAME+index, c_ship->Name(), contact_rect, DT_LEFT, HUD_MIXED_CASE); + } + } + } + + if (contact->Threat(ship) && !ship->IsStarship()) { + if (threat < 1 && c_ship && !c_ship->IsStarship()) + threat = 1; + + if (c_shot) + threat = 2; + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawTrackSegment(Point& t1, Point& t2, Color c) +{ + int x1, y1, x2, y2; + + projector->Transform(t1); + projector->Transform(t2); + + const double CLIP_Z = 0.1; + + if (t1.z < CLIP_Z && t2.z < CLIP_Z) + return; + + if (t1.z < CLIP_Z && t2.z >= CLIP_Z) { + double dx = t2.x - t1.x; + double dy = t2.y - t1.y; + double s = (CLIP_Z - t1.z) / (t2.z - t1.z); + + t1.x += dx * s; + t1.y += dy * s; + t1.z = CLIP_Z; + } + + else if (t2.z < CLIP_Z && t1.z >= CLIP_Z) { + double dx = t1.x - t2.x; + double dy = t1.y - t2.y; + double s = (CLIP_Z - t2.z) / (t1.z - t2.z); + + t2.x += dx * s; + t2.y += dy * s; + t2.z = CLIP_Z; + } + + if (t1.z >= CLIP_Z && t2.z >= CLIP_Z) { + projector->Project(t1, false); + projector->Project(t2, false); + + x1 = (int) t1.x; + y1 = (int) t1.y; + x2 = (int) t2.x; + y2 = (int) t2.y; + + if (window->ClipLine(x1,y1,x2,y2)) + window->DrawLine(x1,y1,x2,y2,c); + } +} + +void +HUDView::DrawTrack(Contact* contact) +{ + Ship* c_ship = contact->GetShip(); + + if (c_ship && c_ship->GetFlightPhase() < Ship::ACTIVE) + return; + + int ctl = contact->TrackLength(); + Color c = MarkerColor(contact); + + Point t1 = contact->Location(); + Point t2 = contact->TrackPoint(0); + + if (t1 != t2) + DrawTrackSegment(t1, t2, c); + + for (int i = 0; i < ctl-1; i++) { + t1 = contact->TrackPoint(i); + t2 = contact->TrackPoint(i+1); + + if (t1 != t2) + DrawTrackSegment(t1, t2, c * ((double) (ctl-i)/ (double) ctl)); + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawRect(SimObject* targ) +{ + Graphic* g = targ->Rep(); + Rect r = g->ScreenRect(); + Color c; + + if (targ->Type() == SimObject::SIM_SHIP) + c = ((Ship*) targ)->MarkerColor(); + else + c = ((Shot*) targ)->MarkerColor(); + + if (r.w > 0 && r.h > 0) { + if (r.w < 8) { + r.x -= (8-r.w)/2; + r.w = 8; + } + + if (r.h < 8) { + r.y -= (8-r.h)/2; + r.h = 8; + } + } + + else { + Point mark_pt = targ->Location(); + projector->Transform(mark_pt); + + // clip: + if (mark_pt.z < 1.0) + return; + + projector->Project(mark_pt); + + int x = (int) mark_pt.x; + int y = (int) mark_pt.y; + + if (x < 4 || x > width-4 || y < 4 || y > height-4) + return; + + r.x = x-4; + r.y = y-4; + r.w = 8; + r.h = 8; + } + + // horizontal + window->DrawLine(r.x, r.y, r.x+8, r.y, c); + window->DrawLine(r.x+r.w-8, r.y, r.x+r.w, r.y, c); + window->DrawLine(r.x, r.y+r.h, r.x+8, r.y+r.h, c); + window->DrawLine(r.x+r.w-8, r.y+r.h, r.x+r.w, r.y+r.h, c); + // vertical + window->DrawLine(r.x, r.y, r.x, r.y+8, c); + window->DrawLine(r.x, r.y+r.h-8, r.x, r.y+r.h, c); + window->DrawLine(r.x+r.w, r.y, r.x+r.w, r.y+8, c); + window->DrawLine(r.x+r.w, r.y+r.h-8, r.x+r.w, r.y+r.h, c); +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawBars() +{ + fpm_sprite->Hide(); + hpm_sprite->Hide(); + lead_sprite->Hide(); + aim_sprite->Hide(); + tgt1_sprite->Hide(); + tgt2_sprite->Hide(); + tgt3_sprite->Hide(); + tgt4_sprite->Hide(); + chase_sprite->Hide(); + + for (int i = 0; i < 31; i++) + pitch_ladder[i]->Hide(); + + const int bar_width = 256; + const int bar_height = 192; + const int box_width = 120; + + int cx = width/2; + int cy = height/2; + int l = cx - bar_width/2; + int r = cx + bar_width/2; + int t = cy - bar_height/2; + int b = cy + bar_height/2; + int align = DT_LEFT; + + if (Game::Paused()) + DrawHUDText(TXT_PAUSED, Game::GetText("HUDView.PAUSED"), Rect(cx-128, cy-60, 256, 12), DT_CENTER); + + if (ship) { + DrawContactMarkers(); + + char txt[256]; + double speed = ship->Velocity().length(); + + if (ship->Velocity() * ship->Heading() < 0) + speed = -speed; + + FormatNumber(txt, speed); + + if (tactical) { + l = box_width + 16; + r = width - box_width - 16; + } + + Rect speed_rect(l-box_width-8, cy-5, box_width, 12); + + align = (tactical) ? DT_LEFT : DT_RIGHT; + DrawHUDText(TXT_SPEED, txt, speed_rect, align); + + // upper left hud quadrant (airborne fighters) + if (ship->IsAirborne()) { + double alt_msl = ship->AltitudeMSL(); + double alt_agl = ship->AltitudeAGL(); + + if (alt_agl <= 1000) + sprintf_s(txt, "R %4d", (int) alt_agl); + else + FormatNumber(txt, alt_msl); + + speed_rect.y -= 20; + + if (arcade) { + char arcade_txt[32]; + sprintf_s(arcade_txt, "%s %s", Game::GetText("HUDView.altitude").data(), txt); + align = (tactical) ? DT_LEFT : DT_RIGHT; + DrawHUDText(TXT_ALTITUDE, arcade_txt, speed_rect, align); + } + else { + align = (tactical) ? DT_LEFT : DT_RIGHT; + DrawHUDText(TXT_ALTITUDE, txt, speed_rect, align); + } + + if (!arcade) { + sprintf_s(txt, "%.1f G", ship->GForce()); + speed_rect.y -= 20; + + align = (tactical) ? DT_LEFT : DT_RIGHT; + DrawHUDText(TXT_GFORCE, txt, speed_rect, align); + + speed_rect.y += 40; + } + } + + // upper left hud quadrant (starships) + else if (ship->IsStarship() && ship->GetFLCSMode() == Ship::FLCS_HELM && !arcade) { + sprintf_s(txt, "%s: %.1f", Game::GetText("HUDView.Pitch").data(), ship->GetHelmPitch()/DEGREES); + speed_rect.y -= 50; + + align = (tactical) ? DT_LEFT : DT_RIGHT; + DrawHUDText(TXT_PITCH, txt, speed_rect, align); + + speed_rect.y -= 10; + int heading_degrees = (int) (ship->GetHelmHeading()/DEGREES); + if (heading_degrees < 0) heading_degrees += 360; + sprintf_s(txt, "%s: %03d", Game::GetText("HUDView.Heading").data(), heading_degrees); + DrawHUDText(TXT_HEADING, txt, speed_rect, align); + + speed_rect.y += 60; + } + + // per user request, all ships should show compass heading + if (!tactical && !arcade) { + Rect heading_rect(l, t+5, bar_width, 12); + int heading_degrees = (int) (ship->CompassHeading()/DEGREES); + if (heading_degrees < 0) heading_degrees += 360; + sprintf_s(txt, "%d", heading_degrees); + DrawHUDText(TXT_COMPASS, txt, heading_rect, DT_CENTER); + } + + switch (mode) { + case HUD_MODE_TAC: strcpy_s(txt, Game::GetText("HUDView.mode.tactical").data()); break; + case HUD_MODE_NAV: strcpy_s(txt, Game::GetText("HUDView.mode.navigation").data()); break; + case HUD_MODE_ILS: strcpy_s(txt, Game::GetText("HUDView.mode.landing").data()); break; + } + + if (tactical) { + speed_rect.y += 76; + align = DT_LEFT; + } + else { + speed_rect.y = cy+76; + align = DT_RIGHT; + } + + DrawHUDText(TXT_HUD_MODE, txt, speed_rect, align); + + // landing gear: + if (ship->IsGearDown()) { + const char* gear_down = Game::GetText("HUDView.gear-down"); + + Rect gear_rect(l, b+20, box_width, 12); + DrawHUDText(TXT_GEAR_DOWN, gear_down, gear_rect, DT_CENTER, HUD_UPPER_CASE, true); + } + + // sensor/missile lock warnings and quantum drive countdown: + QuantumDrive* quantum = ship->GetQuantumDrive(); + + if (threat || (quantum && quantum->JumpTime() > 0)) { + const char* threat_warn = Game::GetText("HUDView.threat-warn"); + bool show_msg = true; + + if (quantum && quantum->JumpTime() > 0) { + static char buf[64]; + sprintf_s(buf, "%s: %d", Game::GetText("HUDView.quantum-jump").data(), (int) quantum->JumpTime()); + threat_warn = buf; + } + + else if (threat > 1) { + threat_warn = Game::GetText("HUDView.missile-warn"); + show_msg = ((Game::RealTime()/500) & 1) != 0; + } + + if (show_msg) { + Rect lock_rect(l, t-25, box_width, 12); + DrawHUDText(TXT_THREAT_WARN, threat_warn, lock_rect, DT_CENTER, HUD_MIXED_CASE, true); + } + } + + if (ship->CanTimeSkip()) { + Rect auto_rect(l, t-40, box_width, 12); + DrawHUDText(TXT_AUTO, Game::GetText("HUDView.AUTO"), auto_rect, DT_CENTER, HUD_MIXED_CASE, true); + } + + if (mode == HUD_MODE_NAV) { + Instruction* next = ship->GetNextNavPoint(); + + if (next) { + double distance = ship->RangeToNavPoint(next); + FormatNumber(txt, distance); + + Rect range_rect(r-20, cy-5, box_width, 12); + DrawHUDText(TXT_RANGE, txt, range_rect, DT_RIGHT); + range_rect.Inflate(2,2); + } + } + + // lower left hud quadrant + else if (mode == HUD_MODE_TAC) { + speed_rect.x = l-box_width-8; + speed_rect.y = cy-5 +20; + speed_rect.w = box_width; + align = (tactical) ? DT_LEFT : DT_RIGHT; + + if (!arcade && ship->GetPrimary() && !ship->IsNetObserver()) + DrawHUDText(TXT_PRIMARY_WEP, ship->GetPrimary()->Abbreviation(), speed_rect, align); + + WeaponGroup* missile = ship->GetSecondaryGroup(); + + if (missile && missile->Ammo() > 0 && !ship->IsNetObserver()) { + if (!arcade) { + speed_rect.y = cy-5 +30; + sprintf_s(txt, "%s %d", missile->Name(), missile->Ammo()); + DrawHUDText(TXT_SECONDARY_WEP, txt, speed_rect, align); + } + + // missile range indicator + if (missile->GetSelected()->Locked()) { + Rect shoot_rect(l, b+5, box_width, 12); + DrawHUDText(TXT_SHOOT, Game::GetText("HUDView.SHOOT"), shoot_rect, DT_CENTER, HUD_MIXED_CASE, true); + } + } + + if (!arcade && !ship->IsNetObserver()) { + if (ship->GetShield()) { + speed_rect.y = cy-5+40; + sprintf_s(txt, "%s - %03d +", Game::GetText("HUDView.SHIELD").data(), ship->ShieldStrength()); + DrawHUDText(TXT_SHIELD, txt, speed_rect, align); + } + else if (ship->GetDecoy()) { + speed_rect.y = cy-5+40; + sprintf_s(txt, "%s %d", Game::GetText("HUDView.DECOY").data(), ship->GetDecoy()->Ammo()); + DrawHUDText(TXT_DECOY, txt, speed_rect, align); + } + + + Rect eta_rect = speed_rect; + eta_rect.y += 10; + + align = DT_RIGHT; + + if (tactical) { + eta_rect.x = 8; + align = DT_LEFT; + } + + for (int i = 0; i < 2; i++) { + int eta = ship->GetMissileEta(i); + if (eta > 0) { + int minutes = (eta/60) % 60; + int seconds = (eta ) % 60; + + char eta_buf[16]; + sprintf_s(eta_buf, "T %d:%02d", minutes, seconds); + DrawHUDText(TXT_MISSILE_T1+i, eta_buf, eta_rect, align); + eta_rect.y += 10; + } + } + } + + NetGame* netgame = NetGame::GetInstance(); + if (netgame && !netgame->IsActive()) { + Rect shoot_rect(l, b+5, box_width, 12); + DrawHUDText(TXT_SHOOT, Game::GetText("HUDView.NET-GAME-OVER"), shoot_rect, DT_CENTER, HUD_MIXED_CASE, true); + } + + else if (ship->IsNetObserver()) { + Rect shoot_rect(l, b+5, box_width, 12); + DrawHUDText(TXT_SHOOT, Game::GetText("HUDView.OBSERVER"), shoot_rect, DT_CENTER, HUD_MIXED_CASE, true); + } + + DrawTarget(); + } + + else if (mode == HUD_MODE_ILS) { + DrawTarget(); + } + + DrawNavInfo(); + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawFPM() +{ + fpm_sprite->Hide(); + + if (ship->Velocity().length() > 50) { + double xtarg = xcenter; + double ytarg = ycenter; + + Point svel = ship->Velocity(); + svel.Normalize(); + + Point tloc = ship->Location() + svel * 1e8; + // Translate into camera relative: + projector->Transform(tloc); + + int behind = tloc.z < 0; + + if (behind) + return; + + // Project into screen coordinates: + projector->Project(tloc); + + xtarg = tloc.x; + ytarg = tloc.y; + + fpm_sprite->Show(); + fpm_sprite->MoveTo(Point(xtarg, ytarg, 1)); + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawPitchLadder() +{ + for (int i = 0; i < 31; i++) + pitch_ladder[i]->Hide(); + + if (ship->IsAirborne() && Game::MaxTexSize() > 128) { + double xtarg = xcenter; + double ytarg = ycenter; + + Point uvec = Point(0,1,0); + Point svel = ship->Velocity(); + + if (svel.length() == 0) + svel = ship->Heading(); + + if (svel.x == 0 && svel.z == 0) + return; + + svel.y = 0; + svel.Normalize(); + + Point gloc = ship->Location(); + gloc.y = 0; + + const double baseline = 1e9; + const double clip_angle = 20*DEGREES; + + Point tloc = gloc + svel * baseline; + + // Translate into camera relative: + projector->Transform(tloc); + + // Project into screen coordinates: + projector->Project(tloc); + + xtarg = tloc.x; + ytarg = tloc.y; + + // compute roll angle: + double roll_angle = 0; + double pitch_angle = 0; + + Point heading = ship->Heading(); + heading.Normalize(); + + if (heading.x != 0 || heading.z != 0) { + Point gheading = heading; + gheading.y = 0; + gheading.Normalize(); + + double dot = gheading * heading; + + if (heading.y < 0) dot = -dot; + + pitch_angle = acos(dot); + + if (pitch_angle > PI/2) + pitch_angle -= PI; + + double s0 = sin(pitch_angle); + double c0 = cos(pitch_angle); + double s1 = sin(pitch_angle + 10*DEGREES); + double c1 = cos(pitch_angle + 10*DEGREES); + + tloc = gloc + (svel * baseline * c0) + (uvec * baseline * s0); + projector->Transform(tloc); + + double x0 = tloc.x; + double y0 = tloc.y; + + tloc = gloc + (svel * baseline * c1) + (uvec * baseline * s1); + projector->Transform(tloc); + + double x1 = tloc.x; + double y1 = tloc.y; + + double dx = x1-x0; + double dy = y1-y0; + + roll_angle = atan2(-dy,dx) + PI/2; + } + + const double alias_limit = 0.1*DEGREES; + + if (fabs(roll_angle) <= alias_limit) { + if (roll_angle > 0) + roll_angle = alias_limit; + else + roll_angle = -alias_limit; + } + + else if (fabs(roll_angle-PI) <= alias_limit) { + roll_angle = PI - alias_limit; + } + + if (fabs(pitch_angle) <= clip_angle) { + pitch_ladder[15]->Show(); + pitch_ladder[15]->MoveTo(Point(xtarg, ytarg, 1)); + pitch_ladder[15]->SetAngle(roll_angle); + } + + for (int i = 1; i <= 15; i++) { + double angle = i * 5 * DEGREES; + + if (i > 12) + angle = (60 + (i-12)*10) * DEGREES; + + double s = sin(angle); + double c = cos(angle); + + if (fabs(pitch_angle - angle) <= clip_angle) { + // positive angle: + tloc = gloc + (svel * baseline * c) + (uvec * baseline * s); + projector->Transform(tloc); + + if (tloc.z > 0) { + projector->Project(tloc); + pitch_ladder[15-i]->Show(); + pitch_ladder[15-i]->MoveTo(Point(tloc.x, tloc.y, 1)); + pitch_ladder[15-i]->SetAngle(roll_angle); + } + } + + if (fabs(pitch_angle + angle) <= clip_angle) { + // negative angle: + tloc = gloc + (svel * baseline * c) + (uvec * -baseline * s); + projector->Transform(tloc); + + if (tloc.z > 0) { + projector->Project(tloc); + pitch_ladder[15+i]->Show(); + pitch_ladder[15+i]->MoveTo(Point(tloc.x, tloc.y, 1)); + pitch_ladder[15+i]->SetAngle(roll_angle); + } + } + } + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawHPM() +{ + hpm_sprite->Hide(); + + if (!ship) + return; + + double xtarg = xcenter; + double ytarg = ycenter; + + double az = ship->GetHelmHeading() - PI; + double el = ship->GetHelmPitch(); + + Point hvec = Point(sin(az), sin(el), cos(az)); + hvec.Normalize(); + + Point tloc = ship->Location() + hvec * 1e8; + // Translate into camera relative: + projector->Transform(tloc); + + int behind = tloc.z < 0; + + if (behind) + return; + + // Project into screen coordinates: + projector->Project(tloc); + + xtarg = tloc.x; + ytarg = tloc.y; + + hpm_sprite->Show(); + hpm_sprite->MoveTo(Point(xtarg, ytarg, 1)); +} + +// +--------------------------------------------------------------------+ + +void +HUDView::HideCompass() +{ + az_ring->Hide(); + az_pointer->Hide(); + el_ring->Hide(); + el_pointer->Hide(); + + Scene* scene = az_ring->GetScene(); + if (scene) { + scene->DelGraphic(az_ring); + scene->DelGraphic(az_pointer); + scene->DelGraphic(el_ring); + scene->DelGraphic(el_pointer); + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawCompass() +{ + if (!ship || !ship->Rep()) + return; + + Solid* solid = (Solid*) ship->Rep(); + Point loc = solid->Location(); + + az_ring->MoveTo(loc); + az_pointer->MoveTo(loc); + el_ring->MoveTo(loc); + el_pointer->MoveTo(loc); + + double helm_heading = ship->GetHelmHeading(); + double helm_pitch = ship->GetHelmPitch(); + double curr_heading = ship->CompassHeading(); + double curr_pitch = ship->CompassPitch(); + + bool show_az = fabs(helm_heading - curr_heading) > 5*DEGREES; + bool show_el = fabs(helm_pitch - curr_pitch) > 5*DEGREES; + + Scene* scene = camview->GetScene(); + + if (show_az || show_el) { + scene->AddGraphic(az_ring); + az_ring->Show(); + + if (show_el || fabs(helm_pitch) > 5 * DEGREES) { + scene->AddGraphic(el_ring); + Matrix ring_orient; + ring_orient.Yaw(helm_heading + PI); + el_ring->SetOrientation(ring_orient); + el_ring->Show(); + + scene->AddGraphic(el_pointer); + Matrix pointer_orient; + pointer_orient.Yaw(helm_heading + PI); + pointer_orient.Pitch(-helm_pitch); + pointer_orient.Roll(PI/2); + el_pointer->SetOrientation(pointer_orient); + el_pointer->Show(); + } + else { + scene->AddGraphic(az_pointer); + Matrix pointer_orient; + pointer_orient.Yaw(helm_heading + PI); + + az_pointer->SetOrientation(pointer_orient); + az_pointer->Show(); + } + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawLCOS(SimObject* targ, double dist) +{ + lead_sprite->Hide(); + aim_sprite->Hide(); + chase_sprite->Hide(); + + double xtarg = xcenter; + double ytarg = ycenter; + + Weapon* prim = ship->GetPrimary(); + if (!prim) return; + + Point tloc = targ->Location(); + // Translate into camera relative: + projector->Transform(tloc); + + int behind = tloc.z < 0; + + if (behind) + tloc.z = -tloc.z; + + // Project into screen coordinates: + projector->Project(tloc); + + // DRAW THE OFFSCREEN CHASE INDICATOR: + if (behind || + tloc.x <= 0 || tloc.x >= width-1 || + tloc.y <= 0 || tloc.y >= height-1) { + + // Left side: + if (tloc.x <= 0 || (behind && tloc.x < width/2)) { + if (tloc.y < ah) tloc.y = ah; + else if (tloc.y >= height-ah) tloc.y = height-1-ah; + + chase_sprite->Show(); + chase_sprite->SetAnimation(&chase_left); + chase_sprite->MoveTo(Point(aw, tloc.y, 1)); + } + + // Right side: + else if (tloc.x >= width-1 || behind) { + if (tloc.y < ah) tloc.y = ah; + else if (tloc.y >= height-ah) tloc.y = height-1-ah; + + chase_sprite->Show(); + chase_sprite->SetAnimation(&chase_right); + chase_sprite->MoveTo(Point(width-1-aw, tloc.y, 1)); + } + else { + if (tloc.x < aw) tloc.x = aw; + else if (tloc.x >= width-aw) tloc.x = width-1-aw; + + // Top edge: + if (tloc.y <= 0) { + chase_sprite->Show(); + chase_sprite->SetAnimation(&chase_top); + chase_sprite->MoveTo(Point(tloc.x, ah, 1)); + } + + // Bottom edge: + else if (tloc.y >= height-1) { + chase_sprite->Show(); + chase_sprite->SetAnimation(&chase_bottom); + chase_sprite->MoveTo(Point(tloc.x, height-1-ah, 1)); + } + } + } + + // DRAW THE LCOS: + else { + if (!ship->IsStarship()) { + Point aim_vec = ship->Heading(); + aim_vec.Normalize(); + + // shot speed is relative to ship speed: + Point shot_vel = ship->Velocity() + aim_vec * prim->Design()->speed; + double shot_speed = shot_vel.length(); + + // time for shot to reach target + double time = dist / shot_speed; + + // LCOS (Lead Computing Optical Sight) + if (gunsight == 0) { + // where the shot will be when it is the same distance + // away from the ship as the target: + Point impact = ship->Location() + (shot_vel * time); + + // where the target will be when the shot reaches it: + Point targ_vel = targ->Velocity(); + Point dest = targ->Location() + (targ_vel * time); + Point delta = impact - dest; + + // draw the gun sight here in 3d world coordinates: + Point sight = targ->Location() + delta; + + // Project into screen coordinates: + projector->Transform(sight); + projector->Project(sight); + + xtarg = sight.x; + ytarg = sight.y; + + aim_sprite->Show(); + aim_sprite->MoveTo(Point(xtarg, ytarg, 1)); + } + + // Wing Commander style lead indicator + else { + // where the target will be when the shot reaches it: + Point targ_vel = targ->Velocity() - ship->Velocity(); + Point dest = targ->Location() + (targ_vel * time); + + // Translate into camera relative: + projector->Transform(dest); + projector->Project(dest); + + xtarg = dest.x; + ytarg = dest.y; + + lead_sprite->Show(); + lead_sprite->MoveTo(Point(xtarg, ytarg, 1)); + } + } + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawTarget() +{ + const int bar_width = 256; + const int bar_height = 192; + const int box_width = 120; + + SimObject* old_target = target; + + if (mode == HUD_MODE_ILS) { + Ship* controller = ship->GetController(); + if (controller && !target) + target = controller; + } + + if (target && target->Rep()) { + Sensor* sensor = ship->GetSensor(); + Contact* contact = 0; + + if (sensor && target->Type() == SimObject::SIM_SHIP) { + contact = sensor->FindContact((Ship*) target); + } + + int cx = width/2; + int cy = height/2; + int l = cx - bar_width/2; + int r = cx + bar_width/2; + int t = cy - bar_height/2; + int b = cy + bar_height/2; + Point delta = target->Location() - ship->Location(); + double distance = delta.length(); + Point delta_v = ship->Velocity() - target->Velocity(); + double speed = delta_v.length(); + char txt[256]; + + if (mode == HUD_MODE_ILS && ship->GetInbound() && ship->GetInbound()->GetDeck()) { + delta = ship->GetInbound()->GetDeck()->EndPoint() - ship->Location(); + distance = delta.length(); + } + + if (delta * ship->Velocity() > 0) { // in front + if (delta_v * ship->Velocity() < 0) // losing ground + speed = -speed; + } + else { // behind + if (delta_v * ship->Velocity() > 0) // passing + speed = -speed; + } + + Rect range_rect(r-20, cy-5, box_width, 12); + + if (tactical) + range_rect.x = width - range_rect.w - 8; + + if (contact) { + Sensor* sensor = ship->GetSensor(); + double limit = 75e3; + + if (sensor) + limit = sensor->GetBeamRange(); + + distance = contact->Range(ship, limit); + + if (!contact->ActLock() && !contact->PasLock()) { + strcpy_s(txt, Game::GetText("HUDView.No-Range").data()); + speed = 0; + } + else { + FormatNumber(txt, distance); + } + } + + else { + FormatNumber(txt, distance); + } + + DrawHUDText(TXT_RANGE, txt, range_rect, DT_RIGHT); + + if (arcade) { + target = old_target; + return; + } + + range_rect.y += 18; + FormatNumber(txt, speed); + DrawHUDText(TXT_CLOSING_SPEED, txt, range_rect, DT_RIGHT); + + // target info: + if (!tactical) { + range_rect.y = cy-76; + } + + else { + range_rect.x = width - 2*box_width - 8; + range_rect.y = cy-76; + range_rect.w = 2*box_width; + } + + DrawHUDText(TXT_TARGET_NAME, target->Name(), range_rect, DT_RIGHT); + + if (target->Type() == SimObject::SIM_SHIP) { + Ship* tgt_ship = (Ship*) target; + + range_rect.y += 10; + DrawHUDText(TXT_TARGET_DESIGN, tgt_ship->Design()->display_name, range_rect, DT_RIGHT); + + if (mode != HUD_MODE_ILS) { + if (tgt_ship->IsStarship()) { + range_rect.y += 10; + sprintf_s(txt, "%s %03d", Game::GetText("HUDView.symbol.shield").data(), (int) tgt_ship->ShieldStrength()); + DrawHUDText(TXT_TARGET_SHIELD, txt, range_rect, DT_RIGHT); + } + + range_rect.y += 10; + sprintf_s(txt, "%s %03d", Game::GetText("HUDView.symbol.hull").data(), (int) (tgt_ship->Integrity() / tgt_ship->Design()->integrity * 100)); + DrawHUDText(TXT_TARGET_HULL, txt, range_rect, DT_RIGHT); + + System* sys = ship->GetSubTarget(); + if (sys) { + Color stat = hud_color; + static DWORD blink = Game::RealTime(); + + int blink_delta = Game::RealTime() - blink; + sprintf_s(txt, "%s %03d", sys->Abbreviation(), (int) sys->Availability()); + + switch (sys->Status()) { + case System::DEGRADED: stat = Color(255,255, 0); break; + case System::CRITICAL: + case System::DESTROYED: stat = Color(255, 0, 0); break; + case System::MAINT: + if (blink_delta < 250) + stat = Color(8,8,8); + break; + } + + if (blink_delta > 500) + blink = Game::RealTime(); + + range_rect.y += 10; + DrawHUDText(TXT_TARGET_SUB, txt, range_rect, DT_RIGHT); + } + } + } + + else if (target->Type() == SimObject::SIM_DRONE) { + Drone* tgt_drone = (Drone*) target; + + range_rect.y += 10; + DrawHUDText(TXT_TARGET_DESIGN, tgt_drone->DesignName(), range_rect, DT_RIGHT); + + range_rect.y += 10; + int eta = tgt_drone->GetEta(); + + if (eta > 0) { + int minutes = (eta/60) % 60; + int seconds = (eta ) % 60; + + char eta_buf[16]; + sprintf_s(eta_buf, "T %d:%02d", minutes, seconds); + DrawHUDText(TXT_TARGET_ETA, eta_buf, range_rect, DT_RIGHT); + } + } + } + + target = old_target; +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawNavInfo() +{ + const int bar_width = 256; + const int bar_height = 192; + const int box_width = 120; + + if (arcade) { + if (ship->IsAutoNavEngaged()) { + Rect info_rect(width/2-box_width, height/2+bar_height, box_width*2, 12); + + if (big_font) + hud_text[TXT_NAV_INDEX].font = big_font; + + DrawHUDText(TXT_NAV_INDEX, Game::GetText("HUDView.Auto-Nav"), info_rect, DT_CENTER); + } + + return; + } + + hud_text[TXT_NAV_INDEX].font = hud_font; + + Instruction* navpt = ship->GetNextNavPoint(); + + if (navpt) { + int cx = width/2; + int cy = height/2; + int l = cx - bar_width/2; + int r = cx + bar_width/2; + int t = cy - bar_height/2; + int b = cy + bar_height/2; + + int index = ship->GetNavIndex(navpt); + double distance = ship->RangeToNavPoint(navpt); + double speed = ship->Velocity().length(); + int etr = 0; + char txt[256]; + + if (speed > 10) + etr = (int) (distance/speed); + + Rect info_rect(r-20, cy+32, box_width, 12); + + if (tactical) + info_rect.x = width - info_rect.w - 8; + + if (ship->IsAutoNavEngaged()) + sprintf_s(txt, "%s %d", Game::GetText("HUDView.Auto-Nav").data(), index); + else + sprintf_s(txt, "%s %d", Game::GetText("HUDView.Nav").data(), index); + DrawHUDText(TXT_NAV_INDEX, txt, info_rect, DT_RIGHT); + + info_rect.y += 10; + if (navpt->Action()) + DrawHUDText(TXT_NAV_ACTION, Instruction::ActionName(navpt->Action()), info_rect, DT_RIGHT); + + info_rect.y += 10; + FormatNumber(txt, navpt->Speed()); + DrawHUDText(TXT_NAV_SPEED, txt, info_rect, DT_RIGHT); + + if (etr > 3600) { + info_rect.y += 10; + sprintf_s(txt, "%s XX:XX", Game::GetText("HUDView.time-enroute").data()); + DrawHUDText(TXT_NAV_ETR, txt, info_rect, DT_RIGHT); + } + else if (etr > 0) { + info_rect.y += 10; + + int minutes = (etr/60) % 60; + int seconds = (etr ) % 60; + sprintf_s(txt, "%s %2d:%02d", Game::GetText("HUDView.time-enroute").data(), minutes, seconds); + DrawHUDText(TXT_NAV_ETR, txt, info_rect, DT_RIGHT); + } + + if (navpt->HoldTime() > 0) { + info_rect.y += 10; + + int hold = (int) navpt->HoldTime(); + int minutes = (hold/60) % 60; + int seconds = (hold ) % 60; + sprintf_s(txt, "%s %2d:%02d", Game::GetText("HUDView.HOLD").data(), minutes, seconds); + DrawHUDText(TXT_NAV_HOLD, txt, info_rect, DT_RIGHT); + } + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawSight() +{ + if (target && target->Rep()) { + Point delta = target->Location() - ship->Location(); + double distance = delta.length(); + + // draw LCOS on target: + if (!tactical) + DrawLCOS(target, distance); + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawDesignators() +{ + double xtarg = xcenter; + double ytarg = ycenter; + SimObject* t1 = 0; + SimObject* t2 = 0; + SimObject* t3 = 0; + Sprite* sprite = 0; + + tgt1_sprite->Hide(); + tgt2_sprite->Hide(); + tgt3_sprite->Hide(); + tgt4_sprite->Hide(); + + // fighters just show primary target: + if (ship->IsDropship()) { + SimObject* t = ship->GetTarget(); + System* s = ship->GetSubTarget(); + + if (t) { + Point tloc = t->Location(); + if (s) { + tloc = s->MountLocation(); + } + else if (t->Type() == SimObject::SIM_SHIP) { + Ship* tgt_ship = (Ship*) t; + + if (tgt_ship->IsGroundUnit()) + tloc += Point(0,150,0); + } + + projector->Transform(tloc); + + if (tloc.z > 0) { + projector->Project(tloc); + + xtarg = tloc.x; + ytarg = tloc.y; + + if (xtarg>0 && xtarg<width-1 && ytarg>0 && ytarg<height-1) { + double range = Point(t->Location() - ship->Location()).length(); + + // use out-of-range crosshair if out of range: + if (!ship->GetPrimaryDesign() || ship->GetPrimaryDesign()->max_range < range) { + tgt4_sprite->Show(); + tgt4_sprite->MoveTo(Point(xtarg, ytarg, 1)); + } + + // else, use in-range primary crosshair: + else { + tgt1_sprite->Show(); + tgt1_sprite->MoveTo(Point(xtarg, ytarg, 1)); + } + } + } + } + } + + // starships show up to three targets: + else { + ListIter<WeaponGroup> w = ship->Weapons(); + while (!t3 && ++w) { + SimObject* t = w->GetTarget(); + System* s = w->GetSubTarget(); + + if (w->Contains(ship->GetPrimary())) { + if (t == 0) t = ship->GetTarget(); + t1 = t; + sprite = tgt1_sprite; + } + + else if (t && w->Contains(ship->GetSecondary())) { + t2 = t; + sprite = tgt2_sprite; + + if (t2 == t1) + continue; // don't overlap target designators + } + + else if (t) { + t3 = t; + sprite = tgt3_sprite; + + if (t3 == t1 || t3 == t2) + continue; // don't overlap target designators + } + + if (t) { + Point tloc = t->Location(); + + if (s) + tloc = s->MountLocation(); + + projector->Transform(tloc); + + if (tloc.z > 0) { + projector->Project(tloc); + + xtarg = tloc.x; + ytarg = tloc.y; + + if (xtarg>0 && xtarg<width-1 && ytarg>0 && ytarg<height-1) { + double range = Point(t->Location() - ship->Location()).length(); + + // flip to out-of-range crosshair + if (sprite == tgt1_sprite) { + if (!ship->GetPrimaryDesign() || ship->GetPrimaryDesign()->max_range < range) { + sprite = tgt4_sprite; + } + } + + sprite->Show(); + sprite->MoveTo(Point(xtarg, ytarg, 1)); + } + } + } + } + } +} + +// +--------------------------------------------------------------------+ + +Color +HUDView::GetStatusColor(System::STATUS status) +{ + Color sc; + + switch (status) { + default: + case System::NOMINAL: sc = Color( 32,192, 32); break; + case System::DEGRADED: sc = Color(255,255, 0); break; + case System::CRITICAL: sc = Color(255, 0, 0); break; + case System::DESTROYED: sc = Color( 0, 0, 0); break; + } + + return sc; +} + +void +HUDView::SetStatusColor(System::STATUS status) +{ + switch (status) { + default: + case System::NOMINAL: status_color = txt_color; break; + case System::DEGRADED: status_color = Color(255,255, 0); break; + case System::CRITICAL: status_color = Color(255, 0, 0); break; + case System::DESTROYED: status_color = Color( 0, 0, 0); break; + } +} + +// +--------------------------------------------------------------------+ + +static int GetReactorStatus(Ship* ship) +{ + if (!ship || ship->Reactors().size() < 1) + return -1; + + int status = System::NOMINAL; + bool maint = false; + + ListIter<PowerSource> iter = ship->Reactors(); + while (++iter) { + PowerSource* s = iter.value(); + + if (s->Status() < status) + status = s->Status(); + } + + if (maint && status == System::NOMINAL) + status = System::MAINT; + + return status; +} + +static int GetDriveStatus(Ship* ship) +{ + if (!ship || ship->Drives().size() < 1) + return -1; + + int status = System::NOMINAL; + bool maint = false; + + ListIter<Drive> iter = ship->Drives(); + while (++iter) { + Drive* s = iter.value(); + + if (s->Status() < status) + status = s->Status(); + + else if (s->Status() == System::MAINT) + maint = true; + } + + if (maint && status == System::NOMINAL) + status = System::MAINT; + + return status; +} + +static int GetQuantumStatus(Ship* ship) +{ + if (!ship || ship->GetQuantumDrive() == 0) + return -1; + + QuantumDrive* s = ship->GetQuantumDrive(); + return s->Status(); +} + +static int GetThrusterStatus(Ship* ship) +{ + if (!ship || ship->GetThruster() == 0) + return -1; + + Thruster* s = ship->GetThruster(); + return s->Status(); +} + +static int GetShieldStatus(Ship* ship) +{ + if (!ship) + return -1; + + Shield* s = ship->GetShield(); + Weapon* d = ship->GetDecoy(); + + if (!s && !d) + return -1; + + int status = System::NOMINAL; + bool maint = false; + + if (s) { + if (s->Status() < status) + status = s->Status(); + + else if (s->Status() == System::MAINT) + maint = true; + } + + if (d) { + if (d->Status() < status) + status = d->Status(); + + else if (d->Status() == System::MAINT) + maint = true; + } + + if (maint && status == System::NOMINAL) + status = System::MAINT; + + return status; +} + +static int GetWeaponStatus(Ship* ship, int index) +{ + if (!ship || ship->Weapons().size() <= index) + return -1; + + WeaponGroup* group = ship->Weapons().at(index); + + int status = System::NOMINAL; + bool maint = false; + + ListIter<Weapon> iter = group->GetWeapons(); + while (++iter) { + Weapon* s = iter.value(); + + if (s->Status() < status) + status = s->Status(); + + else if (s->Status() == System::MAINT) + maint = true; + } + + if (maint && status == System::NOMINAL) + status = System::MAINT; + + return status; +} + +static int GetSensorStatus(Ship* ship) +{ + if (!ship || ship->GetSensor() == 0) + return -1; + + Sensor* s = ship->GetSensor(); + Weapon* p = ship->GetProbeLauncher(); + + int status = s->Status(); + bool maint = s->Status() == System::MAINT; + + if (p) { + if (p->Status() < status) + status = p->Status(); + + else if (p->Status() == System::MAINT) + maint = true; + } + + if (maint && status == System::NOMINAL) + status = System::MAINT; + + return status; +} + +static int GetComputerStatus(Ship* ship) +{ + if (!ship || ship->Computers().size() < 1) + return -1; + + int status = System::NOMINAL; + bool maint = false; + + ListIter<Computer> iter = ship->Computers(); + while (++iter) { + Computer* s = iter.value(); + + if (s->Status() < status) + status = s->Status(); + + else if (s->Status() == System::MAINT) + maint = true; + } + + if (ship->GetNavSystem()) { + NavSystem* s = ship->GetNavSystem(); + + if (s->Status() < status) + status = s->Status(); + + else if (s->Status() == System::MAINT) + maint = true; + } + + if (maint && status == System::NOMINAL) + status = System::MAINT; + + return status; +} + +static int GetFlightDeckStatus(Ship* ship) +{ + if (!ship || ship->FlightDecks().size() < 1) + return -1; + + int status = System::NOMINAL; + bool maint = false; + + ListIter<FlightDeck> iter = ship->FlightDecks(); + while (++iter) { + FlightDeck* s = iter.value(); + + if (s->Status() < status) + status = s->Status(); + + else if (s->Status() == System::MAINT) + maint = true; + } + + if (maint && status == System::NOMINAL) + status = System::MAINT; + + return status; +} + +void +HUDView::DrawWarningPanel() +{ + int box_width = 75; + int box_height = 17; + int row_height = 28; + int box_left = width/2 - box_width*2; + + if (cockpit_hud_texture) { + box_left = 275; + box_height = 18; + row_height = 18; + } + + if (ship) { + if (Game::MaxTexSize() > 128) { + warn_left_sprite->Show(); + warn_right_sprite->Show(); + } + + int x = box_left; + int y = cockpit_hud_texture ? 410 : height-97; + int c = cockpit_hud_texture ? 3 : 4; + + static DWORD blink = Game::RealTime(); + + for (int index = 0; index < 12; index++) { + int stat = -1; + Text abrv = Game::GetText("HUDView.UNKNOWN"); + + switch (index) { + case 0: stat = GetReactorStatus(ship); abrv = Game::GetText("HUDView.REACTOR"); break; + case 1: stat = GetDriveStatus(ship); abrv = Game::GetText("HUDView.DRIVE"); break; + case 2: stat = GetQuantumStatus(ship); abrv = Game::GetText("HUDView.QUANTUM"); break; + case 3: stat = GetShieldStatus(ship); abrv = Game::GetText("HUDView.SHIELD"); + if (ship->GetShield() == 0 && ship->GetDecoy()) + abrv = Game::GetText("HUDView.DECOY"); + break; + + case 4: + case 5: + case 6: + case 7: stat = GetWeaponStatus(ship, index-4); + if (stat >= 0) { + WeaponGroup* g = ship->Weapons().at(index-4); + abrv = g->Name(); + } + break; + + case 8: stat = GetSensorStatus(ship); abrv = Game::GetText("HUDView.SENSOR"); break; + case 9: stat = GetComputerStatus(ship); abrv = Game::GetText("HUDView.COMPUTER"); break; + case 10: stat = GetThrusterStatus(ship); abrv = Game::GetText("HUDView.THRUSTER"); break; + case 11: stat = GetFlightDeckStatus(ship);abrv = Game::GetText("HUDView.FLTDECK"); break; + } + + Rect warn_rect(x, y, box_width, box_height); + + if (cockpit_hud_texture) + cockpit_hud_texture->DrawRect(warn_rect, Color::DarkGray); + + if (stat >= 0) { + SetStatusColor((System::STATUS) stat); + Color tc = status_color; + + if (stat != System::NOMINAL) { + if (Game::RealTime() - blink < 250) { + tc = cockpit_hud_texture ? txt_color : Color(8,8,8); + } + } + + if (cockpit_hud_texture) { + if (tc != txt_color) { + Rect r2 = warn_rect; + r2.Inset(1,1,1,1); + cockpit_hud_texture->FillRect(r2, tc); + tc = Color::Black; + } + + warn_rect.y += 4; + + hud_font->SetColor(tc); + hud_font->DrawText(abrv, -1, + warn_rect, + DT_CENTER | DT_SINGLELINE, + cockpit_hud_texture); + + warn_rect.y -= 4; + } + else { + DrawHUDText(TXT_CAUTION_TXT + index, + abrv, + warn_rect, + DT_CENTER); + + hud_text[TXT_CAUTION_TXT + index].color = tc; + } + } + + x += box_width; + + if (--c <= 0) { + c = cockpit_hud_texture ? 3 : 4; + x = box_left; + y += row_height; + } + } + + if (Game::RealTime() - blink > 500) + blink = Game::RealTime(); + + // reset for next time + SetStatusColor(System::NOMINAL); + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawInstructions() +{ + if (!ship) return; + + if (Game::MaxTexSize() > 128) { + instr_left_sprite->Show(); + instr_right_sprite->Show(); + } + + int ninst = 0; + int nobj = 0; + Element* elem = ship->GetElement(); + + if (elem) { + ninst = elem->NumInstructions(); + nobj = elem->NumObjectives(); + } + + Rect r = Rect(width/2 - 143, height - 105, 290, 17); + + if (ninst) { + int npages = ninst/6 + (ninst%6 ? 1 : 0); + + if (inst_page >= npages) + inst_page = npages-1; + else if (inst_page < 0) + inst_page = 0; + + int first = inst_page * 6; + int last = first + 6; + if (last > ninst) last = ninst; + + int n = TXT_CAUTION_TXT; + + for (int i = first; i < last; i++) { + hud_text[n].color = standard_txt_colors[color]; + DrawHUDText(n++, FormatInstruction(elem->GetInstruction(i)), r, DT_LEFT, HUD_MIXED_CASE); + r.y += 14; + } + + char page[32]; + sprintf_s(page, "%d / %d", inst_page+1, npages); + r = Rect(width/2 + 40, height-16, 110, 16); + DrawHUDText(TXT_INSTR_PAGE, page, r, DT_CENTER, HUD_MIXED_CASE); + } + + else if (nobj) { + int n = TXT_CAUTION_TXT; + + for (int i = 0; i < nobj; i++) { + char desc[256]; + sprintf_s(desc, "* %s", elem->GetObjective(i)->GetShortDescription()); + hud_text[n].color = standard_txt_colors[color]; + DrawHUDText(n++, desc, r, DT_LEFT, HUD_MIXED_CASE); + r.y += 14; + } + } + + else { + hud_text[TXT_CAUTION_TXT].color = standard_txt_colors[color]; + DrawHUDText(TXT_CAUTION_TXT, Game::GetText("HUDView.No-Instructions"), r, DT_LEFT, HUD_MIXED_CASE); + } +} + +// +--------------------------------------------------------------------+ + +const char* +HUDView::FormatInstruction(Text instr) +{ + if (!instr.contains('$')) + return (const char*) instr; + + static char result[256]; + + const char* s = (const char*) instr; + char* d = result; + + KeyMap& keymap = Starshatter::GetInstance()->GetKeyMap(); + + while (*s) { + if (*s == '$') { + s++; + char action[32]; + char* a = action; + while (*s && (isupper(*s) || isdigit(*s) || *s == '_')) *a++ = *s++; + *a = 0; + + int act = KeyMap::GetKeyAction(action); + int key = keymap.FindMapIndex(act); + const char* s2 = keymap.DescribeKey(key); + + if (!s2) s2 = action; + while (*s2) *d++ = *s2++; + } + else { + *d++ = *s++; + } + } + + *d = 0; + + return result; +} + +// +--------------------------------------------------------------------+ + +void +HUDView::CycleInstructions(int direction) +{ + if (direction > 0) + inst_page++; + else + inst_page--; +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawMessages() +{ + int message_queue_empty = true; + + // age messages: + for (int i = 0; i < MAX_MSG; i++) { + if (msg_time[i] > 0) { + msg_time[i] -= Game::GUITime(); + + if (msg_time[i] <= 0) { + msg_time[i] = 0; + msg_text[i] = ""; + } + + message_queue_empty = false; + } + } + + if (!message_queue_empty) { + // advance message pipeline: + for (int i = 0; i < MAX_MSG; i++) { + if (msg_time[0] == 0) { + for (int j = 0; j < MAX_MSG-1; j++) { + msg_time[j] = msg_time[j+1]; + msg_text[j] = msg_text[j+1]; + } + + msg_time[MAX_MSG-1] = 0; + msg_text[MAX_MSG-1] = ""; + } + } + + // draw messages: + for (int i = 0; i < MAX_MSG; i++) { + int index = TXT_MSG_1 + i; + + if (msg_time[i] > 0) { + Rect msg_rect(10, 95 + i*10, width-20, 12); + DrawHUDText(index, msg_text[i], msg_rect, DT_LEFT, HUD_MIXED_CASE); + if (msg_time[i] > 1) + hud_text[index].color = txt_color; + else + hud_text[index].color = txt_color.dim(0.5 + 0.5*msg_time[i]); + } + } + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawNav() +{ + if (!sim) + return; + + active_region = sim->GetActiveRegion(); + + if (ship) { + int nav_index = 1; + Instruction* next = ship->GetNextNavPoint(); + + if (mode == HUD_MODE_NAV) { + if (next && next->Action() == Instruction::LAUNCH) + DrawNavPoint(*next, 0, true); + + ListIter<Instruction> navpt = ship->GetFlightPlan(); + while (++navpt) { + DrawNavPoint(*navpt.value(), nav_index++, (navpt.value() == next)); + } + } + else if (next) { + DrawNavPoint(*next, 0, true); + } + } +} + +void +HUDView::DrawILS() +{ + if (ship) { + bool hoops_drawn = false; + bool same_sector = false; + + InboundSlot* inbound = ship->GetInbound(); + if (inbound) { + FlightDeck* fd = inbound->GetDeck(); + + if (fd && fd->IsRecoveryDeck() && fd->GetCarrier()) { + if (fd->GetCarrier()->GetRegion() == ship->GetRegion()) + same_sector = true; + + if (same_sector && mode == HUD_MODE_ILS && !transition && !docking) { + Point dst = fd->MountLocation(); + projector->Transform(dst); + + if (dst.z > 1.0) { + projector->Project(dst); + + int x = (int) dst.x; + int y = (int) dst.y; + + if (x > 4 && x < width-4 && + y > 4 && y < height-4) { + + window->DrawLine(x-6, y-6, x+6, y+6, hud_color); + window->DrawLine(x+6, y-6, x-6, y+6, hud_color); + } + } + } + + // draw the hoops for this flight deck: + Scene* scene = camview->GetScene(); + for (int h = 0; h < fd->NumHoops(); h++) { + Hoop* hoop = fd->GetHoops() + h; + if (hoop && scene) { + if (same_sector && mode == HUD_MODE_ILS && !transition && !docking) { + scene->AddGraphic(hoop); + hoop->Show(); + + hoops_drawn = true; + } + else { + hoop->Hide(); + scene->DelGraphic(hoop); + } + } + } + } + } + + if (!hoops_drawn) { + ListIter<Ship> iter = ship->GetRegion()->Carriers(); + while (++iter) { + Ship* carrier = iter.value(); + + bool ours = (carrier->GetIFF() == ship->GetIFF()) || + (carrier->GetIFF() == 0); + + for (int i = 0; i < carrier->NumFlightDecks(); i++) { + FlightDeck* fd = carrier->GetFlightDeck(i); + + if (fd && fd->IsRecoveryDeck()) { + if (mode == HUD_MODE_ILS && ours && !transition && !docking) { + Point dst = fd->MountLocation(); + projector->Transform(dst); + + if (dst.z > 1.0) { + projector->Project(dst); + + int x = (int) dst.x; + int y = (int) dst.y; + + if (x > 4 && x < width-4 && + y > 4 && y < height-4) { + + window->DrawLine(x-6, y-6, x+6, y+6, hud_color); + window->DrawLine(x+6, y-6, x-6, y+6, hud_color); + } + } + } + + // draw the hoops for this flight deck: + Scene* scene = camview->GetScene(); + for (int h = 0; h < fd->NumHoops(); h++) { + Hoop* hoop = fd->GetHoops() + h; + if (hoop && scene) { + if (mode == HUD_MODE_ILS && ours && !transition && !docking) { + hoop->Show(); + if (!hoop->GetScene()) + scene->AddGraphic(hoop); + } + else { + hoop->Hide(); + if (hoop->GetScene()) + scene->DelGraphic(hoop); + } + } + } + } + } + } + } + } +} + +void +HUDView::DrawObjective() +{ + if (ship && ship->GetDirector() && ship->GetDirector()->Type() >= SteerAI::SEEKER) { + SteerAI* steer = (SteerAI*) ship->GetDirector(); + Point obj = steer->GetObjective(); + projector->Transform(obj); + + if (obj.z > 1.0) { + projector->Project(obj); + + int x = (int) obj.x; + int y = (int) obj.y; + + if (x > 4 && x < width-4 && + y > 4 && y < height-4) { + + Color c = Color::Cyan; + window->DrawRect(x-6, y-6, x+6, y+6, c); + window->DrawLine(x-6, y-6, x+6, y+6, c); + window->DrawLine(x+6, y-6, x-6, y+6, c); + } + } + + if (steer->GetOther()) { + obj = steer->GetOther()->Location(); + projector->Transform(obj); + + if (obj.z > 1.0) { + projector->Project(obj); + + int x = (int) obj.x; + int y = (int) obj.y; + + if (x > 4 && x < width-4 && + y > 4 && y < height-4) { + + Color c = Color::Orange; + window->DrawRect(x-6, y-6, x+6, y+6, c); + window->DrawLine(x-6, y-6, x+6, y+6, c); + window->DrawLine(x+6, y-6, x-6, y+6, c); + } + } + } + } + /***/ +} + +void +HUDView::DrawNavPoint(Instruction& navpt, int index, int next) +{ + if (index >= 15 || !navpt.Region()) return; + + // transform from starsystem to world coordinates: + Point npt = navpt.Region()->Location() + navpt.Location(); + + if (active_region) + npt -= active_region->Location(); + + npt = npt.OtherHand(); + + // transform from world to camera: + projector->Transform(npt); + + // clip: + if (npt.z > 1.0) { + + // project: + projector->Project(npt); + + int x = (int) npt.x; + int y = (int) npt.y; + + // clip: + if (x > 4 && x < width-4 && + y > 4 && y < height-4) { + + Color c = Color::White; + if (navpt.Status() > Instruction::ACTIVE && navpt.HoldTime() <= 0) + c = Color::DarkGray; + + // draw: + if (next) + window->DrawEllipse(x-6, y-6, x+5, y+5, c); + + window->DrawLine(x-6, y-6, x+6, y+6, c); + window->DrawLine(x+6, y-6, x-6, y+6, c); + + if (index > 0) { + char npt_buf[32]; + Rect npt_rect(x+10, y-4, 200, 12); + + if (navpt.Status() == Instruction::COMPLETE && navpt.HoldTime() > 0) { + char hold_time[32]; + FormatTime(hold_time, navpt.HoldTime()); + sprintf_s(npt_buf, "%d %s", index, hold_time); + } + else { + sprintf_s(npt_buf, "%d", index); + } + + DrawHUDText(TXT_NAV_PT + index, npt_buf, npt_rect, DT_LEFT); + } + } + } + + if (next && mode == HUD_MODE_NAV && navpt.Region() == ship->GetRegion()) { + + // Translate into camera relative: + Point tloc = navpt.Location().OtherHand(); + projector->Transform(tloc); + + int behind = tloc.z < 0; + + if (behind) + tloc.z = -tloc.z; + + // Project into screen coordinates: + projector->Project(tloc); + + // DRAW THE OFFSCREEN CHASE INDICATOR: + if (behind || + tloc.x <= 0 || tloc.x >= width-1 || + tloc.y <= 0 || tloc.y >= height-1) { + + // Left side: + if (tloc.x <= 0 || (behind && tloc.x < width/2)) { + if (tloc.y < ah) tloc.y = ah; + else if (tloc.y >= height-ah) tloc.y = height-1-ah; + + chase_sprite->Show(); + chase_sprite->SetAnimation(&chase_left); + chase_sprite->MoveTo(Point(aw, tloc.y, 1)); + } + + // Right side: + else if (tloc.x >= width-1 || behind) { + if (tloc.y < ah) tloc.y = ah; + else if (tloc.y >= height-ah) tloc.y = height-1-ah; + + chase_sprite->Show(); + chase_sprite->SetAnimation(&chase_right); + chase_sprite->MoveTo(Point(width-1-aw, tloc.y, 1)); + } + else { + if (tloc.x < aw) tloc.x = aw; + else if (tloc.x >= width-aw) tloc.x = width-1-aw; + + // Top edge: + if (tloc.y <= 0) { + chase_sprite->Show(); + chase_sprite->SetAnimation(&chase_top); + chase_sprite->MoveTo(Point(tloc.x, ah, 1)); + } + + // Bottom edge: + else if (tloc.y >= height-1) { + chase_sprite->Show(); + chase_sprite->SetAnimation(&chase_bottom); + chase_sprite->MoveTo(Point(tloc.x, height-1-ah, 1)); + } + } + } + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::SetShip(Ship* s) +{ + if (ship != s) { + double new_scale = 1; + + ship_status = -1; + ship = s; + + if (ship) { + if (ship->Life() == 0 || ship->IsDying() || ship->IsDead()) { + ship = 0; + } + else { + Observe(ship); + new_scale = 1.1 * ship->Radius() / 64; + + if (ship->Design()->hud_icon.Width()) { + TransferBitmap(ship->Design()->hud_icon, icon_ship, icon_ship_shade); + ColorizeBitmap(icon_ship, icon_ship_shade, txt_color); + } + } + } + + if (az_ring) { + az_ring->Rescale(1/compass_scale); + az_ring->Rescale(new_scale); + } + + if (az_pointer) { + az_pointer->Rescale(1/compass_scale); + az_pointer->Rescale(new_scale); + } + + if (el_ring) { + el_ring->Rescale(1/compass_scale); + el_ring->Rescale(new_scale); + } + + if (el_pointer) { + el_pointer->Rescale(1/compass_scale); + el_pointer->Rescale(new_scale); + } + + compass_scale = new_scale; + inst_page = 0; + + if (ship && ship->GetElement() && ship->GetElement()->NumInstructions() > 0) + if (!show_inst) + CycleHUDInst(); + } + + else if (ship && ship->Design()->hud_icon.Width()) { + bool update = false; + System::STATUS s = System::NOMINAL; + int integrity = (int) (ship->Integrity() / ship->Design()->integrity * 100); + + if (integrity < 30) s = System::CRITICAL; + else if (integrity < 60) s = System::DEGRADED; + + if (s != ship_status) { + ship_status = s; + update = true; + } + + if (update) { + SetStatusColor((System::STATUS) ship_status); + ColorizeBitmap(icon_ship, icon_ship_shade, status_color); + } + } + + if (ship && ship->Cockpit()) { + Solid* cockpit = (Solid*) ship->Cockpit(); + + bool change = false; + + if (cockpit->Hidden()) { + if (cockpit_hud_texture) + change = true; + + cockpit_hud_texture = 0; + } + else { + if (!cockpit_hud_texture) + change = true; + + Model* cockpit_model = cockpit->GetModel(); + Material* hud_material = 0; + + if (cockpit_model) { + hud_material = (Material*) cockpit_model->FindMaterial("HUD"); + if (hud_material) { + cockpit_hud_texture = hud_material->tex_emissive; + } + } + } + + if (change) { + SetHUDColorSet(color); + } + } +} + +void +HUDView::SetTarget(SimObject* t) +{ + bool update = false; + + if (target != t) { + tgt_status = -1; + target = t; + if (target) Observe(target); + update = true; + } + + if (target && target->Type() == SimObject::SIM_SHIP) { + System::STATUS s = System::NOMINAL; + Ship* tship = (Ship*) target; + int integrity = (int) (tship->Integrity() / tship->Design()->integrity * 100); + + if (integrity < 30) s = System::CRITICAL; + else if (integrity < 60) s = System::DEGRADED; + + if (s != tgt_status) { + tgt_status = s; + update = true; + } + } + + if (update) { + if (target && target->Type() == SimObject::SIM_SHIP) { + Ship* tship = (Ship*) target; + TransferBitmap(tship->Design()->hud_icon, icon_target, icon_target_shade); + } + else { + PrepareBitmap("hud_icon.pcx", icon_target, icon_target_shade); + } + + SetStatusColor((System::STATUS) tgt_status); + ColorizeBitmap(icon_target, icon_target_shade, status_color); + } +} + +// +--------------------------------------------------------------------+ + +MFD* +HUDView::GetMFD(int n) const +{ + if (n >= 0 && n < 3) + return mfd[n]; + + return 0; +} + +// +--------------------------------------------------------------------+ + +void +HUDView::Refresh() +{ + sim = Sim::GetSim(); + mouse_in = false; + + if (!sim || !camview || !projector) { + return; + } + + if (Mouse::LButton() == 0) { + mouse_latch = 0; + mouse_index = -1; + } + + int mouse_index_old = mouse_index; + + SetShip(sim->GetPlayerShip()); + + if (mode == HUD_MODE_OFF) { + if (cockpit_hud_texture) { + cockpit_hud_texture->FillRect(0,0,512,256,Color::Black); + } + + sim->ShowGrid(false); + return; + } + + if (cockpit_hud_texture && cockpit_hud_texture->Width() == 512) { + Bitmap* hud_bmp = 0; + + if (hud_sprite[0]) { + hud_bmp = hud_sprite[0]->Frame(); + int bmp_w = hud_bmp->Width(); + int bmp_h = hud_bmp->Height(); + + cockpit_hud_texture->BitBlt( 0, 0, *hud_bmp, 0, 0, bmp_w, bmp_h); + } + + if (hud_sprite[1]) { + hud_bmp = hud_sprite[1]->Frame(); + int bmp_w = hud_bmp->Width(); + int bmp_h = hud_bmp->Height(); + + cockpit_hud_texture->BitBlt(256, 0, *hud_bmp, 0, 0, bmp_w, bmp_h); + } + + if (hud_sprite[6]) { + if (hud_sprite[6]->Hidden()) { + cockpit_hud_texture->FillRect(0,384,128,512,Color::Black); + } + else { + hud_bmp = hud_sprite[6]->Frame(); + int bmp_w = hud_bmp->Width(); + int bmp_h = hud_bmp->Height(); + + cockpit_hud_texture->BitBlt(0,384, *hud_bmp, 0, 0, bmp_w, bmp_h); + } + } + + if (hud_sprite[7]) { + if (hud_sprite[7]->Hidden()) { + cockpit_hud_texture->FillRect(128,384,256,512,Color::Black); + } + else { + hud_bmp = hud_sprite[7]->Frame(); + int bmp_w = hud_bmp->Width(); + int bmp_h = hud_bmp->Height(); + + cockpit_hud_texture->BitBlt(128,384, *hud_bmp, 0, 0, bmp_w, bmp_h); + } + } + + for (int i = 8; i < 32; i++) { + if (hud_sprite[i] && !hud_sprite[i]->Hidden()) { + Sprite* s = hud_sprite[i]; + + int cx = (int) s->Location().x; + int cy = (int) s->Location().y; + int w2 = s->Width() / 2; + int h2 = s->Height() / 2; + + window->DrawBitmap(cx-w2, cy-h2, cx+w2, cy+h2, s->Frame(), Video::BLEND_ALPHA); + } + } + } + else { + for (int i = 0; i < 32; i++) { + if (hud_sprite[i] && !hud_sprite[i]->Hidden()) { + Sprite* s = hud_sprite[i]; + + int cx = (int) s->Location().x; + int cy = (int) s->Location().y; + int w2 = s->Width() / 2; + int h2 = s->Height() / 2; + + window->DrawBitmap(cx-w2, cy-h2, cx+w2, cy+h2, s->Frame(), Video::BLEND_ALPHA); + } + } + + Video* video = Video::GetInstance(); + + for (int i = 0; i < 31; i++) { + Sprite* s = pitch_ladder[i]; + + if (s && !s->Hidden()) { + s->Render2D(video); + } + } + } + + //DrawStarSystem(); + DrawMessages(); + + if (ship) { + // no hud in transition: + if (ship->InTransition()) { + transition = true; + HideAll(); + return; + } + + else if (transition) { + transition = false; + RestoreHUD(); + } + + CameraDirector* cam_dir = CameraDirector::GetInstance(); + + // everything is off during docking, except the final message: + if (cam_dir && cam_dir->GetMode() == CameraDirector::MODE_DOCKING) { + docking = true; + HideAll(); + + if (ship->GetFlightPhase() == Ship::DOCKING) { + Rect dock_rect(width/2-100, height/6, 200, 20); + + if (ship->IsAirborne()) + DrawHUDText(TXT_AUTO, Game::GetText("HUDView.SUCCESSFUL-LANDING"), dock_rect, DT_CENTER); + else + DrawHUDText(TXT_AUTO, Game::GetText("HUDView.DOCKING-COMPLETE"), dock_rect, DT_CENTER); + } + return; + } + else if (docking) { + docking = false; + RestoreHUD(); + } + + // go to NAV mode during autopilot: + if (ship->GetNavSystem() && ship->GetNavSystem()->AutoNavEngaged() && !arcade) + mode = HUD_MODE_NAV; + + SetTarget(ship->GetTarget()); + + // internal view of HUD reticule + if (CameraDirector::GetCameraMode() <= CameraDirector::MODE_CHASE) + SetTacticalMode(0); + + // external view + else + SetTacticalMode(!cockpit_hud_texture); + + sim->ShowGrid(tactical && + !ship->IsAirborne() && + CameraDirector::GetCameraMode() != CameraDirector::MODE_VIRTUAL); + + // draw HUD bars: + DrawBars(); + + if (missile_lock_sound) { + if (threat > 1) { + long max_vol = AudioConfig::WrnVolume(); + long volume = -1500; + + if (volume > max_vol) + volume = max_vol; + + missile_lock_sound->SetVolume(volume); + missile_lock_sound->Play(); + } + else { + missile_lock_sound->Stop(); + } + } + + DrawNav(); + DrawILS(); + + // FOR DEBUG PURPOSES ONLY: + // DrawObjective(); + + if (!overlay) { + Rect fov_rect(0, 10, width, 10); + int fov_degrees = 180 - 2 * (int)(projector->XAngle()*180/PI); + + if (fov_degrees > 90) + DrawHUDText(TXT_CAM_ANGLE, Game::GetText("HUDView.Wide-Angle"), fov_rect, DT_CENTER); + + fov_rect.y = 20; + DrawHUDText(TXT_CAM_MODE, CameraDirector::GetModeName(), fov_rect, DT_CENTER); + } + + DrawMFDs(); + + instr_left_sprite->Hide(); + instr_right_sprite->Hide(); + warn_left_sprite->Hide(); + warn_right_sprite->Hide(); + + if (cockpit_hud_texture) + cockpit_hud_texture->FillRect(256,384,512,512,Color::Black); + + if (show_inst) { + DrawInstructions(); + } + + else if (!arcade) { + if (ship->MasterCaution() && !show_warn) + ShowHUDWarn(); + + if (show_warn) + DrawWarningPanel(); + } + + if (width > 640 || (!show_inst && !show_warn)) { + Rect icon_rect(120, height-24, 128, 16); + + if (ship) + DrawHUDText(TXT_ICON_SHIP_TYPE, ship->DesignName(), icon_rect, DT_CENTER); + + icon_rect.x = width - 248; + + if (target && target->Type() == SimObject::SIM_SHIP) { + Ship* tship = (Ship*) target; + DrawHUDText(TXT_ICON_TARGET_TYPE, tship->DesignName(), icon_rect, DT_CENTER); + } + } + } + else { + if (target) { + SetTarget(0); + } + } + + // latch mouse down to prevent dragging into a control: + if (Mouse::LButton() == 1) + mouse_latch = 1; + + if (mouse_index > -1 && mouse_index_old != mouse_index) + MouseFrame(); +} + +void +HUDView::DrawMFDs() +{ + for (int i = 0; i < 3; i++) { + mfd[i]->Show(); + mfd[i]->SetShip(ship); + mfd[i]->SetCockpitHUDTexture(cockpit_hud_texture); + mfd[i]->Draw(); + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::DrawStarSystem() +{ + if (sim && sim->GetStarSystem()) { + StarSystem* sys = sim->GetStarSystem(); + + ListIter<OrbitalBody> iter = sys->Bodies(); + while (++iter) { + OrbitalBody* body = iter.value(); + DrawOrbitalBody(body); + } + } +} + +void +HUDView::DrawOrbitalBody(OrbitalBody* body) +{ + if (body) { + Point p = body->Rep()->Location(); + + projector->Transform(p); + + if (p.z > 100) { + float r = (float) body->Radius(); + r = projector->ProjectRadius(p, r); + projector->Project(p, false); + + window->DrawEllipse((int) (p.x-r), + (int) (p.y-r), + (int) (p.x+r), + (int) (p.y+r), + Color::Cyan); + } + + ListIter<OrbitalBody> iter = body->Satellites(); + while (++iter) { + OrbitalBody* body = iter.value(); + DrawOrbitalBody(body); + } + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::ExecFrame() +{ + // update the position of HUD elements that are + // part of the 3D scene (like fpm and lcos sprites) + HideCompass(); + + if (ship && !transition && !docking && mode != HUD_MODE_OFF) { + Player* p = Player::GetCurrentPlayer(); + gunsight = p->Gunsight(); + + if (ship->IsStarship()) { + if (tactical) { + hud_left_sprite->Hide(); + hud_right_sprite->Hide(); + } + + else if (hud_left_sprite->Frame() != &hud_left_starship) { + hud_left_sprite->SetAnimation(&hud_left_starship); + hud_right_sprite->SetAnimation(&hud_right_starship); + + hud_left_sprite->MoveTo( Point(width/2-128, height/2, 1)); + hud_right_sprite->MoveTo(Point(width/2+128, height/2, 1)); + } + } + + else if (!ship->IsStarship()) { + if (ship->IsAirborne() && hud_left_sprite->Frame() != &hud_left_air) { + hud_left_sprite->SetAnimation(&hud_left_air); + hud_right_sprite->SetAnimation(&hud_right_air); + } + + else if (!ship->IsAirborne() && hud_left_sprite->Frame() != &hud_left_fighter) { + hud_left_sprite->SetAnimation(&hud_left_fighter); + hud_right_sprite->SetAnimation(&hud_right_fighter); + } + } + + if (!tactical) { + if (Game::MaxTexSize() > 128) { + hud_left_sprite->Show(); + hud_right_sprite->Show(); + } + + if (!arcade) + DrawFPM(); + + if (ship->IsStarship() && ship->GetFLCSMode() == Ship::FLCS_HELM) + DrawHPM(); + else if (!arcade) + DrawPitchLadder(); + } + + else { + if (ship->IsStarship() && ship->GetFLCSMode() == Ship::FLCS_HELM) + DrawCompass(); + } + + if (mode == HUD_MODE_TAC) { + DrawSight(); + DrawDesignators(); + } + + if (width > 640 || (!show_inst && !show_warn)) { + icon_ship_sprite->Show(); + icon_target_sprite->Show(); + } + else { + icon_ship_sprite->Hide(); + icon_target_sprite->Hide(); + } + } + + // if the hud is off or prohibited, + // hide all of the sprites: + + else { + hud_left_sprite->Hide(); + hud_right_sprite->Hide(); + instr_left_sprite->Hide(); + instr_right_sprite->Hide(); + warn_left_sprite->Hide(); + warn_right_sprite->Hide(); + icon_ship_sprite->Hide(); + icon_target_sprite->Hide(); + fpm_sprite->Hide(); + hpm_sprite->Hide(); + lead_sprite->Hide(); + aim_sprite->Hide(); + tgt1_sprite->Hide(); + tgt2_sprite->Hide(); + tgt3_sprite->Hide(); + tgt4_sprite->Hide(); + chase_sprite->Hide(); + + for (int i = 0; i < 3; i++) + mfd[i]->Hide(); + + for (int i = 0; i < 31; i++) + pitch_ladder[i]->Hide(); + + DrawILS(); + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::CycleMFDMode(int mfd_index) +{ + if (mfd_index < 0 || mfd_index > 2) return; + + int m = mfd[mfd_index]->GetMode(); + m++; + + if (mfd_index == 2) { + if (m > MFD::MFD_MODE_SHIP) + m = MFD::MFD_MODE_OFF; + } + else { + if (m > MFD::MFD_MODE_3D) + m = MFD::MFD_MODE_OFF; + + if (m == MFD::MFD_MODE_GAME) + m++; + + if (mfd_index != 0 && m == MFD::MFD_MODE_SHIP) + m++; + } + + mfd[mfd_index]->SetMode(m); + HUDSounds::PlaySound(HUDSounds::SND_MFD_MODE); +} + +// +--------------------------------------------------------------------+ + +void +HUDView::ShowHUDWarn() +{ + if (!show_warn) { + show_warn = true; + + if (ship && ship->HullStrength() <= 40) { + // TOO OBNOXIOUS!! + HUDSounds::PlaySound(HUDSounds::SND_RED_ALERT); + } + } +} + +void +HUDView::ShowHUDInst() +{ + show_inst = true; +} + +// +--------------------------------------------------------------------+ + +void +HUDView::HideHUDWarn() +{ + show_warn = false; + + if (ship) { + ship->ClearCaution(); + HUDSounds::StopSound(HUDSounds::SND_RED_ALERT); + } +} + +void +HUDView::HideHUDInst() +{ + show_inst = false; +} + +// +--------------------------------------------------------------------+ + +void +HUDView::CycleHUDWarn() +{ + HUDSounds::PlaySound(HUDSounds::SND_HUD_WIDGET); + show_warn = !show_warn; + + if (ship && !show_warn) { + ship->ClearCaution(); + HUDSounds::StopSound(HUDSounds::SND_RED_ALERT); + } +} + +void +HUDView::CycleHUDInst() +{ + show_inst = !show_inst; + HUDSounds::PlaySound(HUDSounds::SND_HUD_WIDGET); +} + +// +--------------------------------------------------------------------+ + +void +HUDView::SetHUDMode(int m) +{ + if (mode != m) { + mode = m; + + if (mode > HUD_MODE_ILS || mode < HUD_MODE_OFF) + mode = HUD_MODE_OFF; + + if (ship && !ship->IsDropship() && mode == HUD_MODE_ILS) + mode = HUD_MODE_OFF; + + RestoreHUD(); + } +} + +void +HUDView::CycleHUDMode() +{ + mode++; + + if (arcade && mode != HUD_MODE_TAC) + mode = HUD_MODE_OFF; + + else if (mode > HUD_MODE_ILS || mode < HUD_MODE_OFF) + mode = HUD_MODE_OFF; + + else if (!ship->IsDropship() && mode == HUD_MODE_ILS) + mode = HUD_MODE_OFF; + + RestoreHUD(); + HUDSounds::PlaySound(HUDSounds::SND_HUD_MODE); +} + +void +HUDView::RestoreHUD() +{ + if (mode == HUD_MODE_OFF) { + HideAll(); + } + else { + for (int i = 0; i < 3; i++) + mfd[i]->Show(); + + if (width > 640 || (!show_inst && !show_warn)) { + icon_ship_sprite->Show(); + icon_target_sprite->Show(); + } + else { + icon_ship_sprite->Hide(); + icon_target_sprite->Hide(); + } + + if (!tactical && Game::MaxTexSize() > 128) { + hud_left_sprite->Show(); + hud_right_sprite->Show(); + } + + fpm_sprite->Show(); + + if (ship && ship->IsStarship()) + hpm_sprite->Show(); + + if (gunsight == 0) + aim_sprite->Show(); + else + lead_sprite->Show(); + } +} + +void +HUDView::HideAll() +{ + for (int i = 0; i < 3; i++) + mfd[i]->Hide(); + + hud_left_sprite->Hide(); + hud_right_sprite->Hide(); + instr_left_sprite->Hide(); + instr_right_sprite->Hide(); + warn_left_sprite->Hide(); + warn_right_sprite->Hide(); + icon_ship_sprite->Hide(); + icon_target_sprite->Hide(); + fpm_sprite->Hide(); + hpm_sprite->Hide(); + lead_sprite->Hide(); + aim_sprite->Hide(); + tgt1_sprite->Hide(); + tgt2_sprite->Hide(); + tgt3_sprite->Hide(); + tgt4_sprite->Hide(); + chase_sprite->Hide(); + + sim->ShowGrid(false); + + for (int i = 0; i < 31; i++) + pitch_ladder[i]->Hide(); + + if (missile_lock_sound) + missile_lock_sound->Stop(); + + HideCompass(); + DrawILS(); + Mouse::Show(false); +} + +// +--------------------------------------------------------------------+ + +Color +HUDView::Ambient() const +{ + if (!sim || !ship || mode == HUD_MODE_OFF) + return Color::Black; + + SimRegion* rgn = sim->GetActiveRegion(); + + if (!rgn || !rgn->IsAirSpace()) + return Color::Black; + + Color c = sim->GetStarSystem()->Ambient(); + + if (c.Red() > 32 || c.Green() > 32 || c.Blue() > 32) + return Color::Black; + + // if we get this far, the night-vision aid is on + return night_vision_colors[color]; +} + +Color +HUDView::CycleHUDColor() +{ + HUDSounds::PlaySound(HUDSounds::SND_HUD_MODE); + SetHUDColorSet(color+1); + return hud_color; +} + +void +HUDView::SetHUDColorSet(int c) +{ + color = c; + if (color > NUM_HUD_COLORS-1) color = 0; + hud_color = standard_hud_colors[color]; + txt_color = standard_txt_colors[color]; + + ColorizeBitmap(fpm, fpm_shade, hud_color, true); + ColorizeBitmap(hpm, hpm_shade, hud_color, true); + ColorizeBitmap(lead, lead_shade, txt_color * 1.25, true); + ColorizeBitmap(cross, cross_shade, hud_color, true); + ColorizeBitmap(cross1, cross1_shade, hud_color, true); + ColorizeBitmap(cross2, cross2_shade, hud_color, true); + ColorizeBitmap(cross3, cross3_shade, hud_color, true); + ColorizeBitmap(cross4, cross4_shade, hud_color, true); + + if (Game::MaxTexSize() > 128) { + ColorizeBitmap(hud_left_air, hud_left_shade_air, hud_color); + ColorizeBitmap(hud_right_air, hud_right_shade_air, hud_color); + ColorizeBitmap(hud_left_fighter, hud_left_shade_fighter, hud_color); + ColorizeBitmap(hud_right_fighter, hud_right_shade_fighter, hud_color); + ColorizeBitmap(hud_left_starship, hud_left_shade_starship, hud_color); + ColorizeBitmap(hud_right_starship, hud_right_shade_starship, hud_color); + + ColorizeBitmap(instr_left, instr_left_shade, hud_color); + ColorizeBitmap(instr_right, instr_right_shade, hud_color); + ColorizeBitmap(warn_left, warn_left_shade, hud_color); + ColorizeBitmap(warn_right, warn_right_shade, hud_color); + + ColorizeBitmap(pitch_ladder_pos, pitch_ladder_pos_shade, hud_color); + ColorizeBitmap(pitch_ladder_neg, pitch_ladder_neg_shade, hud_color); + } + + ColorizeBitmap(icon_ship, icon_ship_shade, txt_color); + ColorizeBitmap(icon_target, icon_target_shade, txt_color); + + ColorizeBitmap(chase_left, chase_left_shade, hud_color, true); + ColorizeBitmap(chase_right, chase_right_shade, hud_color, true); + ColorizeBitmap(chase_top, chase_top_shade, hud_color, true); + ColorizeBitmap(chase_bottom, chase_bottom_shade, hud_color, true); + + MFD::SetColor(hud_color); + Hoop::SetColor(hud_color); + + for (int i = 0; i < 3; i++) + mfd[i]->SetText3DColor(txt_color); + + Font* font = FontMgr::Find("HUD"); + if (font) + font->SetColor(txt_color); + + for (int i = 0; i < TXT_LAST; i++) + hud_text[i].color = txt_color; +} + +// +--------------------------------------------------------------------+ + +void +HUDView::Message(const char* fmt, ...) +{ + if (fmt) { + char msg[512]; + vsprintf(msg, fmt, (char *)(&fmt+1)); + + char* newline = strchr(msg, '\n'); + if (newline) + *newline = 0; + + Print("%s\n", msg); + + if (hud_view) { + int index = -1; + + for (int i = 0; i < MAX_MSG; i++) { + if (hud_view->msg_time[i] <= 0) { + index = i; + break; + } + } + + // no space; advance pipeline: + if (index < 0) { + for (int i = 0; i < MAX_MSG-1; i++) { + hud_view->msg_text[i] = hud_view->msg_text[i+1]; + hud_view->msg_time[i] = hud_view->msg_time[i+1]; + } + + index = MAX_MSG-1; + } + + hud_view->msg_text[index] = msg; + hud_view->msg_time[index] = 10; + } + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::ClearMessages() +{ + if (hud_view) { + for (int i = 0; i < MAX_MSG-1; i++) { + hud_view->msg_text[i] = Text(); + hud_view->msg_time[i] = 0; + } + } +} + +// +--------------------------------------------------------------------+ + +void +HUDView::PrepareBitmap(const char* name, Bitmap& img, BYTE*& shades) +{ + delete [] shades; + shades = 0; + + DataLoader* loader = DataLoader::GetLoader(); + + loader->SetDataPath("HUD/"); + int loaded = loader->LoadBitmap(name, img, Bitmap::BMP_TRANSPARENT); + loader->SetDataPath(0); + + if (!loaded) + return; + + int w = img.Width(); + int h = img.Height(); + + shades = new(__FILE__,__LINE__) BYTE[w*h]; + + for (int y = 0; y < h; y++) + for (int x = 0; x < w; x++) + shades[y*w+x] = (BYTE) (img.GetColor(x,y).Red() * 0.66); +} + +void +HUDView::TransferBitmap(const Bitmap& src, Bitmap& img, BYTE*& shades) +{ + delete [] shades; + shades = 0; + + if (src.Width() != img.Width() || src.Height() != img.Height()) + return; + + img.CopyBitmap(src); + img.SetType(Bitmap::BMP_TRANSLUCENT); + + int w = img.Width(); + int h = img.Height(); + + shades = new(__FILE__,__LINE__) BYTE[w*h]; + + for (int y = 0; y < h; y++) + for (int x = 0; x < w; x++) + shades[y*w+x] = (BYTE) (img.GetColor(x,y).Red() * 0.5); +} + +void +HUDView::ColorizeBitmap(Bitmap& img, BYTE* shades, Color color, bool force_alpha) +{ + if (!shades) return; + + int max_tex_size = Game::MaxTexSize(); + + if (max_tex_size < 128) + Game::SetMaxTexSize(128); + + if (hud_view && hud_view->cockpit_hud_texture && !force_alpha) { + img.FillColor(Color::Black); + Color* dst = img.HiPixels(); + BYTE* src = shades; + + for (int y = 0; y < img.Height(); y++) { + for (int x = 0; x < img.Width(); x++) { + if (*src) + *dst = color.dim(*src/200.0); + else + *dst = Color::Black; + + dst++; + src++; + } + } + img.MakeTexture(); + } + else { + img.FillColor(color); + img.CopyAlphaImage(img.Width(), img.Height(), shades); + img.MakeTexture(); + } + + if (max_tex_size < 128) + Game::SetMaxTexSize(max_tex_size); +} + +// +--------------------------------------------------------------------+ + +void +HUDView::MouseFrame() +{ + MouseController* ctrl = MouseController::GetInstance(); + if (ctrl && ctrl->Active()) + return; + + if (mouse_index >= TXT_CAUTION_TXT && mouse_index <= TXT_LAST_CAUTION) { + if (show_inst) { + if (mouse_index == TXT_INSTR_PAGE) { + if (Mouse::X() > width/2 + 125) + CycleInstructions(1); + else if (Mouse::X() < width/2 + 65) + CycleInstructions(-1); + } + else + show_inst = false; + } + else { + CycleHUDWarn(); + } + return; + } + + Starshatter* stars = Starshatter::GetInstance(); + if (mouse_index == TXT_PAUSED) + stars->Pause(!Game::Paused()); + + if (mouse_index == TXT_GEAR_DOWN) + ship->ToggleGear(); + + if (mouse_index == TXT_HUD_MODE) { + CycleHUDMode(); + + if (mode == HUD_MODE_OFF) + CycleHUDMode(); + } + + if (mouse_index == TXT_PRIMARY_WEP) { + HUDSounds::PlaySound(HUDSounds::SND_WEP_MODE); + ship->CyclePrimary(); + } + + if (mouse_index == TXT_SECONDARY_WEP) { + HUDSounds::PlaySound(HUDSounds::SND_WEP_MODE); + ship->CycleSecondary(); + } + + if (mouse_index == TXT_DECOY) + ship->FireDecoy(); + + if (mouse_index == TXT_SHIELD) { + Shield* shield = ship->GetShield(); + + if (shield) { + double level = shield->GetPowerLevel(); + + const Rect& r = hud_text[TXT_SHIELD].rect; + if (Mouse::X() < r.x + r.w * 0.75) + shield->SetPowerLevel(level - 10); + else + shield->SetPowerLevel(level + 10); + + HUDSounds::PlaySound(HUDSounds::SND_SHIELD_LEVEL); + } + } + + if (mouse_index == TXT_AUTO) + ship->TimeSkip(); + + if (mouse_index >= TXT_NAV_INDEX && mouse_index <= TXT_NAV_ETR) { + ship->SetAutoNav(!ship->IsAutoNavEngaged()); + SetHUDMode(HUD_MODE_TAC); + } +} + +// +--------------------------------------------------------------------+ + +bool +HUDView::IsMouseLatched() +{ + bool result = mouse_in; + + if (!result) { + HUDView* hud = HUDView::GetInstance(); + + for (int i = 0; i < 3; i++) + result = result || hud->mfd[i]->IsMouseLatched(); + } + + return result; +} + +// +--------------------------------------------------------------------+ + +bool +HUDView::IsNameCrowded(int x, int y) +{ + for (int i = 0; i < MAX_CONTACT; i++) { + HUDText& test = hud_text[TXT_CONTACT_NAME + i]; + + if (!test.hidden) { + Rect r = test.rect; + + int dx = r.x - x; + int dy = r.y - y; + int d = dx*dx + dy*dy; + + if (d <= 400) + return true; + } + + test = hud_text[TXT_CONTACT_INFO + i]; + + if (!test.hidden) { + Rect r = test.rect; + + int dx = r.x - x; + int dy = r.y - y; + int d = dx*dx + dy*dy; + + if (d <= 400) + return true; + } + } + + return false; +} + +void +HUDView::DrawDiamond(int x, int y, int r, Color c) +{ + POINT diamond[4]; + + diamond[0].x = x; + diamond[0].y = y-r; + + diamond[1].x = x+r; + diamond[1].y = y; + + diamond[2].x = x; + diamond[2].y = y+r; + + diamond[3].x = x-r; + diamond[3].y = y; + + window->DrawPoly(4, diamond, c); +} |