Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
TacticalView.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: TacticalView.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  View class for Tactical Data Readout HUD Overlay
13 */
14 
15 #include "MemDebug.h"
16 #include "TacticalView.h"
17 #include "QuantumView.h"
18 #include "RadioView.h"
19 #include "RadioMessage.h"
20 #include "RadioTraffic.h"
21 #include "HUDSounds.h"
22 #include "HUDView.h"
23 #include "WepView.h"
24 #include "CameraDirector.h"
25 #include "Ship.h"
26 #include "ShipCtrl.h"
27 #include "ShipDesign.h"
28 #include "QuantumDrive.h"
29 #include "Farcaster.h"
30 #include "Instruction.h"
31 #include "Element.h"
32 #include "Contact.h"
33 #include "Sim.h"
34 #include "Starshatter.h"
35 #include "GameScreen.h"
36 #include "MenuView.h"
37 
38 #include "Projector.h"
39 #include "Color.h"
40 #include "Window.h"
41 #include "Video.h"
42 #include "DataLoader.h"
43 #include "Scene.h"
44 #include "FontMgr.h"
45 #include "Keyboard.h"
46 #include "Mouse.h"
47 #include "MouseController.h"
48 #include "Menu.h"
49 #include "Game.h"
50 #include "FormatUtil.h"
51 
52 static Color hud_color = Color::Black;
53 static Color txt_color = Color::Black;
54 
55 // +--------------------------------------------------------------------+
56 
58 
59 // +--------------------------------------------------------------------+
60 
62 : View(c), gamescreen(parent), ship(0), camview(0), projector(0),
63 mouse_down(0), right_down(0), shift_down(0),
64 show_move(0), show_action(0), active_menu(0), menu_view(0),
65 msg_ship(0), base_alt(0), move_alt(0)
66 {
67  tac_view = this;
68  sim = Sim::GetSim();
69 
70  width = window->Width();
71  height = window->Height();
72  xcenter = (width / 2.0) - 0.5;
73  ycenter = (height / 2.0) + 0.5;
74  font = FontMgr::Find("HUD");
75 
77 
78  mouse_start.x = 0;
79  mouse_start.y = 0;
80  mouse_action.x = 0;
81  mouse_action.y = 0;
82 
83  menu_view = new(__FILE__,__LINE__) MenuView(window);
84 }
85 
87 {
88  delete menu_view;
89  tac_view = 0;
90 }
91 
92 void
94 {
95  width = window->Width();
96  height = window->Height();
97  xcenter = (width / 2.0) - 0.5;
98  ycenter = (height / 2.0) + 0.5;
99 
100  if (menu_view)
102 }
103 
104 // +--------------------------------------------------------------------+
105 
106 bool
108 {
109  if (obj == ship) {
110  ship = 0;
111  }
112 
113  if (obj == msg_ship) {
114  msg_ship = 0;
115  }
116 
117  return SimObserver::Update(obj);
118 }
119 
120 const char*
122 {
123  return "TacticalView";
124 }
125 
126 void
128 {
129  projector = p;
130 }
131 
132 // +--------------------------------------------------------------------+
133 
134 void
136 {
137  sim = Sim::GetSim();
138 
139  if (sim) {
140  bool rebuild = false;
141 
142  if (ship != sim->GetPlayerShip()) {
143  ship = sim->GetPlayerShip();
144 
145  if (ship) {
146  if (ship->Life() == 0 || ship->IsDying() || ship->IsDead()) {
147  ship = 0;
148  }
149  else {
150  Observe(ship);
151  }
152  }
153 
154  rebuild = true;
155  }
156 
157  if (ship) {
158  if (current_sector != ship->GetRegion()->Name())
159  rebuild = true;
160 
161  if (rebuild) {
162  BuildMenu();
164  }
165  }
166  }
167 
168  if (!ship || ship->InTransition())
169  return;
170 
171  DrawMouseRect();
172 
173  if (sim) {
175 
176  if (sel.size()) {
177  while (++sel) {
178  Ship* selection = sel.value();
179 
180  // draw selection rect on selected ship:
181  if (selection && selection->Rep())
182  DrawSelection(selection);
183  }
184 
187 
188  if ((!rv || !rv->IsMenuShown()) && (!qv || !qv->IsMenuShown())) {
189  sel.reset();
190 
191  if (sel.size() == 1) {
192  DrawSelectionInfo(sel.next());
193  }
194  else {
195  DrawSelectionList(sel);
196  }
197  }
198  }
199  }
200 
201  DrawMenu();
202 
203  if (show_move) {
204  Mouse::Show(false);
205  DrawMove();
206  }
207  else if (show_action) {
208  Mouse::Show(false);
209  DrawAction();
210  }
211 }
212 
213 // +--------------------------------------------------------------------+
214 
215 void
217 {
218  HUDView* hud = HUDView::GetInstance();
219  if (hud) {
220  if (hud_color != hud->GetTextColor()) {
221  hud_color = hud->GetTextColor();
222  SetColor(hud_color);
223  }
224  }
225 }
226 
227 // +--------------------------------------------------------------------+
228 
229 void
231 {
232  HUDView* hud = HUDView::GetInstance();
233 
234  if (hud) {
235  hud_color = hud->GetHUDColor();
236  txt_color = hud->GetTextColor();
237  }
238  else {
239  hud_color = c;
240  txt_color = c;
241  }
242 }
243 
244 // +--------------------------------------------------------------------+
245 
246 void
248 {
249  if (mouse_rect.w > 0 && mouse_rect.h > 0) {
250  Color c = hud_color * 0.66;
251 
252  if (shift_down)
253  c = Color::Orange;
254 
256  }
257 }
258 
259 // +--------------------------------------------------------------------+
260 
261 void
263 {
264  if (!seln)
265  return;
266 
267  Graphic* g = seln->Rep();
268  Rect r = g->ScreenRect();
269 
270  Point mark_pt = seln->Location();
271 
272  projector->Transform(mark_pt);
273 
274  // clip:
275  if (mark_pt.z > 1.0) {
276  projector->Project(mark_pt);
277 
278  int x = (int) mark_pt.x;
279  int y = r.y;
280 
281  if (y >= 2000)
282  y = (int) mark_pt.y;
283 
284  if (x > 4 && x < width-4 &&
285  y > 4 && y < height-4) {
286 
287  const int BAR_LENGTH = 40;
288 
289  // life bars:
290  int sx = x - BAR_LENGTH/2;
291  int sy = y - 8;
292 
293  double hull_strength = seln->HullStrength() / 100.0;
294 
295  int hw = (int) (BAR_LENGTH * hull_strength);
296  int sw = (int) (BAR_LENGTH * (seln->ShieldStrength() / 100.0));
297 
298  if (hw < 0) hw = 0;
299  if (sw < 0) sw = 0;
300 
302 
303  if (hull_strength < 0.30) s = System::CRITICAL;
304  else if (hull_strength < 0.60) s = System::DEGRADED;
305 
307  Color sc = hud_color;
308 
309  window->FillRect(sx, sy, sx+hw, sy+1, hc);
310  window->FillRect(sx, sy+3, sx+sw, sy+4, sc);
311  }
312  }
313 }
314 
315 // +--------------------------------------------------------------------+
316 
317 void
319 {
320  if (!ship || !seln) return;
321 
322  Rect label_rect(width-140, 10, 90, 12);
323  Rect info_rect(width-100, 10, 90, 12);
324 
325  if (width >= 800) {
326  label_rect.x -= 20;
327  info_rect.x -= 20;
328  info_rect.w += 20;
329  }
330 
331  static char name[64];
332  static char design[64];
333  static char shield[32];
334  static char hull[32];
335  static char range[32];
336  static char heading[32];
337  static char speed[32];
338  static char orders[64];
339  static char psv[32];
340  static char act[32];
341 
342  int show_labels = width > 640;
343  int full_info = true;
344  int shield_val = seln->ShieldStrength();
345  int hull_val = seln->HullStrength();
346 
347  if (shield_val < 0) shield_val = 0;
348  if (hull_val < 0) hull_val = 0;
349 
350  sprintf_s(name, "%s", seln->Name());
351 
352  if (show_labels) {
353  sprintf_s(shield, "%s %03d", Game::GetText("HUDView.symbol.shield").data(), shield_val);
354  sprintf_s(hull, "%s %03d", Game::GetText("HUDView.symbol.hull").data(), hull_val);
355  }
356  else {
357  sprintf_s(shield, "%03d", shield_val);
358  sprintf_s(hull, "%03d", hull_val);
359  }
360 
361  FormatNumberExp(range, Point(seln->Location()-ship->Location()).length()/1000);
362  strcat_s(range, " km");
363  sprintf_s(heading, "%03d %s", (int) (seln->CompassHeading() / DEGREES), Game::GetText("HUDView.symbol.degrees").data());
364 
365  double ss = seln->Velocity().length();
366  if (seln->Velocity() * seln->Heading() < 0)
367  ss = -ss;
368 
369  FormatNumberExp(speed, ss);
370  strcat_s(speed, " m/s");
371 
372  Contact* contact = 0;
373 
374  // always recognize ownside:
375  if (seln->GetIFF() != ship->GetIFF()) {
377  while (++c) {
378  if (c->GetShip() == seln) {
379  contact = c.value();
380  if (c->GetIFF(ship) > seln->GetIFF()) {
381  sprintf_s(name, "%s %04d", Game::GetText("TacView.contact").data(), seln->GetContactID());
382  full_info = false;
383  }
384 
385  break;
386  }
387  }
388  }
389 
390  if (show_labels) {
391  font->SetColor(txt_color);
392  font->SetAlpha(1);
393 
394  font->DrawText(Game::GetText("TacView.name"), 5, label_rect, DT_LEFT);
395  label_rect.y += 10;
396  font->DrawText(Game::GetText("TacView.type"), 5, label_rect, DT_LEFT);
397  label_rect.y += 10;
398 
399  if (full_info) {
400  font->DrawText(Game::GetText("TacView.shield"), 5, label_rect, DT_LEFT);
401  label_rect.y += 10;
402  font->DrawText(Game::GetText("TacView.hull"), 5, label_rect, DT_LEFT);
403  label_rect.y += 10;
404  }
405 
406  font->DrawText(Game::GetText("TacView.range"), 4, label_rect, DT_LEFT);
407  label_rect.y += 10;
408 
409  if (full_info) {
410  font->DrawText(Game::GetText("TacView.speed"), 4, label_rect, DT_LEFT);
411  label_rect.y += 10;
412  font->DrawText(Game::GetText("TacView.heading"), 4, label_rect, DT_LEFT);
413  label_rect.y += 10;
414  }
415  else {
416  font->DrawText(Game::GetText("TacView.passive"), 4, label_rect, DT_LEFT);
417  label_rect.y += 10;
418  font->DrawText(Game::GetText("TacView.active"), 4, label_rect, DT_LEFT);
419  label_rect.y += 10;
420  }
421  }
422 
423  font->DrawText(name, 0, info_rect, DT_LEFT);
424  info_rect.y += 10;
425 
426  if (full_info) {
427  sprintf_s(design, "%s %s", seln->Abbreviation(), seln->Design()->display_name);
428  font->DrawText(design, 0, info_rect, DT_LEFT);
429  info_rect.y += 10;
430  }
431  else {
432  if (seln->IsStarship())
433  font->DrawText(Game::GetText("TacView.starship"), 8, info_rect, DT_LEFT);
434  else
435  font->DrawText(Game::GetText("TacView.fighter"), 7, info_rect, DT_LEFT);
436 
437  info_rect.y += 10;
438  }
439 
440  if (full_info) {
441  font->DrawText(shield, 0, info_rect, DT_LEFT);
442  info_rect.y += 10;
443 
444  font->DrawText(hull, 0, info_rect, DT_LEFT);
445  info_rect.y += 10;
446  }
447 
448  font->DrawText(range, 0, info_rect, DT_LEFT);
449  info_rect.y += 10;
450 
451  if (full_info) {
452  font->DrawText(speed, 0, info_rect, DT_LEFT);
453  info_rect.y += 10;
454 
455  font->DrawText(heading, 0, info_rect, DT_LEFT);
456  info_rect.y += 10;
457 
458  if (seln->GetIFF() == ship->GetIFF()) {
459  Instruction* instr = seln->GetRadioOrders();
460  if (instr && instr->Action()) {
461  strcpy_s(orders, RadioMessage::ActionName(instr->Action()));
462 
463  if (instr->Action() == RadioMessage::QUANTUM_TO) {
464  strcat_s(orders, " ");
465  strcat_s(orders, instr->RegionName());
466  }
467  }
468  else {
469  *orders = 0;
470  }
471 
472  if (*orders) {
473  if (show_labels) {
474  font->DrawText(Game::GetText("TacView.orders"), 5, label_rect, DT_LEFT);
475  label_rect.y += 10;
476  }
477 
478  font->DrawText(orders, 0, info_rect, DT_LEFT);
479  info_rect.y += 10;
480  }
481  }
482  }
483  else {
484  sprintf_s(psv, "%03d", (int) (contact->PasReturn() * 100.0));
485  sprintf_s(act, "%03d", (int) (contact->ActReturn() * 100.0));
486 
487  if (contact->Threat(ship))
488  strcat_s(psv, " !");
489 
490  font->DrawText(psv, 0, info_rect, DT_LEFT);
491  info_rect.y += 10;
492  font->DrawText(act, 0, info_rect, DT_LEFT);
493  info_rect.y += 10;
494  }
495 
496  /*** XXX DEBUG
497 font->DrawText(seln->GetDirectorInfo(), 0, info_rect, DT_LEFT);
498 info_rect.y += 10;
499 /***/
500 }
501 
502 // +--------------------------------------------------------------------+
503 
504 void
506 {
507  int index = 0;
508  Rect info_rect(width-100, 10, 90, 12);
509 
510  while (++seln) {
511  char name[64];
512  sprintf_s(name, "%s", seln->Name());
513 
514  // always recognize ownside:
515  if (seln->GetIFF() != ship->GetIFF()) {
517  while (++c) {
518  if (c->GetShip() == seln.value()) {
519  if (c->GetIFF(ship) > seln->GetIFF()) {
520  sprintf_s(name, "%s %04d", Game::GetText("TacView.contact").data(), seln->GetContactID());
521  }
522 
523  break;
524  }
525  }
526  }
527 
528  font->DrawText(name, 0, info_rect, DT_LEFT);
529  info_rect.y += 10;
530  index++;
531 
532  if (index >= 10)
533  break;
534  }
535 }
536 
537 // +--------------------------------------------------------------------+
538 
539 void
541 {
542  static DWORD rbutton_latch = 0;
543 
545 
546  if (stars->InCutscene())
547  return;
548 
549  if (Mouse::RButton()) {
551  if (!right_down && (!mouse_con || !mouse_con->Active())) {
552  rbutton_latch = Game::RealTime();
553  right_down = true;
554  }
555  }
556  else {
557  if (sim && right_down && (Game::RealTime() - rbutton_latch < 250)) {
558  Ship* seln = WillSelectAt(Mouse::X(), Mouse::Y());
559 
560  if (seln && sim->IsSelected(seln) &&
561  seln->GetIFF() == ship->GetIFF() &&
562  ship->GetElement()->CanCommand(seln->GetElement())) {
563 
564  msg_ship = seln;
565  Observe(msg_ship);
566  }
567 
568  else if (ship && seln == ship &&
569  (!ship->GetDirector() ||
571 
572  msg_ship = seln;
573  }
574 
575  else {
576  msg_ship = 0;
577  }
578  }
579 
580  right_down = false;
581  }
582 
583  if (menu_view)
585 
587 
588  if (!mouse_con || !mouse_con->Active()) {
589  if (Mouse::LButton()) {
590  if (!mouse_down) {
591  mouse_start.x = Mouse::X();
592  mouse_start.y = Mouse::Y();
593 
594  shift_down = Keyboard::KeyDown(VK_SHIFT);
595  }
596 
597  else {
598  if (Mouse::X() < mouse_start.x) {
599  mouse_rect.x = Mouse::X();
601  }
602  else {
605  }
606 
607  if (Mouse::Y() < mouse_start.y) {
608  mouse_rect.y = Mouse::Y();
610  }
611  else {
614  }
615 
616  // don't draw seln rectangle while zooming:
617  if (Mouse::RButton() || show_move || show_action) {
618  mouse_rect.w = 0;
619  mouse_rect.h = 0;
620  }
621 
622  else {
624  }
625  }
626 
627  mouse_down = true;
628  }
629 
630  else {
631  if (mouse_down) {
632  int mouse_x = Mouse::X();
633  int mouse_y = Mouse::Y();
634 
635  if (menu_view && menu_view->GetAction()) {
637  Mouse::Show(true);
638  }
639  else if (show_move) {
640  SendMove();
641  show_move = false;
642  Mouse::Show(true);
643  }
644  else if (show_action) {
645  SendAction();
646  show_action = false;
647  Mouse::Show(true);
648  }
649  else {
651  int dx = (int) fabs((double) (mouse_x - mouse_start.x));
652  int dy = (int) fabs((double) (mouse_y - mouse_start.y));
653 
654  static DWORD click_time = 0;
655 
656  if (dx < 3 && dy < 3) {
657  bool hit = SelectAt(mouse_x, mouse_y);
658 
659  if (ship->IsStarship() && Game::RealTime() - click_time < 350)
660  SetHelm(hit);
661 
662  click_time = Game::RealTime();
663  }
664  }
665  }
666 
667  mouse_rect = Rect();
668  mouse_down = false;
669  }
670  }
671  }
672 
673  if (show_action && !mouse_down && !right_down) {
674  mouse_action.x = Mouse::X();
675  mouse_action.y = Mouse::Y();
676  }
677 }
678 
679 // +--------------------------------------------------------------------+
680 
681 bool
683 {
684  if (!ship) return false;
685 
686  Ship* selection = WillSelectAt(x,y);
687 
688  if (selection && shift_down)
689  ship->SetTarget(selection);
690 
691  else if (sim && selection)
692  sim->SetSelection(selection);
693 
694  return selection != 0;
695 }
696 
697 // +--------------------------------------------------------------------+
698 
699 bool
701 {
702  bool result = false;
703 
704  if (!ship || !sim) return result;
705 
706  if (rect.w > 8 || rect.h > 8)
707  sim->ClearSelection();
708 
709  // check distance to each contact:
710  List<Contact>& contact_list = ship->ContactList();
711 
712  for (int i = 0; i < ship->NumContacts(); i++) {
713  Ship* test = contact_list[i]->GetShip();
714 
715  if (test && test != ship) {
716 
717  Point test_loc = test->Location();
718  projector->Transform(test_loc);
719 
720  if (test_loc.z > 1) {
721  projector->Project(test_loc);
722 
723  if (rect.Contains((int) test_loc.x, (int) test_loc.y)) {
724  // shift-select targets:
725  if (shift_down) {
726  if (test->GetIFF() == 0 || test->GetIFF() == ship->GetIFF())
727  continue;
728 
729  ship->SetTarget(test);
730  result = true;
731  }
732  else {
733  sim->AddSelection(test);
734  result = true;
735  }
736  }
737  }
738  }
739  }
740 
741  // select self only in orbit cam
743  Point test_loc = ship->Location();
744  projector->Transform(test_loc);
745 
746  if (test_loc.z > 1) {
747  projector->Project(test_loc);
748 
749  if (rect.Contains((int) test_loc.x, (int) test_loc.y)) {
751  result = true;
752  }
753  }
754  }
755 
756  return result;
757 }
758 
759 // +--------------------------------------------------------------------+
760 
761 Ship*
763 {
764  Ship* selection = 0;
765 
766  if (ship) {
767  // check distance to each contact:
768  List<Contact>& contact_list = ship->ContactList();
769 
770  for (int i = 0; i < ship->NumContacts(); i++) {
771  Ship* test = contact_list[i]->GetShip();
772 
773  if (test) {
774  // shift-select targets:
775  if (shift_down) {
776  if (test->GetIFF() == 0 || test->GetIFF() == ship->GetIFF())
777  continue;
778  }
779 
780  Graphic* g = test->Rep();
781  if (g) {
782  Rect r = g->ScreenRect();
783 
784  if (r.x == 2000 && r.y == 2000 && r.w == 0 && r.h == 0) {
785  if (projector) {
786  Point loc = test->Location();
787  projector->Transform(loc);
788  projector->Project(loc);
789 
790  r.x = (int) loc.x;
791  r.y = (int) loc.y;
792  }
793  }
794 
795  if (r.w < 20 || r.h < 20)
796  r.Inflate(20,20);
797  else
798  r.Inflate(10,10);
799 
800  if (r.Contains(x,y)) {
801  selection = test;
802  break;
803  }
804  }
805  }
806  }
807 
808  if (!selection && !shift_down) {
809  Graphic* g = ship->Rep();
810  if (g) {
811  Rect r = g->ScreenRect();
812 
813  if (r.Contains(x,y)) {
814  selection = ship;
815  }
816  }
817  }
818  }
819 
821  selection = 0;
822 
823  return selection;
824 }
825 
826 // +--------------------------------------------------------------------+
827 
828 void
829 TacticalView::SetHelm(bool approach)
830 {
831  Point delta;
832 
833  // double-click on ship: set helm to approach
834  if (sim && approach) {
835  ListIter<Ship> iter = sim->GetSelection();
836  ++iter;
837  Ship* selection = iter.value();
838 
839  if (selection != ship) {
840  delta = selection->Location() - ship->Location();
841  delta.Normalize();
842  }
843  }
844 
845  // double-click on space: set helm in direction
846  if (delta.length() < 1) {
847  int mx = Mouse::X();
848  int my = Mouse::Y();
849 
850  if (projector) {
851  double focal_dist = width / tan(projector->XAngle());
852 
853  delta = projector->vpn() * focal_dist +
854  projector->vup() * -1 * (my-height/2) +
855  projector->vrt() * (mx-width/2);
856 
857  delta.Normalize();
858  }
859 
860  else {
861  return;
862  }
863  }
864 
865  double az = atan2(fabs(delta.x), delta.z);
866  double el = asin(delta.y);
867 
868  if (delta.x < 0)
869  az *= -1;
870 
871  az += PI;
872 
873  if (az >= 2*PI)
874  az -= 2*PI;
875 
876  ship->SetHelmHeading(az);
877  ship->SetHelmPitch(el);
878 }
879 
880 // +====================================================================+
881 //
882 // TACTICAL COMMUNICATIONS MENU:
883 //
884 
885 static Menu* main_menu = 0;
886 static Menu* view_menu = 0;
887 static Menu* emcon_menu = 0;
888 
889 static Menu* fighter_menu = 0;
890 static Menu* starship_menu = 0;
891 static Menu* action_menu = 0;
892 static Menu* formation_menu = 0;
893 static Menu* sensors_menu = 0;
894 static Menu* quantum_menu = 0;
895 static Menu* farcast_menu = 0;
896 
897 static Element* dst_elem = 0;
898 
899 enum VIEW_MENU {
900  VIEW_FORWARD = 1000,
910 };
911 
912 const int QUANTUM = 2000;
913 const int FARCAST = 2001;
914 
915 void
917 {
918  static int initialized = 0;
919  if (initialized) return;
920 
921  view_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.view"));
922  view_menu->AddItem(Game::GetText("TacView.item.forward"), VIEW_FORWARD);
923  view_menu->AddItem(Game::GetText("TacView.item.chase"), VIEW_CHASE);
924  view_menu->AddItem(Game::GetText("TacView.item.orbit"), VIEW_ORBIT);
925  view_menu->AddItem(Game::GetText("TacView.item.padlock"), VIEW_PADLOCK);
926 
927  emcon_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.emcon"));
928 
929  quantum_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.quantum"));
930  farcast_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.farcast"));
931 
932  main_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.main"));
933 
934  action_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.action"));
935  action_menu->AddItem(Game::GetText("TacView.item.engage"), RadioMessage::ATTACK);
936  action_menu->AddItem(Game::GetText("TacView.item.bracket"), RadioMessage::BRACKET);
937  action_menu->AddItem(Game::GetText("TacView.item.escort"), RadioMessage::ESCORT);
938  action_menu->AddItem(Game::GetText("TacView.item.identify"), RadioMessage::IDENTIFY);
939  action_menu->AddItem(Game::GetText("TacView.item.hold"), RadioMessage::WEP_HOLD);
940 
941  formation_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.formation"));
942  formation_menu->AddItem(Game::GetText("TacView.item.diamond"), RadioMessage::GO_DIAMOND);
943  formation_menu->AddItem(Game::GetText("TacView.item.spread"), RadioMessage::GO_SPREAD);
944  formation_menu->AddItem(Game::GetText("TacView.item.box"), RadioMessage::GO_BOX);
945  formation_menu->AddItem(Game::GetText("TacView.item.trail"), RadioMessage::GO_TRAIL);
946 
947  sensors_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.emcon"));
948  sensors_menu->AddItem(Game::GetText("TacView.item.emcon-1"), RadioMessage::GO_EMCON1);
949  sensors_menu->AddItem(Game::GetText("TacView.item.emcon-2"), RadioMessage::GO_EMCON2);
950  sensors_menu->AddItem(Game::GetText("TacView.item.emcon-3"), RadioMessage::GO_EMCON3);
951  sensors_menu->AddItem(Game::GetText("TacView.item.probe"), RadioMessage::LAUNCH_PROBE);
952 
953  fighter_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.context"));
954  fighter_menu->AddMenu(Game::GetText("TacView.item.action"), action_menu);
955  fighter_menu->AddMenu(Game::GetText("TacView.item.formation"), formation_menu);
956  fighter_menu->AddMenu(Game::GetText("TacView.item.sensors"), sensors_menu);
957  fighter_menu->AddItem(Game::GetText("TacView.item.patrol"), RadioMessage::MOVE_PATROL);
958  fighter_menu->AddItem(Game::GetText("TacView.item.cancel"), RadioMessage::RESUME_MISSION);
959  fighter_menu->AddItem("", 0);
960  fighter_menu->AddItem(Game::GetText("TacView.item.rtb"), RadioMessage::RTB);
961  fighter_menu->AddItem(Game::GetText("TacView.item.dock"), RadioMessage::DOCK_WITH);
962  fighter_menu->AddMenu(Game::GetText("TacView.item.farcast"), farcast_menu);
963 
964  starship_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.context"));
965  starship_menu->AddMenu(Game::GetText("TacView.item.action"), action_menu);
966  starship_menu->AddMenu(Game::GetText("TacView.item.sensors"), sensors_menu);
967  starship_menu->AddItem(Game::GetText("TacView.item.patrol"), RadioMessage::MOVE_PATROL);
968  starship_menu->AddItem(Game::GetText("TacView.item.cancel"), RadioMessage::RESUME_MISSION);
969  starship_menu->AddItem("", 0);
970  starship_menu->AddMenu(Game::GetText("TacView.item.quantum"), quantum_menu);
971  starship_menu->AddMenu(Game::GetText("TacView.item.farcast"), farcast_menu);
972 
973  initialized = 1;
974 }
975 
976 void
978 {
979  delete view_menu;
980  delete emcon_menu;
981  delete main_menu;
982  delete fighter_menu;
983  delete starship_menu;
984  delete action_menu;
985  delete formation_menu;
986  delete sensors_menu;
987  delete quantum_menu;
988  delete farcast_menu;
989 }
990 
991 // +--------------------------------------------------------------------+
992 
993 void
995 {
997 
998  switch (action) {
1000  show_move = true;
1001  base_alt = 0;
1002  move_alt = 0;
1003 
1004  if (msg_ship) base_alt = msg_ship->Location().y;
1005  break;
1006 
1007  case RadioMessage::ATTACK:
1008  case RadioMessage::BRACKET:
1009  case RadioMessage::ESCORT:
1012  show_action = action;
1013  break;
1014 
1017  case RadioMessage::RTB:
1020  case RadioMessage::GO_BOX:
1026  if (msg_ship) {
1027  Element* elem = msg_ship->GetElement();
1028  RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, action);
1029  if (msg)
1031  }
1032  else if (ship) {
1033  if (action == RadioMessage::GO_EMCON1)
1034  ship->SetEMCON(1);
1035  else if (action == RadioMessage::GO_EMCON2)
1036  ship->SetEMCON(2);
1037  else if (action == RadioMessage::GO_EMCON3)
1038  ship->SetEMCON(3);
1039  else if (action == RadioMessage::LAUNCH_PROBE)
1040  ship->LaunchProbe();
1041  }
1042  break;
1043 
1045  case VIEW_CHASE: stars->PlayerCam(CameraDirector::MODE_CHASE); break;
1046  case VIEW_PADLOCK: stars->PlayerCam(CameraDirector::MODE_TARGET); break;
1047  case VIEW_ORBIT: stars->PlayerCam(CameraDirector::MODE_ORBIT); break;
1048 
1049  case VIEW_NAV: gamescreen->ShowNavDlg(); break;
1050  case VIEW_WEP: gamescreen->ShowWeaponsOverlay(); break;
1051  case VIEW_ENG: gamescreen->ShowEngDlg(); break;
1052  case VIEW_INS: HUDView::GetInstance()->CycleHUDInst(); break;
1053  case VIEW_FLT: gamescreen->ShowFltDlg(); break;
1054 
1055  case VIEW_CMD: if (ship && ship->IsStarship()) {
1056  ship->CommandMode();
1057  }
1058  break;
1059 
1060  case QUANTUM: if (sim) {
1061  Ship* s = msg_ship;
1062 
1063  if (!s)
1064  s = ship;
1065 
1066  if (s && s->GetQuantumDrive()) {
1067  QuantumDrive* quantum = s->GetQuantumDrive();
1068  if (quantum) {
1069  MenuItem* menu_item = menu_view->GetMenuItem();
1070  Text rgn_name = menu_item->GetText();
1071  SimRegion* rgn = sim->FindRegion(rgn_name);
1072 
1073  if (rgn) {
1074  if (s == ship) {
1075  quantum->SetDestination(rgn, Point(0,0,0));
1076  quantum->Engage();
1077  }
1078 
1079  else {
1080  Element* elem = msg_ship->GetElement();
1081  RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, RadioMessage::QUANTUM_TO);
1082  if (msg) {
1083  msg->SetInfo(rgn_name);
1085  }
1086  }
1087  }
1088  }
1089  }
1090  }
1091  break;
1092 
1093  case FARCAST: if (sim && msg_ship) {
1094  MenuItem* menu_item = menu_view->GetMenuItem();
1095  Text rgn_name = menu_item->GetText();
1096  SimRegion* rgn = sim->FindRegion(rgn_name);
1097 
1098  if (rgn) {
1099  Element* elem = msg_ship->GetElement();
1100  RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, RadioMessage::FARCAST_TO);
1101  if (msg) {
1102  msg->SetInfo(rgn_name);
1104  }
1105  }
1106  }
1107  break;
1108 
1109  default:
1110  break;
1111  }
1112 }
1113 
1114 // +--------------------------------------------------------------------+
1115 
1116 void
1118 {
1119  main_menu->ClearItems();
1120  quantum_menu->ClearItems();
1121  farcast_menu->ClearItems();
1122  emcon_menu->ClearItems();
1123 
1124  if (!ship)
1125  return;
1126 
1127  // prepare quantum and farcast menus:
1129  while (++iter) {
1130  SimRegion* rgn = iter.value();
1131  if (rgn != ship->GetRegion() && rgn->Type() != SimRegion::AIR_SPACE)
1132  quantum_menu->AddItem(rgn->Name(), QUANTUM);
1133  }
1134 
1135  if (ship->GetRegion()) {
1136  ListIter<Ship> iter = ship->GetRegion()->Ships();
1137  while (++iter) {
1138  Ship* s = iter.value();
1139 
1140  if (s && s->GetFarcaster()) {
1141  Farcaster* farcaster = s->GetFarcaster();
1142 
1143  // ensure that the farcaster is connected:
1144  farcaster->ExecFrame(0);
1145 
1146  // now find the destination
1147  const Ship* dest = farcaster->GetDest();
1148 
1149  if (dest && dest->GetRegion()) {
1150  SimRegion* rgn = dest->GetRegion();
1151  farcast_menu->AddItem(rgn->Name(), FARCAST);
1152  }
1153  }
1154  }
1155  }
1156 
1157  // build the main menu:
1158  main_menu->AddMenu(Game::GetText("TacView.item.camera"), view_menu);
1159  main_menu->AddItem("", 0);
1160  main_menu->AddItem(Game::GetText("TacView.item.instructions"), VIEW_INS);
1161  main_menu->AddItem(Game::GetText("TacView.item.navigation"), VIEW_NAV);
1162 
1163  if (ship->Design()->repair_screen)
1164  main_menu->AddItem(Game::GetText("TacView.item.engineering"), VIEW_ENG);
1165 
1166  if (ship->Design()->wep_screen)
1167  main_menu->AddItem(Game::GetText("TacView.item.weapons"), VIEW_WEP);
1168 
1169  if (ship->NumFlightDecks() > 0)
1170  main_menu->AddItem(Game::GetText("TacView.item.flight"), VIEW_FLT);
1171 
1172  emcon_menu->AddItem(Game::GetText("TacView.item.emcon-1"), RadioMessage::GO_EMCON1);
1173  emcon_menu->AddItem(Game::GetText("TacView.item.emcon-2"), RadioMessage::GO_EMCON2);
1174  emcon_menu->AddItem(Game::GetText("TacView.item.emcon-3"), RadioMessage::GO_EMCON3);
1175 
1176  if (ship->GetProbeLauncher())
1177  emcon_menu->AddItem(Game::GetText("TacView.item.probe"), RadioMessage::LAUNCH_PROBE);
1178 
1179  main_menu->AddItem("", 0);
1180  main_menu->AddMenu(Game::GetText("TacView.item.sensors"), emcon_menu);
1181 
1182  if (sim && ship->GetQuantumDrive()) {
1183  main_menu->AddItem("", 0);
1184  main_menu->AddMenu(Game::GetText("TacView.item.quantum"), quantum_menu);
1185  }
1186 
1187  if (ship->IsStarship()) {
1188  main_menu->AddItem("", 0);
1189  main_menu->AddItem(Game::GetText("TacView.item.command"), VIEW_CMD);
1190  }
1191 }
1192 
1193 // +--------------------------------------------------------------------+
1194 
1195 void
1197 {
1198  active_menu = 0;
1199 
1200  if (ship)
1201  active_menu = main_menu;
1202 
1203  if (msg_ship) {
1204  if (msg_ship->IsStarship())
1205  active_menu = starship_menu;
1206  else if (msg_ship->IsDropship())
1207  active_menu = fighter_menu;
1208  }
1209 
1210  if (menu_view) {
1211  menu_view->SetBackColor(hud_color);
1212  menu_view->SetTextColor(txt_color);
1214  menu_view->Refresh();
1215  }
1216 }
1217 
1218 // +--------------------------------------------------------------------+
1219 
1220 bool
1222 {
1223  int mx = Mouse::X();
1224  int my = Mouse::Y();
1225 
1226  if (projector) {
1227  double focal_dist = width / tan(projector->XAngle());
1228  Point focal_vect = projector->vpn() * focal_dist +
1229  projector->vup() * -1 * (my-height/2) +
1230  projector->vrt() * (mx-width/2);
1231 
1232  focal_vect.Normalize();
1233 
1234  if (Keyboard::KeyDown(VK_SHIFT)) {
1235  if (Mouse::RButton())
1236  return true;
1237 
1238  if (fabs(focal_vect.x) > fabs(focal_vect.z)) {
1239  double dx = move_loc.x - projector->Pos().x;
1240  double t = -1 * ((projector->Pos().x - dx) / focal_vect.x);
1241 
1242  if (t > 0) {
1243  Point p = projector->Pos() + focal_vect * t;
1244  move_alt = p.y - base_alt;
1245  }
1246  }
1247  else {
1248  double dz = move_loc.z - projector->Pos().z;
1249  double t = -1 * ((projector->Pos().z - dz) / focal_vect.z);
1250  Point p = projector->Pos() + focal_vect * t;
1251 
1252  if (t > 0) {
1253  Point p = projector->Pos() + focal_vect * t;
1254  move_alt = p.y - base_alt;
1255  }
1256  }
1257 
1258  if (move_alt > 25e3)
1259  move_alt = 25e3;
1260  else if (move_alt < -25e3)
1261  move_alt = -25e3;
1262 
1263  return true;
1264  }
1265  else {
1266  if (fabs(focal_vect.y) > 1e-5) {
1267  if (Mouse::RButton())
1268  return true;
1269 
1270  bool clamp = false;
1271  double t = -1 * ((projector->Pos().y - base_alt) / focal_vect.y);
1272 
1273  while (t <= 0 && my < height-1) {
1274  my++;
1275  clamp = true;
1276 
1277  focal_vect = projector->vpn() * focal_dist +
1278  projector->vup() * -1 * (my-height/2) +
1279  projector->vrt() * (mx-width/2);
1280 
1281  focal_vect.Normalize();
1282  t = -1 * ((projector->Pos().y - base_alt) / focal_vect.y);
1283  }
1284 
1285  if (t > 0) {
1286  if (clamp)
1287  Mouse::SetCursorPos(mx, my);
1288 
1289  move_loc = projector->Pos() + focal_vect * t;
1290  }
1291 
1292  return true;
1293  }
1294  }
1295  }
1296 
1297  return false;
1298 }
1299 
1300 void
1302 {
1303  if (!projector || !show_move || !msg_ship) return;
1304 
1305  Point origin = msg_ship->Location();
1306 
1307  if (GetMouseLoc3D()) {
1308  Point dest = move_loc;
1309 
1310  double distance = (dest - origin).length();
1311 
1312  projector->Transform(origin);
1313  projector->Project(origin);
1314 
1315  int x0 = (int) origin.x;
1316  int y0 = (int) origin.y;
1317 
1318  projector->Transform(dest);
1319  projector->Project(dest);
1320 
1321  int x = (int) dest.x;
1322  int y = (int) dest.y;
1323 
1324  window->DrawEllipse(x-10, y-10, x+10, y+10, Color::White);
1325  window->DrawLine(x0, y0, x, y, Color::White);
1326 
1327  char range[32];
1328  Rect range_rect(x+12, y-8, 120, 20);
1329 
1330  if (fabs(move_alt) > 1) {
1331  dest = move_loc;
1332  dest.y += move_alt;
1333  distance = (dest - msg_ship->Location()).length();
1334 
1335  projector->Transform(dest);
1336  projector->Project(dest);
1337 
1338  int x1 = (int) dest.x;
1339  int y1 = (int) dest.y;
1340 
1341  window->DrawEllipse(x1-10, y1-10, x1+10, y1+10, Color::White);
1342  window->DrawLine(x0, y0, x1, y1, Color::White);
1343  window->DrawLine(x1, y1, x, y, Color::White);
1344 
1345  range_rect.x = x1+12;
1346  range_rect.y = y1-8;
1347  }
1348 
1349  FormatNumber(range, distance);
1351  font->DrawText(range, 0, range_rect, DT_LEFT | DT_SINGLELINE);
1352  font->SetColor(txt_color);
1353  }
1354 }
1355 
1356 void
1358 {
1359  if (!projector || !show_move || !msg_ship) return;
1360 
1361  if (GetMouseLoc3D()) {
1362  Element* elem = msg_ship->GetElement();
1363  RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, RadioMessage::MOVE_PATROL);
1364  Point dest = move_loc;
1365  dest.y += move_alt;
1366  msg->SetLocation(dest);
1369  }
1370 }
1371 
1372 // +--------------------------------------------------------------------+
1373 
1374 static int invalid_action = false;
1375 
1376 void
1378 {
1379  if (!projector || !show_action || !msg_ship) return;
1380 
1381  Point origin = msg_ship->Location();
1382  projector->Transform(origin);
1383  projector->Project(origin);
1384 
1385  int x0 = (int) origin.x;
1386  int y0 = (int) origin.y;
1387 
1388  int mx = mouse_action.x;
1389  int my = mouse_action.y;
1390  int r = 10;
1391 
1392  int enemy = 2;
1393  if (ship->GetIFF() > 1)
1394  enemy = 1;
1395 
1396  Ship* tgt = WillSelectAt(mx, my);
1397  int tgt_iff = 0;
1398 
1399  if (tgt)
1400  tgt_iff = tgt->GetIFF();
1401 
1402  Color c = Color::White;
1403 
1404  switch (show_action) {
1405  case RadioMessage::ATTACK:
1406  case RadioMessage::BRACKET:
1407  c = Ship::IFFColor(enemy);
1408  if (tgt) {
1409  if (tgt_iff == ship->GetIFF() || tgt_iff == 0)
1410  r = 0;
1411  }
1412  break;
1413 
1414  case RadioMessage::ESCORT:
1416  c = ship->MarkerColor();
1417  if (tgt) {
1418  if (tgt_iff == enemy)
1419  r = 0;
1420 
1421  // must have a hangar to dock with...
1422  if (show_action == RadioMessage::DOCK_WITH && tgt->GetHangar() == 0)
1423  r = 0;
1424  }
1425  break;
1426 
1427  default:
1428  if (tgt) {
1429  if (tgt_iff == ship->GetIFF())
1430  r = 0;
1431  }
1432  break;
1433  }
1434 
1435  if (tgt && r) {
1436  if ((Game::RealTime()/200) & 1)
1437  r = 20;
1438  else
1439  r = 15;
1440  }
1441 
1442  if (r) {
1443  invalid_action = false;
1444  window->DrawEllipse(mx-r, my-r, mx+r, my+r, c);
1445  }
1446 
1447  else {
1448  invalid_action = true;
1449  window->DrawLine(mx-10, my-10, mx+10, my+10, c);
1450  window->DrawLine(mx+10, my-10, mx-10, my+10, c);
1451  }
1452 
1453  window->DrawLine(x0, y0, mx, my, c);
1454 }
1455 
1456 void
1458 {
1459  if (!show_action || !msg_ship || invalid_action) {
1461  return;
1462  }
1463 
1464  int mx = mouse_action.x;
1465  int my = mouse_action.y;
1466 
1467  Ship* tgt = WillSelectAt(mx, my);
1468 
1469  if (tgt) {
1470  Element* elem = msg_ship->GetElement();
1471  RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, show_action);
1472 
1473  /***
1474  Element* tgt_elem = tgt->GetElement();
1475 
1476  if (tgt_elem) {
1477  for (int i = 1; i <= tgt_elem->NumShips(); i++)
1478  msg->AddTarget(tgt_elem->GetShip(i));
1479  }
1480  else {
1481  msg->AddTarget(tgt);
1482  }
1483  ***/
1484 
1485  msg->AddTarget(tgt);
1486 
1489  }
1490  else {
1492  }
1493 }
1494 
1495