From 3c487c5cd69c53d6fea948643c0a76df03516605 Mon Sep 17 00:00:00 2001 From: Aki Date: Fri, 1 Apr 2022 21:23:39 +0200 Subject: Moved Stars45 to StarsEx --- StarsEx/Mfd.cpp | 1405 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1405 insertions(+) create mode 100644 StarsEx/Mfd.cpp (limited to 'StarsEx/Mfd.cpp') diff --git a/StarsEx/Mfd.cpp b/StarsEx/Mfd.cpp new file mode 100644 index 0000000..f79f2af --- /dev/null +++ b/StarsEx/Mfd.cpp @@ -0,0 +1,1405 @@ +/* Starshatter: The Open Source Project + Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors + Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors + Copyright (c) 1997-2006, Destroyer Studios LLC. + + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Class for all Multi Function Displays +*/ + +#include "Mfd.h" +#include "HUDView.h" +#include "Ship.h" +#include "NavSystem.h" +#include "Power.h" +#include "Shield.h" +#include "Sensor.h" +#include "Contact.h" +#include "ShipDesign.h" +#include "Shot.h" +#include "Weapon.h" +#include "WeaponGroup.h" +#include "Sim.h" +#include "StarSystem.h" +#include "Starshatter.h" +#include "Drive.h" +#include "QuantumDrive.h" +#include "Power.h" +#include "Instruction.h" + +#include "NetGame.h" + +#include "CameraView.h" +#include "Color.h" +#include "Font.h" +#include "FontMgr.h" +#include "Window.h" +#include "Video.h" +#include "Screen.h" +#include "DataLoader.h" +#include "Scene.h" +#include "Graphic.h" +#include "Sprite.h" +#include "Keyboard.h" +#include "Mouse.h" +#include "Game.h" +#include "Clock.h" +#include "ContentBundle.h" + +static Bitmap sensor_fov; +static Bitmap sensor_fwd; +static Bitmap sensor_hsd; +static Bitmap sensor_3d; + +static BYTE* sensor_fov_shade; +static BYTE* sensor_fwd_shade; +static BYTE* sensor_hsd_shade; +static BYTE* sensor_3d_shade; + +static Color hud_color = Color::Black; +static Color txt_color = Color::Black; + +// +--------------------------------------------------------------------+ + +MFD::MFD(Window* c, int n) +: window(c), rect(0,0,0,0), index(n), mode(MFD_MODE_OFF), sprite(0), +ship(0), hidden(true), camview(0), lines(0), mouse_latch(0), mouse_in(false), +cockpit_hud_texture(0) +{ + sprite = new Sprite(&sensor_fov); + + sprite->SetBlendMode(2); + sprite->SetFilter(0); + sprite->Hide(); + + Font* font = FontMgr::Find("HUD"); + + for (int i = 0; i < TXT_LAST; i++) { + mfd_text[i].font = font; + mfd_text[i].color = Color::White; + mfd_text[i].hidden = true; + } +} + +MFD::~MFD() +{ + GRAPHIC_DESTROY(sprite); +} + +// +--------------------------------------------------------------------+ + +void +MFD::Initialize() +{ + static int initialized = 0; + if (initialized) return; + + HUDView::PrepareBitmap("sensor_fov.pcx", sensor_fov, sensor_fov_shade); + HUDView::PrepareBitmap("sensor_fwd.pcx", sensor_fwd, sensor_fwd_shade); + HUDView::PrepareBitmap("sensor_hsd.pcx", sensor_hsd, sensor_hsd_shade); + HUDView::PrepareBitmap("sensor_3d.pcx", sensor_3d, sensor_3d_shade); + + sensor_fov.SetType(Bitmap::BMP_TRANSLUCENT); + sensor_fwd.SetType(Bitmap::BMP_TRANSLUCENT); + sensor_hsd.SetType(Bitmap::BMP_TRANSLUCENT); + sensor_3d.SetType(Bitmap::BMP_TRANSLUCENT); + + initialized = 1; +} + +void +MFD::Close() +{ + sensor_fov.ClearImage(); + sensor_fwd.ClearImage(); + sensor_hsd.ClearImage(); + sensor_3d.ClearImage(); + + delete [] sensor_fov_shade; + delete [] sensor_fwd_shade; + delete [] sensor_hsd_shade; + delete [] sensor_3d_shade; +} + +// +--------------------------------------------------------------------+ + +void +MFD::UseCameraView(CameraView* v) +{ + if (v && !camview) { + camview = v; + } +} + +void +MFD::SetColor(Color c) +{ + HUDView* hud = HUDView::GetInstance(); + + if (hud) { + hud_color = hud->GetHUDColor(); + txt_color = hud->GetTextColor(); + } + else { + hud_color = c; + txt_color = c; + } + + HUDView::ColorizeBitmap(sensor_fov, sensor_fov_shade, c); + HUDView::ColorizeBitmap(sensor_fwd, sensor_fwd_shade, c); + HUDView::ColorizeBitmap(sensor_hsd, sensor_hsd_shade, c); + HUDView::ColorizeBitmap(sensor_3d, sensor_3d_shade, c); +} + +void +MFD::SetText3DColor(Color c) +{ + for (int i = 0; i < TXT_LAST; i++) + mfd_text[i].color = c; +} + +// +--------------------------------------------------------------------+ + +void +MFD::Show() +{ + switch (mode) { + case MFD_MODE_FOV: + case MFD_MODE_HSD: + case MFD_MODE_3D: + if (sprite) + sprite->Show(); + break; + } + + hidden = false; +} + +void +MFD::Hide() +{ + if (sprite) + sprite->Hide(); + + for (int i = 0; i < TXT_LAST; i++) + HideMFDText(i); + + hidden = true; +} + +// +--------------------------------------------------------------------+ + +void +MFD::SetRect(const Rect& r) +{ + rect = r; + + if (sprite) + sprite->MoveTo(Point(rect.x + sprite->Width()/2, + rect.y + sprite->Height()/2, + 1)); +} + +// +--------------------------------------------------------------------+ + +void +MFD::SetMode(int m) +{ + if (m < MFD_MODE_OFF || m > MFD_MODE_3D) + mode = MFD_MODE_OFF; + else + mode = m; + + sprite->Hide(); + + for (int i = 0; i < TXT_LAST; i++) + HideMFDText(i); + + switch (mode) { + case MFD_MODE_GAME: + case MFD_MODE_SHIP: + lines = 0; + break; + + case MFD_MODE_FOV: + sprite->SetAnimation(&sensor_fov); + sprite->Show(); + sprite->Reshape(sensor_fov.Width()-8, 16); + break; + case MFD_MODE_HSD: + sprite->SetAnimation(&sensor_hsd); + sprite->Show(); + sprite->Reshape(sensor_hsd.Width()-8, 16); + break; + case MFD_MODE_3D: + sprite->SetAnimation(&sensor_3d); + sprite->Show(); + sprite->Reshape(sensor_3d.Width()-8, 16); + break; + } +} + +// +--------------------------------------------------------------------+ + +void +MFD::Draw() +{ + mouse_in = false; + + if (Mouse::LButton() == 0) + mouse_latch = 0; + + if (rect.Contains(Mouse::X(), Mouse::Y())) + mouse_in = true; + + // click to turn on MFD when off: + if (mode < MFD_MODE_FOV && Mouse::LButton() && !mouse_latch) { + mouse_latch = 1; + if (mouse_in) { + HUDView* hud = HUDView::GetInstance(); + if (hud) + hud->CycleMFDMode(index); + } + } + + for (int i = 0; i < TXT_LAST; i++) + HideMFDText(i); + + if (hidden || mode < MFD_MODE_FOV) { + if (cockpit_hud_texture) { + int x1 = index*128; + int y1 = 256; + int x2 = x1 + 128; + int y2 = y1 + 128; + + cockpit_hud_texture->FillRect(x1, y1, x2, y2, Color::Black); + } + + if (hidden) + return; + } + + if (sprite && !sprite->Hidden()) { + if (cockpit_hud_texture) { + int x1 = index*128; + int y1 = 256; + int w = sprite->Width(); + int h = sprite->Height(); + + cockpit_hud_texture->BitBlt(x1, y1, *sprite->Frame(), 0,0,w,h); + } + else { + int cx = rect.x + rect.w/2; + int cy = rect.y + rect.h/2; + int w2 = sprite->Width()/2; + int h2 = sprite->Height()/2; + + window->DrawBitmap(cx-w2, cy-h2, cx+w2, cy+h2, sprite->Frame(), Video::BLEND_ALPHA); + } + } + + switch (mode) { + default: + case MFD_MODE_OFF: break; + case MFD_MODE_GAME: DrawGameMFD(); break; + case MFD_MODE_SHIP: DrawStatusMFD(); break; + + // sensor sub-modes: + case MFD_MODE_FOV: DrawSensorMFD(); break; + case MFD_MODE_HSD: DrawHSD(); break; + case MFD_MODE_3D: Draw3D(); break; + } +} + +// +--------------------------------------------------------------------+ + +void +MFD::DrawSensorLabels(const char* mfd_mode) +{ + Sensor* sensor = ship->GetSensor(); + char mode_buf[8] = " "; + int scan_r = rect.w; + int scan_x = rect.x; + int scan_y = rect.y; + + switch (sensor->GetMode()) { + case Sensor::PAS: strcpy_s(mode_buf, ContentBundle::GetInstance()->GetText("MFD.mode.passive").data()); break; + case Sensor::STD: strcpy_s(mode_buf, ContentBundle::GetInstance()->GetText("MFD.mode.standard").data()); break; + case Sensor::ACM: strcpy_s(mode_buf, ContentBundle::GetInstance()->GetText("MFD.mode.auto-combat").data()); break; + case Sensor::GM: strcpy_s(mode_buf, ContentBundle::GetInstance()->GetText("MFD.mode.ground").data()); break; + case Sensor::PST: strcpy_s(mode_buf, ContentBundle::GetInstance()->GetText("MFD.mode.passive").data()); break; + case Sensor::CST: strcpy_s(mode_buf, ContentBundle::GetInstance()->GetText("MFD.mode.combined").data()); break; + default: break; + } + + Rect mode_rect(scan_x+2, scan_y+2, 40, 12); + DrawMFDText(0, mode_buf, mode_rect, DT_LEFT); + + char range_txt[12]; + double beam_range = sensor->GetBeamRange() + 1; + if (beam_range >= 1e6) + sprintf_s(range_txt, "-%dM+", (int) (beam_range / 1e6)); + else + sprintf_s(range_txt, "-%3d+", (int) (beam_range / 1e3)); + + Rect range_rect(scan_x+2, scan_y+scan_r-12, 40, 12); + DrawMFDText(1, range_txt, range_rect, DT_LEFT); + + Rect disp_rect(scan_x+scan_r-41, scan_y+2, 40, 12); + DrawMFDText(2, mfd_mode, disp_rect, DT_RIGHT); + + Rect probe_rect(scan_x+scan_r-41, scan_y+scan_r-12, 40, 12); + + if (ship->GetProbeLauncher()) { + char probes[32]; + sprintf_s(probes, "%s %02d", ContentBundle::GetInstance()->GetText("MFD.probe").data(), ship->GetProbeLauncher()->Ammo()); + DrawMFDText(3, probes, probe_rect, DT_RIGHT); + } + else { + HideMFDText(3); + } + + if (Mouse::LButton() && !mouse_latch) { + mouse_latch = 1; + + if (mode_rect.Contains(Mouse::X(), Mouse::Y())) { + if (sensor->GetMode() < Sensor::PST) { + int sensor_mode = sensor->GetMode() + 1; + if (sensor_mode > Sensor::GM) + sensor_mode = Sensor::PAS; + + sensor->SetMode((Sensor::Mode) sensor_mode); + } + } + + else if (range_rect.Contains(Mouse::X(), Mouse::Y())) { + if (Mouse::X() > range_rect.x+range_rect.w/2) + sensor->IncreaseRange(); + else + sensor->DecreaseRange(); + } + + else if (disp_rect.Contains(Mouse::X(), Mouse::Y())) { + HUDView* hud = HUDView::GetInstance(); + if (hud) + hud->CycleMFDMode(index); + } + + else if (probe_rect.Contains(Mouse::X(), Mouse::Y())) { + ship->LaunchProbe(); + } + } +} + +// +--------------------------------------------------------------------+ + +// AZIMUTH-ELEVATION ANGULAR SCANNER + +void +MFD::DrawSensorMFD() +{ + int scan_r = rect.w; + int scan_x = cockpit_hud_texture ? (index*128) : rect.x; + int scan_y = cockpit_hud_texture ? 256 : rect.y; + int r = scan_r / 2; + + double xctr = (scan_r / 2.0) - 0.5; + double yctr = (scan_r / 2.0) + 0.5; + + Sensor* sensor = ship->GetSensor(); + if (!sensor) { + DrawMFDText(0, ContentBundle::GetInstance()->GetText("MFD.inactive").data(), rect, DT_CENTER); + return; + } + + int w = sprite->Width(); + int h = sprite->Height(); + + if (w < sprite->Frame()->Width()) + w += 2; + + if (h < sprite->Frame()->Height()) + h += 16; + + sprite->Reshape(w, h); + sprite->Show(); + + if (h < sprite->Frame()->Height()) + return; + + double sweep_scale = r / (PI/2); + + if (sensor->GetBeamLimit() > 90*DEGREES) + sweep_scale = (double) r / (90*DEGREES); + + int az = (int) (sensor->GetBeamLimit() * sweep_scale); + int el = az; + int xc = (int) (scan_x + xctr); + int yc = (int) (scan_y + yctr); + + if (mode == MFD_MODE_FOV) { + if (sensor->GetMode() < Sensor::GM) { + if (cockpit_hud_texture) + cockpit_hud_texture->DrawEllipse(xc-az, yc-el, xc+az, yc+el, hud_color); + else + window->DrawEllipse(xc-az, yc-el, xc+az, yc+el, hud_color); + } + } + else { + char az_txt[8]; + sprintf_s(az_txt, "%d", (int) (sensor->GetBeamLimit() / DEGREES)); + + Rect az_rect(scan_x+2, scan_y+scan_r-12, 32, 12); + DrawMFDText(1, az_txt, az_rect, DT_LEFT); + + az_rect.x = scan_x + (scan_r/2) - (az_rect.w/2); + DrawMFDText(2, "0", az_rect, DT_CENTER); + + az_rect.x = scan_x + scan_r - az_rect.w - 2; + DrawMFDText(3, az_txt, az_rect, DT_RIGHT); + } + + // draw next nav point: + Instruction* navpt = ship->GetNextNavPoint(); + if (navpt && navpt->Region() == ship->GetRegion()) { + const Camera* cam = &ship->Cam(); + + // translate: + Point pt = navpt->Location().OtherHand() - ship->Location(); + + // rotate: + double tx = pt * cam->vrt(); + double ty = pt * cam->vup(); + double tz = pt * cam->vpn(); + + if (tz > 1.0) { + // convert to spherical coords: + double rng = pt.length(); + double az = asin(fabs(tx) / rng); + double el = asin(fabs(ty) / rng); + + if (tx < 0) az = -az; + if (ty < 0) el = -el; + + if (fabs(az) < 90*DEGREES) { + az *= sweep_scale; + el *= sweep_scale; + + int x = (int) (r + az); + int y = (int) (r - el); + + // clip again: + if (x > 0 && x < scan_r && + y > 0 && y < scan_r) { + + // draw: + int xc = scan_x + x; + int yc = scan_y + y; + + if (cockpit_hud_texture) { + cockpit_hud_texture->DrawLine(xc-2, yc-2, xc+2, yc+2, Color::White); + cockpit_hud_texture->DrawLine(xc-2, yc+2, xc+2, yc-2, Color::White); + } + else { + window->DrawLine(xc-2, yc-2, xc+2, yc+2, Color::White); + window->DrawLine(xc-2, yc+2, xc+2, yc-2, Color::White); + } + } + } + } + } + + int num_contacts = ship->NumContacts(); + ListIter iter = ship->ContactList(); + + while (++iter) { + Contact* contact = iter.value(); + Ship* c_ship = contact->GetShip(); + double az, el, rng; + bool aft = false; + + if (c_ship == ship) continue; + + contact->GetBearing(ship, az, el, rng); + + // clip (is in-front): + if (fabs(az) < 90*DEGREES) { + az *= sweep_scale; + el *= sweep_scale; + } + + // rear anulus: + else { + double len = sqrt(az*az + el*el); + + if (len > 1e-6) { + az = r * az/len; + el = r * el/len; + } + else { + az = -r; + el = 0; + } + + aft = true; + } + + int x = (int) (r + az); + int y = (int) (r - el); + + // clip again: + if (x < 0 || x > scan_r) continue; + if (y < 0 || y > scan_r) continue; + + // draw: + Color mark = HUDView::MarkerColor(contact); + + if (aft) + mark = mark * 0.75; + + int xc = scan_x + x; + int yc = scan_y + y; + int size = 1; + + if (c_ship && c_ship == ship->GetTarget()) + size = 2; + + if (cockpit_hud_texture) + cockpit_hud_texture->FillRect(xc-size, yc-size, xc+size, yc+size, mark); + else + window->FillRect(xc-size, yc-size, xc+size, yc+size, mark); + + if (contact->Threat(ship)) { + if (c_ship) { + if (cockpit_hud_texture) + cockpit_hud_texture->DrawEllipse(xc-4, yc-4, xc+3, yc+3, mark); + else + window->DrawEllipse(xc-4, yc-4, xc+3, yc+3, mark); + } + else { + if (cockpit_hud_texture) { + cockpit_hud_texture->DrawLine(xc, yc-5, xc+5, yc, mark); + cockpit_hud_texture->DrawLine(xc+5, yc, xc, yc+5, mark); + cockpit_hud_texture->DrawLine(xc, yc+5, xc-5, yc, mark); + cockpit_hud_texture->DrawLine(xc-5, yc, xc, yc-5, mark); + } + else { + window->DrawLine(xc, yc-5, xc+5, yc, mark); + window->DrawLine(xc+5, yc, xc, yc+5, mark); + window->DrawLine(xc, yc+5, xc-5, yc, mark); + window->DrawLine(xc-5, yc, xc, yc-5, mark); + } + } + } + } + + DrawSensorLabels(ContentBundle::GetInstance()->GetText("MFD.mode.field-of-view").data()); +} + +// +--------------------------------------------------------------------+ + +// HORIZONTAL SITUATION DISPLAY + +void +MFD::DrawHSD() +{ + int scan_r = rect.w; + int scan_x = cockpit_hud_texture ? (index*128) : rect.x; + int scan_y = cockpit_hud_texture ? 256 : rect.y; + int r = scan_r / 2 - 4; + + double xctr = (scan_r / 2.0) - 0.5; + double yctr = (scan_r / 2.0) + 0.5; + + int xc = (int) xctr + scan_x; + int yc = (int) yctr + scan_y; + + Sensor* sensor = ship->GetSensor(); + if (!sensor) { + DrawMFDText(0, ContentBundle::GetInstance()->GetText("MFD.inactive").data(), rect, DT_CENTER); + return; + } + + int w = sprite->Width(); + int h = sprite->Height(); + + if (w < sprite->Frame()->Width()) + w += 2; + + if (h < sprite->Frame()->Height()) + h += 16; + + sprite->Reshape(w, h); + sprite->Show(); + + if (h < sprite->Frame()->Height()) + return; + + if (sensor->GetMode() < Sensor::PST) { + double s = sin(sensor->GetBeamLimit()); + double c = cos(sensor->GetBeamLimit()); + + int x0 = (int) (0.1*r*s); + int y0 = (int) (0.1*r*c); + int x1 = (int) (1.0*r*s); + int y1 = (int) (1.0*r*c); + + if (cockpit_hud_texture) { + cockpit_hud_texture->DrawLine(xc-x0, yc-y0, xc-x1, yc-y1, hud_color); + cockpit_hud_texture->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color); + } + else { + window->DrawLine(xc-x0, yc-y0, xc-x1, yc-y1, hud_color); + window->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color); + } + } + + double rscale = (double) r/(sensor->GetBeamRange()); + + Camera hsd_cam = ship->Cam(); + Point look = ship->Location() + ship->Heading() * 1000; + look.y = ship->Location().y; + + hsd_cam.LookAt(look); + + // draw tick marks on range rings: + for (int dir = 0; dir < 4; dir++) { + Point tick; + + switch (dir) { + case 0: tick = Point( 0, 0, 1000); break; + case 1: tick = Point( 1000, 0, 0); break; + case 2: tick = Point( 0, 0, -1000); break; + case 3: tick = Point(-1000, 0, 0); break; + } + + double tx = tick * hsd_cam.vrt(); + double tz = tick * hsd_cam.vpn(); + double az = asin(fabs(tx) / 1000); + + if (tx < 0) az = -az; + + if (tz < 0) + if (az < 0) az = -PI - az; + else az = PI - az; + + for (double range = 0.3; range < 1; range += 0.3) { + int x0 = (int) (sin(az) * r * range); + int y0 = (int) (cos(az) * r * range); + int x1 = (int) (sin(az) * r * (range + 0.1)); + int y1 = (int) (cos(az) * r * (range + 0.1)); + + if (cockpit_hud_texture) { + cockpit_hud_texture->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color); + } + else { + window->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color); + } + } + } + + // draw next nav point: + Instruction* navpt = ship->GetNextNavPoint(); + if (navpt && navpt->Region() == ship->GetRegion()) { + const Camera* cam = &hsd_cam; + + // translate: + Point pt = navpt->Location().OtherHand() - ship->Location(); + + // rotate: + double tx = pt * cam->vrt(); + double ty = pt * cam->vup(); + double tz = pt * cam->vpn(); + + // convert to spherical coords: + double rng = pt.length(); + double az = asin(fabs(tx) / rng); + + if (rng > sensor->GetBeamRange()) + rng = sensor->GetBeamRange(); + + if (tx < 0) + az = -az; + + if (tz < 0) + if (az < 0) + az = -PI - az; + else + az = PI - az; + + // draw: + int x = (int) (xc + sin(az) * rng * rscale); + int y = (int) (yc - cos(az) * rng * rscale); + + if (cockpit_hud_texture) { + cockpit_hud_texture->DrawLine(x-2, y-2, x+2, y+2, Color::White); + cockpit_hud_texture->DrawLine(x-2, y+2, x+2, y-2, Color::White); + } + else { + window->DrawLine(x-2, y-2, x+2, y+2, Color::White); + window->DrawLine(x-2, y+2, x+2, y-2, Color::White); + } + } + + // draw contact markers: + double limit = sensor->GetBeamRange(); + ListIter contact = ship->ContactList(); + + while (++contact) { + Ship* c_ship = contact->GetShip(); + if (c_ship == ship) continue; + + // translate: + Point targ_pt = contact->Location() - hsd_cam.Pos(); + + // rotate: + double tx = targ_pt * hsd_cam.vrt(); + double rg = contact->Range(ship, limit); + double true_range = targ_pt.length(); + double az = asin(fabs(tx) / true_range); + + // clip: + if (rg > limit || rg <= 0) + continue; + + if (tx < 0) + az = -az; + + if (!contact->InFront(ship)) + if (az < 0) + az = -PI - az; + else + az = PI - az; + + // draw: + int x = (int) (xc + sin(az) * rg * rscale); + int y = (int) (yc - cos(az) * rg * rscale); + int size = 2; + + // clip again: + if (x < scan_x || y < scan_y) + continue; + + if (c_ship && c_ship == ship->GetTarget()) + size = 3; + + Color mark = HUDView::MarkerColor(contact.value()); + if (cockpit_hud_texture) { + cockpit_hud_texture->FillRect(x-size, y-size, x+size, y+size, mark); + } + else { + window->FillRect(x-size, y-size, x+size, y+size, mark); + } + + if (contact->Threat(ship)) { + if (c_ship) { + if (cockpit_hud_texture) { + cockpit_hud_texture->DrawEllipse(x-4, y-4, x+3, y+3, mark); + } + else { + window->DrawEllipse(x-4, y-4, x+3, y+3, mark); + } + } + else { + if (cockpit_hud_texture) { + cockpit_hud_texture->DrawLine(x, y-5, x+5, y, mark); + cockpit_hud_texture->DrawLine(x+5, y, x, y+5, mark); + cockpit_hud_texture->DrawLine(x, y+5, x-5, y, mark); + cockpit_hud_texture->DrawLine(x-5, y, x, y-5, mark); + } + else { + window->DrawLine(x, y-5, x+5, y, mark); + window->DrawLine(x+5, y, x, y+5, mark); + window->DrawLine(x, y+5, x-5, y, mark); + window->DrawLine(x-5, y, x, y-5, mark); + } + } + } + } + + DrawSensorLabels(ContentBundle::GetInstance()->GetText("MFD.mode.horizontal").data()); +} + +// +--------------------------------------------------------------------+ + +// ELITE-STYLE 3D RADAR + +void +MFD::Draw3D() +{ + int scan_r = rect.w; + int scan_x = cockpit_hud_texture ? (index*128) : rect.x; + int scan_y = cockpit_hud_texture ? 256 : rect.y; + int r = scan_r / 2 - 4; + + double xctr = (scan_r / 2.0) - 0.5; + double yctr = (scan_r / 2.0) + 0.5; + + int xc = (int) xctr + scan_x; + int yc = (int) yctr + scan_y; + + Sensor* sensor = ship->GetSensor(); + if (!sensor) { + DrawMFDText(0, ContentBundle::GetInstance()->GetText("MFD.inactive").data(), rect, DT_CENTER); + return; + } + + int w = sprite->Width(); + int h = sprite->Height(); + + if (w < sprite->Frame()->Width()) + w += 2; + + if (h < sprite->Frame()->Height()) + h += 16; + + sprite->Reshape(w, h); + sprite->Show(); + + if (h < sprite->Frame()->Height()) + return; + + double rscale = (double) r/(sensor->GetBeamRange()); + + Camera hsd_cam = ship->Cam(); + + if (ship->IsStarship()) { + Point look = ship->Location() + ship->Heading() * 1000; + look.y = ship->Location().y; + + hsd_cam.LookAt(look); + } + + + // draw next nav point: + Instruction* navpt = ship->GetNextNavPoint(); + if (navpt && navpt->Region() == ship->GetRegion()) { + const Camera* cam = &hsd_cam; + + // translate: + Point pt = navpt->Location().OtherHand() - ship->Location(); + + // rotate: + double tx = pt * cam->vrt(); + double ty = pt * cam->vup(); + double tz = pt * cam->vpn(); + + // convert to cylindrical coords: + double rng = pt.length(); + double az = asin(fabs(tx) / rng); + + if (rng > sensor->GetBeamRange()) + rng = sensor->GetBeamRange(); + + if (tx < 0) + az = -az; + + if (tz < 0) { + if (az < 0) + az = -PI - az; + else + az = PI - az; + } + + // accentuate vertical: + if (ty > 10) + ty = log10(ty-9) * r/8; + + else if (ty < -10) + ty = -log10(9-ty) * r/8; + + else + ty = 0; + + // draw: + int x = (int) (sin(az) * rng * rscale); + int y = (int) (cos(az) * rng * rscale/2); + int z = (int) (ty); + + int x0 = xc+x; + int y0 = yc-y-z; + + if (cockpit_hud_texture) { + cockpit_hud_texture->DrawLine(x0-2, y0-2, x0+2, y0+2, Color::White); + cockpit_hud_texture->DrawLine(x0-2, y0+2, x0+2, y0-2, Color::White); + } + else { + window->DrawLine(x0-2, y0-2, x0+2, y0+2, Color::White); + window->DrawLine(x0-2, y0+2, x0+2, y0-2, Color::White); + } + + if (cockpit_hud_texture) { + if (z > 0) + cockpit_hud_texture->DrawLine(x0, y0+1, x0, y0+z, Color::White); + else if (z < 0) + cockpit_hud_texture->DrawLine(x0, y0+z, x0, y0-1, Color::White); + } + else { + if (z > 0) + window->DrawLine(x0, y0+1, x0, y0+z, Color::White); + else if (z < 0) + window->DrawLine(x0, y0+z, x0, y0-1, Color::White); + } + } + + + // draw contact markers: + double limit = sensor->GetBeamRange(); + ListIter contact = ship->ContactList(); + + while (++contact) { + Ship* c_ship = contact->GetShip(); + if (c_ship == ship) continue; + + // translate: + Point targ_pt = contact->Location() - hsd_cam.Pos(); + + // rotate: + double tx = targ_pt * hsd_cam.vrt(); + double ty = targ_pt * hsd_cam.vup(); + double rg = contact->Range(ship, limit); + double true_range = targ_pt.length(); + double az = asin(fabs(tx) / true_range); + + // clip: + if (rg > limit || rg <= 0) + continue; + + if (tx < 0) + az = -az; + + if (!contact->InFront(ship)) + if (az < 0) + az = -PI - az; + else + az = PI - az; + + // accentuate vertical: + ty *= 4; + + // draw: + int x = (int) (sin(az) * rg * rscale); + int y = (int) (cos(az) * rg * rscale/2); + int z = (int) (ty * rscale/2); + int size = 1; + + int x0 = xc+x; + int y0 = yc-y-z; + + if (c_ship && c_ship == ship->GetTarget()) + size = 2; + + Color mark = HUDView::MarkerColor(contact.value()); + + if (cockpit_hud_texture) { + cockpit_hud_texture->FillRect(x0-size, y0-size, x0+size, y0+size, mark); + + if (contact->Threat(ship)) { + if (c_ship) { + cockpit_hud_texture->DrawEllipse(x0-4, y0-4, x0+3, y0+3, mark); + } + else { + cockpit_hud_texture->DrawLine(x0, y0-5, x0+5, y0, mark); + cockpit_hud_texture->DrawLine(x0+5, y0, x0, y0+5, mark); + cockpit_hud_texture->DrawLine(x0, y0+5, x0-5, y0, mark); + cockpit_hud_texture->DrawLine(x0-5, y0, x0, y0-5, mark); + } + } + + if (z > 0) + cockpit_hud_texture->FillRect(x0-1, y0+size, x0, y0+z, mark); + else if (z < 0) + cockpit_hud_texture->FillRect(x0-1, y0+z, x0, y0-size, mark); + } + else { + window->FillRect(x0-size, y0-size, x0+size, y0+size, mark); + + if (contact->Threat(ship)) { + if (c_ship) { + window->DrawEllipse(x0-4, y0-4, x0+3, y0+3, mark); + } + else { + window->DrawLine(x0, y0-5, x0+5, y0, mark); + window->DrawLine(x0+5, y0, x0, y0+5, mark); + window->DrawLine(x0, y0+5, x0-5, y0, mark); + window->DrawLine(x0-5, y0, x0, y0-5, mark); + } + } + + if (z > 0) + window->FillRect(x0-1, y0+size, x0, y0+z, mark); + else if (z < 0) + window->FillRect(x0-1, y0+z, x0, y0-size, mark); + } + } + + DrawSensorLabels(ContentBundle::GetInstance()->GetText("MFD.mode.3D").data()); +} + +// +--------------------------------------------------------------------+ + +// GROUND MAP + +void +MFD::DrawMap() +{ + Rect text_rect(rect.x, rect.y, rect.w, 12); + DrawMFDText(0, ContentBundle::GetInstance()->GetText("MFD.mode.ground").data(), text_rect, DT_CENTER); +} + +// +--------------------------------------------------------------------+ + +void +MFD::DrawGauge(int x, int y, int percent) +{ + if (cockpit_hud_texture) { + x += this->index * 128 - this->rect.x; + y += 256 - this->rect.y; + cockpit_hud_texture->DrawRect(x, y, x+53, y+8, Color::DarkGray); + } + else { + window->DrawRect(x, y, x+53, y+8, Color::DarkGray); + } + + if (percent < 3) return; + if (percent > 100) percent = 100; + + percent /= 2; + + if (cockpit_hud_texture) + cockpit_hud_texture->FillRect(x+2, y+2, x+2+percent, y+7, Color::Gray); + else + window->FillRect(x+2, y+2, x+2+percent, y+7, Color::Gray); +} + +void +MFD::DrawGameMFD() +{ + if (lines < 10) lines++; + + char txt[64]; + Rect txt_rect(rect.x, rect.y, rect.w, 12); + + int t = 0; + + if (!HUDView::IsArcade() && HUDView::ShowFPS()) { + sprintf_s(txt, "FPS: %6.2f", Clock::GetInstance()->Rate()); + DrawMFDText(t++, txt, txt_rect, DT_LEFT); + txt_rect.y += 10; + + if (lines <= 1) return; + + Starshatter* game = Starshatter::GetInstance(); + sprintf_s(txt, "Polys: %d", game->GetPolyStats().npolys); + DrawMFDText(t++, txt, txt_rect, DT_LEFT); + txt_rect.y += 10; + } + + if (ship) { + DrawMFDText(t++, ship->Name(), txt_rect, DT_LEFT); + txt_rect.y += 10; + } + + if (lines <= 2) return; + + int hours = (Clock::GetInstance()->GameTime() / 3600000) ; + int minutes = (Clock::GetInstance()->GameTime() / 60000) % 60; + int seconds = (Clock::GetInstance()->GameTime() / 1000) % 60; + + if (ship) { + DWORD clock = ship->MissionClock(); + + hours = (clock / 3600000) ; + minutes = (clock / 60000) % 60; + seconds = (clock / 1000) % 60; + } + + if (static_cast(Clock::GetInstance()->TimeCompression()) != 1) + sprintf_s(txt, "%02d:%02d:%02d x%.1f", hours, minutes, seconds, Clock::GetInstance()->TimeCompression()); //-V576 + else + sprintf_s(txt, "%02d:%02d:%02d", hours, minutes, seconds); + + DrawMFDText(t++, txt, txt_rect, DT_LEFT); + txt_rect.y += 10; + + if (HUDView::IsArcade() || lines <= 3) return; + + DrawMFDText(t++, ship->GetRegion()->Name(), txt_rect, DT_LEFT); + txt_rect.y += 10; + + if (lines <= 4) return; + + if (ship) { + switch (ship->GetFlightPhase()) { + case Ship::DOCKED: DrawMFDText(t++, ContentBundle::GetInstance()->GetText("MFD.phase.DOCKED").data(), txt_rect, DT_LEFT); break; + case Ship::ALERT: DrawMFDText(t++, ContentBundle::GetInstance()->GetText("MFD.phase.ALERT").data(), txt_rect, DT_LEFT); break; + case Ship::LOCKED: DrawMFDText(t++, ContentBundle::GetInstance()->GetText("MFD.phase.LOCKED").data(), txt_rect, DT_LEFT); break; + case Ship::LAUNCH: DrawMFDText(t++, ContentBundle::GetInstance()->GetText("MFD.phase.LAUNCH").data(), txt_rect, DT_LEFT); break; + case Ship::TAKEOFF: DrawMFDText(t++, ContentBundle::GetInstance()->GetText("MFD.phase.TAKEOFF").data(), txt_rect, DT_LEFT); break; + case Ship::ACTIVE: DrawMFDText(t++, ContentBundle::GetInstance()->GetText("MFD.phase.ACTIVE").data(), txt_rect, DT_LEFT); break; + case Ship::APPROACH: DrawMFDText(t++, ContentBundle::GetInstance()->GetText("MFD.phase.APPROACH").data(), txt_rect, DT_LEFT); break; + case Ship::RECOVERY: DrawMFDText(t++, ContentBundle::GetInstance()->GetText("MFD.phase.RECOVERY").data(), txt_rect, DT_LEFT); break; + case Ship::DOCKING: DrawMFDText(t++, ContentBundle::GetInstance()->GetText("MFD.phase.DOCKING").data(), txt_rect, DT_LEFT); break; + } + } +} + +void +MFD::DrawStatusMFD() +{ + if (lines < 10) lines++; + + Rect status_rect(rect.x, rect.y, rect.w, 12); + int row = 0; + char txt[32]; + + if (ship) { + if (status_rect.y > 320 && !ship->IsStarship()) + status_rect.y += 32; + + Drive* drive = ship->GetDrive(); + if (drive) { + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.THRUST").data(), status_rect, DT_LEFT); + DrawGauge(status_rect.x+70, status_rect.y, (int) ship->Throttle()); + status_rect.y += 10; + } + + if (lines <= 1) return; + + if (ship->Reactors().size() > 0) { + PowerSource* reactor = ship->Reactors()[0]; + if (reactor) { + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.FUEL").data(), status_rect, DT_LEFT); + DrawGauge(status_rect.x+70, status_rect.y, reactor->Charge()); + status_rect.y += 10; + } + } + + if (lines <= 2) return; + + QuantumDrive* quantum_drive = ship->GetQuantumDrive(); + if (quantum_drive) { + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.QUANTUM").data(), status_rect, DT_LEFT); + DrawGauge(status_rect.x+70, status_rect.y, (int) quantum_drive->Charge()); + status_rect.y += 10; + } + + if (lines <= 3) return; + + double hull = ship->Integrity() / ship->Design()->integrity * 100; + int hull_status = System::CRITICAL; + + if (hull > 66) + hull_status = System::NOMINAL; + else if (hull > 33) + hull_status = System::DEGRADED; + + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.HULL").data(), status_rect, DT_LEFT); + DrawGauge(status_rect.x+70, status_rect.y, (int) hull); + status_rect.y += 10; + + if (lines <= 4) return; + + Shield* shield = ship->GetShield(); + if (shield) { + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.SHIELD").data(), status_rect, DT_LEFT); + DrawGauge(status_rect.x+70, status_rect.y, ship->ShieldStrength()); + status_rect.y += 10; + } + + if (lines <= 5) return; + + Weapon* primary = ship->GetPrimary(); + if (primary) { + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.GUNS").data(), status_rect, DT_LEFT); + DrawGauge(status_rect.x+70, status_rect.y, primary->Charge()); + status_rect.y += 10; + } + + if (lines <= 6) return; + + if (HUDView::IsArcade()) { + for (int i = 0; i < ship->Weapons().size() && i < 4; i++) { + WeaponGroup* w = ship->Weapons().at(i); + + if (w->IsMissile()) { + char ammo[8]; + + if (ship->GetSecondaryGroup() == w) + sprintf_s(ammo, "%d *", w->Ammo()); + else + sprintf_s(ammo, "%d", w->Ammo()); + + DrawMFDText(row++, (const char*) w->GetDesign()->name, status_rect, DT_LEFT); + status_rect.x += 70; + DrawMFDText(row++, ammo, status_rect, DT_LEFT); + status_rect.x -= 70; + status_rect.y += 10; + } + } + + if (ship->GetDecoy()) { + char ammo[8]; + sprintf_s(ammo, "%d", ship->GetDecoy()->Ammo()); + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.DECOY").data(), status_rect, DT_LEFT); + status_rect.x += 70; + DrawMFDText(row++, ammo, status_rect, DT_LEFT); + status_rect.x -= 70; + status_rect.y += 10; + } + + if (NetGame::GetInstance()) { + char lives[8]; + sprintf_s(lives, "%d", ship->RespawnCount() + 1); + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.LIVES").data(), status_rect, DT_LEFT); + status_rect.x += 70; + DrawMFDText(row++, lives, status_rect, DT_LEFT); + status_rect.x -= 70; + status_rect.y += 10; + } + + return; + } + + Sensor* sensor = ship->GetSensor(); + if (sensor) { + if (ship->GetFlightPhase() != Ship::ACTIVE) { + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.SENSOR").data(), status_rect, DT_LEFT); + status_rect.x += 70; + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.OFFLINE").data(), status_rect, DT_LEFT); + status_rect.x -= 70; + status_rect.y += 10; + } + + else { + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.EMCON").data(), status_rect, DT_LEFT); + status_rect.x += 70; + + sprintf_s(txt, "%s %d", ContentBundle::GetInstance()->GetText("MFD.status.MODE").data(), ship->GetEMCON()); + + if (!sensor->IsPowerOn() || sensor->GetEnergy() == 0) { + if (!Game::GetInstance()->Paused() && (Clock::GetInstance()->RealTime()/1000) & 2) + strcpy_s(txt, ContentBundle::GetInstance()->GetText("MFD.status.SENSOR-OFF").data()); + } + + DrawMFDText(row++, txt, status_rect, DT_LEFT); + status_rect.x -= 70; + status_rect.y += 10; + } + } + + if (lines <= 7) return; + + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.SYSTEMS").data(), status_rect, DT_LEFT); + status_rect.x += 70; + DrawMFDText(row++, ship->GetDirectorInfo(), status_rect, DT_LEFT); + + if (NetGame::GetInstance()) { + char lives[8]; + sprintf_s(lives, "%d", ship->RespawnCount() + 1); + status_rect.x -= 70; + status_rect.y += 10; + DrawMFDText(row++, ContentBundle::GetInstance()->GetText("MFD.status.LIVES").data(), status_rect, DT_LEFT); + status_rect.x += 70; + DrawMFDText(row++, lives, status_rect, DT_LEFT); + } + } +} + +// +--------------------------------------------------------------------+ + +void +MFD::SetStatusColor(int status) +{ + Color status_color; + + 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; + } +} + +// +--------------------------------------------------------------------+ + +bool MFD::IsMouseLatched() const +{ + return mouse_in; +} + +// +--------------------------------------------------------------------+ + +void MFD::DrawMFDText(int index, const char* txt, Rect& txt_rect, int align, int status) +{ + if (index >= MFD::TXT_LAST) { + Print("MFD DrawMFDText() invalid mfd_text index %d '%s'\n", index, txt); + } + else { + HUDText& mt = mfd_text[index]; + Color mc = mt.color; + + switch (status) { + default: + case System::NOMINAL: mc = txt_color; break; + case System::DEGRADED: mc = Color(255,255, 0); break; + case System::CRITICAL: mc = Color(255, 0, 0); break; + case System::DESTROYED: mc = Color( 0, 0, 0); break; + } + + char txt_buf[256]; + int n = strlen(txt); + + if (n > 250) n = 250; + int i; + + for (i = 0; i < n; i++) { + if (islower(txt[i])) + txt_buf[i] = toupper(txt[i]); + else + txt_buf[i] = txt[i]; + } + + txt_buf[i] = 0; + + + if (cockpit_hud_texture) { + Rect hud_rect(txt_rect); + + hud_rect.x = txt_rect.x + this->index * 128 - this->rect.x; + hud_rect.y = txt_rect.y + 256 - this->rect.y; + + mt.font->SetColor(mc); + mt.font->DrawText(txt_buf, 0, hud_rect, align | DT_SINGLELINE, cockpit_hud_texture); + mt.rect = rect; + mt.hidden = false; + } + else { + if (txt_rect.Contains(Mouse::X(), Mouse::Y())) + mc = Color::White; + + mt.font->SetColor(mc); + mt.font->DrawText(txt_buf, 0, txt_rect, align | DT_SINGLELINE); + mt.rect = rect; + mt.hidden = false; + } + + } +} + +void MFD::HideMFDText(int index) +{ + if (index >= MFD::TXT_LAST) + Print("MFD HideMFDText() invalid mfd_text index %d\n", index); + else + mfd_text[index].hidden = true; +} + + + + -- cgit v1.1