Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Sim.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: Sim.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Simulation Universe and Region classes
13 */
14 
15 #include "MemDebug.h"
16 #include "Sim.h"
17 #include "SimEvent.h"
18 #include "SimObject.h"
19 #include "Starshatter.h"
20 #include "StarSystem.h"
21 #include "Contact.h"
22 #include "Ship.h"
23 #include "ShipDesign.h"
24 #include "Element.h"
25 #include "Instruction.h"
26 #include "RadioTraffic.h"
27 #include "Shot.h"
28 #include "Drone.h"
29 #include "Explosion.h"
30 #include "Debris.h"
31 #include "Asteroid.h"
32 #include "Drive.h"
33 #include "QuantumDrive.h"
34 #include "Sensor.h"
35 #include "NavLight.h"
36 #include "Shield.h"
37 #include "Weapon.h"
38 #include "WeaponGroup.h"
39 #include "Hangar.h"
40 #include "FlightDeck.h"
41 #include "Sky.h"
42 #include "Grid.h"
43 #include "MFD.h"
44 #include "AudioConfig.h"
45 #include "Mission.h"
46 #include "MissionEvent.h"
47 #include "CameraDirector.h"
48 #include "MusicDirector.h"
49 #include "Combatant.h"
50 #include "CombatGroup.h"
51 #include "CombatUnit.h"
52 #include "HUDView.h"
53 #include "SeekerAI.h"
54 #include "ShipAI.h"
55 #include "Power.h"
56 #include "Callsign.h"
57 #include "GameScreen.h"
58 #include "Terrain.h"
59 #include "TerrainPatch.h"
60 
61 #include "NetGame.h"
62 #include "NetClientConfig.h"
63 #include "NetServerConfig.h"
64 #include "NetPlayer.h"
65 #include "NetUtil.h"
66 #include "NetData.h"
67 
68 #include "Game.h"
69 #include "Sound.h"
70 #include "Bolt.h"
71 #include "Solid.h"
72 #include "Sprite.h"
73 #include "Light.h"
74 #include "Bitmap.h"
75 #include "DataLoader.h"
76 #include "ParseUtil.h"
77 #include "MouseController.h"
78 #include "Player.h"
79 #include "Random.h"
80 #include "Video.h"
81 
82 const char* FormatGameTime();
83 
84 // +--------------------------------------------------------------------+
85 
86 class SimHyper
87 {
88 public:
89  SimHyper(Ship* o, SimRegion* r, const Point& l, int t, bool h, Ship* fc1, Ship* fc2)
90  : ship(o), rgn(r), loc(l), type(t), hyperdrive(h), fc_src(fc1), fc_dst(fc2) { }
91 
95  int type;
96  bool hyperdrive;
99 };
100 
101 // +--------------------------------------------------------------------+
102 
104 {
105 public:
106  SimSplash(SimRegion* r, const Point& l, double d, double n)
107  : rgn(r), loc(l), damage(d), range(n),
108  owner_name("Collateral Damage"), missile(false) { }
109 
112  double damage;
113  double range;
115  bool missile;
116 };
117 
118 // +--------------------------------------------------------------------+
119 
120 static bool first_frame = true;
121 Sim* Sim::sim = 0;
122 
124 : ctrl(c), test_mode(false), grid_shown(false), dust(0),
125 star_system(0), active_region(0), mission(0), netgame(0),
126 start_time(0)
127 {
133  MFD::Initialize();
135 
136  if (!sim)
137  sim = this;
138 
140 }
141 
143 {
144  UnloadMission();
145 
146  Shot::Close();
148  NavLight::Close();
149  Token::close();
150  Asteroid::Close();
151 
152  if (sim == this)
153  sim = 0;
154 }
155 
156 // +--------------------------------------------------------------------+
157 
158 void
160 {
161  for (int i = 0; i < regions.size(); i++)
162  regions[i]->CommitMission();
163 
164  if (ShipStats::NumStats() > 0) {
165  Print("\n\nFINAL SCORE '%s'\n", (const char*) mission->Name());
166  Print("Name Kill1 Kill2 Died Colls Points Cmd Pts\n");
167  Print("---------------- ----- ----- ----- ----- ------ ------\n");
168 
169  int tk1 = 0;
170  int tk2 = 0;
171  int td = 0;
172  int tc = 0;
173 
174  for (int i = 0; i < ShipStats::NumStats(); i++) {
176  s->Summarize();
177 
178  Print("%-16s %5d %5d %5d %5d %6d %6d\n",
179  s->GetName(),
180  s->GetGunKills(),
181  s->GetMissileKills(),
182  s->GetDeaths(),
183  s->GetColls(),
184  s->GetPoints(),
185  s->GetCommandPoints());
186 
187  tk1 += s->GetGunKills();
188  tk2 += s->GetMissileKills();
189  td += s->GetDeaths();
190  tc += s->GetColls();
191 
192  CombatGroup* group = s->GetCombatGroup();
193 
194  if (group) {
195  Combatant* c = group->GetCombatant();
196 
197  if (c)
198  c->AddScore(s->GetPoints());
199 
200  if (s->GetElementIndex() == 1)
201  group->SetSorties(group->Sorties() + 1);
202 
203  group->SetKills(group->Kills() + s->GetGunKills() + s->GetMissileKills());
204  group->SetPoints(group->Points() + s->GetPoints());
205  }
206 
207  if (s->IsPlayer()) {
209  p->ProcessStats(s, start_time);
210 
211  if (mission && mission->Type() == Mission::TRAINING &&
212  s->GetDeaths() == 0 && s->GetColls() == 0)
213  p->SetTrained(mission->Identity());
214 
215  Player::Save(); // save training state right now before we forget!
216  }
217  }
218 
219  Print("--------------------------------------------\n");
220  Print("TOTAL %5d %5d %5d %5d\n\n", tk1, tk2, td, tc);
221 
223  }
224 }
225 
226 // +--------------------------------------------------------------------+
227 
228 void
230 {
231  if (netgame) {
232  delete netgame;
233  netgame = 0;
234  }
235 
236  HUDView* hud = HUDView::GetInstance();
237  if (hud)
238  hud->HideAll();
239 
241 
242  events.destroy();
244  elements.destroy();
245  finished.destroy();
246 
247  if (active_region)
249 
250  if (star_system)
252 
253  if (mission) {
254  mission->SetActive(false);
255  mission->SetComplete(true);
256  }
257 
258  regions.destroy();
259  scene.Collect();
260 
262 
263  star_system = 0;
264  active_region = 0;
265  mission = 0;
266 
267  // reclaim memory used by radio traffic:
269 
270  // release texture memory for 2D screens:
272  if (stars)
273  stars->InvalidateTextureCache();
274 
276  if (cam_dir)
277  cam_dir->SetShip(0);
278 
280 }
281 
282 bool
284 {
285  return mission && mission->IsActive();
286 }
287 
288 bool
290 {
291  return mission && mission->IsComplete();
292 }
293 
294 // +--------------------------------------------------------------------+
295 
296 void
297 Sim::LoadMission(Mission* m, bool preload_textures)
298 {
300 
301  if (!mission) {
302  mission = m;
303  mission->SetActive(true);
304 
305  if (preload_textures) {
306  Video* video = Game::GetVideo();
307  List<Model> all_models;
308  //List<Bitmap> all_textures;
309 
311  while (++elem_iter) {
312  MissionElement* elem = elem_iter.value();
313  const ShipDesign* design = elem->GetDesign();
314 
315  if (design) {
316  for (int i = 0; i < 4; i++) {
317  List<Model>& models = (List<Model>&) design->models[i]; // cast-away const
318 
319  ListIter<Model> model_iter = models;
320  while (++model_iter) {
321  Model* model = model_iter.value();
322  if (!all_models.contains(model)) {
323  all_models.append(model);
324  //model->GetAllTextures(all_textures);
325 
326  ListIter<Surface> surf_iter = model->GetSurfaces();
327  while (++surf_iter) {
328  Surface* surface = surf_iter.value();
329  video->PreloadSurface(surface);
330  }
331  }
332  }
333  }
334  }
335  }
336 
337  /*
338  if (video && all_textures.size() > 0) {
339  ::Print("Preloading %d textures into video texture cache\n", all_textures.size());
340  ListIter<Bitmap> bmp_iter = all_textures;
341  while (++bmp_iter) {
342  Bitmap* bmp = bmp_iter.value();
343  video->PreloadTexture(bmp);
344  }
345  }
346  */
347  }
348  }
349 }
350 
351 void
353 {
355 
356  if (!mission) {
357  Print("Sim::ExecMission() - No mission to execute.\n");
358  return;
359  }
360 
361  if (elements.size() || finished.size()) {
362  Print("Sim::ExecMission(%s) mission is already executing.\n", mission->Name());
363  return;
364  }
365 
366  Print("\nExec Mission: '%s'\n", (const char*) mission->Name());
367 
368  if (cam_dir)
369  cam_dir->Reset();
370 
371  if (mission->Stardate() > 0)
373 
376 
377  int dust_factor = 0;
378 
380  dust_factor = Starshatter::GetInstance()->Dust();
381 
382  if (star_system->NumDust() * dust_factor) {
383  dust = new(__FILE__,__LINE__) Dust(star_system->NumDust() * 2*(dust_factor+1), dust_factor > 1);
385  }
386 
387  CreateRegions();
388  BuildLinks();
389  CreateElements();
390  CopyEvents();
391 
392  if (netgame) {
393  delete netgame;
394  netgame = 0;
395  }
396 
397  first_frame = true;
399 
401 }
402 
403 // +--------------------------------------------------------------------+
404 
405 void
407 {
408  const char* active_region_name = 0;
409 
410  if (mission)
411  active_region_name = mission->GetRegion();
412 
414  while (++iter) {
415  StarSystem* sys = iter.value();
416 
417  // insert objects from star system:
418  ListIter<OrbitalBody> star = sys->Bodies();
419  while (++star) {
420  ListIter<OrbitalBody> planet = star->Satellites();
421  while (++planet) {
422  ListIter<OrbitalBody> moon = planet->Satellites();
423  while (++moon) {
424  ListIter<OrbitalRegion> rgn = moon->Regions();
425  while (++rgn) {
426  SimRegion* sim_region = new(__FILE__,__LINE__) SimRegion(this, rgn.value());
427  regions.append(sim_region);
428  if (!strcmp(active_region_name, sim_region->Name())) {
429  ActivateRegion(sim_region);
430  }
431  }
432  }
433 
434  ListIter<OrbitalRegion> rgn = planet->Regions();
435  while (++rgn) {
436  SimRegion* sim_region = new(__FILE__,__LINE__) SimRegion(this, rgn.value());
437  regions.append(sim_region);
438  if (!strcmp(active_region_name, sim_region->Name())) {
439  ActivateRegion(sim_region);
440  }
441  }
442  }
443 
444  ListIter<OrbitalRegion> rgn = star->Regions();
445  while (++rgn) {
446  SimRegion* sim_region = new(__FILE__,__LINE__) SimRegion(this, rgn.value());
447  regions.append(sim_region);
448  if (!strcmp(active_region_name, sim_region->Name())) {
449  ActivateRegion(sim_region);
450  }
451  }
452  }
453  }
454 }
455 
456 // +--------------------------------------------------------------------+
457 
458 void
460 {
462  while (++iter) {
463  SimRegion* rgn = iter.value();
464  OrbitalRegion* orb = rgn->GetOrbitalRegion();
465 
466  if (orb) {
467  ListIter<Text> lnk_iter = orb->Links();
468  while (++lnk_iter) {
469  Text* t = lnk_iter.value();
470 
471  SimRegion* tgt = FindRegion(*t);
472 
473  if (tgt && !rgn->Links().contains(tgt))
474  rgn->Links().append(tgt);
475  }
476  }
477  }
478 }
479 
480 void
482 {
484  while (++e_iter) {
485  MissionElement* msn_elem = e_iter.value();
486 
487  // add element to a carrier?
488  if (msn_elem->IsSquadron()) {
489  Ship* carrier = FindShip(msn_elem->Carrier());
490  if (carrier) {
491  Hangar* hangar = carrier->GetHangar();
492 
493  if (hangar) {
494  int* def_load = 0;
495 
496  if (msn_elem->Loadouts().size()) {
497  MissionLoad* m = msn_elem->Loadouts().at(0);
498 
499  if (m->GetName().length()) {
500  ShipDesign* dsn = (ShipDesign*) msn_elem->GetDesign();
501  ListIter<ShipLoad> sl_iter = dsn->loadouts;
502  while (++sl_iter) {
503  ShipLoad* sl = sl_iter.value();
504 
505  if (m->GetName() == sl->name)
506  def_load = sl->load;
507  }
508  }
509 
510  if (!def_load) {
511  def_load = m->GetStations();
512  }
513  }
514 
515  hangar->CreateSquadron(msn_elem->Name(), msn_elem->GetCombatGroup(),
516  msn_elem->GetDesign(), msn_elem->Count(),
517  msn_elem->GetIFF(),
518  def_load, msn_elem->MaintCount(), msn_elem->DeadCount());
519 
520  Element* element = CreateElement(msn_elem->Name(),
521  msn_elem->GetIFF(),
522  msn_elem->MissionRole());
523 
524  element->SetCarrier(carrier);
525  element->SetCombatGroup(msn_elem->GetCombatGroup());
526  element->SetCombatUnit(msn_elem->GetCombatUnit());
527  element->SetCount(msn_elem->Count());
528  element->SetRogue(false);
529  element->SetPlayable(false);
530  element->SetLoadout(def_load);
531  }
532  }
533  }
534 
535  // create the element in space:
536  else {
537  Ship* carrier = 0;
538  Hangar* hangar = 0;
539  int squadron = -1;
540  int slot = 0;
541 
542  // first create the package element:
543  Element* element = CreateElement(msn_elem->Name(),
544  msn_elem->GetIFF(),
545  msn_elem->MissionRole());
546 
547  element->SetPlayer(msn_elem->Player());
548  element->SetCombatGroup(msn_elem->GetCombatGroup());
549  element->SetCombatUnit(msn_elem->GetCombatUnit());
550  element->SetCommandAILevel(msn_elem->CommandAI());
551  element->SetHoldTime(msn_elem->HoldTime());
552  element->SetZoneLock(msn_elem->ZoneLock() ? true : false);
553  element->SetRogue(msn_elem->IsRogue());
554  element->SetPlayable(msn_elem->IsPlayable());
555  element->SetIntelLevel(msn_elem->IntelLevel());
556 
557  // if this is the player's element, make sure to activate the region:
558  if (msn_elem->Player()) {
559  SimRegion* rgn = FindRegion(msn_elem->Region());
560 
561  if (rgn && rgn != active_region)
562  ActivateRegion(rgn);
563  }
564 
565  // if element belongs to a squadron,
566  // find the carrier, squadron, flight deck, etc.:
567  if (msn_elem->Squadron().length() > 0) {
568  MissionElement* squadron_elem = mission->FindElement(msn_elem->Squadron());
569 
570  if (squadron_elem) {
571  element->SetSquadron(msn_elem->Squadron());
572 
573  Element* cmdr = FindElement(squadron_elem->Carrier());
574 
575  if (cmdr) {
576  element->SetCommander(cmdr);
577  carrier = cmdr->GetShip(1);
578 
579  if (carrier) {
580  element->SetCarrier(carrier);
581  hangar = carrier->GetHangar();
582 
583  for (int s = 0; s < hangar->NumSquadrons(); s++) {
584  if (hangar->SquadronName(s) == msn_elem->Squadron()) {
585  squadron = s;
586  break;
587  }
588  }
589  }
590  }
591  }
592  }
593 
594  else if (msn_elem->Commander().length() > 0) {
595  Element* cmdr = FindElement(msn_elem->Commander());
596 
597  if (cmdr) {
598  element->SetCommander(cmdr);
599  }
600  }
601 
602  ListIter<Instruction> obj = msn_elem->Objectives();
603  while (++obj) {
604  Instruction* o = obj.value();
605  Instruction* instr = 0;
606 
607  instr = new(__FILE__,__LINE__) Instruction(*o);
608 
609  element->AddObjective(instr);
610  }
611 
612  if (msn_elem->Instructions().size() > 0) {
613  ListIter<Text> instr = msn_elem->Instructions();
614  while (++instr) {
615  element->AddInstruction(*instr);
616  }
617  }
618 
619  ListIter<Instruction> nav = msn_elem->NavList();
620  while (++nav) {
621  SimRegion* rgn = FindRegion(nav->RegionName());
622 
623  if (!rgn)
624  rgn = FindRegion(msn_elem->Region());
625 
626  if (rgn) {
627  Instruction* npt = new(__FILE__,__LINE__)
628  Instruction(rgn, nav->Location(), nav->Action());
629 
630  npt->SetStatus(nav->Status());
631  npt->SetEMCON(nav->EMCON());
632  npt->SetFormation(nav->Formation());
633  npt->SetSpeed(nav->Speed());
634  npt->SetTarget(nav->TargetName());
635  npt->SetHoldTime(nav->HoldTime());
636  npt->SetFarcast(nav->Farcast());
637 
638  element->AddNavPoint(npt);
639  }
640  }
641 
642  bool alertPrep = false;
643  int* loadout = 0;
644  int respawns = msn_elem->RespawnCount();
645 
646  // if ships are to start on alert,
647  // spot them onto the appropriate launch deck:
648  if (hangar && element && msn_elem->Count() > 0 && msn_elem->IsAlert()) {
649  FlightDeck* deck = 0;
650  int queue = 1000;
651  const ShipDesign* dsn = msn_elem->GetDesign();
652 
653  if (dsn) {
654  for (int i = 0; i < carrier->NumFlightDecks(); i++) {
655  FlightDeck* d = carrier->GetFlightDeck(i);
656  int dq = hangar->PreflightQueue(d);
657 
658  if (d && d->IsLaunchDeck() && d->SpaceLeft(dsn->type) && dq < queue) {
659  queue = dq;
660  deck = d;
661  }
662  }
663  }
664 
665  if (deck) {
666  alertPrep = true;
667 
668  // choose best loadout:
669  if (msn_elem->Loadouts().size()) {
670  MissionLoad* l = msn_elem->Loadouts().at(0);
671  if (l->GetName().length()) {
672  ListIter<ShipLoad> sl = ((ShipDesign*) dsn)->loadouts;
673  while (++sl) {
674  if (!_stricmp(sl->name, l->GetName()))
675  loadout = sl->load;
676  }
677  }
678 
679  else {
680  loadout = l->GetStations();
681  }
682  }
683 
684  element->SetLoadout(loadout);
685 
686  for (int i = 0; i < msn_elem->Count(); i++) {
687  int squadron = -1;
688  int slot = -1;
689 
690  if (hangar->FindAvailSlot(msn_elem->GetDesign(), squadron, slot)) {
691  alertPrep = alertPrep &&
692  hangar->GotoAlert(squadron,
693  slot,
694  deck,
695  element,
696  loadout,
697  true, // package for launch
698  true); // expedite
699 
700  HangarSlot* s = (HangarSlot*) hangar->GetSlot(squadron, slot);
701  Ship* alertShip = hangar->GetShip(s);
702 
703  if (alertShip) {
704  alertShip->SetRespawnCount(respawns);
705 
706  if (msn_elem->Player() == i+1) {
707  if (alertShip->GetRegion()) {
708  alertShip->GetRegion()->SetPlayerShip(alertShip);
709  }
710  else {
711  ::Print("WARNING: alert ship '%s' region is null\n", alertShip->Name());
712  }
713  }
714  }
715  }
716  }
717  }
718  }
719 
720  if (!alertPrep) {
721  // then, create the ships:
722  for (int i = 0; i < msn_elem->Count(); i++) {
723  MissionShip* msn_ship = 0;
724  Text sname = msn_elem->GetShipName(i);
725  Text rnum = msn_elem->GetRegistry(i);
726  Text rgn_name = msn_elem->Region();
727 
728  if (msn_elem->Ships().size() > i) {
729  msn_ship = msn_elem->Ships()[i];
730  sname = msn_ship->Name();
731  rnum = msn_ship->RegNum();
732  rgn_name = msn_ship->Region();
733  }
734 
735  Point l2 = msn_elem->Location();
736 
737  if (msn_ship && fabs(msn_ship->Location().x) < 1e9) {
738  l2 = msn_ship->Location();
739  }
740  else if (i) {
741  Point offset = RandomPoint();
742  offset.z = Random(-1e3, 1e3);
743 
744  if (msn_elem->Count() < 5)
745  offset *= 0.3;
746 
747  l2 += offset;
748  }
749 
750  // choose best loadout:
751  ListIter<MissionLoad> l = msn_elem->Loadouts();
752  while (++l) {
753  if ((l->GetShip() == i) || (l->GetShip() < 0 && loadout == 0)) {
754  if (l->GetName().length()) {
755  ListIter<ShipLoad> sl = ((ShipDesign*) msn_elem->GetDesign())->loadouts;
756  while (++sl) {
757  if (!_stricmp(sl->name, l->GetName()))
758  loadout = sl->load;
759  }
760  }
761 
762  else {
763  loadout = l->GetStations();
764  }
765  }
766  }
767 
768  element->SetLoadout(loadout);
769 
770  Ship* ship = CreateShip(sname, rnum,
771  (ShipDesign*) msn_elem->GetDesign(),
772  rgn_name, l2,
773  msn_elem->GetIFF(),
774  msn_elem->CommandAI(),
775  loadout);
776 
777  if (ship) {
778  double heading = msn_elem->Heading();
779  const Skin* skin = msn_elem->GetSkin();
780 
781  if (msn_ship) {
782  heading = msn_ship->Heading();
783 
784  if (msn_ship->GetSkin())
785  skin = msn_ship->GetSkin();
786  }
787 
788  ship->SetRogue(msn_elem->IsRogue());
789  ship->SetInvulnerable(msn_elem->IsInvulnerable());
790  ship->SetHeading(0, 0, heading + PI);
791  ship->SetRespawnCount(respawns);
792  ship->UseSkin(skin);
793 
794  if (!netgame)
795  ship->SetRespawnLoc(RandomPoint() * 2);
796 
797  if (ship->IsStarship())
798  ship->SetHelmHeading(heading);
799 
800  else if (ship->IsAirborne() && ship->AltitudeAGL() > 25)
801  ship->SetVelocity(ship->Heading() * 250);
802 
803  if (element)
804  element->AddShip(ship);
805 
806  if (hangar)
807  hangar->FindSlot(ship, squadron, slot, Hangar::ACTIVE);
808 
809  if (ship->GetRegion() && msn_elem->Player() == i+1)
810  ship->GetRegion()->SetPlayerShip(ship);
811 
812  if (ship->NumFlightDecks()) {
813  for (int i = 0; i < ship->NumFlightDecks(); i++) {
814  FlightDeck* deck = ship->GetFlightDeck(i);
815  if (deck)
816  deck->Orient(ship);
817  }
818  }
819 
820  if (msn_ship) {
821  ship->SetVelocity(msn_ship->Velocity().OtherHand());
822  ship->SetIntegrity((float) msn_ship->Integrity());
823  ship->SetRespawnCount(msn_ship->Respawns());
824 
825  if (msn_ship->Ammo()[0] > -10) {
826  for (int i = 0; i < 64; i++) {
827  Weapon* w = ship->GetWeaponByIndex(i+1);
828  if (w)
829  w->SetAmmo(msn_ship->Ammo()[i]);
830  else
831  break;
832  }
833  }
834 
835  if (msn_ship->Fuel()[0] > -10) {
836  for (int i = 0; i < 4; i++) {
837  if (ship->Reactors().size() > i) {
838  PowerSource* p = ship->Reactors()[i];
839  p->SetCapacity(msn_ship->Fuel()[i]);
840  }
841  }
842  }
843 
844  if (msn_ship->Decoys() > -10) {
845  Weapon* w = ship->GetDecoy();
846  if (w)
847  w->SetAmmo(msn_ship->Decoys());
848  }
849 
850  if (msn_ship->Probes() > -10) {
851  Weapon* w = ship->GetProbeLauncher();
852  if (w)
853  w->SetAmmo(msn_ship->Probes());
854  }
855  }
856 
857  Shield* shield = ship->GetShield();
858 
859  if (shield) {
860  shield->SetPowerLevel(50);
861  }
862 
863  if (ship->Class() > Ship::FRIGATE) {
864  ListIter<WeaponGroup> iter = ship->Weapons();
865  while (++iter) {
866  WeaponGroup* weapon = iter.value();
867 
868  // anti-air weapon?
869  if (weapon->GetDesign()->target_type & Ship::DRONE) {
871  }
872  else {
874  }
875  }
876  }
877 
878  if (ship->Class() > Ship::DRONE && ship->Class() < Ship::STATION) {
879  ShipStats* stats = ShipStats::Find(sname);
880  if (stats) {
881  char design[64];
882  sprintf_s(design, "%s %s", ship->Abbreviation(), ship->Design()->display_name);
883  stats->SetType(design);
884  stats->SetShipClass(ship->Class());
885  stats->SetRole(Mission::RoleName(msn_elem->MissionRole()));
886  stats->SetIFF(ship->GetIFF());
887  stats->SetRegion(msn_elem->Region());
888  stats->SetCombatGroup(msn_elem->GetCombatGroup());
889  stats->SetCombatUnit(msn_elem->GetCombatUnit());
890  stats->SetPlayer(msn_elem->Player() == i+1);
891  stats->SetElementIndex(ship->GetElementIndex());
892  }
893  }
894  } // ship
895  } // count
896  }
897  }
898  }
899 }
900 
901 void
903 {
904  events.destroy();
905 
906  if (mission) {
908  while (++iter) {
909  MissionEvent* orig = iter.value();
910  MissionEvent* event = new(__FILE__,__LINE__) MissionEvent(*orig);
911  events.append(event);
912  }
913  }
914 }
915 
916 // +--------------------------------------------------------------------+
917 
918 const char*
920 {
921  const char* call = "Unidentified";
922 
923  for (int i = 0; i < 32; i++) {
924  call = Callsign::GetCallsign(IFF);
925 
926  if (!FindElement(call))
927  break;
928  }
929 
930  return call;
931 }
932 
933 Element*
934 Sim::CreateElement(const char* callsign, int IFF, int type)
935 {
936  Element* elem = new(__FILE__,__LINE__) Element(callsign, IFF, type);
937  elements.append(elem);
938  return elem;
939 }
940 
941 void
943 {
944  if (elements.contains(elem))
945  elements.remove(elem);
946 
947  delete elem;
948 }
949 
950 Element*
951 Sim::FindElement(const char* name)
952 {
954 
955  while (++iter) {
956  Element* elem = iter.value();
957  Text ename = elem->Name();
958 
959  if (ename == name)
960  return elem;
961  }
962 
963  return 0;
964 }
965 
966 // +--------------------------------------------------------------------+
967 
968 int
970 {
971  assigned.clear();
972 
973  if (elem) {
974  for (int i = 0; i < elements.size(); i++) {
975  Element* e = elements.at(i);
976  if (!e->IsSquadron() && e->GetAssignment() == elem)
977  assigned.append(e);
978  }
979  }
980 
981  return assigned.size();
982 }
983 
984 // +--------------------------------------------------------------------+
985 
986 Ship*
987 Sim::CreateShip(const char* name, const char* reg_num, ShipDesign* design, const char* rgn_name, const Point& loc, int IFF, int cmd_ai, const int* loadout)
988 {
989  if (!design) {
990  Print("WARNING: CreateShip(%s): invalid design\n", name);
991  return 0;
992  }
993 
994  SimRegion* rgn = FindRegion(rgn_name);
995 
996  if (!rgn) {
997  return 0;
998  }
999 
1000  Ship* ship = new(__FILE__,__LINE__) Ship(name, reg_num, design, IFF, cmd_ai, loadout);
1001  ship->MoveTo(loc.OtherHand());
1002 
1003  if (rgn) {
1004  Print("Inserting Ship(%s) into Region(%s) (%s)\n", ship->Name(), rgn->Name(), FormatGameTime());
1005  rgn->InsertObject(ship);
1006 
1007  if (ship->IsAirborne() && ship->AltitudeAGL() > 25)
1008  ship->SetVelocity(ship->Heading() * 250);
1009  }
1010 
1011  return ship;
1012 }
1013 
1014 Ship*
1015 Sim::FindShip(const char* name, const char* rgn_name)
1016 {
1017  Ship* ship = 0;
1018 
1019  if (rgn_name) {
1020  SimRegion* rgn = FindRegion(rgn_name);
1021  if (rgn)
1022  ship = rgn->FindShip(name);
1023  }
1024 
1025  if (!ship) {
1027  while (++rgn && !ship)
1028  ship = rgn->FindShip(name);
1029  }
1030 
1031  return ship;
1032 }
1033 
1034 void
1036 {
1037  SimRegion* rgn = ship->GetRegion();
1038  if (rgn)
1039  rgn->DestroyShip(ship);
1040 }
1041 
1042 void
1043 Sim::NetDockShip(Ship* ship, Ship* carrier, FlightDeck* deck)
1044 {
1045  SimRegion* rgn = ship->GetRegion();
1046  if (rgn)
1047  rgn->NetDockShip(ship, carrier, deck);
1048 }
1049 
1050 Ship*
1052 {
1053  Ship* ship = 0;
1054 
1056  while (++rgn && !ship)
1057  ship = rgn->FindShipByObjID(objid);
1058 
1059  return ship;
1060 }
1061 
1062 Shot*
1064 {
1065  Shot* shot = 0;
1066 
1068  while (++rgn && !shot)
1069  shot = rgn->FindShotByObjID(objid);
1070 
1071  return shot;
1072 }
1073 
1074 // +--------------------------------------------------------------------+
1075 
1076 Orbital*
1077 Sim::FindOrbitalBody(const char* name)
1078 {
1079  Orbital* body = 0;
1080 
1081  if (mission) {
1083  while (++iter && !body) {
1084  StarSystem* sys = iter.value();
1085  body = sys->FindOrbital(name);
1086  }
1087  }
1088 
1089  return body;
1090 }
1091 
1092 
1093 // +--------------------------------------------------------------------+
1094 
1095 Shot*
1096 Sim::CreateShot(const Point& pos, const Camera& shot_cam, WeaponDesign* design, const Ship* ship, SimRegion* rgn)
1097 {
1098  Shot* shot = 0;
1099 
1100  if (design->drone)
1101  shot = new(__FILE__,__LINE__) Drone(pos, shot_cam, design, ship);
1102  else
1103  shot = new(__FILE__,__LINE__) Shot( pos, shot_cam, design, ship);
1104 
1105  if (rgn)
1106  rgn->InsertObject(shot);
1107 
1108  else if (active_region)
1109  active_region->InsertObject(shot);
1110 
1111  return shot;
1112 }
1113 
1114 // +--------------------------------------------------------------------+
1115 
1116 Explosion*
1117 Sim::CreateExplosion(const Point& pos, const Point& vel, int type, float exp_scale, float part_scale, SimRegion* rgn, SimObject* source, System* sys)
1118 {
1119  // don't bother creating explosions that can't be seen:
1120  if (!rgn || !active_region || rgn != active_region)
1121  return 0;
1122 
1123  Explosion* exp = new(__FILE__,__LINE__) Explosion(type, pos, vel, exp_scale, part_scale, rgn, source);
1124 
1125  if (rgn)
1126  rgn->InsertObject(exp);
1127 
1128  else if (active_region)
1130 
1131  return exp;
1132 }
1133 
1134 // +--------------------------------------------------------------------+
1135 
1136 Debris*
1137 Sim::CreateDebris(const Point& pos, const Point& vel, Model* model, double mass, SimRegion* rgn)
1138 {
1139  Debris* debris = new(__FILE__,__LINE__) Debris(model, pos, vel, mass);
1140 
1141  if (rgn)
1142  rgn->InsertObject(debris);
1143 
1144  else if (active_region)
1145  active_region->InsertObject(debris);
1146 
1147  return debris;
1148 }
1149 
1150 // +--------------------------------------------------------------------+
1151 
1152 Asteroid*
1153 Sim::CreateAsteroid(const Point& pos, int t, double mass, SimRegion* rgn)
1154 {
1155  Asteroid* asteroid = new(__FILE__,__LINE__) Asteroid(t, pos, mass);
1156 
1157  if (rgn)
1158  rgn->InsertObject(asteroid);
1159 
1160  else if (active_region)
1161  active_region->InsertObject(asteroid);
1162 
1163  return asteroid;
1164 }
1165 
1166 // +--------------------------------------------------------------------+
1167 
1168 void
1170 {
1171  if (ship && ship->GetRegion() && ship->Design()->splash_radius > 1) {
1172  SimSplash* splash = new(__FILE__,__LINE__)
1173  SimSplash(ship->GetRegion(),
1174  ship->Location(),
1175  ship->Design()->integrity / 4,
1176  ship->Design()->splash_radius);
1177 
1178  splash->owner_name = ship->Name();
1179  splashlist.append(splash);
1180  }
1181 }
1182 
1183 // +--------------------------------------------------------------------+
1184 
1185 void
1187 {
1188  if (shot && shot->GetRegion()) {
1189  double damage = shot->Damage();
1190  if (damage < shot->Design()->damage)
1191  damage = shot->Design()->damage;
1192 
1193  SimSplash* splash = new(__FILE__,__LINE__)
1194  SimSplash(shot->GetRegion(),
1195  shot->Location(),
1196  damage,
1197  shot->Design()->lethal_radius);
1198 
1199  if (shot->Owner())
1200  splash->owner_name = shot->Owner()->Name();
1201 
1202  splash->missile = shot->IsMissile();
1203 
1204  splashlist.append(splash);
1205  CreateExplosion(shot->Location(), Point(), Explosion::SHOT_BLAST, 20.0f, 1.0f, shot->GetRegion());
1206  }
1207 }
1208 
1209 // +--------------------------------------------------------------------+
1210 
1211 void
1212 Sim::ShowGrid(int show)
1213 {
1214  Player* player = Player::GetCurrentPlayer();
1215 
1216  if (player && player->GridMode() == 0) {
1217  show = 0;
1218  grid_shown = false;
1219  }
1220 
1222  while (++rgn) {
1223  rgn->ShowGrid(show);
1224  }
1225 
1226  grid_shown = show?true:false;
1227 }
1228 
1229 bool
1231 {
1232  return grid_shown;
1233 }
1234 
1235 // +--------------------------------------------------------------------+
1236 
1239 {
1240  if (mission)
1241  return mission->GetSystemList();
1242 
1243  static List<StarSystem> dummy_system_list;
1244  return dummy_system_list;
1245 }
1246 
1247 // +--------------------------------------------------------------------+
1248 
1249 void
1251 {
1252  if (active_region)
1254 }
1255 
1256 Ship*
1258 {
1259  if (active_region)
1260  return active_region->GetPlayerShip();
1261 
1263  if (stars && stars->InCutscene()) {
1264  Ship* player = 0;
1265 
1267  while (++rgn && !player) {
1268  player = rgn->GetPlayerShip();
1269  }
1270 
1271  return player;
1272  }
1273 
1274  return 0;
1275 }
1276 
1277 Element*
1279 {
1280  Element* elem = 0;
1281 
1282  for (int i = 0; i < elements.size(); i++) {
1283  Element* e = elements[i];
1284 
1285  if (e->Player() > 0)
1286  elem = e;
1287  }
1288 
1289  return elem;
1290 }
1291 
1292 bool
1294 {
1295  if (active_region)
1296  return active_region->IsSelected(s);
1297 
1298  return false;
1299 }
1300 
1303 {
1304  if (active_region)
1305  return active_region->GetSelection();
1306 
1307  static List<Ship> empty;
1308  return empty;
1309 }
1310 
1311 void
1313 {
1314  if (active_region)
1316 }
1317 
1318 void
1320 {
1321  if (active_region)
1323 }
1324 
1325 void
1327 {
1328  if (active_region)
1329  active_region->SetSelection(newsel);
1330 }
1331 
1332 // +--------------------------------------------------------------------+
1333 
1334 void
1336 {
1337  test_mode = t;
1338  Ship* pship = GetPlayerShip();
1339 
1340  if (pship)
1341  if (IsTestMode())
1342  pship->SetControls(0);
1343  else
1344  pship->SetControls(ctrl);
1345 }
1346 
1347 // +--------------------------------------------------------------------+
1348 
1349 SimRegion*
1350 Sim::FindRegion(const char* name)
1351 {
1353  while (++rgn)
1354  if (rgn->name == name)
1355  return rgn.value();
1356 
1357  return 0;
1358 }
1359 
1360 SimRegion*
1362 {
1364  while (++rgn)
1365  if (rgn->orbital_region == orgn)
1366  return rgn.value();
1367 
1368  return 0;
1369 }
1370 
1371 // +--------------------------------------------------------------------+
1372 
1373 SimRegion*
1375 {
1376  return FindNearestRegion(object, REAL_SPACE);
1377 }
1378 
1379 SimRegion*
1381 {
1382  return FindNearestRegion(object, AIR_SPACE);
1383 }
1384 
1385 SimRegion*
1387 {
1388  if (!object) return 0;
1389 
1390  SimRegion* result = 0;
1391  double distance = 1.0e40;
1392  Point objloc = object->Location();
1393 
1394  objloc = objloc.OtherHand();
1395 
1396  if (object->GetRegion())
1397  objloc += object->GetRegion()->Location();
1398 
1400  while (++rgn) {
1401  if (rgn->Type() == type) {
1402  OrbitalRegion* orgn = rgn->GetOrbitalRegion();
1403  if (orgn) {
1404  double test = fabs((orgn->Location() - objloc).length());
1405  if (test < distance) {
1406  result = rgn.value();
1407  distance = test;
1408  }
1409  }
1410  }
1411  }
1412 
1413  return result;
1414 }
1415 
1416 SimRegion*
1418 {
1419  SimRegion* result = 0;
1420 
1421  if (!body)
1422  return result;
1423 
1425  while (++rgn && !result) {
1426  if (rgn->IsOrbital()) {
1427  OrbitalRegion* orgn = rgn->GetOrbitalRegion();
1428  if (orgn) {
1429  ListIter<OrbitalRegion> iter = body->Regions();
1430  while (++iter) {
1431  if (iter.value() == orgn)
1432  result = rgn.value();
1433  }
1434  }
1435  }
1436  }
1437 
1438  return result;
1439 }
1440 
1441 // +--------------------------------------------------------------------+
1442 
1443 bool
1445 {
1446  if (rgn && active_region != rgn && regions.contains(rgn)) {
1447  if (active_region)
1449 
1450  if (!active_region || active_region->System() != rgn->System()) {
1451  if (active_region)
1453  rgn->System()->Activate(scene);
1454  }
1455 
1456  active_region = rgn;
1458 
1459  if (star_system) {
1461  }
1462  else {
1463  ::Print("WARNING: Sim::ActivateRegion() No star system found for rgn '%s'", rgn->Name());
1464  }
1465 
1467  return true;
1468  }
1469 
1470  return false;
1471 }
1472 
1473 // +--------------------------------------------------------------------+
1474 
1475 void
1477 int type, Ship* fc1, Ship* fc2)
1478 {
1479  bool hyperdrive = false;
1480 
1481  if (obj->GetQuantumDrive() && obj->GetQuantumDrive()->Subtype() == QuantumDrive::HYPER)
1482  hyperdrive = true;
1483 
1484  jumplist.append(new(__FILE__,__LINE__) SimHyper(obj, rgn, loc, type, hyperdrive, fc1, fc2));
1485 }
1486 
1487 // +--------------------------------------------------------------------+
1488 
1489 void
1490 Sim::ExecFrame(double seconds)
1491 {
1492  if (first_frame) {
1493  first_frame = false;
1495  }
1496 
1497  if (netgame)
1498  netgame->ExecFrame();
1499 
1500  if (regions.isEmpty()) {
1501  active_region = 0;
1502  rgn_queue.clear();
1503  jumplist.destroy();
1504  scene.Collect();
1505  return;
1506  }
1507 
1508  ListIter<Element> elem = elements;
1509  while (++elem)
1510  if (!elem->IsSquadron())
1511  elem->ExecFrame(seconds);
1512 
1514  while (++rgn)
1515  if (rgn.value() != active_region && rgn->NumShips() && !rgn_queue.contains(rgn.value()))
1516  rgn_queue.append(rgn.value());
1517 
1518  // execframe for one inactive sim region:
1519  if (rgn_queue.size()) {
1520  SimRegion* exec_rgn = rgn_queue.removeIndex(0);
1521 
1522  while (exec_rgn && (exec_rgn->NumShips() == 0 || exec_rgn == active_region))
1523  if (rgn_queue.size())
1524  exec_rgn = rgn_queue.removeIndex(0);
1525  else
1526  exec_rgn = 0;
1527 
1528  if (exec_rgn)
1529  exec_rgn->ExecFrame(seconds);
1530  }
1531 
1532  if (active_region)
1533  active_region->ExecFrame(seconds);
1534 
1535  ExecEvents(seconds);
1536  ResolveHyperList();
1538 
1539  // GC all the dead objects:
1540  scene.Collect();
1541 
1542  if (!IsTestMode()) {
1543  ListIter<Element> e_iter = elements;
1544  while (++e_iter) {
1545  Element* elem = e_iter.value();
1546  if (!elem->IsSquadron() && elem->IsFinished()) {
1547  finished.append(e_iter.removeItem());
1548  }
1549  }
1550  }
1551 
1552  // setup music
1553  if (!MusicDirector::IsNoMusic()) {
1555  if (stars && stars->GetGameMode() == Starshatter::PLAY_MODE) {
1556  Ship* player_ship = GetPlayerShip();
1557  if (player_ship) {
1558  int phase = player_ship->GetFlightPhase();
1559 
1560  if (phase < Ship::ACTIVE) {
1562  }
1563 
1564  else if (phase > Ship::ACTIVE) {
1566  }
1567 
1568  else {
1569  if (player_ship->IsInCombat()) {
1571  }
1572 
1573  else {
1575  }
1576  }
1577  }
1578  }
1579  }
1580 }
1581 
1582 void
1583 Sim::ExecEvents(double seconds)
1584 {
1586  while (++iter) {
1587  MissionEvent* event = iter.value();
1588  event->ExecFrame(seconds);
1589  }
1590 }
1591 
1592 void
1594 {
1595  // resolve the hyper space transitions:
1596  if (jumplist.size()) {
1597  Ship* pship = GetPlayerShip();
1598 
1599  ListIter<SimHyper> j_iter = jumplist;
1600  while (++j_iter) {
1601  SimHyper* jump = j_iter.value();
1602  Ship* jumpship = jump->ship;
1603 
1604  if (jumpship) {
1605  SimRegion* dest = jump->rgn;
1606 
1607  if (!dest)
1608  dest = FindNearestSpaceRegion(jumpship);
1609 
1610  if (dest) {
1611  // bring along fighters on deck:
1612  ListIter<FlightDeck> deck = jumpship->FlightDecks();
1613  while (++deck) {
1614  for (int i = 0; i < deck->NumSlots(); i++) {
1615  Ship* s = deck->GetShip(i);
1616 
1617  if (s) {
1618  dest->InsertObject(s);
1619  s->ClearTrack();
1620  }
1621  }
1622  }
1623 
1624  if (jump->type == 0 && !jump->hyperdrive) {
1625  // bring along nearby ships:
1626  // have to do it in two parts, because inserting the ships
1627  // into the destination corrupts the iter over the current
1628  // region's list of ships...
1629 
1630  // part one: gather the ships that will be jumping:
1631  List<Ship> riders;
1632  ListIter<Ship> neighbor = jumpship->GetRegion()->Ships();
1633  while (++neighbor) {
1634  if (neighbor->IsDropship()) {
1635  Ship* s = neighbor.value();
1636  if (s == jumpship) continue;
1637 
1638  Point delta = s->Location() - jumpship->Location();
1639 
1640  if (delta.length() < 5e3) {
1641  riders.append(s);
1642  }
1643  }
1644  }
1645 
1646  // part two: now transfer the list to the destination:
1647  for (int i = 0; i < riders.size(); i++) {
1648  Ship* s = riders[i];
1649  Point delta = s->Location() - jumpship->Location();
1650  dest->InsertObject(s);
1651  s->MoveTo(jump->loc.OtherHand() + delta);
1652  s->ClearTrack();
1653 
1654  if (jump->fc_dst) {
1655  double r = jump->fc_dst->Roll();
1656  double p = jump->fc_dst->Pitch();
1657  double w = jump->fc_dst->Yaw();
1658 
1659  s->SetAbsoluteOrientation(r, p, w);
1660  s->SetVelocity(jump->fc_dst->Heading() * 500);
1661  }
1662 
1664  }
1665  }
1666 
1667  // now it is safe to move the main jump ship:
1668  dest->InsertObject(jumpship);
1669  jumpship->MoveTo(jump->loc.OtherHand());
1670  jumpship->ClearTrack();
1671 
1673  NetUtil::SendObjHyper(jumpship, dest->Name(), jump->loc, jump->fc_src, jump->fc_dst, jump->type);
1674 
1675  // if using farcaster:
1676  if (jump->fc_src) {
1677  ::Print("Ship '%s' farcast to '%s'\n", jumpship->Name(), dest->Name());
1678  CreateExplosion(jumpship->Location(), Point(0,0,0), Explosion::QUANTUM_FLASH, 1.0f, 0, dest);
1679 
1680  if (jump->fc_dst) {
1681  double r = jump->fc_dst->Roll();
1682  double p = jump->fc_dst->Pitch();
1683  double w = jump->fc_dst->Yaw();
1684 
1685  jumpship->SetAbsoluteOrientation(r, p, w);
1686  jumpship->SetVelocity(jump->fc_dst->Heading() * 500);
1687  }
1688 
1689  jumpship->SetHelmHeading(jumpship->CompassHeading());
1690  jumpship->SetHelmPitch(0);
1691  }
1692 
1693  // break orbit:
1694  else if (jump->type == Ship::TRANSITION_DROP_ORBIT) {
1695  ::Print("Ship '%s' broke orbit to '%s'\n", jumpship->Name(), dest->Name());
1696  jumpship->SetAbsoluteOrientation(0,PI/4,0);
1697  jumpship->SetVelocity(jumpship->Heading() * 1.0e3);
1698  }
1699 
1700  // make orbit:
1701  else if (jump->type == Ship::TRANSITION_MAKE_ORBIT) {
1702  ::Print("Ship '%s' achieved orbit '%s'\n", jumpship->Name(), dest->Name());
1703  jumpship->LookAt(Point(0,0,0));
1704  jumpship->SetVelocity(jumpship->Heading() * 500.0);
1705  }
1706 
1707  // hyper jump:
1708  else {
1709  ::Print("Ship '%s' quantum to '%s'\n", jumpship->Name(), dest->Name());
1710 
1711  if (jump->hyperdrive)
1712  CreateExplosion(jumpship->Location(), Point(0,0,0), Explosion::HYPER_FLASH, 1, 1, dest);
1713  else
1714  CreateExplosion(jumpship->Location(), Point(0,0,0), Explosion::QUANTUM_FLASH, 1, 0, dest);
1715 
1716  jumpship->LookAt(Point(0,0,0));
1717  jumpship->SetVelocity(jumpship->Heading() * 500.0);
1718  jumpship->SetHelmHeading(jumpship->CompassHeading());
1719  jumpship->SetHelmPitch(0);
1720  }
1721  }
1722 
1723  else if (regions.size() > 1) {
1724  ::Print("Warning: Unusual jump request for ship '%s'\n", jumpship->Name());
1725  regions[1]->InsertObject(jumpship);
1726  }
1727 
1728  Sensor* sensor = jumpship->GetSensor();
1729  if (sensor)
1730  sensor->ClearAllContacts();
1731  }
1732  }
1733 
1734  jumplist.destroy();
1735 
1736  if (pship && pship->GetRegion()) {
1737  if (active_region != pship->GetRegion()) {
1738  pship->GetRegion()->SetPlayerShip(pship);
1739  }
1740  }
1741  }
1742 }
1743 
1744 void
1746 {
1747  if (splashlist.size()) {
1749  while (++iter) {
1750  SimSplash* splash = iter.value();
1751 
1752  if (!splash->rgn)
1753  continue;
1754 
1755  // damage ships:
1756  ListIter<Ship> s_iter = splash->rgn->Ships();
1757  while (++s_iter) {
1758  Ship* ship = s_iter.value();
1759 
1760  double distance = (ship->Location() - splash->loc).length();
1761 
1762  if (distance > 1 && distance < splash->range) {
1763  double damage = splash->damage * (1 - distance/splash->range);
1764  if (!NetGame::IsNetGameClient()) {
1765  ship->InflictDamage(damage);
1766  }
1767 
1768  int ship_destroyed = (!ship->InTransition() && ship->Integrity() < 1.0f);
1769 
1770  // then delete the ship:
1771  if (ship_destroyed) {
1773  Print(" %s Killed %s (%s)\n", (const char*) splash->owner_name, ship->Name(), FormatGameTime());
1774 
1775  // record the kill
1776  ShipStats* killer = ShipStats::Find(splash->owner_name);
1777  if (killer) {
1778  if (splash->missile)
1779  killer->AddEvent(SimEvent::MISSILE_KILL, ship->Name());
1780  else
1781  killer->AddEvent(SimEvent::GUNS_KILL, ship->Name());
1782  }
1783 
1784  Ship* owner = FindShip(splash->owner_name, splash->rgn->Name());
1785  if (owner && owner->GetIFF() != ship->GetIFF()) {
1786  if (ship->GetIFF() > 0 || owner->GetIFF() > 1) {
1787  killer->AddPoints(ship->Value());
1788 
1789  Element* elem = owner->GetElement();
1790  if (elem) {
1791  if (owner->GetElementIndex() > 1) {
1792  Ship* s = elem->GetShip(1);
1793 
1794  if (s) {
1795  ShipStats* cmdr_stats = ShipStats::Find(s->Name());
1796  if (cmdr_stats) {
1797  cmdr_stats->AddCommandPoints(ship->Value()/2);
1798  }
1799  }
1800  }
1801 
1802  Element* cmdr = elem->GetCommander();
1803  if (cmdr) {
1804  Ship* s = cmdr->GetShip(1);
1805 
1806  if (s) {
1807  ShipStats* cmdr_stats = ShipStats::Find(s->Name());
1808  if (cmdr_stats) {
1809  cmdr_stats->AddCommandPoints(ship->Value()/2);
1810  }
1811  }
1812  }
1813  }
1814  }
1815  }
1816 
1817  ShipStats* killee = ShipStats::Find(ship->Name());
1818  if (killee)
1819  killee->AddEvent(SimEvent::DESTROYED, splash->owner_name);
1820 
1821  ship->DeathSpiral();
1822  }
1823  }
1824  }
1825 
1826  // damage drones:
1827  ListIter<Drone> drone_iter = splash->rgn->Drones();
1828  while (++drone_iter) {
1829  Drone* drone = drone_iter.value();
1830 
1831  double distance = (drone->Location() - splash->loc).length();
1832 
1833  if (distance > 1 && distance < splash->range) {
1834  double damage = splash->damage * (1 - distance/splash->range);
1835  drone->InflictDamage(damage);
1836 
1837  int destroyed = (drone->Integrity() < 1.0f);
1838 
1839  // then mark the drone for deletion:
1840  if (destroyed) {
1841  NetUtil::SendWepDestroy(drone);
1842  sim->CreateExplosion(drone->Location(), drone->Velocity(), 21 /* was LARGE_EXP */, 1.0f, 1.0f, splash->rgn);
1843  drone->SetLife(0);
1844  }
1845  }
1846  }
1847  }
1848 
1849  splashlist.destroy();
1850  }
1851 }
1852 
1853 // +--------------------------------------------------------------------+
1854 
1855 void
1856 Sim::ProcessEventTrigger(int type, int event_id, const char* ship, int param)
1857 {
1858  Text ship_name = ship;
1859 
1861  while (++iter) {
1862  MissionEvent* event = iter.value();
1863 
1864  if (event->IsPending() && event->Trigger() == type) {
1865  switch (type) {
1872  if (event->TriggerParam() <= param) {
1873  if (ship_name.indexOf(event->TriggerShip()) == 0)
1874  event->Activate();
1875  }
1876  break;
1877 
1879  if (event->TriggerParam() == param) {
1880  if (ship_name.indexOf(event->TriggerShip()) == 0)
1881  event->Activate();
1882  }
1883  break;
1884 
1887  if (event->TriggerParam() == event_id)
1888  event->Activate();
1889  break;
1890  }
1891  }
1892  }
1893 }
1894 
1895 double
1897 {
1898  return (Game::GameTime() - start_time) / 1000.0;
1899 }
1900 
1901 // +--------------------------------------------------------------------+
1902 
1903 void
1905 {
1907  if (stars && stars->InCutscene()) {
1909  bool end = false;
1910  double end_time = 0;
1911 
1912  while (++iter && !end) {
1913  MissionEvent* event = iter.value();
1914 
1915  if (event->IsPending() || event->IsActive()) {
1916  if (event->Event() == MissionEvent::END_SCENE ||
1917  event->Event() == MissionEvent::END_MISSION) {
1918  end = true;
1919  end_time = event->Time();
1920  }
1921 
1922  if (event->Event() == MissionEvent::FIRE_WEAPON) {
1923  event->Skip();
1924  }
1925 
1926  else {
1927  event->Activate();
1928  event->Execute(true);
1929  }
1930  }
1931  }
1932 
1933  double skip_time = end_time - MissionClock();
1934  if (skip_time > 0) {
1935  Game::SkipGameTime(skip_time);
1936  }
1937  }
1938 }
1939 
1940 // +--------------------------------------------------------------------+
1941 
1942 void
1943 Sim::ResolveTimeSkip(double seconds)
1944 {
1945  double skipped = 0;
1946 
1947  // allow elements to process hold time, and release as needed:
1948  ListIter<Element> elem = elements;
1949  while (++elem)
1950  elem->ExecFrame(seconds);
1951 
1952  // step through the skip, ten seconds at a time:
1953  if (active_region) {
1954  double total_skip = seconds;
1955  double frame_skip = 10;
1956  Ship* player = GetPlayerShip();
1957 
1958  while (total_skip > frame_skip) {
1959  if (active_region->CanTimeSkip()) {
1960  active_region->ResolveTimeSkip(frame_skip);
1961  total_skip -= frame_skip;
1962  skipped += frame_skip;
1963  }
1964  // break out early if player runs into bad guys...
1965  else {
1966  total_skip = 0;
1967  }
1968  }
1969 
1970  if (total_skip > 0)
1971  active_region->ResolveTimeSkip(total_skip);
1972  skipped += total_skip;
1973  }
1974 
1975  // give player control after time skip:
1976  Ship* player_ship = GetPlayerShip();
1977  if (player_ship) {
1978  player_ship->SetAutoNav(false);
1979  player_ship->SetThrottle(75);
1980 
1981  HUDView* hud = HUDView::GetInstance();
1982  if (hud)
1984 
1985  if (IsTestMode())
1986  player_ship->SetControls(0);
1987  }
1988 
1989  Game::SkipGameTime(skipped);
1991 }
1992 
1993 // +--------------------------------------------------------------------+
1994 
1997 {
1999 
2000  ListIter<Element> iter = elements;
2001  while (++iter) {
2002  Element* elem = iter.value();
2003 
2004  int num_live_ships = 0;
2005 
2006  for (int i = 0; i < elem->NumShips(); i++) {
2007  Ship* s = elem->GetShip(i+1);
2008 
2009  if (s && !s->IsDying() && !s->IsDead())
2010  num_live_ships++;
2011  }
2012 
2013  if (elem->IsSquadron() || num_live_ships > 0) {
2014  MissionElement* msn_elem = CreateMissionElement(elem);
2015 
2016  if (msn_elem)
2017  mission_elements.append(msn_elem);
2018  }
2019  }
2020 
2021  return mission_elements;
2022 }
2023 
2026 {
2027  MissionElement* msn_elem = 0;
2028 
2029  if (elem->IsSquadron()) {
2030  if (!elem->GetCarrier() || elem->GetCarrier()->Integrity() < 1)
2031  return msn_elem;
2032  }
2033 
2034  if (elem && !elem->IsNetObserver()) {
2035  msn_elem = new(__FILE__,__LINE__) MissionElement;
2036 
2037  msn_elem->SetName(elem->Name());
2038  msn_elem->SetIFF(elem->GetIFF());
2039  msn_elem->SetMissionRole(elem->Type());
2040 
2041  if (elem->IsSquadron() && elem->GetCarrier()) {
2042  Ship* carrier = elem->GetCarrier();
2043 
2044  msn_elem->SetCarrier(carrier->Name());
2045  msn_elem->SetCount(elem->GetCount());
2046  msn_elem->SetLocation(carrier->Location().OtherHand());
2047 
2048  if (carrier->GetRegion())
2049  msn_elem->SetRegion(carrier->GetRegion()->Name());
2050 
2051  int squadron_index = 0;
2052  Hangar* hangar = FindSquadron(elem->Name(), squadron_index);
2053 
2054  if (hangar) {
2055  msn_elem->SetDeadCount(hangar->NumShipsDead(squadron_index));
2056  msn_elem->SetMaintCount(hangar->NumShipsMaint(squadron_index));
2057 
2058  const ShipDesign* design = hangar->SquadronDesign(squadron_index);
2059  msn_elem->SetDesign(design);
2060 
2061  Text design_path = design->path_name;
2062  design_path.setSensitive(false);
2063 
2064  if (design_path.indexOf("/Mods/Ships") == 0) {
2065  design_path = design_path.substring(11, 1000);
2066  msn_elem->SetPath(design_path);
2067  }
2068  }
2069  }
2070 
2071  else {
2072  msn_elem->SetSquadron(elem->GetSquadron());
2073  msn_elem->SetCount(elem->NumShips());
2074  }
2075 
2076  if (elem->GetCommander())
2077  msn_elem->SetCommander(elem->GetCommander()->Name());
2078 
2079  msn_elem->SetCombatGroup(elem->GetCombatGroup());
2080  msn_elem->SetCombatUnit(elem->GetCombatUnit());
2081 
2082  Ship* ship = elem->GetShip(1);
2083  if (ship) {
2084  if (ship->GetRegion())
2085  msn_elem->SetRegion(ship->GetRegion()->Name());
2086 
2087  msn_elem->SetLocation(ship->Location().OtherHand());
2088  msn_elem->SetDesign(ship->Design());
2089 
2090  msn_elem->SetPlayer(elem->Player());
2091  msn_elem->SetCommandAI(elem->GetCommandAILevel());
2092  msn_elem->SetHoldTime((int) elem->GetHoldTime());
2093  msn_elem->SetZoneLock(elem->GetZoneLock());
2094  msn_elem->SetHeading(ship->CompassHeading());
2095 
2096  msn_elem->SetPlayable(elem->IsPlayable());
2097  msn_elem->SetRogue(elem->IsRogue());
2098  msn_elem->SetIntelLevel(elem->IntelLevel());
2099 
2100  Text design_path = ship->Design()->path_name;
2101  design_path.setSensitive(false);
2102 
2103  if (design_path.indexOf("/Mods/Ships") == 0) {
2104  design_path = design_path.substring(11, 1000);
2105  msn_elem->SetPath(design_path);
2106  }
2107 
2108  msn_elem->SetRespawnCount(ship->RespawnCount());
2109  }
2110 
2111  MissionLoad* loadout = new(__FILE__,__LINE__) MissionLoad;
2112  CopyMemory(loadout->GetStations(), elem->Loadout(), 16 * sizeof(int));
2113 
2114  msn_elem->Loadouts().append(loadout);
2115 
2116  int num_obj = elem->NumObjectives();
2117  for (int i = 0; i < num_obj; i++) {
2118  Instruction* o = elem->GetObjective(i);
2119  Instruction* instr = 0;
2120 
2121  instr = new(__FILE__,__LINE__) Instruction(*o);
2122 
2123  msn_elem->AddObjective(instr);
2124  }
2125 
2126  int num_inst = elem->NumInstructions();
2127  for (int i = 0; i < num_inst; i++) {
2128  Text instr = elem->GetInstruction(i);
2129  msn_elem->AddInstruction(instr);
2130  }
2131 
2132  ListIter<Instruction> nav_iter = elem->GetFlightPlan();
2133  while (++nav_iter) {
2134  Instruction* nav = nav_iter.value();
2135  Instruction* npt = new(__FILE__,__LINE__)
2136  Instruction(nav->RegionName(), nav->Location(), nav->Action());
2137 
2138  npt->SetFormation(nav->Formation());
2139  npt->SetSpeed(nav->Speed());
2140  npt->SetTarget(nav->TargetName());
2141  npt->SetHoldTime(nav->HoldTime());
2142  npt->SetFarcast(nav->Farcast());
2143  npt->SetStatus(nav->Status());
2144 
2145  msn_elem->AddNavPoint(npt);
2146  }
2147 
2148  for (int i = 0; i < elem->NumShips(); i++) {
2149  ship = elem->GetShip(i+1);
2150 
2151  if (ship) {
2152  MissionShip* s = new(__FILE__,__LINE__) MissionShip;
2153 
2154  s->SetName(ship->Name());
2155  s->SetRegNum(ship->Registry());
2156  s->SetRegion(ship->GetRegion()->Name());
2157  s->SetLocation(ship->Location().OtherHand());
2158  s->SetVelocity(ship->Velocity().OtherHand());
2159 
2160  s->SetRespawns(ship->RespawnCount());
2161  s->SetHeading(ship->CompassHeading());
2162  s->SetIntegrity(ship->Integrity());
2163 
2164  if (ship->GetDecoy())
2165  s->SetDecoys(ship->GetDecoy()->Ammo());
2166 
2167  if (ship->GetProbeLauncher())
2168  s->SetProbes(ship->GetProbeLauncher()->Ammo());
2169 
2170  int n;
2171  int ammo[16];
2172  int fuel[4];
2173 
2174  for (n = 0; n < 16; n++) {
2175  Weapon* w = ship->GetWeaponByIndex(n+1);
2176 
2177  if (w)
2178  ammo[n] = w->Ammo();
2179  else
2180  ammo[n] = -10;
2181  }
2182 
2183  for (n = 0; n < 4; n++) {
2184  if (ship->Reactors().size() > n)
2185  fuel[n] = ship->Reactors()[n]->Charge();
2186  else
2187  fuel[n] = -10;
2188  }
2189 
2190  s->SetAmmo(ammo);
2191  s->SetFuel(fuel);
2192 
2193  msn_elem->Ships().append(s);
2194  }
2195  }
2196  }
2197 
2198  return msn_elem;
2199 }
2200 
2201 Hangar*
2202 Sim::FindSquadron(const char* name, int& index)
2203 {
2204  Hangar* hangar = 0;
2205 
2207  while (++iter && !hangar) {
2208  SimRegion* rgn = iter.value();
2209 
2210  ListIter<Ship> s_iter = rgn->Carriers();
2211  while (++s_iter && !hangar) {
2212  Ship* carrier = s_iter.value();
2213  Hangar* h = carrier->GetHangar();
2214 
2215  for (int i = 0; i < h->NumSquadrons() && !hangar; i++) {
2216  if (h->SquadronName(i) == name) {
2217  hangar = h;
2218  index = i;
2219  }
2220  }
2221  }
2222  }
2223 
2224  return hangar;
2225 }
2226 
2227 // +===================================================================-+
2228 
2229 SimRegion::SimRegion(Sim* s, const char* n, int t)
2230 : sim(s), name(n), type(t), orbital_region(0), star_system(0)
2231 , player_ship(0), grid(0), active(false), current_view(0), sim_time(0)
2232 , ai_index(0), terrain(0)
2233 {
2234  if (sim) {
2236  }
2237 }
2238 
2240 : sim(s), orbital_region(r), type(REAL_SPACE), star_system(0)
2241 , player_ship(0), grid(0), active(false), current_view(0), sim_time(0)
2242 , ai_index(0), terrain(0)
2243 {
2244  if (r) {
2245  star_system = r->System();
2246  }
2247 
2248  if (orbital_region) {
2249  name = orbital_region->Name();
2250  grid = new(__FILE__,__LINE__) Grid((int) orbital_region->Radius(),
2251  (int) orbital_region->GridSpace());
2252 
2253 
2254  if (orbital_region->Type() == Orbital::TERRAIN) {
2256  terrain = new(__FILE__,__LINE__) Terrain(trgn);
2257 
2258  type = AIR_SPACE;
2259  }
2260 
2261  else if (orbital_region->Asteroids() > 0) {
2263 
2264  for (int i = 0; i < asteroids; i++) {
2265  Point init_loc((rand()-16384.0f) * 30,
2266  (rand()-16384.0f) * 3,
2267  (rand()-16384.0f) * 30);
2268  sim->CreateAsteroid(init_loc, i, Random(1e7, 1e8), this);
2269  }
2270  }
2271  }
2272  else {
2273  name = Game::GetText("Unknown");
2274  }
2275 }
2276 
2278 {
2280  delete terrain;
2281  explosions.destroy();
2282  shots.destroy();
2283  ships.destroy();
2284  debris.destroy();
2285  asteroids.destroy();
2286  dead_ships.destroy();
2287 
2288  for (int i = 0; i < 5; i++)
2289  track_database[i].destroy();
2290 }
2291 
2292 int
2294 {
2296 }
2297 
2298 int
2300 {
2302 }
2303 
2304 // +--------------------------------------------------------------------+
2305 
2306 void
2308 {
2309  // there can only be a player ship when playing the game locally
2310  if (Starshatter::GetInstance()) {
2311  int player_index = ships.index(ship);
2312 
2313  if (player_index >= 0) {
2314  if (sim->GetActiveRegion() != this)
2315  sim->ActivateRegion(this);
2316 
2317  AttachPlayerShip(player_index);
2318  }
2319 
2320  else {
2321  Print("SimRegion %s could not set player ship '%s' - not in region\n",
2322  name.data(), ship ? ship->Name() : "(null)");
2323  }
2324  }
2325 
2326  // if this is a stand-alone server, set player ship to null
2327  else {
2328  if (player_ship)
2330 
2331  current_view = -1;
2332  player_ship = 0;
2333  }
2334 }
2335 
2336 void
2338 {
2339  if (player_ship)
2341 
2342  current_view = index;
2344 
2346  if (cam_dir)
2347  cam_dir->SetShip(player_ship);
2348 
2349  if (sim->dust)
2351 
2352  if (!sim->IsTestMode())
2354 
2356  if (mouse_con)
2357  mouse_con->SetActive(false);
2358 }
2359 
2360 void
2362 {
2363  if (ships.size()) {
2364  int original_view = current_view;
2365 
2366  do {
2367  current_view++;
2368  if (current_view >= ships.size()) {
2369  current_view = 0;
2370  }
2371  }
2372  while (ships[current_view]->Life() == 0 && current_view != original_view);
2373 
2374  if (current_view != original_view) {
2375  ClearSelection();
2376 
2377  if (!sim->IsTestMode())
2379 
2380  if (player_ship->Rep())
2381  player_ship->Rep()->Show();
2382 
2384  }
2385  }
2386 }
2387 
2388 bool
2390 {
2391  return selection.contains(s);
2392 }
2393 
2396 {
2397  return selection;
2398 }
2399 
2400 void
2402 {
2403  selection.clear();
2404  selection.append(newsel);
2405 }
2406 
2407 void
2409 {
2410  selection.clear();
2411 }
2412 
2413 void
2415 {
2416  if (!newsel ||
2417  newsel->GetFlightPhase() < Ship::ACTIVE ||
2418  newsel->GetFlightPhase() >= Ship::RECOVERY)
2419  return;
2420 
2421  if (!selection.contains(newsel))
2422  selection.append(newsel);
2423 }
2424 
2425 // +--------------------------------------------------------------------+
2426 
2427 void
2429 {
2430  if (!sim) return;
2431 
2432  ListIter<Ship> ship = ships;
2433  while (++ship)
2434  ship->Activate(sim->scene);
2435 
2436  ListIter<Shot> shot = shots;
2437  while (++shot)
2438  shot->Activate(sim->scene);
2439 
2441  while (++exp)
2442  exp->Activate(sim->scene);
2443 
2444  ListIter<Debris> deb = debris;
2445  while (++deb)
2446  deb->Activate(sim->scene);
2447 
2449  while (++a)
2450  a->Activate(sim->scene);
2451 
2452  if (grid)
2454 
2455  if (terrain)
2457 
2458  player_ship = 0;
2459  active = true;
2460 }
2461 
2462 // +--------------------------------------------------------------------+
2463 
2464 void
2466 {
2467  if (!sim) return;
2468 
2469  ListIter<Ship> ship = ships;
2470  while (++ship)
2471  ship->Deactivate(sim->scene);
2472 
2473  ListIter<Shot> shot = shots;
2474  while (++shot)
2475  shot->Deactivate(sim->scene);
2476 
2478  while (++exp)
2479  exp->Deactivate(sim->scene);
2480 
2481  ListIter<Debris> deb = debris;
2482  while (++deb)
2483  deb->Deactivate(sim->scene);
2484 
2486  while (++a)
2487  a->Deactivate(sim->scene);
2488 
2489  if (grid)
2491 
2492  if (terrain)
2494 
2495  player_ship = 0;
2496  active = false;
2497 
2498  for (int i = 0; i < 5; i++)
2499  track_database[i].destroy();
2500 }
2501 
2502 // +--------------------------------------------------------------------+
2503 
2504 void
2506 {
2507  if (!sim) return;
2508 
2509  double seconds = secs;
2510 
2511  // DON'T REALLY KNOW WHAT PURPOSE THIS SERVES....
2512  if (!active) {
2513  double max_frame = 3 * Game::GetMaxFrameLength();
2514  long new_time = Game::GameTime();
2515  double delta = new_time - sim_time;
2516  seconds = delta / 1000.0;
2517 
2518  if (seconds > max_frame)
2519  seconds = max_frame;
2520  }
2521 
2523 
2524  if (orbital_region)
2526 
2528 
2529  Point ref;
2530 
2531  if (active && cam_dir) {
2532  ref = cam_dir->GetCamera()->Pos();
2533  UpdateSky(seconds, ref);
2534  }
2535 
2536  if (terrain)
2537  terrain->ExecFrame(seconds);
2538 
2539  UpdateTracks(seconds);
2540  UpdateShips(seconds);
2541  UpdateShots(seconds);
2542  UpdateExplosions(seconds);
2543 
2544  if (!Game::Paused()) {
2545  DamageShips();
2546  DockShips();
2547 
2548  if (active) {
2549  CollideShips();
2550  CrashShips();
2551  }
2552 
2553  DestroyShips();
2554  }
2555 
2556  if (active && cam_dir && player_ship) {
2557  Sound::SetListener(*(cam_dir->GetCamera()), player_ship->Velocity());
2558  }
2559 }
2560 
2561 // +--------------------------------------------------------------------+
2562 
2563 void
2565 {
2566  if (grid) {
2567  if (show)
2568  grid->Show();
2569  else
2570  grid->Hide();
2571  }
2572 }
2573 
2574 // +--------------------------------------------------------------------+
2575 
2576 void
2577 SimRegion::UpdateSky(double seconds, const Point& ref)
2578 {
2579  Dust* dust = sim->dust;
2580 
2581  if (dust) {
2583  dust->Hide();
2584  }
2585  else {
2586  dust->Show();
2587 
2588  dust->ExecFrame(seconds, ref);
2589 
2590  if (player_ship && dust->Hidden()) {
2591  dust->Reset(player_ship->Location());
2592  dust->Show();
2593  }
2594  }
2595  }
2596 
2598  while (++a) {
2599  a->ExecFrame(seconds);
2600  }
2601 }
2602 
2603 // +--------------------------------------------------------------------+
2604 
2605 void
2606 SimRegion::UpdateShips(double seconds)
2607 {
2608  int ship_index = 0;
2609  if (ai_index > ships.size())
2610  ai_index = 0;
2611 
2612  ListIter<Ship> ship_iter = ships;
2613  Ship* ship = 0;
2614 
2615  while (++ship_iter) {
2616  ship = ship_iter.value();
2617 
2618  if (ship_index == ai_index || ship == player_ship)
2619  ship->SetAIMode(2);
2620  else
2621  ship->SetAIMode(1);
2622 
2623  ship->ExecFrame(seconds);
2624  ship_index++;
2625  }
2626 
2627  ai_index++;
2628 }
2629 
2630 // +--------------------------------------------------------------------+
2631 
2632 void
2633 SimRegion::UpdateShots(double seconds)
2634 {
2635  ListIter<Shot> shot_iter = shots;
2636  while (++shot_iter) {
2637  Shot* shot = shot_iter.value();
2638  shot->ExecFrame(seconds);
2639 
2640  if (shot->Design()->flak) {
2641  SeekerAI* seeker = (SeekerAI*) shot->GetDirector();
2642 
2643  if (shot->Life() < 0.02 || seeker && seeker->Overshot()) {
2644  shot->SetFuse(0.001); // set lifetime to ~zero
2645  sim->CreateSplashDamage(shot);
2646  }
2647  }
2648 
2649  if (shot->Life() < 0.01) { // died of old age
2651 
2652  if (shot->IsDrone())
2653  drones.remove((Drone*) shot);
2654 
2655  shot_iter.removeItem();
2656  delete shot;
2657  shot = 0;
2658  }
2659  }
2660 }
2661 
2662 // +--------------------------------------------------------------------+
2663 
2664 void
2666 {
2667  ListIter<Explosion> exp_iter = explosions;
2668  while (++exp_iter) {
2669  Explosion* exp = exp_iter.value();
2670  exp->ExecFrame(seconds);
2671 
2672  if (exp->Life() < 0.01) { // died of old age
2673  exp_iter.removeItem();
2674  delete exp;
2675  }
2676  }
2677 
2678  ListIter<Debris> debris_iter = debris;
2679  while (++debris_iter) {
2680  Debris* d = debris_iter.value();
2681  d->ExecFrame(seconds);
2682 
2683  if (d->Life() < 0.01) { // died of old age
2684  debris_iter.removeItem();
2685  delete d;
2686  }
2687  }
2688 }
2689 
2690 // +--------------------------------------------------------------------+
2691 // Check for collisions between ships and shots, and apply damage.
2692 // Also look for damage to drones and debris.
2693 
2694 void
2696 {
2697  if (ships.size() == 0 || shots.size() == 0)
2698  return;
2699 
2700  Point impact;
2701 
2702  // FOR EACH SHOT IN THE REGION:
2703  ListIter<Shot> shot_iter = shots;
2704  while (++shot_iter) {
2705  Shot* shot = shot_iter.value();
2706  const Ship* owner = shot->Owner();
2707  const char* owner_name;
2708 
2709  if (owner)
2710  owner_name = owner->Name();
2711  else
2712  owner_name = "[KIA]";
2713 
2714  // CHECK FOR COLLISION WITH A SHIP:
2715  ListIter<Ship> ship_iter = ships;
2716  while (shot && ++ship_iter) {
2717  Ship* ship = ship_iter.value();
2718  int hit = ship->HitBy(shot, impact);
2719 
2720  if (hit) {
2721  // recon imager:
2722  if (shot->Damage() < 0) {
2723  ShipStats* shooter = ShipStats::Find(owner_name);
2724  if (shooter) {
2725  shooter->AddEvent(SimEvent::SCAN_TARGET, ship->Name());
2726  }
2727  }
2728 
2729  // live round:
2730  else if (shot->Damage() > 0) {
2731  int ship_destroyed = (!ship->InTransition() && ship->Integrity() < 1.0f);
2732 
2733  // then delete the ship:
2734  if (ship_destroyed) {
2736  Director* director;
2737 
2738  Print(" %s Killed %s (%s)\n", owner_name, ship->Name(), FormatGameTime());
2739 
2740  if (owner)
2741  director = owner->GetDirector();
2742 
2743  // alert the killer
2744  if (director && director->Type() > SteerAI::SEEKER && director->Type() < SteerAI::GROUND) {
2745  ShipAI* shipAI = (ShipAI*) director;
2746  shipAI->Splash(ship);
2747  }
2748 
2749  // record the kill
2750  ShipStats* killer = ShipStats::Find(owner_name);
2751  if (killer) {
2752  if (shot->IsMissile())
2753  killer->AddEvent(SimEvent::MISSILE_KILL, ship->Name());
2754  else
2755  killer->AddEvent(SimEvent::GUNS_KILL, ship->Name());
2756  }
2757 
2758  if (owner && owner->GetIFF() != ship->GetIFF()) {
2759  if (ship->GetIFF() > 0 || owner->GetIFF() > 1) {
2760  killer->AddPoints(ship->Value());
2761 
2762  Element* elem = owner->GetElement();
2763  if (elem) {
2764  if (owner->GetElementIndex() > 1) {
2765  Ship* s = elem->GetShip(1);
2766 
2767  if (s) {
2768  ShipStats* cmdr_stats = ShipStats::Find(s->Name());
2769  if (cmdr_stats) {
2770  cmdr_stats->AddCommandPoints(ship->Value()/2);
2771  }
2772  }
2773  }
2774 
2775  Element* cmdr = elem->GetCommander();
2776  if (cmdr) {
2777  Ship* s = cmdr->GetShip(1);
2778 
2779  if (s) {
2780  ShipStats* cmdr_stats = ShipStats::Find(s->Name());
2781  if (cmdr_stats) {
2782  cmdr_stats->AddCommandPoints(ship->Value()/2);
2783  }
2784  }
2785  }
2786  }
2787  }
2788  }
2789 
2790  ShipStats* killee = ShipStats::Find(ship->Name());
2791  if (killee)
2792  killee->AddEvent(SimEvent::DESTROYED, owner_name);
2793 
2794  ship->DeathSpiral();
2795  }
2796  }
2797 
2798  // finally, consume the shot:
2799  if (!shot->IsBeam()) {
2800  if (owner) {
2801  ShipStats* stats = ShipStats::Find(owner_name);
2802  if (shot->Design()->primary)
2803  stats->AddGunHit();
2804  else if (shot->Damage() > 0)
2805  stats->AddMissileHit();
2806  }
2807 
2809 
2810  if (shot->IsDrone())
2811  drones.remove((Drone*) shot);
2812 
2813  shot_iter.removeItem();
2814  delete shot;
2815  shot = 0;
2816  }
2817  else if (!shot->HitTarget()) {
2818  shot->SetHitTarget(true);
2819 
2820  if (owner) {
2821  ShipStats* stats = ShipStats::Find(owner_name);
2822  if (shot->Design()->primary)
2823  stats->AddGunHit();
2824  }
2825  }
2826  }
2827  }
2828 
2829  // CHECK FOR COLLISION WITH A DRONE:
2830  if (shot && shot->Design()->target_type & Ship::DRONE) {
2831  ListIter<Drone> drone_iter = drones;
2832  while (shot && ++drone_iter) {
2833  Drone* d = drone_iter.value();
2834 
2835  if (d == shot || d->Owner() == owner)
2836  continue;
2837 
2838  int hit = d->HitBy(shot, impact);
2839  if (hit) {
2840  int destroyed = (d->Integrity() < 1.0f);
2841 
2842  // then mark the drone for deletion:
2843  if (destroyed) {
2845  sim->CreateExplosion(d->Location(), d->Velocity(), 21, 1.0f, 1.0f, this);
2846  d->SetLife(0);
2847  }
2848 
2849  // finally, consume the shot:
2850  if (!shot->IsBeam()) {
2851  if (owner) {
2852  ShipStats* stats = ShipStats::Find(owner_name);
2853  if (shot->Design()->primary)
2854  stats->AddGunHit();
2855  else
2856  stats->AddMissileHit();
2857  }
2858 
2860 
2861  if (shot->IsDrone())
2862  drones.remove((Drone*) shot);
2863 
2864  shot_iter.removeItem();
2865  delete shot;
2866  shot = 0;
2867  }
2868  }
2869  }
2870  }
2871 
2872  // CHECK FOR COLLISION WITH DEBRIS:
2873  ListIter<Debris> debris_iter = debris;
2874  while (shot && ++debris_iter) {
2875  Debris* d = debris_iter.value();
2876 
2877  if (d->Radius() < 50)
2878  continue;
2879 
2880  int hit = d->HitBy(shot, impact);
2881  if (hit) {
2882  int destroyed = (d->Integrity() < 1.0f);
2883 
2884  // then delete the debris:
2885  if (destroyed) {
2886  sim->CreateExplosion(d->Location(), d->Velocity(), Explosion::LARGE_EXPLOSION, 1.0f, 1.0f, this);
2887  debris_iter.removeItem();
2888  delete d;
2889  }
2890 
2891  // finally, consume the shot:
2892  if (!shot->IsBeam()) {
2894  if (shot->IsDrone())
2895  drones.remove((Drone*) shot);
2896 
2897  shot_iter.removeItem();
2898  delete shot;
2899  shot = 0;
2900  }
2901  }
2902  }
2903 
2904  // CHECK FOR COLLISION WITH ASTEROIDS:
2905  ListIter<Asteroid> a_iter = asteroids;
2906  while (shot && ++a_iter) {
2907  Asteroid* a = a_iter.value();
2908 
2909  int hit = a->HitBy(shot, impact);
2910  if (hit) {
2911  if (!shot->IsBeam()) {
2912  if (shot->IsDrone())
2913  drones.remove((Drone*) shot);
2914 
2915  shot_iter.removeItem();
2916  delete shot;
2917  shot = 0;
2918  }
2919  }
2920  }
2921  }
2922 }
2923 
2924 // +--------------------------------------------------------------------+
2925 
2926 void
2928 {
2929  if (ships.size() < 2 && debris.size() < 1)
2930  return;
2931 
2932  List<Ship> kill_list;
2933 
2934  int s_index = 0;
2935 
2936  ListIter<Ship> ship_iter = ships;
2937  while (++ship_iter) {
2938  Ship* ship = ship_iter.value();
2939 
2940  if (ship->InTransition() ||
2941  ship->GetFlightPhase() < Ship::ACTIVE ||
2942  ship->MissionClock() < 10000 ||
2943  ship->IsNetObserver())
2944  continue;
2945 
2946  int t_index = 0;
2947  ListIter<Ship> targ_iter = ships;
2948  while (++targ_iter) {
2949  Ship* targ = targ_iter.value();
2950 
2951  if (t_index++ <= s_index) continue;
2952 
2953  if (targ == ship) continue;
2954 
2955  if (targ->InTransition() ||
2956  targ->GetFlightPhase() < Ship::ACTIVE ||
2957  targ->MissionClock() < 10000 ||
2958  targ->IsNetObserver())
2959  continue;
2960 
2961  // ignore AI fighter collisions:
2962  if (ship->IsDropship() &&
2963  ship != player_ship &&
2964  targ->IsDropship() &&
2965  targ != player_ship)
2966  continue;
2967 
2968  // don't collide with own runway!
2969  if (ship->IsAirborne() && ship->GetCarrier() == targ)
2970  continue;
2971  if (targ->IsAirborne() && targ->GetCarrier() == ship)
2972  continue;
2973 
2974  // impact:
2975  if (ship->CollidesWith(*targ)) {
2976  Vec3 tv1 = targ->Velocity();
2977  Vec3 sv1 = ship->Velocity();
2978 
2979  Physical::SemiElasticCollision(*ship, *targ);
2980 
2981  Vec3 tv2 = targ->Velocity();
2982  Vec3 sv2 = ship->Velocity();
2983 
2984  double dvs = (sv2-sv1).length();
2985  double dvt = (tv2-tv1).length();
2986 
2987  if (dvs > 20) dvs *= dvs;
2988  if (dvt > 20) dvt *= dvt;
2989 
2990  if (!NetGame::IsNetGameClient()) {
2991  double old_integrity = ship->Integrity();
2992  ship->InflictDamage(dvs);
2993  double hull_damage = old_integrity - ship->Integrity();
2994  NetUtil::SendObjDamage(ship, hull_damage);
2995 
2996  old_integrity = targ->Integrity();
2997  targ->InflictDamage(dvt);
2998  hull_damage = old_integrity - targ->Integrity();
2999  NetUtil::SendObjDamage(targ, hull_damage);
3000  }
3001 
3002  // then delete the ship:
3003  if (targ->Integrity() < 1.0f) {
3005  Print(" ship %s died in collision with %s (%s)\n", targ->Name(), ship->Name(), FormatGameTime());
3006  if (!kill_list.contains(targ)) {
3007  ShipStats* r = ShipStats::Find(targ->Name());
3008  if (r) r->AddEvent(SimEvent::COLLIDE, ship->Name());
3009 
3010  if (targ->GetIFF() > 0 && ship->GetIFF() != targ->GetIFF()) {
3011  r = ShipStats::Find(ship->Name());
3012  if (r) r->AddPoints(targ->Value());
3013  }
3014 
3015  kill_list.insert(targ);
3016  }
3017  }
3018 
3019  if (ship->Integrity() < 1.0f) {
3021  Print(" ship %s died in collision with %s (%s)\n", ship->Name(), targ->Name(), FormatGameTime());
3022  if (!kill_list.contains(ship)) {
3023  ShipStats* r = ShipStats::Find(ship->Name());
3024  if (r) r->AddEvent(SimEvent::COLLIDE, targ->Name());
3025 
3026  if (ship->GetIFF() > 0 && ship->GetIFF() != targ->GetIFF()) {
3027  r = ShipStats::Find(targ->Name());
3028  if (r) r->AddPoints(ship->Value());
3029  }
3030 
3031  kill_list.insert(ship);
3032  }
3033  }
3034  }
3035  }
3036 
3037  ListIter<Debris> debris_iter = debris;
3038  while (++debris_iter) {
3039  Debris* d = debris_iter.value();
3040 
3041  if (d->Radius() < 50)
3042  continue;
3043 
3044  if (ship->CollidesWith(*d)) {
3045  Vec3 tv1 = d->Velocity();
3046  Vec3 sv1 = ship->Velocity();
3047 
3048  Physical::SemiElasticCollision(*ship, *d);
3049 
3050  Vec3 tv2 = d->Velocity();
3051  Vec3 sv2 = ship->Velocity();
3052 
3053  if (!NetGame::IsNetGameClient()) {
3054  ship->InflictDamage((sv2-sv1).length());
3055  }
3056 
3057  d->InflictDamage((tv2-tv1).length());
3058 
3059  // then delete the debris:
3060  if (d->Integrity() < 1.0f) {
3061  sim->CreateExplosion(d->Location(), d->Velocity(), Explosion::LARGE_EXPLOSION, 1.0f, 1.0f, this);
3062  debris_iter.removeItem();
3063  delete d;
3064  }
3065 
3066  if (ship->Integrity() < 1.0f) {
3067  if (!kill_list.contains(ship)) {
3068  ShipStats* r = ShipStats::Find(ship->Name());
3069  if (r) r->AddEvent(SimEvent::COLLIDE, Game::GetText("DEBRIS"));
3070 
3071  kill_list.insert(ship);
3072  }
3073  }
3074  }
3075  }
3076 
3077  ListIter<Asteroid> a_iter = asteroids;
3078  while (++a_iter) {
3079  Asteroid* a = a_iter.value();
3080 
3081  if (ship->CollidesWith(*a)) {
3082  Vec3 sv1 = ship->Velocity();
3083  Physical::SemiElasticCollision(*ship, *a);
3084  Vec3 sv2 = ship->Velocity();
3085 
3086  if (!NetGame::IsNetGameClient()) {
3087  ship->InflictDamage((sv2-sv1).length() * 10);
3088  }
3089 
3090  if (ship->Integrity() < 1.0f) {
3091  if (!kill_list.contains(ship)) {
3092  ShipStats* r = ShipStats::Find(ship->Name());
3093  if (r) r->AddEvent(SimEvent::COLLIDE, Game::GetText("ASTEROID"));
3094 
3095  kill_list.insert(ship);
3096  }
3097  }
3098  }
3099  }
3100 
3101  s_index++;
3102  }
3103 
3104  ListIter<Ship> killed(kill_list);
3105  while (++killed) {
3106  Ship* kill = killed.value();
3107  kill->DeathSpiral();
3108  }
3109 }
3110 
3111 // +--------------------------------------------------------------------+
3112 
3113 void
3115 {
3117  return;
3118 
3119  ListIter<Ship> ship_iter = ships;
3120  while (++ship_iter) {
3121  Ship* ship = ship_iter.value();
3122 
3123  if (!ship->IsGroundUnit() &&
3124  !ship->InTransition() &&
3125  ship->Class() != Ship::LCA &&
3126  ship->AltitudeAGL() < ship->Radius()/2) {
3127  if (ship->GetFlightPhase() == Ship::ACTIVE || ship->GetFlightPhase() == Ship::APPROACH) {
3128  ship->InflictDamage(1e6);
3129 
3130  if (ship->Integrity() < 1.0f) {
3131  Print(" ship destroyed by crash: %s (%s)\n", ship->Name(), FormatGameTime());
3132  ShipStats* r = ShipStats::Find(ship->Name());
3133  if (r) r->AddEvent(SimEvent::CRASH);
3134 
3135  ship->DeathSpiral();
3136  }
3137  }
3138  }
3139  }
3140 
3141  ListIter<Shot> shot_iter = shots;
3142  while (++shot_iter) {
3143  Shot* shot = shot_iter.value();
3144 
3145  if (shot->IsBeam() || shot->IsDecoy())
3146  continue;
3147 
3148  if (shot->AltitudeMSL() < 5e3 &&
3149  shot->AltitudeAGL() < 5) {
3150 
3151  // shot hit the ground, destroy it:
3153 
3154  if (shot->IsDrone())
3155  drones.remove((Drone*) shot);
3156 
3157  shot_iter.removeItem();
3158  delete shot;
3159  }
3160  }
3161 }
3162 
3163 // +--------------------------------------------------------------------+
3164 
3165 void
3167 {
3168  ListIter<Ship> ship_iter = ships;
3169  while (++ship_iter) {
3170  Ship* ship = ship_iter.value();
3171 
3172  if (ship->IsDead()) {
3173  // must use the iterator to remove the current
3174  // item from the container:
3175  ship_iter.removeItem();
3176  DestroyShip(ship);
3177  }
3178  }
3179 }
3180 
3181 // +--------------------------------------------------------------------+
3182 
3183 void
3185 {
3186  if (!ship) return;
3187 
3188  Ship* spawn = 0;
3189 
3190  ships.remove(ship);
3191  carriers.remove(ship);
3192  selection.remove(ship);
3193 
3194  Text rgn_name;
3195  if (ship->GetRegion())
3196  rgn_name = ship->GetRegion()->Name();
3197 
3198  bool player_destroyed = (player_ship == ship);
3199 
3200  char ship_name[64];
3201  char ship_reg[64];
3202  strcpy_s(ship_name, ship->Name());
3203  strcpy_s(ship_reg, ship->Registry());
3204 
3205  ShipDesign* ship_design = (ShipDesign*) ship->Design();
3206  int ship_iff = ship->GetIFF();
3207  int cmd_ai = ship->GetCommandAILevel();
3208  bool respawn = sim->IsTestMode() && !ship->IsGroundUnit();
3209  bool observe = false;
3210 
3211  if (!respawn)
3212  respawn = ship->RespawnCount() > 0;
3213 
3214  if (sim->netgame) {
3215  if (!respawn)
3216  observe = player_destroyed;
3217  }
3218 
3219  if (respawn || observe) {
3220  if (!sim->netgame || !respawn)
3221  ship->SetRespawnLoc(RandomPoint() * 2);
3222 
3223  Point spawn_loc = ship->RespawnLoc();
3224 
3225  if (ship->IsAirborne() && spawn_loc.z < 5e3)
3226  spawn_loc.z = Random(8e3, 10e3);
3227 
3228  spawn = sim->CreateShip(ship_name, ship_reg, ship_design, rgn_name, spawn_loc, ship_iff, cmd_ai, observe ? 0 : ship->GetLoadout());
3229  spawn->SetRespawnCount(ship->RespawnCount() - 1);
3230  spawn->SetNetObserver(observe);
3231 
3232  if (sim->netgame && respawn)
3233  sim->netgame->Respawn(ship->GetObjID(), spawn);
3234 
3235  int n = strlen(ship_name);
3236  if (ship_name[n-2] == ' ' && isdigit(ship_name[n-1]))
3237  ship_name[n-2] = 0;
3238 
3239  Element* elem = sim->FindElement(ship_name);
3240  if (elem)
3241  elem->AddShip(spawn, ship->GetOrigElementIndex());
3242  else
3243  Print("Warning: No Element found for '%s' on respawn.\n", ship_name);
3244 
3245  if (player_destroyed)
3246  SetPlayerShip(spawn);
3247  }
3248  else {
3249  // close mission, return to menu:
3250  if (player_destroyed) {
3252  if (stars)
3254  }
3255  }
3256 
3258 
3259  dead_ships.insert(ship);
3260  ship->Destroy();
3261 }
3262 
3263 // +--------------------------------------------------------------------+
3264 
3265 void
3267 {
3268  if (!ship || !carrier || !deck) return;
3269 
3270  deck->Dock(ship);
3271 }
3272 
3273 // +--------------------------------------------------------------------+
3274 
3275 Ship*
3276 SimRegion::FindShip(const char* ship_name)
3277 {
3278  Ship* ship = 0;
3279 
3280  if (ship_name && *ship_name) {
3281  int name_len = strlen(ship_name);
3282 
3283  ListIter<Ship> ship_iter = ships;
3284  while (++ship_iter && !ship) {
3285  Ship* test = ship_iter.value();
3286  if (!strncmp(test->Name(), ship_name, name_len)) {
3287  int test_len = strlen(test->Name());
3288 
3289  // The only fuzzy match is for element indices.
3290  // The desired name "Alpha" matches "Alpha 1" and "Alpha 2"
3291  // but not "Alpha-Centauri"
3292 
3293  if (test_len > name_len && test->Name()[name_len] != ' ')
3294  continue;
3295 
3296  ship = test;
3297  }
3298  }
3299  }
3300 
3301  return ship;
3302 }
3303 
3304 Ship*
3306 {
3307  Ship* ship = 0;
3308 
3309  ListIter<Ship> ship_iter = ships;
3310  while (++ship_iter && !ship) {
3311  Ship* test = ship_iter.value();
3312  if (test->GetObjID() == objid)
3313  ship = test;
3314  }
3315 
3316  return ship;
3317 }
3318 
3319 Shot*
3321 {
3322  Shot* shot = 0;
3323 
3324  ListIter<Shot> shot_iter = shots;
3325  while (++shot_iter && !shot) {
3326  Shot* test = shot_iter.value();
3327  if (test->GetObjID() == objid)
3328  shot = test;
3329  }
3330 
3331  if (!shot) {
3332  ListIter<Drone> drone_iter = drones;
3333  while (++drone_iter && !shot) {
3334  Drone* test = drone_iter.value();
3335  if (test->GetObjID() == objid)
3336  shot = test;
3337  }
3338  }
3339 
3340  return shot;
3341 }
3342 
3343 // +--------------------------------------------------------------------+
3344 
3345 void
3347 {
3348  if (ships.size() == 0)
3349  return;
3350 
3351  ListIter<Ship> ship_iter = ships;
3352  while (++ship_iter) {
3353  Ship* ship = ship_iter.value();
3354  int docked = (ship->GetFlightPhase() == Ship::DOCKED);
3355 
3356  if (docked) {
3358 
3359  // who did this ship dock with?
3360  Ship* carrier = ship->GetCarrier();
3361 
3362  if (carrier) {
3363  ShipStats* s = ShipStats::Find(ship->Name());
3364  if (s) {
3365  if (ship->IsAirborne())
3366  s->AddEvent(SimEvent::LAND, carrier->Name());
3367  else
3368  s->AddEvent(SimEvent::DOCK, carrier->Name());
3369  }
3370 
3371  ShipStats* c = ShipStats::Find(carrier->Name());
3372  if (c) c->AddEvent(SimEvent::RECOVER_SHIP, ship->Name());
3373  }
3374 
3375  // then delete the ship:
3376  int player_docked = (player_ship == ship);
3377  char ship_name[33];
3378  strcpy_s(ship_name, ship->Name());
3379 
3380  selection.remove(ship);
3381  dead_ships.insert(ship_iter.removeItem());
3382  ship->Destroy();
3383 
3384  if (player_docked) {
3385  // close mission, return to menu:
3387  if (stars)
3389  }
3390 
3391  if (carrier)
3392  Print(" %s Docked with %s\n", ship_name, carrier->Name());
3393  }
3394  }
3395 }
3396 
3397 // +--------------------------------------------------------------------+
3398 
3399 void
3401 {
3402  if (!ship) return;
3403 
3404  SimRegion* orig = ship->GetRegion();
3405 
3406  if (orig != this) {
3407  if (orig != 0) {
3408  if (orig->active)
3409  ship->Deactivate(sim->scene);
3410 
3411  orig->ships.remove(ship);
3412  orig->carriers.remove(ship);
3413  orig->selection.remove(ship);
3414  }
3415 
3416  ships.append(ship);
3417 
3418  if (ship->NumFlightDecks())
3419  carriers.append(ship);
3420 
3421  TranslateObject(ship);
3422  ship->SetRegion(this);
3423 
3424  if (active)
3425  ship->Activate(sim->scene);
3426  }
3427 }
3428 
3429 void
3431 {
3432  if (!shot) return;
3433 
3434  SimRegion* orig = shot->GetRegion();
3435 
3436  if (orig != this) {
3437  if (orig != 0)
3438  orig->shots.remove(shot);
3439 
3440  shots.append(shot);
3441  if (shot->IsDrone())
3442  drones.append((Drone*) shot);
3443 
3444  TranslateObject(shot);
3445  shot->SetRegion(this);
3446 
3447  if (active)
3448  shot->Activate(sim->scene);
3449  }
3450 }
3451 
3452 void
3454 {
3455  if (!exp) return;
3456 
3457  SimRegion* orig = exp->GetRegion();
3458 
3459  if (orig != this) {
3460  if (orig != 0)
3461  orig->explosions.remove(exp);
3462 
3463  explosions.append(exp);
3464  TranslateObject(exp);
3465  exp->SetRegion(this);
3466 
3467  if (active)
3468  exp->Activate(sim->scene);
3469  }
3470 }
3471 
3472 void
3474 {
3475  if (!d) return;
3476 
3477  SimRegion* orig = d->GetRegion();
3478 
3479  if (orig != this) {
3480  if (orig != 0)
3481  orig->debris.remove(d);
3482 
3483  debris.append(d);
3484  TranslateObject(d);
3485  d->SetRegion(this);
3486 
3487  if (active)
3488  d->Activate(sim->scene);
3489  }
3490 }
3491 
3492 void
3494 {
3495  if (!a) return;
3496 
3497  SimRegion* orig = a->GetRegion();
3498 
3499  if (orig != this) {
3500  if (orig != 0)
3501  orig->asteroids.remove(a);
3502 
3503  asteroids.append(a);
3504  TranslateObject(a);
3505  a->SetRegion(this);
3506 
3507  if (active)
3508  a->Activate(sim->scene);
3509  }
3510 }
3511 
3512 // +--------------------------------------------------------------------+
3513 
3514 void
3516 {
3517  if (orbital_region)
3519 
3520  if (object) {
3521  SimRegion* orig = object->GetRegion();
3522  if (orig) {
3523  Point delta = Location() - orig->Location();
3524  delta = delta.OtherHand();
3525  object->TranslateBy(delta);
3526  }
3527  }
3528 }
3529 
3530 // +--------------------------------------------------------------------+
3531 
3534 {
3535  if (iff >= 0 && iff < 5)
3536  return track_database[iff];
3537 
3538  static List<Contact> empty;
3539  return empty;
3540 }
3541 
3542 void
3544 {
3545  for (int i = 0; i < 5; i++) {
3546  ListIter<Contact> track_iter = track_database[i];
3547 
3548  while (++track_iter) {
3549  Contact* t = track_iter.value();
3550  Ship* c_ship = t->GetShip();
3551  Shot* c_shot = t->GetShot();
3552  double c_life = 0;
3553 
3554  if (c_ship) {
3555  c_life = c_ship->Life();
3556 
3557  // look for quantum jumps and orbit transitions:
3558  if (c_ship->GetRegion() != this || c_ship->IsNetObserver())
3559  c_life = 0;
3560  }
3561 
3562  else if (c_shot)
3563  c_life = c_shot->Life();
3564 
3565  if (t->Age() < 0 || c_life == 0) {
3566  track_iter.removeItem();
3567  delete t;
3568  }
3569 
3570  else {
3571  t->Reset();
3572  }
3573  }
3574  }
3575 }
3576 
3577 // +--------------------------------------------------------------------+
3578 
3579 bool
3581 {
3582  bool ok = false;
3583 
3584  if (player_ship) {
3585  ok = true;
3586 
3587  for (int i = 0; ok && i < ships.size(); i++) {
3588  Ship* s = ships[i];
3589 
3590  if (s != player_ship && s->GetIFF() && s->GetIFF() != player_ship->GetIFF()) {
3591  double dist = Point(s->Location() - player_ship->Location()).length();
3592 
3593  if (s->IsStarship())
3594  ok = dist > 60e3;
3595  else
3596  ok = dist > 30e3;
3597  }
3598  }
3599  }
3600 
3601  return ok;
3602 }
3603 
3604 // +--------------------------------------------------------------------+
3605 
3606 void
3608 {
3609  for (int i = 0; i < ships.size(); i++) {
3610  Ship* ship = ships[i];
3611  Ship* ward = ship->GetWard();
3612 
3613  // remember to burn fuel and fix stuff...
3614  ship->ExecSystems(seconds);
3615  ship->ExecMaintFrame(seconds);
3616 
3617  ship->ClearTrack();
3618  ListIter<Contact> contact = ship->ContactList();
3619  while (++contact)
3620  contact->ClearTrack();
3621 
3622  if (ship->IsStatic())
3623  continue;
3624 
3625  // if ship is cleared inbound, land him:
3626  InboundSlot* inbound = ship->GetInbound();
3627  if (inbound) {
3628  if (inbound->Cleared()) {
3629  FlightDeck* deck = inbound->GetDeck();
3630 
3631  if (deck) {
3632  ship->SetCarrier((Ship*) deck->GetCarrier(), deck);
3634  ship->Stow();
3635  deck->Clear(inbound->Index());
3636  }
3637  }
3638 
3639  // cleared or not, once you're inbound, don't seek navpoints:
3640  continue;
3641  }
3642 
3643  if (ship->GetHangar()) {
3644  ship->GetHangar()->ExecFrame(seconds);
3645 
3646  List<FlightDeck>& flight_decks = ship->FlightDecks();
3647  for (int n = 0; n < flight_decks.size(); n++)
3648  flight_decks[n]->ExecFrame(seconds);
3649  }
3650 
3651  Instruction* navpt = ship->GetNextNavPoint();
3652  Point dest = ship->Location();
3653  double speed = 500;
3654  double space = 2.0e3 * (ship->GetElementIndex() - 1);
3655 
3656  if (ship->IsStarship())
3657  space *= 5;
3658 
3659  if (navpt && navpt->Action() == Instruction::LAUNCH) {
3660  ship->SetNavptStatus(navpt, Instruction::COMPLETE);
3661  navpt = ship->GetNextNavPoint();
3662  }
3663 
3664  if (navpt) {
3665  dest = navpt->Location().OtherHand();
3666  speed = navpt->Speed();
3667  }
3668 
3669  else if (ward) {
3670  Point delta = ship->Location() - ward->Location();
3671  delta.y = 0;
3672 
3673  if (delta.length() > 25e3) {
3674  delta.Normalize();
3675  dest = ward->Location() + delta * 25e3;
3676  }
3677  }
3678 
3679  Point delta = dest - ship->Location();
3680  Point unit = delta;
3681  double dist = unit.Normalize() - space;
3682 
3683  if (dist > 1e3) {
3684  if (speed < 50)
3685  speed = 500;
3686 
3687  double etr = dist / speed;
3688 
3689  if (etr > seconds)
3690  etr = seconds;
3691 
3692  Point trans = unit * (speed * etr);
3693 
3694  if (ship->GetFuelLevel() > 1) {
3695  ship->MoveTo(ship->Location() + trans);
3696  ship->SetVelocity(unit * speed);
3697  }
3698  ship->LookAt(dest);
3699 
3700  if (ship->IsStarship()) {
3702  ship->SetHelmHeading(ship->CompassHeading());
3703  ship->SetHelmPitch(ship->CompassPitch());
3704  }
3705  }
3706 
3707  else if (navpt && navpt->Status() <= Instruction::ACTIVE) {
3708  ship->SetNavptStatus(navpt, Instruction::COMPLETE);
3709  }
3710 
3711  if (ward) {
3712  Point ward_heading = ward->Heading();
3713  ward_heading.y = 0;
3714  ward_heading.Normalize();
3715 
3716  if (ship->GetFuelLevel() > 1) {
3717  ship->SetVelocity(ward->Velocity());
3718  }
3719  ship->LookAt(ship->Location() + ward_heading * 1e6);
3720 
3721  if (ship->IsStarship()) {
3723  ship->SetHelmHeading(ship->CompassHeading());
3724  ship->SetHelmPitch(ship->CompassPitch());
3725  }
3726  }
3727 
3728  if (dist > 1 || ward) {
3729  for (int j = 0; j < ships.size(); j++) {
3730  Ship* test = ships[j];
3731 
3732  if (ship != test && test->Mass() >= ship->Mass()) {
3733  Point delta = ship->Location() - test->Location();
3734 
3735  if (delta.length() < ship->Radius() * 2 + test->Radius() * 2) {
3736  ship->MoveTo(test->Location() + RandomPoint().OtherHand());
3737  }
3738  }
3739  }
3740  }
3741  }
3742 
3743  DockShips();
3744 }
3745 
3746 // +--------------------------------------------------------------------+
3747 
3748 void
3750 {
3751  for (int i = 0; i < dead_ships.size(); i++) {
3752  Ship* s = dead_ships[i];
3753 
3754  if (s->GetCombatUnit() && s->GetFlightPhase() != Ship::DOCKED)
3755  s->GetCombatUnit()->Kill(1);
3756  }
3757 
3758  for (int i = 0; i < ships.size(); i++) {
3759  Ship* s = ships[i];
3760  CombatUnit* u = s->GetCombatUnit();
3761 
3762  if (u) {
3763  Point u_loc = s->Location().OtherHand();
3764  if (u_loc.z > 20e3)
3765  u_loc.z = 20e3;
3766  else if (u_loc.z < -20e3)
3767  u_loc.z = -20e3;
3768 
3769  if (u->IsStarship()) {
3770  u->SetRegion(s->GetRegion()->Name());
3771  u->MoveTo(u_loc);
3772  }
3773 
3774  if (!u->IsDropship()) {
3775  if (s->Integrity() < 1)
3776  u->Kill(1);
3777  else
3778  u->SetSustainedDamage(s->Design()->integrity - s->Integrity());
3779  }
3780 
3781  CombatGroup* g = u->GetCombatGroup();
3782  if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) {
3783  if (!g->IsZoneLocked())
3784  g->SetRegion(s->GetRegion()->Name());
3785  else
3786  u->SetRegion(g->GetRegion());
3787 
3788  g->MoveTo(u_loc);
3789  }
3790  }
3791  }
3792 }
3793 
3794 // +--------------------------------------------------------------------+
3795 
3796 const char* FormatGameTime()
3797 {
3798  static char txt[64];
3799 
3800  int t = Game::GameTime();
3801 
3802  int h = ( t / 3600000);
3803  int m = ((t - h*3600000) / 60000);
3804  int s = ((t - h*3600000 - m*60000) / 1000);
3805  int e = ( t - h*3600000 - m*60000 - s*1000);
3806 
3807  if (h > 0)
3808  sprintf_s(txt, "%02d:%02d:%02d.%03d", h,m,s,e);
3809  else
3810  sprintf_s(txt, "%02d:%02d.%03d", m,s,e);
3811 
3812  return txt;
3813 }