Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Mfd.cpp
Go to the documentation of this file.
1 /* Project Starshatter 5.0
2  Destroyer Studios LLC
3  Copyright (C) 1997-2007. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: MFD.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Class for all Multi Function Displays
13 */
14 
15 #include "MemDebug.h"
16 #include "MFD.h"
17 #include "HUDView.h"
18 #include "Ship.h"
19 #include "NavSystem.h"
20 #include "Power.h"
21 #include "Shield.h"
22 #include "Sensor.h"
23 #include "Contact.h"
24 #include "ShipDesign.h"
25 #include "Shot.h"
26 #include "Weapon.h"
27 #include "WeaponGroup.h"
28 #include "Sim.h"
29 #include "StarSystem.h"
30 #include "Starshatter.h"
31 #include "Drive.h"
32 #include "QuantumDrive.h"
33 #include "Power.h"
34 #include "Instruction.h"
35 
36 #include "NetGame.h"
37 
38 #include "CameraView.h"
39 #include "Color.h"
40 #include "Font.h"
41 #include "FontMgr.h"
42 #include "Window.h"
43 #include "Video.h"
44 #include "Screen.h"
45 #include "DataLoader.h"
46 #include "Scene.h"
47 #include "Graphic.h"
48 #include "Sprite.h"
49 #include "Keyboard.h"
50 #include "Mouse.h"
51 #include "Game.h"
52 
53 static Bitmap sensor_fov;
54 static Bitmap sensor_fwd;
55 static Bitmap sensor_hsd;
56 static Bitmap sensor_3d;
57 
58 static BYTE* sensor_fov_shade;
59 static BYTE* sensor_fwd_shade;
60 static BYTE* sensor_hsd_shade;
61 static BYTE* sensor_3d_shade;
62 
63 static Color hud_color = Color::Black;
64 static Color txt_color = Color::Black;
65 
66 // +--------------------------------------------------------------------+
67 
68 MFD::MFD(Window* c, int n)
69 : window(c), rect(0,0,0,0), index(n), mode(MFD_MODE_OFF), sprite(0),
70 ship(0), hidden(true), camview(0), lines(0), mouse_latch(0), mouse_in(false),
71 cockpit_hud_texture(0)
72 {
73  sprite = new(__FILE__,__LINE__) Sprite(&sensor_fov);
74 
75  sprite->SetBlendMode(2);
76  sprite->SetFilter(0);
77  sprite->Hide();
78 
79  Font* font = FontMgr::Find("HUD");
80 
81  for (int i = 0; i < TXT_LAST; i++) {
82  mfd_text[i].font = font;
84  mfd_text[i].hidden = true;
85  }
86 }
87 
89 {
91 }
92 
93 // +--------------------------------------------------------------------+
94 
95 void
97 {
98  static int initialized = 0;
99  if (initialized) return;
100 
101  HUDView::PrepareBitmap("sensor_fov.pcx", sensor_fov, sensor_fov_shade);
102  HUDView::PrepareBitmap("sensor_fwd.pcx", sensor_fwd, sensor_fwd_shade);
103  HUDView::PrepareBitmap("sensor_hsd.pcx", sensor_hsd, sensor_hsd_shade);
104  HUDView::PrepareBitmap("sensor_3d.pcx", sensor_3d, sensor_3d_shade);
105 
106  sensor_fov.SetType(Bitmap::BMP_TRANSLUCENT);
107  sensor_fwd.SetType(Bitmap::BMP_TRANSLUCENT);
108  sensor_hsd.SetType(Bitmap::BMP_TRANSLUCENT);
109  sensor_3d.SetType(Bitmap::BMP_TRANSLUCENT);
110 
111  initialized = 1;
112 }
113 
114 void
116 {
117  sensor_fov.ClearImage();
118  sensor_fwd.ClearImage();
119  sensor_hsd.ClearImage();
120  sensor_3d.ClearImage();
121 
122  delete [] sensor_fov_shade;
123  delete [] sensor_fwd_shade;
124  delete [] sensor_hsd_shade;
125  delete [] sensor_3d_shade;
126 }
127 
128 // +--------------------------------------------------------------------+
129 
130 void
132 {
133  if (v && !camview) {
134  camview = v;
135  }
136 }
137 
138 void
140 {
141  HUDView* hud = HUDView::GetInstance();
142 
143  if (hud) {
144  hud_color = hud->GetHUDColor();
145  txt_color = hud->GetTextColor();
146  }
147  else {
148  hud_color = c;
149  txt_color = c;
150  }
151 
152  HUDView::ColorizeBitmap(sensor_fov, sensor_fov_shade, c);
153  HUDView::ColorizeBitmap(sensor_fwd, sensor_fwd_shade, c);
154  HUDView::ColorizeBitmap(sensor_hsd, sensor_hsd_shade, c);
155  HUDView::ColorizeBitmap(sensor_3d, sensor_3d_shade, c);
156 }
157 
158 void
160 {
161  for (int i = 0; i < TXT_LAST; i++)
162  mfd_text[i].color = c;
163 }
164 
165 // +--------------------------------------------------------------------+
166 
167 void
169 {
170  switch (mode) {
171  case MFD_MODE_FOV:
172  case MFD_MODE_HSD:
173  case MFD_MODE_3D:
174  if (sprite)
175  sprite->Show();
176  break;
177  }
178 
179  hidden = false;
180 }
181 
182 void
184 {
185  if (sprite)
186  sprite->Hide();
187 
188  for (int i = 0; i < TXT_LAST; i++)
189  HideMFDText(i);
190 
191  hidden = true;
192 }
193 
194 // +--------------------------------------------------------------------+
195 
196 void
198 {
199  rect = r;
200 
201  if (sprite)
202  sprite->MoveTo(Point(rect.x + sprite->Width()/2,
203  rect.y + sprite->Height()/2,
204  1));
205 }
206 
207 // +--------------------------------------------------------------------+
208 
209 void
211 {
212  if (m < MFD_MODE_OFF || m > MFD_MODE_3D)
213  mode = MFD_MODE_OFF;
214  else
215  mode = m;
216 
217  sprite->Hide();
218 
219  for (int i = 0; i < TXT_LAST; i++)
220  HideMFDText(i);
221 
222  switch (mode) {
223  case MFD_MODE_GAME:
224  case MFD_MODE_SHIP:
225  lines = 0;
226  break;
227 
228  case MFD_MODE_FOV:
229  sprite->SetAnimation(&sensor_fov);
230  sprite->Show();
231  sprite->Reshape(sensor_fov.Width()-8, 16);
232  break;
233  case MFD_MODE_HSD:
234  sprite->SetAnimation(&sensor_hsd);
235  sprite->Show();
236  sprite->Reshape(sensor_hsd.Width()-8, 16);
237  break;
238  case MFD_MODE_3D:
239  sprite->SetAnimation(&sensor_3d);
240  sprite->Show();
241  sprite->Reshape(sensor_3d.Width()-8, 16);
242  break;
243  }
244 }
245 
246 // +--------------------------------------------------------------------+
247 
248 void
250 {
251  mouse_in = false;
252 
253  if (Mouse::LButton() == 0)
254  mouse_latch = 0;
255 
256  if (rect.Contains(Mouse::X(), Mouse::Y()))
257  mouse_in = true;
258 
259  // click to turn on MFD when off:
260  if (mode < MFD_MODE_FOV && Mouse::LButton() && !mouse_latch) {
261  mouse_latch = 1;
262  if (mouse_in) {
263  HUDView* hud = HUDView::GetInstance();
264  if (hud)
265  hud->CycleMFDMode(index);
266  }
267  }
268 
269  for (int i = 0; i < TXT_LAST; i++)
270  HideMFDText(i);
271 
272  if (hidden || mode < MFD_MODE_FOV) {
273  if (cockpit_hud_texture) {
274  int x1 = index*128;
275  int y1 = 256;
276  int x2 = x1 + 128;
277  int y2 = y1 + 128;
278 
279  cockpit_hud_texture->FillRect(x1, y1, x2, y2, Color::Black);
280  }
281 
282  if (hidden)
283  return;
284  }
285 
286  if (sprite && !sprite->Hidden()) {
287  if (cockpit_hud_texture) {
288  int x1 = index*128;
289  int y1 = 256;
290  int w = sprite->Width();
291  int h = sprite->Height();
292 
293  cockpit_hud_texture->BitBlt(x1, y1, *sprite->Frame(), 0,0,w,h);
294  }
295  else {
296  int cx = rect.x + rect.w/2;
297  int cy = rect.y + rect.h/2;
298  int w2 = sprite->Width()/2;
299  int h2 = sprite->Height()/2;
300 
301  window->DrawBitmap(cx-w2, cy-h2, cx+w2, cy+h2, sprite->Frame(), Video::BLEND_ALPHA);
302  }
303  }
304 
305  switch (mode) {
306  default:
307  case MFD_MODE_OFF: break;
308  case MFD_MODE_GAME: DrawGameMFD(); break;
309  case MFD_MODE_SHIP: DrawStatusMFD(); break;
310 
311  // sensor sub-modes:
312  case MFD_MODE_FOV: DrawSensorMFD(); break;
313  case MFD_MODE_HSD: DrawHSD(); break;
314  case MFD_MODE_3D: Draw3D(); break;
315  }
316 }
317 
318 // +--------------------------------------------------------------------+
319 
320 void
321 MFD::DrawSensorLabels(const char* mfd_mode)
322 {
323  Sensor* sensor = ship->GetSensor();
324  char mode_buf[8] = " ";
325  int scan_r = rect.w;
326  int scan_x = rect.x;
327  int scan_y = rect.y;
328 
329  switch (sensor->GetMode()) {
330  case Sensor::PAS: strcpy_s(mode_buf, Game::GetText("MFD.mode.passive").data()); break;
331  case Sensor::STD: strcpy_s(mode_buf, Game::GetText("MFD.mode.standard").data()); break;
332  case Sensor::ACM: strcpy_s(mode_buf, Game::GetText("MFD.mode.auto-combat").data()); break;
333  case Sensor::GM: strcpy_s(mode_buf, Game::GetText("MFD.mode.ground").data()); break;
334  case Sensor::PST: strcpy_s(mode_buf, Game::GetText("MFD.mode.passive").data()); break;
335  case Sensor::CST: strcpy_s(mode_buf, Game::GetText("MFD.mode.combined").data()); break;
336  default: break;
337  }
338 
339  Rect mode_rect(scan_x+2, scan_y+2, 40, 12);
340  DrawMFDText(0, mode_buf, mode_rect, DT_LEFT);
341 
342  char range_txt[12];
343  double beam_range = sensor->GetBeamRange() + 1;
344  if (beam_range >= 1e6)
345  sprintf_s(range_txt, "-%dM+", (int) (beam_range / 1e6));
346  else
347  sprintf_s(range_txt, "-%3d+", (int) (beam_range / 1e3));
348 
349  Rect range_rect(scan_x+2, scan_y+scan_r-12, 40, 12);
350  DrawMFDText(1, range_txt, range_rect, DT_LEFT);
351 
352  Rect disp_rect(scan_x+scan_r-41, scan_y+2, 40, 12);
353  DrawMFDText(2, mfd_mode, disp_rect, DT_RIGHT);
354 
355  Rect probe_rect(scan_x+scan_r-41, scan_y+scan_r-12, 40, 12);
356 
357  if (ship->GetProbeLauncher()) {
358  char probes[32];
359  sprintf_s(probes, "%s %02d", Game::GetText("MFD.probe").data(), ship->GetProbeLauncher()->Ammo());
360  DrawMFDText(3, probes, probe_rect, DT_RIGHT);
361  }
362  else {
363  HideMFDText(3);
364  }
365 
366  if (Mouse::LButton() && !mouse_latch) {
367  mouse_latch = 1;
368 
369  if (mode_rect.Contains(Mouse::X(), Mouse::Y())) {
370  if (sensor->GetMode() < Sensor::PST) {
371  int sensor_mode = sensor->GetMode() + 1;
372  if (sensor_mode > Sensor::GM)
373  sensor_mode = Sensor::PAS;
374 
375  sensor->SetMode((Sensor::Mode) sensor_mode);
376  }
377  }
378 
379  else if (range_rect.Contains(Mouse::X(), Mouse::Y())) {
380  if (Mouse::X() > range_rect.x+range_rect.w/2)
381  sensor->IncreaseRange();
382  else
383  sensor->DecreaseRange();
384  }
385 
386  else if (disp_rect.Contains(Mouse::X(), Mouse::Y())) {
387  HUDView* hud = HUDView::GetInstance();
388  if (hud)
389  hud->CycleMFDMode(index);
390  }
391 
392  else if (probe_rect.Contains(Mouse::X(), Mouse::Y())) {
393  ship->LaunchProbe();
394  }
395  }
396 }
397 
398 // +--------------------------------------------------------------------+
399 
400 // AZIMUTH-ELEVATION ANGULAR SCANNER
401 
402 void
404 {
405  int scan_r = rect.w;
406  int scan_x = cockpit_hud_texture ? (index*128) : rect.x;
407  int scan_y = cockpit_hud_texture ? 256 : rect.y;
408  int r = scan_r / 2;
409 
410  double xctr = (scan_r / 2.0) - 0.5;
411  double yctr = (scan_r / 2.0) + 0.5;
412 
413  Sensor* sensor = ship->GetSensor();
414  if (!sensor) {
415  DrawMFDText(0, Game::GetText("MFD.inactive").data(), rect, DT_CENTER);
416  return;
417  }
418 
419  int w = sprite->Width();
420  int h = sprite->Height();
421 
422  if (w < sprite->Frame()->Width())
423  w += 2;
424 
425  if (h < sprite->Frame()->Height())
426  h += 16;
427 
428  sprite->Reshape(w, h);
429  sprite->Show();
430 
431  if (h < sprite->Frame()->Height())
432  return;
433 
434  double sweep_scale = r / (PI/2);
435 
436  if (sensor->GetBeamLimit() > 90*DEGREES)
437  sweep_scale = (double) r / (90*DEGREES);
438 
439  int az = (int) (sensor->GetBeamLimit() * sweep_scale);
440  int el = az;
441  int xc = (int) (scan_x + xctr);
442  int yc = (int) (scan_y + yctr);
443 
444  if (mode == MFD_MODE_FOV) {
445  if (sensor->GetMode() < Sensor::GM) {
447  cockpit_hud_texture->DrawEllipse(xc-az, yc-el, xc+az, yc+el, hud_color);
448  else
449  window->DrawEllipse(xc-az, yc-el, xc+az, yc+el, hud_color);
450  }
451  }
452  else {
453  char az_txt[8];
454  sprintf_s(az_txt, "%d", (int) (sensor->GetBeamLimit() / DEGREES));
455 
456  Rect az_rect(scan_x+2, scan_y+scan_r-12, 32, 12);
457  DrawMFDText(1, az_txt, az_rect, DT_LEFT);
458 
459  az_rect.x = scan_x + (scan_r/2) - (az_rect.w/2);
460  DrawMFDText(2, "0", az_rect, DT_CENTER);
461 
462  az_rect.x = scan_x + scan_r - az_rect.w - 2;
463  DrawMFDText(3, az_txt, az_rect, DT_RIGHT);
464  }
465 
466  // draw next nav point:
467  Instruction* navpt = ship->GetNextNavPoint();
468  if (navpt && navpt->Region() == ship->GetRegion()) {
469  const Camera* cam = &ship->Cam();
470 
471  // translate:
472  Point pt = navpt->Location().OtherHand() - ship->Location();
473 
474  // rotate:
475  double tx = pt * cam->vrt();
476  double ty = pt * cam->vup();
477  double tz = pt * cam->vpn();
478 
479  if (tz > 1.0) {
480  // convert to spherical coords:
481  double rng = pt.length();
482  double az = asin(fabs(tx) / rng);
483  double el = asin(fabs(ty) / rng);
484 
485  if (tx < 0) az = -az;
486  if (ty < 0) el = -el;
487 
488  if (fabs(az) < 90*DEGREES) {
489  az *= sweep_scale;
490  el *= sweep_scale;
491 
492  int x = (int) (r + az);
493  int y = (int) (r - el);
494 
495  // clip again:
496  if (x > 0 && x < scan_r &&
497  y > 0 && y < scan_r) {
498 
499  // draw:
500  int xc = scan_x + x;
501  int yc = scan_y + y;
502 
503  if (cockpit_hud_texture) {
504  cockpit_hud_texture->DrawLine(xc-2, yc-2, xc+2, yc+2, Color::White);
505  cockpit_hud_texture->DrawLine(xc-2, yc+2, xc+2, yc-2, Color::White);
506  }
507  else {
508  window->DrawLine(xc-2, yc-2, xc+2, yc+2, Color::White);
509  window->DrawLine(xc-2, yc+2, xc+2, yc-2, Color::White);
510  }
511  }
512  }
513  }
514  }
515 
516  int num_contacts = ship->NumContacts();
518 
519  while (++iter) {
520  Contact* contact = iter.value();
521  Ship* c_ship = contact->GetShip();
522  double az, el, rng;
523  bool aft = false;
524 
525  if (c_ship == ship) continue;
526 
527  contact->GetBearing(ship, az, el, rng);
528 
529  // clip (is in-front):
530  if (fabs(az) < 90*DEGREES) {
531  az *= sweep_scale;
532  el *= sweep_scale;
533  }
534 
535  // rear anulus:
536  else {
537  double len = sqrt(az*az + el*el);
538 
539  if (len > 1e-6) {
540  az = r * az/len;
541  el = r * el/len;
542  }
543  else {
544  az = -r;
545  el = 0;
546  }
547 
548  aft = true;
549  }
550 
551  int x = (int) (r + az);
552  int y = (int) (r - el);
553 
554  // clip again:
555  if (x < 0 || x > scan_r) continue;
556  if (y < 0 || y > scan_r) continue;
557 
558  // draw:
559  Color mark = HUDView::MarkerColor(contact);
560 
561  if (aft)
562  mark = mark * 0.75;
563 
564  int xc = scan_x + x;
565  int yc = scan_y + y;
566  int size = 1;
567 
568  if (c_ship && c_ship == ship->GetTarget())
569  size = 2;
570 
572  cockpit_hud_texture->FillRect(xc-size, yc-size, xc+size, yc+size, mark);
573  else
574  window->FillRect(xc-size, yc-size, xc+size, yc+size, mark);
575 
576  if (contact->Threat(ship)) {
577  if (c_ship) {
579  cockpit_hud_texture->DrawEllipse(xc-4, yc-4, xc+3, yc+3, mark);
580  else
581  window->DrawEllipse(xc-4, yc-4, xc+3, yc+3, mark);
582  }
583  else {
584  if (cockpit_hud_texture) {
585  cockpit_hud_texture->DrawLine(xc, yc-5, xc+5, yc, mark);
586  cockpit_hud_texture->DrawLine(xc+5, yc, xc, yc+5, mark);
587  cockpit_hud_texture->DrawLine(xc, yc+5, xc-5, yc, mark);
588  cockpit_hud_texture->DrawLine(xc-5, yc, xc, yc-5, mark);
589  }
590  else {
591  window->DrawLine(xc, yc-5, xc+5, yc, mark);
592  window->DrawLine(xc+5, yc, xc, yc+5, mark);
593  window->DrawLine(xc, yc+5, xc-5, yc, mark);
594  window->DrawLine(xc-5, yc, xc, yc-5, mark);
595  }
596  }
597  }
598  }
599 
600  DrawSensorLabels(Game::GetText("MFD.mode.field-of-view").data());
601 }
602 
603 // +--------------------------------------------------------------------+
604 
605 // HORIZONTAL SITUATION DISPLAY
606 
607 void
609 {
610  int scan_r = rect.w;
611  int scan_x = cockpit_hud_texture ? (index*128) : rect.x;
612  int scan_y = cockpit_hud_texture ? 256 : rect.y;
613  int r = scan_r / 2 - 4;
614 
615  double xctr = (scan_r / 2.0) - 0.5;
616  double yctr = (scan_r / 2.0) + 0.5;
617 
618  int xc = (int) xctr + scan_x;
619  int yc = (int) yctr + scan_y;
620 
621  Sensor* sensor = ship->GetSensor();
622  if (!sensor) {
623  DrawMFDText(0, Game::GetText("MFD.inactive").data(), rect, DT_CENTER);
624  return;
625  }
626 
627  int w = sprite->Width();
628  int h = sprite->Height();
629 
630  if (w < sprite->Frame()->Width())
631  w += 2;
632 
633  if (h < sprite->Frame()->Height())
634  h += 16;
635 
636  sprite->Reshape(w, h);
637  sprite->Show();
638 
639  if (h < sprite->Frame()->Height())
640  return;
641 
642  if (sensor->GetMode() < Sensor::PST) {
643  double s = sin(sensor->GetBeamLimit());
644  double c = cos(sensor->GetBeamLimit());
645 
646  int x0 = (int) (0.1*r*s);
647  int y0 = (int) (0.1*r*c);
648  int x1 = (int) (1.0*r*s);
649  int y1 = (int) (1.0*r*c);
650 
651  if (cockpit_hud_texture) {
652  cockpit_hud_texture->DrawLine(xc-x0, yc-y0, xc-x1, yc-y1, hud_color);
653  cockpit_hud_texture->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color);
654  }
655  else {
656  window->DrawLine(xc-x0, yc-y0, xc-x1, yc-y1, hud_color);
657  window->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color);
658  }
659  }
660 
661  double rscale = (double) r/(sensor->GetBeamRange());
662 
663  Camera hsd_cam = ship->Cam();
664  Point look = ship->Location() + ship->Heading() * 1000;
665  look.y = ship->Location().y;
666 
667  hsd_cam.LookAt(look);
668 
669  // draw tick marks on range rings:
670  for (int dir = 0; dir < 4; dir++) {
671  Point tick;
672 
673  switch (dir) {
674  case 0: tick = Point( 0, 0, 1000); break;
675  case 1: tick = Point( 1000, 0, 0); break;
676  case 2: tick = Point( 0, 0, -1000); break;
677  case 3: tick = Point(-1000, 0, 0); break;
678  }
679 
680  double tx = tick * hsd_cam.vrt();
681  double tz = tick * hsd_cam.vpn();
682  double az = asin(fabs(tx) / 1000);
683 
684  if (tx < 0) az = -az;
685 
686  if (tz < 0)
687  if (az < 0) az = -PI - az;
688  else az = PI - az;
689 
690  for (double range = 0.3; range < 1; range += 0.3) {
691  int x0 = (int) (sin(az) * r * range);
692  int y0 = (int) (cos(az) * r * range);
693  int x1 = (int) (sin(az) * r * (range + 0.1));
694  int y1 = (int) (cos(az) * r * (range + 0.1));
695 
696  if (cockpit_hud_texture) {
697  cockpit_hud_texture->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color);
698  }
699  else {
700  window->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color);
701  }
702  }
703  }
704 
705  // draw next nav point:
706  Instruction* navpt = ship->GetNextNavPoint();
707  if (navpt && navpt->Region() == ship->GetRegion()) {
708  const Camera* cam = &hsd_cam;
709 
710  // translate:
711  Point pt = navpt->Location().OtherHand() - ship->Location();
712 
713  // rotate:
714  double tx = pt * cam->vrt();
715  double ty = pt * cam->vup();
716  double tz = pt * cam->vpn();
717 
718  // convert to spherical coords:
719  double rng = pt.length();
720  double az = asin(fabs(tx) / rng);
721 
722  if (rng > sensor->GetBeamRange())
723  rng = sensor->GetBeamRange();
724 
725  if (tx < 0)
726  az = -az;
727 
728  if (tz < 0)
729  if (az < 0)
730  az = -PI - az;
731  else
732  az = PI - az;
733 
734  // draw:
735  int x = (int) (xc + sin(az) * rng * rscale);
736  int y = (int) (yc - cos(az) * rng * rscale);
737 
738  if (cockpit_hud_texture) {
739  cockpit_hud_texture->DrawLine(x-2, y-2, x+2, y+2, Color::White);
740  cockpit_hud_texture->DrawLine(x-2, y+2, x+2, y-2, Color::White);
741  }
742  else {
743  window->DrawLine(x-2, y-2, x+2, y+2, Color::White);
744  window->DrawLine(x-2, y+2, x+2, y-2, Color::White);
745  }
746  }
747 
748  // draw contact markers:
749  double limit = sensor->GetBeamRange();
750  ListIter<Contact> contact = ship->ContactList();
751 
752  while (++contact) {
753  Ship* c_ship = contact->GetShip();
754  if (c_ship == ship) continue;
755 
756  // translate:
757  Point targ_pt = contact->Location() - hsd_cam.Pos();
758 
759  // rotate:
760  double tx = targ_pt * hsd_cam.vrt();
761  double rg = contact->Range(ship, limit);
762  double true_range = targ_pt.length();
763  double az = asin(fabs(tx) / true_range);
764 
765  // clip:
766  if (rg > limit || rg <= 0)
767  continue;
768 
769  if (tx < 0)
770  az = -az;
771 
772  if (!contact->InFront(ship))
773  if (az < 0)
774  az = -PI - az;
775  else
776  az = PI - az;
777 
778  // draw:
779  int x = (int) (xc + sin(az) * rg * rscale);
780  int y = (int) (yc - cos(az) * rg * rscale);
781  int size = 2;
782 
783  // clip again:
784  if (x < scan_x || y < scan_y)
785  continue;
786 
787  if (c_ship && c_ship == ship->GetTarget())
788  size = 3;
789 
790  Color mark = HUDView::MarkerColor(contact.value());
791  if (cockpit_hud_texture) {
792  cockpit_hud_texture->FillRect(x-size, y-size, x+size, y+size, mark);
793  }
794  else {
795  window->FillRect(x-size, y-size, x+size, y+size, mark);
796  }
797 
798  if (contact->Threat(ship)) {
799  if (c_ship) {
800  if (cockpit_hud_texture) {
801  cockpit_hud_texture->DrawEllipse(x-4, y-4, x+3, y+3, mark);
802  }
803  else {
804  window->DrawEllipse(x-4, y-4, x+3, y+3, mark);
805  }
806  }
807  else {
808  if (cockpit_hud_texture) {
809  cockpit_hud_texture->DrawLine(x, y-5, x+5, y, mark);
810  cockpit_hud_texture->DrawLine(x+5, y, x, y+5, mark);
811  cockpit_hud_texture->DrawLine(x, y+5, x-5, y, mark);
812  cockpit_hud_texture->DrawLine(x-5, y, x, y-5, mark);
813  }
814  else {
815  window->DrawLine(x, y-5, x+5, y, mark);
816  window->DrawLine(x+5, y, x, y+5, mark);
817  window->DrawLine(x, y+5, x-5, y, mark);
818  window->DrawLine(x-5, y, x, y-5, mark);
819  }
820  }
821  }
822  }
823 
824  DrawSensorLabels(Game::GetText("MFD.mode.horizontal").data());
825 }
826 
827 // +--------------------------------------------------------------------+
828 
829 // ELITE-STYLE 3D RADAR
830 
831 void
833 {
834  int scan_r = rect.w;
835  int scan_x = cockpit_hud_texture ? (index*128) : rect.x;
836  int scan_y = cockpit_hud_texture ? 256 : rect.y;
837  int r = scan_r / 2 - 4;
838 
839  double xctr = (scan_r / 2.0) - 0.5;
840  double yctr = (scan_r / 2.0) + 0.5;
841 
842  int xc = (int) xctr + scan_x;
843  int yc = (int) yctr + scan_y;
844 
845  Sensor* sensor = ship->GetSensor();
846  if (!sensor) {
847  DrawMFDText(0, Game::GetText("MFD.inactive").data(), rect, DT_CENTER);
848  return;
849  }
850 
851  int w = sprite->Width();
852  int h = sprite->Height();
853 
854  if (w < sprite->Frame()->Width())
855  w += 2;
856 
857  if (h < sprite->Frame()->Height())
858  h += 16;
859 
860  sprite->Reshape(w, h);
861  sprite->Show();
862 
863  if (h < sprite->Frame()->Height())
864  return;
865 
866  double rscale = (double) r/(sensor->GetBeamRange());
867 
868  Camera hsd_cam = ship->Cam();
869 
870  if (ship->IsStarship()) {
871  Point look = ship->Location() + ship->Heading() * 1000;
872  look.y = ship->Location().y;
873 
874  hsd_cam.LookAt(look);
875  }
876 
877 
878  // draw next nav point:
879  Instruction* navpt = ship->GetNextNavPoint();
880  if (navpt && navpt->Region() == ship->GetRegion()) {
881  const Camera* cam = &hsd_cam;
882 
883  // translate:
884  Point pt = navpt->Location().OtherHand() - ship->Location();
885 
886  // rotate:
887  double tx = pt * cam->vrt();
888  double ty = pt * cam->vup();
889  double tz = pt * cam->vpn();
890 
891  // convert to cylindrical coords:
892  double rng = pt.length();
893  double az = asin(fabs(tx) / rng);
894 
895  if (rng > sensor->GetBeamRange())
896  rng = sensor->GetBeamRange();
897 
898  if (tx < 0)
899  az = -az;
900 
901  if (tz < 0) {
902  if (az < 0)
903  az = -PI - az;
904  else
905  az = PI - az;
906  }
907 
908  // accentuate vertical:
909  if (ty > 10)
910  ty = log10(ty-9) * r/8;
911 
912  else if (ty < -10)
913  ty = -log10(9-ty) * r/8;
914 
915  else
916  ty = 0;
917 
918  // draw:
919  int x = (int) (sin(az) * rng * rscale);
920  int y = (int) (cos(az) * rng * rscale/2);
921  int z = (int) (ty);
922 
923  int x0 = xc+x;
924  int y0 = yc-y-z;
925 
926  if (cockpit_hud_texture) {
927  cockpit_hud_texture->DrawLine(x0-2, y0-2, x0+2, y0+2, Color::White);
928  cockpit_hud_texture->DrawLine(x0-2, y0+2, x0+2, y0-2, Color::White);
929  }
930  else {
931  window->DrawLine(x0-2, y0-2, x0+2, y0+2, Color::White);
932  window->DrawLine(x0-2, y0+2, x0+2, y0-2, Color::White);
933  }
934 
935  if (cockpit_hud_texture) {
936  if (z > 0)
937  cockpit_hud_texture->DrawLine(x0, y0+1, x0, y0+z, Color::White);
938  else if (z < 0)
939  cockpit_hud_texture->DrawLine(x0, y0+z, x0, y0-1, Color::White);
940  }
941  else {
942  if (z > 0)
943  window->DrawLine(x0, y0+1, x0, y0+z, Color::White);
944  else if (z < 0)
945  window->DrawLine(x0, y0+z, x0, y0-1, Color::White);
946  }
947  }
948 
949 
950  // draw contact markers:
951  double limit = sensor->GetBeamRange();
952  ListIter<Contact> contact = ship->ContactList();
953 
954  while (++contact) {
955  Ship* c_ship = contact->GetShip();
956  if (c_ship == ship) continue;
957 
958  // translate:
959  Point targ_pt = contact->Location() - hsd_cam.Pos();
960 
961  // rotate:
962  double tx = targ_pt * hsd_cam.vrt();
963  double ty = targ_pt * hsd_cam.vup();
964  double rg = contact->Range(ship, limit);
965  double true_range = targ_pt.length();
966  double az = asin(fabs(tx) / true_range);
967 
968  // clip:
969  if (rg > limit || rg <= 0)
970  continue;
971 
972  if (tx < 0)
973  az = -az;
974 
975  if (!contact->InFront(ship))
976  if (az < 0)
977  az = -PI - az;
978  else
979  az = PI - az;
980 
981  // accentuate vertical:
982  ty *= 4;
983 
984  // draw:
985  int x = (int) (sin(az) * rg * rscale);
986  int y = (int) (cos(az) * rg * rscale/2);
987  int z = (int) (ty * rscale/2);
988  int size = 1;
989 
990  int x0 = xc+x;
991  int y0 = yc-y-z;
992 
993  if (c_ship && c_ship == ship->GetTarget())
994  size = 2;
995 
996  Color mark = HUDView::MarkerColor(contact.value());
997 
998  if (cockpit_hud_texture) {
999  cockpit_hud_texture->FillRect(x0-size, y0-size, x0+size, y0+size, mark);
1000 
1001  if (contact->Threat(ship)) {
1002  if (c_ship) {
1003  cockpit_hud_texture->DrawEllipse(x0-4, y0-4, x0+3, y0+3, mark);
1004  }
1005  else {
1006  cockpit_hud_texture->DrawLine(x0, y0-5, x0+5, y0, mark);
1007  cockpit_hud_texture->DrawLine(x0+5, y0, x0, y0+5, mark);
1008  cockpit_hud_texture->DrawLine(x0, y0+5, x0-5, y0, mark);
1009  cockpit_hud_texture->DrawLine(x0-5, y0, x0, y0-5, mark);
1010  }
1011  }
1012 
1013  if (z > 0)
1014  cockpit_hud_texture->FillRect(x0-1, y0+size, x0, y0+z, mark);
1015  else if (z < 0)
1016  cockpit_hud_texture->FillRect(x0-1, y0+z, x0, y0-size, mark);
1017  }
1018  else {
1019  window->FillRect(x0-size, y0-size, x0+size, y0+size, mark);
1020 
1021  if (contact->Threat(ship)) {
1022  if (c_ship) {
1023  window->DrawEllipse(x0-4, y0-4, x0+3, y0+3, mark);
1024  }
1025  else {
1026  window->DrawLine(x0, y0-5, x0+5, y0, mark);
1027  window->DrawLine(x0+5, y0, x0, y0+5, mark);
1028  window->DrawLine(x0, y0+5, x0-5, y0, mark);
1029  window->DrawLine(x0-5, y0, x0, y0-5, mark);
1030  }
1031  }
1032 
1033  if (z > 0)
1034  window->FillRect(x0-1, y0+size, x0, y0+z, mark);
1035  else if (z < 0)
1036  window->FillRect(x0-1, y0+z, x0, y0-size, mark);
1037  }
1038  }
1039 
1040  DrawSensorLabels(Game::GetText("MFD.mode.3D").data());
1041 }
1042 
1043 // +--------------------------------------------------------------------+
1044 
1045 // GROUND MAP
1046 
1047 void
1049 {
1050  DrawMFDText(0, Game::GetText("MFD.mode.ground").data(), Rect(rect.x, rect.y, rect.w, 12), DT_CENTER);
1051 }
1052 
1053 // +--------------------------------------------------------------------+
1054 
1055 void
1056 MFD::DrawGauge(int x, int y, int percent)
1057 {
1058  if (cockpit_hud_texture) {
1059  x += this->index * 128 - this->rect.x;
1060  y += 256 - this->rect.y;
1061  cockpit_hud_texture->DrawRect(x, y, x+53, y+8, Color::DarkGray);
1062  }
1063  else {
1064  window->DrawRect(x, y, x+53, y+8, Color::DarkGray);
1065  }
1066 
1067  if (percent < 3) return;
1068  if (percent > 100) percent = 100;
1069 
1070  percent /= 2;
1071 
1072  if (cockpit_hud_texture)
1073  cockpit_hud_texture->FillRect(x+2, y+2, x+2+percent, y+7, Color::Gray);
1074  else
1075  window->FillRect(x+2, y+2, x+2+percent, y+7, Color::Gray);
1076 }
1077 
1078 void
1080 {
1081  if (lines < 10) lines++;
1082 
1083  char txt[64];
1084  Rect txt_rect(rect.x, rect.y, rect.w, 12);
1085 
1086  int t = 0;
1087 
1088  if (!HUDView::IsArcade() && HUDView::ShowFPS()) {
1089  sprintf_s(txt, "FPS: %6.2f", Game::FrameRate());
1090  DrawMFDText(t++, txt, txt_rect, DT_LEFT);
1091  txt_rect.y += 10;
1092 
1093  if (lines <= 1) return;
1094 
1096  sprintf_s(txt, "Polys: %d", game->GetPolyStats().npolys);
1097  DrawMFDText(t++, txt, txt_rect, DT_LEFT);
1098  txt_rect.y += 10;
1099  }
1100 
1101  if (ship) {
1102  DrawMFDText(t++, ship->Name(), txt_rect, DT_LEFT);
1103  txt_rect.y += 10;
1104  }
1105 
1106  if (lines <= 2) return;
1107 
1108  int hours = (Game::GameTime() / 3600000) ;
1109  int minutes = (Game::GameTime() / 60000) % 60;
1110  int seconds = (Game::GameTime() / 1000) % 60;
1111 
1112  if (ship) {
1113  DWORD clock = ship->MissionClock();
1114 
1115  hours = (clock / 3600000) ;
1116  minutes = (clock / 60000) % 60;
1117  seconds = (clock / 1000) % 60;
1118  }
1119 
1120  if (Game::TimeCompression() > 1)
1121  sprintf_s(txt, "%02d:%02d:%02d x%d", hours, minutes, seconds, Game::TimeCompression());
1122  else
1123  sprintf_s(txt, "%02d:%02d:%02d", hours, minutes, seconds);
1124 
1125  DrawMFDText(t++, txt, txt_rect, DT_LEFT);
1126  txt_rect.y += 10;
1127 
1128  if (HUDView::IsArcade() || lines <= 3) return;
1129 
1130  DrawMFDText(t++, ship->GetRegion()->Name(), txt_rect, DT_LEFT);
1131  txt_rect.y += 10;
1132 
1133  if (lines <= 4) return;
1134 
1135  if (ship) {
1136  switch (ship->GetFlightPhase()) {
1137  case Ship::DOCKED: DrawMFDText(t++, Game::GetText("MFD.phase.DOCKED").data(), txt_rect, DT_LEFT); break;
1138  case Ship::ALERT: DrawMFDText(t++, Game::GetText("MFD.phase.ALERT").data(), txt_rect, DT_LEFT); break;
1139  case Ship::LOCKED: DrawMFDText(t++, Game::GetText("MFD.phase.LOCKED").data(), txt_rect, DT_LEFT); break;
1140  case Ship::LAUNCH: DrawMFDText(t++, Game::GetText("MFD.phase.LAUNCH").data(), txt_rect, DT_LEFT); break;
1141  case Ship::TAKEOFF: DrawMFDText(t++, Game::GetText("MFD.phase.TAKEOFF").data(), txt_rect, DT_LEFT); break;
1142  case Ship::ACTIVE: DrawMFDText(t++, Game::GetText("MFD.phase.ACTIVE").data(), txt_rect, DT_LEFT); break;
1143  case Ship::APPROACH: DrawMFDText(t++, Game::GetText("MFD.phase.APPROACH").data(), txt_rect, DT_LEFT); break;
1144  case Ship::RECOVERY: DrawMFDText(t++, Game::GetText("MFD.phase.RECOVERY").data(), txt_rect, DT_LEFT); break;
1145  case Ship::DOCKING: DrawMFDText(t++, Game::GetText("MFD.phase.DOCKING").data(), txt_rect, DT_LEFT); break;
1146  }
1147  }
1148 }
1149 
1150 void
1152 {
1153  if (lines < 10) lines++;
1154 
1155  Rect status_rect(rect.x, rect.y, rect.w, 12);
1156  int row = 0;
1157  char txt[32];
1158 
1159  if (ship) {
1160  if (status_rect.y > 320 && !ship->IsStarship())
1161  status_rect.y += 32;
1162 
1163  Drive* drive = ship->GetDrive();
1164  if (drive) {
1165  DrawMFDText(row++, Game::GetText("MFD.status.THRUST").data(), status_rect, DT_LEFT);
1166  DrawGauge(status_rect.x+70, status_rect.y, (int) ship->Throttle());
1167  status_rect.y += 10;
1168  }
1169 
1170  if (lines <= 1) return;
1171 
1172  if (ship->Reactors().size() > 0) {
1173  PowerSource* reactor = ship->Reactors()[0];
1174  if (reactor) {
1175  DrawMFDText(row++, Game::GetText("MFD.status.FUEL").data(), status_rect, DT_LEFT);
1176  DrawGauge(status_rect.x+70, status_rect.y, reactor->Charge());
1177  status_rect.y += 10;
1178  }
1179  }
1180 
1181  if (lines <= 2) return;
1182 
1183  QuantumDrive* quantum_drive = ship->GetQuantumDrive();
1184  if (quantum_drive) {
1185  DrawMFDText(row++, Game::GetText("MFD.status.QUANTUM").data(), status_rect, DT_LEFT);
1186  DrawGauge(status_rect.x+70, status_rect.y, (int) quantum_drive->Charge());
1187  status_rect.y += 10;
1188  }
1189 
1190  if (lines <= 3) return;
1191 
1192  double hull = ship->Integrity() / ship->Design()->integrity * 100;
1193  int hull_status = System::CRITICAL;
1194 
1195  if (hull > 66)
1196  hull_status = System::NOMINAL;
1197  else if (hull > 33)
1198  hull_status = System::DEGRADED;
1199 
1200  DrawMFDText(row++, Game::GetText("MFD.status.HULL").data(), status_rect, DT_LEFT);
1201  DrawGauge(status_rect.x+70, status_rect.y, (int) hull);
1202  status_rect.y += 10;
1203 
1204  if (lines <= 4) return;
1205 
1206  Shield* shield = ship->GetShield();
1207  if (shield) {
1208  DrawMFDText(row++, Game::GetText("MFD.status.SHIELD").data(), status_rect, DT_LEFT);
1209  DrawGauge(status_rect.x+70, status_rect.y, ship->ShieldStrength());
1210  status_rect.y += 10;
1211  }
1212 
1213  if (lines <= 5) return;
1214 
1215  Weapon* primary = ship->GetPrimary();
1216  if (primary) {
1217  DrawMFDText(row++, Game::GetText("MFD.status.GUNS").data(), status_rect, DT_LEFT);
1218  DrawGauge(status_rect.x+70, status_rect.y, primary->Charge());
1219  status_rect.y += 10;
1220  }
1221 
1222  if (lines <= 6) return;
1223 
1224  if (HUDView::IsArcade()) {
1225  for (int i = 0; i < ship->Weapons().size() && i < 4; i++) {
1226  WeaponGroup* w = ship->Weapons().at(i);
1227 
1228  if (w->IsMissile()) {
1229  char ammo[8];
1230 
1231  if (ship->GetSecondaryGroup() == w)
1232  sprintf_s(ammo, "%d *", w->Ammo());
1233  else
1234  sprintf_s(ammo, "%d", w->Ammo());
1235 
1236  DrawMFDText(row++, (const char*) w->GetDesign()->name, status_rect, DT_LEFT);
1237  status_rect.x += 70;
1238  DrawMFDText(row++, ammo, status_rect, DT_LEFT);
1239  status_rect.x -= 70;
1240  status_rect.y += 10;
1241  }
1242  }
1243 
1244  if (ship->GetDecoy()) {
1245  char ammo[8];
1246  sprintf_s(ammo, "%d", ship->GetDecoy()->Ammo());
1247  DrawMFDText(row++, Game::GetText("MFD.status.DECOY").data(), status_rect, DT_LEFT);
1248  status_rect.x += 70;
1249  DrawMFDText(row++, ammo, status_rect, DT_LEFT);
1250  status_rect.x -= 70;
1251  status_rect.y += 10;
1252  }
1253 
1254  if (NetGame::GetInstance()) {
1255  char lives[8];
1256  sprintf_s(lives, "%d", ship->RespawnCount() + 1);
1257  DrawMFDText(row++, Game::GetText("MFD.status.LIVES").data(), status_rect, DT_LEFT);
1258  status_rect.x += 70;
1259  DrawMFDText(row++, lives, status_rect, DT_LEFT);
1260  status_rect.x -= 70;
1261  status_rect.y += 10;
1262  }
1263 
1264  return;
1265  }
1266 
1267  Sensor* sensor = ship->GetSensor();
1268  if (sensor) {
1269  if (ship->GetFlightPhase() != Ship::ACTIVE) {
1270  DrawMFDText(row++, Game::GetText("MFD.status.SENSOR").data(), status_rect, DT_LEFT);
1271  status_rect.x += 70;
1272  DrawMFDText(row++, Game::GetText("MFD.status.OFFLINE").data(), status_rect, DT_LEFT);
1273  status_rect.x -= 70;
1274  status_rect.y += 10;
1275  }
1276 
1277  else {
1278  DrawMFDText(row++, Game::GetText("MFD.status.EMCON").data(), status_rect, DT_LEFT);
1279  status_rect.x += 70;
1280 
1281  sprintf_s(txt, "%s %d", Game::GetText("MFD.status.MODE").data(), ship->GetEMCON());
1282 
1283  if (!sensor->IsPowerOn() || sensor->GetEnergy() == 0) {
1284  if (!Game::Paused() && (Game::RealTime()/1000) & 2)
1285  strcpy_s(txt, Game::GetText("MFD.status.SENSOR-OFF").data());
1286  }
1287 
1288  DrawMFDText(row++, txt, status_rect, DT_LEFT);
1289  status_rect.x -= 70;
1290  status_rect.y += 10;
1291  }
1292  }
1293 
1294  if (lines <= 7) return;
1295 
1296  DrawMFDText(row++, Game::GetText("MFD.status.SYSTEMS").data(), status_rect, DT_LEFT);
1297  status_rect.x += 70;
1298  DrawMFDText(row++, ship->GetDirectorInfo(), status_rect, DT_LEFT);
1299 
1300  if (NetGame::GetInstance()) {
1301  char lives[8];
1302  sprintf_s(lives, "%d", ship->RespawnCount() + 1);
1303  status_rect.x -= 70;
1304  status_rect.y += 10;
1305  DrawMFDText(row++, Game::GetText("MFD.status.LIVES").data(), status_rect, DT_LEFT);
1306  status_rect.x += 70;
1307  DrawMFDText(row++, lives, status_rect, DT_LEFT);
1308  }
1309  }
1310 }
1311 
1312 // +--------------------------------------------------------------------+
1313 
1314 void
1316 {
1317  Color status_color;
1318 
1319  switch (status) {
1320  default:
1321  case System::NOMINAL: status_color = txt_color; break;
1322  case System::DEGRADED: status_color = Color(255,255, 0); break;
1323  case System::CRITICAL: status_color = Color(255, 0, 0); break;
1324  case System::DESTROYED: status_color = Color( 0, 0, 0); break;
1325  }
1326 }
1327 
1328 // +--------------------------------------------------------------------+
1329 
1331 {
1332  return mouse_in;
1333 }
1334 
1335 // +--------------------------------------------------------------------+
1336 
1337 void MFD::DrawMFDText(int index, const char* txt, Rect& txt_rect, int align, int status)
1338 {
1339  if (index >= MFD::TXT_LAST) {
1340  Print("MFD DrawMFDText() invalid mfd_text index %d '%s'\n", index, txt);
1341  }
1342  else {
1343  HUDText& mt = mfd_text[index];
1344  Color mc = mt.color;
1345 
1346  switch (status) {
1347  default:
1348  case System::NOMINAL: mc = txt_color; break;
1349  case System::DEGRADED: mc = Color(255,255, 0); break;
1350  case System::CRITICAL: mc = Color(255, 0, 0); break;
1351  case System::DESTROYED: mc = Color( 0, 0, 0); break;
1352  }
1353 
1354  char txt_buf[256];
1355  int n = strlen(txt);
1356 
1357  if (n > 250) n = 250;
1358  int i;
1359 
1360  for (i = 0; i < n; i++) {
1361  if (islower(txt[i]))
1362  txt_buf[i] = toupper(txt[i]);
1363  else
1364  txt_buf[i] = txt[i];
1365  }
1366 
1367  txt_buf[i] = 0;
1368 
1369 
1370  if (cockpit_hud_texture) {
1371  Rect hud_rect(txt_rect);
1372 
1373  hud_rect.x = txt_rect.x + this->index * 128 - this->rect.x;
1374  hud_rect.y = txt_rect.y + 256 - this->rect.y;
1375 
1376  mt.font->SetColor(mc);
1377  mt.font->DrawText(txt_buf, 0, hud_rect, align | DT_SINGLELINE, cockpit_hud_texture);
1378  mt.rect = rect;
1379  mt.hidden = false;
1380  }
1381  else {
1382  if (txt_rect.Contains(Mouse::X(), Mouse::Y()))
1383  mc = Color::White;
1384 
1385  mt.font->SetColor(mc);
1386  mt.font->DrawText(txt_buf, 0, txt_rect, align | DT_SINGLELINE);
1387  mt.rect = rect;
1388  mt.hidden = false;
1389  }
1390 
1391  }
1392 }
1393 
1394 void MFD::HideMFDText(int index)
1395 {
1396  if (index >= MFD::TXT_LAST)
1397  Print("MFD HideMFDText() invalid mfd_text index %d\n", index);
1398  else
1399  mfd_text[index].hidden = true;
1400 }
1401 
1402 
1403 
1404