Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
FlightDeck.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: FlightDeck.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Everything needed to launch and recover space craft...
13 */
14 
15 #include "MemDebug.h"
16 #include "FlightDeck.h"
17 #include "Ship.h"
18 #include "ShipCtrl.h"
19 #include "ShipDesign.h"
20 #include "Element.h"
21 #include "Mission.h"
22 #include "MissionEvent.h"
23 #include "Drive.h"
24 #include "Sim.h"
25 #include "Instruction.h"
26 #include "Hoop.h"
27 #include "LandingGear.h"
28 #include "RadioMessage.h"
29 #include "RadioTraffic.h"
30 #include "SimEvent.h"
31 #include "AudioConfig.h"
32 #include "CameraDirector.h"
33 #include "Combatant.h"
34 #include "CombatGroup.h"
35 #include "CombatUnit.h"
36 
37 #include "NetData.h"
38 #include "NetUtil.h"
39 
40 #include "Game.h"
41 #include "Solid.h"
42 #include "Light.h"
43 #include "Sound.h"
44 #include "DataLoader.h"
45 
46 static Sound* tire_sound = 0;
47 static Sound* catapult_sound = 0;
48 
49 // +======================================================================+
50 
52 {
53 public:
54  FlightDeckSlot() : ship(0), state(0), sequence(0), time(0), filter(0xf) { }
55  int operator == (const FlightDeckSlot& that) const { return this == &that; }
56 
60 
61  int state;
62  int sequence;
63  double time;
64  double clearance;
65  DWORD filter;
66 };
67 
68 // +======================================================================+
69 
70 InboundSlot::InboundSlot(Ship* s, FlightDeck* d, int squad, int index)
71 : ship(s), deck(d), squadron(squad), slot(index), cleared(0), final(0), approach(0)
72 {
73  if (ship)
74  Observe(ship);
75 
76  if (deck && deck->GetCarrier())
77  Observe((SimObject*) deck->GetCarrier());
78 }
79 
80 int InboundSlot::operator < (const InboundSlot& that) const
81 {
82  double dthis = 0;
83  double dthat = 0;
84 
85  if (ship == that.ship) {
86  return false;
87  }
88 
89  if (cleared && !that.cleared) {
90  return true;
91  }
92 
93  if (!cleared && that.cleared) {
94  return false;
95  }
96 
97  if (ship && deck && that.ship) {
98  dthis = Point(ship->Location() - deck->MountLocation()).length();
99  dthat = Point(that.ship->Location() - deck->MountLocation()).length();
100  }
101 
102  if (dthis == dthat) {
103  return (DWORD) this < (DWORD) &that;
104  }
105 
106  return dthis < dthat;
107 }
108 
110 {
111  double dthis = 0;
112  double dthat = 0;
113 
114  if (ship == that.ship)
115  return true;
116 
117  if (cleared && !that.cleared)
118  return true;
119 
120  if (!cleared && that.cleared)
121  return false;
122 
123  if (ship && deck && that.ship) {
124  dthis = Point(ship->Location() - deck->MountLocation()).length();
125  dthat = Point(that.ship->Location() - deck->MountLocation()).length();
126  }
127 
128  return dthis <= dthat;
129 }
130 
132 {
133  return this == &that;
134 }
135 
136 void InboundSlot::Clear(bool c)
137 {
138  if (ship)
139  cleared = c;
140 }
141 
143 {
144  if (ship && deck) {
145  return Point(ship->Location() - deck->MountLocation()).length();
146  }
147 
148  return 1e9;
149 }
150 
151 // +----------------------------------------------------------------------+
152 
153 bool
155 {
156  if (obj->Type() == SimObject::SIM_SHIP) {
157  Ship* s = (Ship*) obj;
158 
159  if (s == ship) {
160  ship = 0;
161  }
162 
163  // Actually, this can't happen. The flight deck
164  // owns the slot. When the carrier is destroyed,
165  // the flight decks and slots are destroyed before
166  // the carrier updates the observers.
167 
168  // I'm leaving this block in, just in case.
169 
170  else if (deck && s == deck->GetCarrier()) {
171  ship = 0;
172  deck = 0;
173  }
174  }
175 
176  return SimObserver::Update(obj);
177 }
178 
179 const char*
181 {
182  return "InboundSlot";
183 }
184 
185 // +======================================================================+
186 
188 : System(FLIGHT_DECK, FLIGHT_DECK_LAUNCH, "Flight Deck", 1, 1),
189 carrier(0), index(0), num_slots(0), slots(0), cycle_time(5), num_hoops(0), hoops(0),
190 azimuth(0), light(0), num_catsounds(0), num_approach_pts(0)
191 {
192  name = Game::GetText("sys.flight-deck");
193  abrv = Game::GetText("sys.flight-deck.abrv");
194 }
195 
196 // +----------------------------------------------------------------------+
197 
199 : System(s),
200 carrier(0), index(0), start_rel(s.start_rel),
201 end_rel(s.end_rel), cam_rel(s.cam_rel),
202 cycle_time(s.cycle_time),
203 num_slots(s.num_slots), slots(0),
204 num_hoops(0), hoops(0), azimuth(s.azimuth), light(0),
205 num_catsounds(0), num_approach_pts(s.num_approach_pts)
206 {
207  Mount(s);
208 
209  slots = new(__FILE__,__LINE__) FlightDeckSlot[num_slots];
210  for (int i = 0; i < num_slots; i++) {
211  slots[i].spot_rel = s.slots[i].spot_rel;
212  slots[i].filter = s.slots[i].filter;
213  }
214 
215  for (int i = 0; i < NUM_APPROACH_PTS; i++)
216  approach_rel[i] = s.approach_rel[i];
217 
218  for (int i = 0; i < 2; i++)
219  runway_rel[i] = s.runway_rel[i];
220 
221  if (s.light) {
222  light = new(__FILE__,__LINE__) Light(*s.light);
223  }
224 }
225 
226 // +--------------------------------------------------------------------+
227 
229 {
230  if (hoops && num_hoops) {
231  for (int i = 0; i < num_hoops; i++) {
232  Hoop* h = &hoops[i];
233  Scene* scene = h->GetScene();
234  if (scene)
235  scene->DelGraphic(h);
236  }
237  }
238 
239  delete [] slots;
240  delete [] hoops;
241 
243 
245 }
246 
247 // +--------------------------------------------------------------------+
248 
249 void
251 {
252  static int initialized = 0;
253  if (initialized) return;
254 
255  DataLoader* loader = DataLoader::GetLoader();
256  loader->SetDataPath("Sounds/");
257 
258  const int SOUND_FLAGS = Sound::LOCALIZED |
260 
261  loader->LoadSound("Tires.wav", tire_sound, SOUND_FLAGS);
262  loader->LoadSound("Catapult.wav", catapult_sound, SOUND_FLAGS);
263  loader->SetDataPath(0);
264 
265  if (tire_sound)
266  tire_sound->SetMaxDistance(2.5e3f);
267 
268  if (catapult_sound)
269  catapult_sound->SetMaxDistance(0.5e3f);
270 
271  initialized = 1;
272 }
273 
274 // +--------------------------------------------------------------------+
275 
276 void
278 {
279  delete tire_sound;
280  delete catapult_sound;
281 
282  tire_sound = 0;
283  catapult_sound = 0;
284 }
285 
286 // +--------------------------------------------------------------------+
287 
288 void
289 FlightDeck::ExecFrame(double seconds)
290 {
291  System::ExecFrame(seconds);
292 
293  bool advance_queue = false;
294  long max_vol = AudioConfig::EfxVolume();
295  long volume = -1000;
296  Sim* sim = Sim::GetSim();
297 
298  if (volume > max_vol)
299  volume = max_vol;
300 
301  // update ship status:
302  for (int i = 0; i < num_slots; i++) {
303  FlightDeckSlot* slot = &slots[i];
304  Ship* slot_ship = 0;
305 
306  if (slot->ship == 0) {
307  slot->state = CLEAR;
308  }
309  else {
310  slot_ship = slot->ship;
311  slot_ship->SetThrottle(0);
312  }
313 
314  switch (slot->state) {
315  case CLEAR:
316  if (slot->time > 0) {
317  slot->time -= seconds;
318  }
319  else if (IsRecoveryDeck()) {
320  GrantClearance();
321  }
322  break;
323 
324  case READY: {
325  Camera c;
326  c.Clone(carrier->Cam());
327  c.Yaw(azimuth);
328 
329  if (slot_ship) {
330  slot_ship->CloneCam(c);
331  slot_ship->MoveTo(slot->spot_loc);
332  slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
333  slot_ship->SetVelocity(carrier->Velocity());
334  }
335  slot->time = 0;
336  }
337  break;
338 
339  case QUEUED:
340  if (slot->time > 0) {
341  Camera c;
342  c.Clone(carrier->Cam());
343  c.Yaw(azimuth);
344 
345  slot->time -= seconds;
346  if (slot_ship) {
347  slot_ship->CloneCam(c);
348  slot_ship->MoveTo(slot->spot_loc);
349  slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
350  slot_ship->SetFlightPhase(Ship::ALERT);
351  }
352  }
353 
354  if (slot->sequence == 1 && slot->time <= 0) {
355  bool clear_for_launch = true;
356  for (int i = 0; i < num_slots; i++)
357  if (slots[i].state == LOCKED)
358  clear_for_launch = false;
359 
360  if (clear_for_launch) {
361  slot->sequence = 0;
362  slot->state = LOCKED;
363  slot->time = cycle_time;
364  if (slot_ship)
365  slot_ship->SetFlightPhase(Ship::LOCKED);
366  num_catsounds = 0;
367 
368  advance_queue = true;
369  }
370  }
371  break;
372 
373  case LOCKED:
374  if (slot->time > 0) {
375  slot->time -= seconds;
376 
377  if (slot_ship) {
378  double ct4 = cycle_time/4;
379 
380  double dx = start_rel.x - slot->spot_rel.x;
381  double dy = start_rel.z - slot->spot_rel.z;
382  double dyaw = atan2(dx,dy) - azimuth;
383 
384  Camera c;
385  c.Clone(carrier->Cam());
386  c.Yaw(azimuth);
387 
388  // rotate:
389  if (slot->time > 3*ct4) {
390  double step = 1 - (slot->time - 3*ct4) / ct4;
391  c.Yaw(dyaw * step);
392  slot_ship->CloneCam(c);
393  slot_ship->MoveTo(slot->spot_loc);
394  slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
395 
396  if (carrier->IsGroundUnit()) {
397  slot_ship->SetThrottle(25);
398  }
399 
400  else if (num_catsounds < 1) {
401  if (catapult_sound) {
402  Sound* sound = catapult_sound->Duplicate();
403  sound->SetLocation(slot_ship->Location());
404  sound->SetVolume(volume);
405  sound->Play();
406  }
407  num_catsounds = 1;
408  }
409  }
410 
411  // translate:
412  else if (slot->time > 2*ct4) {
413  double step = (slot->time - 2*ct4) / ct4;
414 
415  Point loc = start_point +
416  (slot->spot_loc - start_point) * step;
417 
418  c.Yaw(dyaw);
419  slot_ship->CloneCam(c);
420  slot_ship->MoveTo(loc);
421  slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
422 
423  if (carrier->IsGroundUnit()) {
424  slot_ship->SetThrottle(25);
425  }
426 
427  else if (num_catsounds < 2) {
428  if (catapult_sound) {
429  Sound* sound = catapult_sound->Duplicate();
430  sound->SetLocation(slot_ship->Location());
431  sound->SetVolume(volume);
432  sound->Play();
433  }
434  num_catsounds = 2;
435  }
436  }
437 
438  // rotate:
439  else if (slot->time > ct4) {
440  double step = (slot->time - ct4) / ct4;
441  c.Yaw(dyaw*step);
442  slot_ship->CloneCam(c);
443  slot_ship->MoveTo(start_point);
444  slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
445 
446  if (carrier->IsGroundUnit()) {
447  slot_ship->SetThrottle(25);
448  }
449 
450  else if (num_catsounds < 3) {
451  if (catapult_sound) {
452  Sound* sound = catapult_sound->Duplicate();
453  sound->SetLocation(slot_ship->Location());
454  sound->SetVolume(volume);
455  sound->Play();
456  }
457  num_catsounds = 3;
458  }
459  }
460 
461  // wait:
462  else {
463  slot_ship->SetThrottle(100);
464  slot_ship->CloneCam(c);
465  slot_ship->MoveTo(start_point);
466  slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
467  }
468 
469  slot_ship->SetFlightPhase(Ship::LOCKED);
470  }
471  }
472  else {
473  slot->state = LAUNCH;
474  slot->time = 0;
475 
476  }
477  break;
478 
479  case LAUNCH:
480  LaunchShip(slot_ship);
481  break;
482 
483  case DOCKING:
484  if (slot_ship && !slot_ship->IsAirborne()) {
485  // do arresting gear stuff:
486  if (slot_ship->GetFlightModel() == Ship::FM_ARCADE)
487  slot_ship->ArcadeStop();
488 
489  slot_ship->SetVelocity(carrier->Velocity());
490  }
491 
492  if (slot->time > 0) {
493  slot->time -= seconds;
494  }
495  else {
496  if (slot_ship) {
497  slot_ship->SetFlightPhase(Ship::DOCKED);
498  slot_ship->Stow();
499 
501  }
502 
503  Clear(i);
504  }
505  break;
506 
507  default:
508  break;
509  }
510  }
511 
512  if (advance_queue) {
513  for (int i = 0; i < num_slots; i++) {
514  FlightDeckSlot* slot = &slots[i];
515  if (slot->state == QUEUED && slot->sequence > 1)
516  slot->sequence--;
517  }
518  }
519 }
520 
521 bool
523 {
524  FlightDeckSlot* slot = 0;
525  Sim* sim = Sim::GetSim();
526 
527  if (slot_ship) {
528  for (int i = 0; i < num_slots; i++) {
529  if (slots[i].ship == slot_ship)
530  slot = &(slots[i]);
531  }
532 
533  // only suggest a launch point if no flight plan has been filed...
534  if (slot_ship->GetElement()->FlightPlanLength() == 0) {
535  Point departure = end_point;
536  departure = departure.OtherHand();
537 
538  Instruction* launch_point = new(__FILE__,__LINE__)
540  launch_point->SetSpeed(350);
541 
542  slot_ship->SetLaunchPoint(launch_point);
543  }
544 
545  if (!slot_ship->IsAirborne()) {
546  Point cat;
547  if (fabs(azimuth) < 5*DEGREES) {
548  cat = carrier->Heading() * 300;
549  }
550  else {
551  Camera c;
552  c.Clone(carrier->Cam());
553  c.Yaw(azimuth);
554  cat = c.vpn() * 300;
555  }
556 
557  slot_ship->SetVelocity(carrier->Velocity() + cat);
558  slot_ship->SetFlightPhase(Ship::LAUNCH);
559  }
560  else {
561  slot_ship->SetFlightPhase(Ship::TAKEOFF);
562  }
563 
564  Director* dir = slot_ship->GetDirector();
565  if (dir && dir->Type() == ShipCtrl::DIR_TYPE) {
566  ShipCtrl* ctrl = (ShipCtrl*) dir;
567  ctrl->Launch();
568  }
569 
571  if (c) c->AddEvent(SimEvent::LAUNCH_SHIP, slot_ship->Name());
572 
573  ShipStats* stats = ShipStats::Find(slot_ship->Name());
574  if (stats) {
575  stats->SetRegion(carrier->GetRegion()->Name());
576  stats->SetType(slot_ship->Design()->name);
577 
578  if (slot_ship->GetElement()) {
579  Element* elem = slot_ship->GetElement();
580  stats->SetRole(Mission::RoleName(elem->Type()));
581  stats->SetCombatGroup(elem->GetCombatGroup());
582  stats->SetCombatUnit(elem->GetCombatUnit());
583  stats->SetElementIndex(slot_ship->GetElementIndex());
584  }
585 
586  stats->SetIFF(slot_ship->GetIFF());
588 
589  if (slot_ship == sim->GetPlayerShip())
590  stats->SetPlayer(true);
591  }
592 
594 
595  if (slot) {
596  slot->ship = 0;
597  slot->state = CLEAR;
598  slot->sequence = 0;
599  slot->time = 0;
600  }
601 
602  return true;
603  }
604 
605  return false;
606 }
607 
608 // +----------------------------------------------------------------------+
609 
610 void
612 {
614  light = new(__FILE__,__LINE__) Light((float) l);
615 }
616 
617 void
619 {
620  if (i >= 0 && i < NUM_APPROACH_PTS) {
621  approach_rel[i] = loc;
622 
623  if (i >= num_approach_pts)
624  num_approach_pts = i+1;
625  }
626 }
627 
628 void
630 {
631  if (i >= 0 && i < 2)
632  runway_rel[i] = loc;
633 }
634 
635 void
637 {
638  start_rel = loc;
639 }
640 
641 void
643 {
644  end_rel = loc;
645 }
646 
647 void
649 {
650  cam_rel = loc;
651 }
652 
653 // +----------------------------------------------------------------------+
654 
655 void
656 FlightDeck::AddSlot(const Point& spot, DWORD filter)
657 {
658  if (!slots)
659  slots = new(__FILE__,__LINE__) FlightDeckSlot[10];
660 
661  slots[num_slots].spot_rel = spot;
662  slots[num_slots].filter = filter;
663  num_slots++;
664 }
665 
666 // +----------------------------------------------------------------------+
667 
668 void
670 {
671  cycle_time = t;
672 }
673 
674 // +----------------------------------------------------------------------+
675 
676 void
678 {
679  System::Orient(rep);
680 
681  Matrix orientation = rep->Cam().Orientation();
682  Point loc = rep->Location();
683 
684  start_point = (start_rel * orientation) + loc;
685  end_point = (end_rel * orientation) + loc;
686  cam_loc = (cam_rel * orientation) + loc;
687 
688  for (int i = 0; i < num_approach_pts; i++)
689  approach_point[i] = (approach_rel[i] * orientation) + loc;
690 
691  for (int i = 0; i < num_slots; i++)
692  slots[i].spot_loc = (slots[i].spot_rel * orientation) + loc;
693 
694  if (IsRecoveryDeck()) {
695  if (carrier->IsAirborne()) {
696  runway_point[0] = (runway_rel[0] * orientation) + loc;
697  runway_point[1] = (runway_rel[1] * orientation) + loc;
698  }
699 
700  if (num_hoops < 1) {
701  num_hoops = 4;
702  hoops = new(__FILE__,__LINE__) Hoop[num_hoops];
703  }
704 
705  Point hoop_vec = start_point - end_point;
706  double hoop_d = hoop_vec.Normalize() / num_hoops;
707 
708  orientation.Yaw(azimuth);
709 
710  for (int i = 0; i < num_hoops; i++) {
711  hoops[i].MoveTo(end_point + hoop_vec * (i+1) * hoop_d);
712  hoops[i].SetOrientation(orientation);
713  }
714  }
715 
716  if (light)
718 }
719 
720 // +----------------------------------------------------------------------+
721 
722 int
723 FlightDeck::SpaceLeft(int type) const
724 {
725  int space_left = 0;
726 
727  for (int i = 0; i < num_slots; i++)
728  if (slots[i].ship == 0 && (slots[i].filter & type))
729  space_left++;
730 
731  return space_left;
732 }
733 
734 // +----------------------------------------------------------------------+
735 
736 bool
737 FlightDeck::Spot(Ship* s, int& index)
738 {
739  if (!s)
740  return false;
741 
742  if (index < 0) {
743  for (int i = 0; i < num_slots; i++) {
744  if (slots[i].ship == 0 && (slots[i].filter & s->Class())) {
745  index = i;
746  break;
747  }
748  }
749  }
750 
751  if (index >= 0 && index < num_slots && slots[index].ship == 0) {
752  slots[index].state = READY;
753  slots[index].ship = s;
754  slots[index].clearance = 0;
755 
756  if (s->GetGear())
757  slots[index].clearance = s->GetGear()->GetClearance();
758 
759  if (IsRecoveryDeck() && !s->IsAirborne()) {
760  s->SetVelocity(s->Velocity() * 0.01); // hit the brakes!
761  }
762 
763  if (!IsRecoveryDeck()) {
764  Camera work;
765  work.Clone(carrier->Cam());
766  work.Yaw(azimuth);
767 
768  s->CloneCam(work);
769  s->MoveTo(slots[index].spot_loc);
770 
771  LandingGear* g = s->GetGear();
772  if (g) {
774  g->ExecFrame(0);
776  }
777 
779  }
780 
781  s->SetCarrier(carrier, this);
782  Observe(s);
783 
784  return true;
785  }
786 
787  return false;
788 }
789 
790 bool
792 {
793  if (index >= 0 && index < num_slots && slots[index].ship != 0) {
794  Ship* s = slots[index].ship;
795 
796  slots[index].ship = 0;
798  slots[index].state = CLEAR;
799 
801  while (++iter) {
802  if (iter->GetShip() == s) {
803  delete iter.removeItem(); // ??? SHOULD DELETE HERE ???
804  break;
805  }
806  }
807 
808  return true;
809  }
810 
811  return false;
812 }
813 
814 // +----------------------------------------------------------------------+
815 
816 bool
818 {
819  if (subtype == FLIGHT_DECK_LAUNCH && index >= 0 && index < num_slots) {
820  FlightDeckSlot* slot = &slots[index];
821 
822  if (slot->ship && slot->state == READY) {
823  int last = 0;
824  FlightDeckSlot* last_slot = 0;
825  FlightDeckSlot* lock_slot = 0;
826 
827  for (int i = 0; i < num_slots; i++) {
828  FlightDeckSlot* s = &slots[i];
829 
830  if (s->state == QUEUED && s->sequence > last) {
831  last = s->sequence;
832  last_slot = s;
833  }
834 
835  else if (s->state == LOCKED) {
836  lock_slot = s;
837  }
838  }
839 
840  // queue the slot for launch:
841  slot->state = QUEUED;
842  slot->sequence = last + 1;
843  slot->time = 0;
844 
845  if (last_slot)
846  slot->time = last_slot->time + cycle_time;
847 
848  else if (lock_slot)
849  slot->time = lock_slot->time;
850 
851  return true;
852  }
853  }
854 
855  return false;
856 }
857 
858 // +----------------------------------------------------------------------+
859 
860 bool
862 {
863  if (s && subtype == FLIGHT_DECK_RECOVERY) {
864  if (OverThreshold(s)) {
865  if (s->GetFlightPhase() < Ship::RECOVERY) {
867  s->SetCarrier(carrier, this); // let ship know which flight deck it's in (needed for dock cam)
868  }
869 
870  // are we there yet?
872  if (slots[0].ship == 0) { // only need to ask this on approach
873  double dock_distance = (s->Location() - MountLocation()).length();
874 
875  if (s->IsAirborne()) {
876  double altitude = s->Location().y - MountLocation().y;
877  if (dock_distance < Radius()*3 && altitude < s->Radius())
878  Dock(s);
879  }
880  else {
881  if (dock_distance < s->Radius())
882  Dock(s);
883  }
884  }
885  }
886 
887  return true;
888  }
889  else {
890  if (s->GetFlightPhase() == Ship::RECOVERY)
892  }
893  }
894 
895  return false;
896 }
897 
898 bool
900 {
901  if (s && subtype == FLIGHT_DECK_RECOVERY && slots[0].time <= 0) {
902  int index = 0;
903  if (Spot(s, index)) {
905  s->SetCarrier(carrier, this);
906 
907  // hard landings?
908  if (Ship::GetLandingModel() == 0) {
909  double base_damage = s->Design()->integrity;
910 
911  // did player do something stupid?
912  if (s->GetGear() && !s->IsGearDown()) {
913  Print("FlightDeck::Dock(%s) Belly landing!\n", s->Name());
914  s->InflictDamage(0.5 * base_damage);
915  }
916 
917  double docking_deflection = fabs(carrier->Cam().vup().y - s->Cam().vup().y);
918 
919  if (docking_deflection > 0.35) {
920  Print("Landing upside down? y = %.3f\n", docking_deflection);
921  s->InflictDamage(0.8 * base_damage);
922  }
923 
924  // did incoming ship exceed safe landing parameters?
925  if (s->IsAirborne()) {
926  if (s->Velocity().y < -20) {
927  Print("FlightDeck::Dock(%s) Slammed it!\n", s->Name());
928  s->InflictDamage(0.1 * base_damage);
929  }
930  }
931 
932  // did incoming ship exceed safe docking speed?
933  else {
934  Point delta_v = s->Velocity() - carrier->Velocity();
935  double excess = (delta_v.length() - 100);
936 
937  if (excess > 0)
938  s->InflictDamage(excess);
939  }
940  }
941 
942  if (s->IsAirborne()) {
943  if (s == Sim::GetSim()->GetPlayerShip() && tire_sound) {
944  Sound* sound = tire_sound->Duplicate();
945  sound->Play();
946  }
947  }
948 
949  if (s->GetDrive())
950  s->GetDrive()->PowerOff();
951 
953  slots[index].time = 5;
954 
955  if (s->IsAirborne())
956  slots[index].time = 7.5;
957  return true;
958  }
959  }
960 
961  return false;
962 }
963 
964 int
966 {
967  if (s && s->GetShip()) {
968  Ship* inbound = s->GetShip();
969 
970  if (recovery_queue.contains(s)) {
971  InboundSlot* orig = s;
972  s = recovery_queue.find(s);
973  delete orig;
974  }
975  else {
977  Observe(s->GetShip());
978  }
979 
980  inbound->SetInbound(s);
981 
982  // find the best initial approach point for this ship:
983  double current_distance = 1e9;
984  for (int i = 0; i < num_approach_pts; i++) {
985  double distance = Point(inbound->Location() - approach_point[i]).length();
986  if (distance < current_distance) {
987  current_distance = distance;
988  s->SetApproach(i);
989  }
990  }
991 
992  Point offset(rand()-16000, rand()-16000, rand()-16000);
993  offset.Normalize();
994  offset *= 200;
995 
996  s->SetOffset(offset);
997 
998  // *** DEBUG ***
999  // PrintQueue();
1000 
1001  // if the new guy is first in line, and the deck is almost ready,
1002  // go ahead and clear him for approach now
1003  recovery_queue.sort();
1004  if (recovery_queue[0] == s && !s->Cleared() && slots[0].time <= 3)
1005  s->Clear();
1006 
1007  return recovery_queue.index(s) + 1;
1008  }
1009 
1010  return 0;
1011 }
1012 
1013 void
1015 {
1016  if (recovery_queue.size() > 0) {
1017  if (recovery_queue[0]->Cleared() && recovery_queue[0]->Distance() > 45e3) {
1018  recovery_queue[0]->Clear(false);
1019  }
1020 
1021  if (!recovery_queue[0]->Cleared()) {
1022  recovery_queue.sort();
1023 
1024  if (recovery_queue[0]->Distance() < 35e3) {
1025  recovery_queue[0]->Clear();
1026 
1027  Ship* dst = recovery_queue[0]->GetShip();
1028 
1029  RadioMessage* clearance = new(__FILE__,__LINE__) RadioMessage(dst, carrier, RadioMessage::CALL_CLEARANCE);
1030  clearance->SetInfo(Text("for final approach to ") + Name());
1031  RadioTraffic::Transmit(clearance);
1032  }
1033  }
1034  }
1035 }
1036 
1037 // +----------------------------------------------------------------------+
1038 
1039 void
1041 {
1042  Print("\nRecovery Queue for %s\n", Name());
1043  if (recovery_queue.size() < 1) {
1044  Print(" (empty)\n\n");
1045  return;
1046  }
1047 
1048  for (int i = 0; i < recovery_queue.size(); i++) {
1049  InboundSlot* s = recovery_queue.at(i);
1050 
1051  if (!s) {
1052  Print(" %2d. null\n", i);
1053  }
1054  else if (!s->GetShip()) {
1055  Print(" %2d. ship is null\n", i);
1056  }
1057  else {
1058  double d = Point(s->GetShip()->Location() - MountLocation()).length();
1059  Print(" %2d. %c %-20s %8d km\n", i, s->Cleared()?'*':' ', s->GetShip()->Name(), (int) (d/1000));
1060  }
1061  }
1062 
1063  Print("\n");
1064 }
1065 
1066 // +----------------------------------------------------------------------+
1067 
1068 Ship*
1069 FlightDeck::GetShip(int index) const
1070 {
1071  if (index >= 0 && index < num_slots)
1072  return slots[index].ship;
1073 
1074  return 0;
1075 }
1076 
1077 double
1079 {
1080  if (index >= 0 && index < num_slots)
1081  return slots[index].time;
1082 
1083  return 0;
1084 }
1085 
1086 
1087 int
1088 FlightDeck::State(int index) const
1089 {
1090  if (index >= 0 && index < num_slots)
1091  return slots[index].state;
1092 
1093  return 0;
1094 }
1095 
1096 int
1097 FlightDeck::Sequence(int index) const
1098 {
1099  if (index >= 0 && index < num_slots)
1100  return slots[index].sequence;
1101 
1102  return 0;
1103 }
1104 
1105 // +----------------------------------------------------------------------+
1106 
1107 bool
1109 {
1110  if (obj->Type() == SimObject::SIM_SHIP) {
1111  Ship* s = (Ship*) obj;
1112 
1114  while (++iter) {
1115  InboundSlot* recovery_slot = iter.value();
1116 
1117  if (recovery_slot->GetShip() == s || recovery_slot->GetShip() == 0) {
1118  delete iter.removeItem();
1119  }
1120  }
1121 
1122  for (int i = 0; i < num_slots; i++) {
1123  FlightDeckSlot* slot = &slots[i];
1124 
1125  if (slot->ship == s) {
1126  slot->ship = 0;
1127  slot->state = 0;
1128  slot->sequence = 0;
1129  slot->time = 0;
1130  break;
1131  }
1132  }
1133  }
1134 
1135  return SimObserver::Update(obj);
1136 }
1137 
1138 const char*
1140 {
1141  return Name();
1142 }
1143 
1144 // +----------------------------------------------------------------------+
1145 
1146 bool
1148 {
1149  if (carrier->IsAirborne()) {
1150  if (s->AltitudeAGL() > s->Radius() * 4)
1151  return false;
1152 
1153  const Point& sloc = s->Location();
1154 
1155  // is ship between the markers?
1156  double distance = 1e9;
1157 
1158  Point d0 = runway_point[0] - sloc;
1159  Point d1 = runway_point[1] - sloc;
1160  double d = d0 * d1;
1161  bool between = (d0 * d1) < 0;
1162 
1163  if (between) {
1164  // distance from point to line:
1165  Point src = runway_point[0];
1166  Point dir = runway_point[1] - src;
1167  Point w = (sloc - src).cross(dir);
1168 
1169  distance = w.length() / dir.length();
1170  }
1171 
1172  return distance < Radius();
1173  }
1174 
1175  else {
1176  return (s->Location() - MountLocation()).length() < (s->Radius() + Radius());
1177  }
1178 }
1179 
1180 // +----------------------------------------------------------------------+
1181 
1182 bool
1184 {
1185  return (p - MountLocation()).length() < Radius();
1186 }