Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Sensor.cpp
Go to the documentation of this file.
1 /* Project Starshatter 5.0
2  Destroyer Studios LLC
3  Copyright © 1997-2007. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: Sensor.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Integrated (Passive and Active) Sensor Package class implementation
13 */
14 
15 #include "MemDebug.h"
16 #include "Sensor.h"
17 #include "Contact.h"
18 #include "Element.h"
19 #include "Ship.h"
20 #include "ShipDesign.h"
21 #include "Shot.h"
22 #include "Drone.h"
23 #include "WeaponDesign.h"
24 #include "Sim.h"
25 #include "CombatGroup.h"
26 #include "CombatUnit.h"
27 
28 #include "Game.h"
29 
30 // +----------------------------------------------------------------------+
31 
32 const double SENSOR_THRESHOLD = 0.25;
33 
34 // +----------------------------------------------------------------------+
35 
37 : System(SENSOR, 1, "Dual Sensor Pkg", 1, 10, 10, 10),
38 mode(STD), target(0),
39 nsettings(0), range_index(0)
40 {
41  name = Game::GetText("sys.sensor");
42  abrv = Game::GetText("sys.sensor.abrv");
43 
44  SetMode(mode);
46 
47  ZeroMemory(range_settings, sizeof(range_settings));
48 }
49 
50 // +----------------------------------------------------------------------+
51 
53 : System(s), mode(STD), target(0),
54 nsettings(s.nsettings), range_index(0)
55 {
56  Mount(s);
57 
58  SetMode(mode);
60 
61  CopyMemory(range_settings, s.range_settings, sizeof(range_settings));
62 
63  if (nsettings)
65 }
66 
67 // +--------------------------------------------------------------------+
68 
70 {
72 }
73 
74 // +--------------------------------------------------------------------+
75 
76 void
78 {
79  contacts.destroy();
80 }
81 
82 // +--------------------------------------------------------------------+
83 
84 double
86 {
87  if (mode == ACM)
88  return 15*DEGREES;
89 
90  if (mode <= GM)
91  return 45*DEGREES;
92 
93  return 175*DEGREES;
94 }
95 
96 double
98 {
99  return (double) range_settings[range_index];
100 }
101 
102 void
104 {
105  if (range_index < nsettings-1)
106  range_index++;
107  else
109 }
110 
111 void
113 {
114  if (range_index > 0)
115  range_index--;
116  else
117  range_index = 0;
118 }
119 
120 void
122 {
123  if (nsettings < 8)
124  range_settings[nsettings++] = (float) r;
125 
127 }
128 
129 void
131 {
132  if (mode != m) {
133  // dump the contact list when changing in/out of GM:
134  if (mode == GM || m == GM)
136 
137  // dump the current target on mode changes:
138  if (m <= GM) {
139  if (ship)
140  ship->DropTarget();
141 
142  Ignore(target);
143  target = 0;
144  }
145  }
146 
147  mode = m;
148 }
149 
150 // +----------------------------------------------------------------------+
151 
152 bool
154 {
155  if (obj == target) {
156  target = 0;
157  }
158 
159  return SimObserver::Update(obj);
160 }
161 
162 const char*
164 {
165  return "Sensor";
166 }
167 
168 // +--------------------------------------------------------------------+
169 
170 void
171 Sensor::ExecFrame(double seconds)
172 {
173  if (Game::Paused())
174  return;
175 
176  System::ExecFrame(seconds);
177 
178  if (!IsPowerOn() || energy <= 0) {
180  return;
181  }
182 
183  if (ship && ship->GetAIMode() < 2) {
184  // just pretend to work this frame:
185  energy = 0.0f;
186  return;
187  }
188 
189  if (ship && ship->GetRegion()) {
190  const Camera* cam = &ship->Cam();
191  double az1 = -45*DEGREES;
192  double az2 = 45*DEGREES;
193 
194  if (mode > GM) {
195  az1 = -175*DEGREES;
196  az2 = 175*DEGREES;
197  }
198 
199  ListIter<Ship> ship_iter(ship->GetRegion()->Ships());
200  while (++ship_iter) {
201  Ship* c_ship = ship_iter.value();
202 
203  if (c_ship != ship) {
204  ProcessContact(c_ship, az1, az2);
205  }
206  else {
207  Contact* c = FindContact(c_ship);
208 
209  if (!c) {
210  c = new(__FILE__,__LINE__) Contact(c_ship, 0.0f, 0.0f);
211  contacts.append(c);
212  }
213 
214  // update track:
215  if (c) {
216  c->loc = c_ship->Location();
217  c->d_pas = 2.0f;
218  c->d_act = 2.0f;
219 
220  c->UpdateTrack();
221  }
222  }
223  }
224 
225  ListIter<Shot> threat_iter(ship->GetThreatList());
226  while (++threat_iter) {
227  ProcessContact(threat_iter.value(), az1, az2);
228  }
229 
230  ListIter<Drone> drone_iter(ship->GetRegion()->Drones());
231  while (++drone_iter) {
232  ProcessContact(drone_iter.value(), az1, az2);
233  }
234 
235  List<Contact>& track_list = ship->GetRegion()->TrackList(ship->GetIFF());
236  ListIter<Contact> c_iter(contacts);
237 
238  while (++c_iter) {
239  Contact* contact = c_iter.value();
240  Ship* c_ship = contact->GetShip();
241  Shot* c_shot = contact->GetShot();
242  double c_life = -1;
243 
244  if (c_ship) {
245  c_life = c_ship->Life();
246 
247  // look for quantum jumps and orbit transitions:
248  if (c_ship->GetRegion() != ship->GetRegion())
249  c_life = 0;
250  }
251 
252  else if (c_shot) {
253  c_life = c_shot->Life();
254  }
255 
256  else {
257  c_life = 0;
258  }
259 
260  if (contact->Age() < 0 || c_life == 0) {
261  delete c_iter.removeItem();
262  }
263 
264  else if (ship && ship->GetIFF() >= 0 && ship->GetIFF() < 5) {
265  // update shared track database:
266  Contact* t = track_list.find(contact);
267 
268  if (c_ship) {
269  if (!t) {
270  Contact* track = new(__FILE__,__LINE__) Contact(c_ship, contact->d_pas, contact->d_act);
271  track->loc = c_ship->Location();
272  track_list.append(track);
273  }
274 
275  else {
276  t->loc = c_ship->Location();
277  t->Merge(contact);
278  t->UpdateTrack();
279  }
280  }
281 
282  else if (c_shot) {
283  if (!t) {
284  Contact* track = new(__FILE__,__LINE__) Contact(c_shot, contact->d_pas, contact->d_act);
285  track->loc = c_shot->Location();
286  track_list.append(track);
287  }
288 
289  else {
290  t->loc = c_shot->Location();
291  t->Merge(contact);
292  t->UpdateTrack();
293  }
294  }
295  }
296  }
297 
298 
299  if (mode == ACM) {
300  if (!ship->GetTarget())
301  ship->LockTarget(SimObject::SIM_SHIP, true, true);
302  }
303  }
304 
305  energy = 0.0f;
306 }
307 
308 // +--------------------------------------------------------------------+
309 
310 void
311 Sensor::ProcessContact(Ship* c_ship, double az1, double az2)
312 {
313  if (c_ship->IsNetObserver())
314  return;
315 
316  double sensor_range = GetBeamRange();
317 
318  // translate:
319  const Camera* cam = &ship->Cam();
320  Point targ_pt = c_ship->Location() - ship->Location();
321 
322  // rotate:
323  double tx = targ_pt * cam->vrt();
324  double ty = targ_pt * cam->vup();
325  double tz = targ_pt * cam->vpn();
326 
327  // convert to spherical coords:
328  double rng = targ_pt.length();
329  double az = asin(fabs(tx) / rng);
330  double el = asin(fabs(ty) / rng);
331  if (tx < 0) az = -az;
332  if (ty < 0) el = -el;
333 
334  double min_range = rng;
335  Drone* probe = ship->GetProbe();
336  bool probescan = false;
337 
338  if (ship->GetIFF() == c_ship->GetIFF()) {
339  min_range = 1;
340  }
341 
342  else if (probe) {
343  Point probe_pt = c_ship->Location() - probe->Location();
344  double prng = probe_pt.length();
345 
346  if (prng < probe->Design()->lethal_radius && prng < rng) {
347  min_range = prng;
348  probescan = true;
349  }
350  }
351 
352  bool vis = tz > 1 && (c_ship->Radius()/rng > 0.001);
353  bool threat = (c_ship->Life() != 0 &&
354  c_ship->GetIFF() &&
355  c_ship->GetIFF() != ship->GetIFF() &&
356  c_ship->GetEMCON() > 2 &&
357  c_ship->IsTracking(ship));
358 
359  if (!threat) {
360  if (mode == GM && !c_ship->IsGroundUnit())
361  return;
362 
363  if (mode != GM && c_ship->IsGroundUnit())
364  return;
365 
366  if (min_range > sensor_range || min_range > c_ship->Design()->detet) {
367  if (c_ship == target) {
368  ship->DropTarget();
369  Ignore(target);
370  target = 0;
371  }
372 
373  return;
374  }
375  }
376 
377  // clip:
378  if (threat || vis || mode >= PST || tz > 1) {
379 
380  // correct az/el for back hemisphere:
381  if (tz < 0) {
382  if (az < 0) az = -PI - az;
383  else az = PI - az;
384  }
385 
386  double d_pas = 0;
387  double d_act = 0;
388  double effectivity = energy/capacity * availability;
389 
390  // did this contact get scanned this frame?
391  if (effectivity > SENSOR_THRESHOLD) {
392  if (az >= az1 && az <= az2 && (mode >= PST || fabs(el) < 45*DEGREES)) {
393  double passive_range_limit = 500e3;
394  if (c_ship->Design()->detet > passive_range_limit)
395  passive_range_limit = c_ship->Design()->detet;
396 
397  d_pas = c_ship->PCS() * effectivity * (1 - min_range/passive_range_limit);
398 
399  if (d_pas < 0)
400  d_pas = 0;
401 
402  if (probescan) {
403  double max_range = probe->Design()->lethal_radius;
404  d_act = c_ship->ACS() * (1 - min_range/max_range);
405  }
406 
407  else if (mode != PAS && mode != PST) {
408  double max_range = sensor_range;
409  d_act = c_ship->ACS() * effectivity * (1 - min_range/max_range);
410  }
411 
412  if (d_act < 0)
413  d_act = 0;
414  }
415  }
416 
417  // yes, update or add new contact:
418  if (threat || vis || d_pas > SENSOR_THRESHOLD || d_act > SENSOR_THRESHOLD) {
419  Element* elem = c_ship->GetElement();
420  CombatUnit* unit = c_ship->GetCombatUnit();
421 
422  if (elem && ship && elem->GetIFF() != ship->GetIFF() && elem->IntelLevel() < Intel::LOCATED) {
424  }
425 
426  if (unit && ship && unit->GetIFF() != ship->GetIFF()) {
427  CombatGroup* group = unit->GetCombatGroup();
428 
429  if (group && group->IntelLevel() < Intel::LOCATED &&
430  group->IntelLevel() > Intel::RESERVE) {
432  }
433  }
434 
435  Contact* c = FindContact(c_ship);
436 
437  if (!c) {
438  c = new(__FILE__,__LINE__) Contact(c_ship, 0.0f, 0.0f);
439  contacts.append(c);
440  }
441 
442  // update track:
443  if (c) {
444  c->loc = c_ship->Location();
445  c->d_pas = (float) d_pas;
446  c->d_act = (float) d_act;
447  c->probe = probescan;
448 
449  c->UpdateTrack();
450  }
451  }
452  }
453 }
454 
455 // +--------------------------------------------------------------------+
456 
457 void
458 Sensor::ProcessContact(Shot* c_shot, double az1, double az2)
459 {
460  double sensor_range = GetBeamRange();
461 
462  if (c_shot->IsPrimary() || c_shot->IsDecoy())
463  return;
464 
465  // translate:
466  const Camera* cam = &ship->Cam();
467  Point targ_pt = c_shot->Location() - ship->Location();
468 
469  // rotate:
470  double tx = targ_pt * cam->vrt();
471  double ty = targ_pt * cam->vup();
472  double tz = targ_pt * cam->vpn();
473 
474  // convert to spherical coords:
475  double rng = targ_pt.length();
476  double az = asin(fabs(tx) / rng);
477  double el = asin(fabs(ty) / rng);
478  if (tx < 0) az = -az;
479  if (ty < 0) el = -el;
480 
481  bool vis = tz > 1 && (c_shot->Radius()/rng > 0.001);
482  bool threat = (c_shot->IsTracking(ship));
483 
484  // clip:
485  if (threat || vis || ((mode >= PST || tz > 1) && rng <= sensor_range)) {
486 
487  // correct az/el for back hemisphere:
488  if (tz < 0) {
489  if (az < 0) az = -PI - az;
490  else az = PI - az;
491  }
492 
493  double d_pas = 0;
494  double d_act = 0;
495  double effectivity = energy/capacity * availability;
496 
497  // did this contact get scanned this frame?
498  if (effectivity > SENSOR_THRESHOLD) {
499  if (az >= az1 && az <= az2 && (mode >= PST || fabs(el) < 45*DEGREES)) {
500  if (rng < sensor_range/2)
501  d_pas = 1.5;
502  else
503  d_pas = 0.5;
504 
505  if (mode != PAS && mode != PST)
506  d_act = effectivity * (1 - rng/sensor_range);
507 
508  if (d_act < 0)
509  d_act = 0;
510  }
511  }
512 
513  // yes, update or add new contact:
514  if (threat || vis || d_pas > SENSOR_THRESHOLD || d_act > SENSOR_THRESHOLD) {
515  Contact* c = FindContact(c_shot);
516 
517  if (!c) {
518  c = new(__FILE__,__LINE__) Contact(c_shot, 0.0f, 0.0f);
519  contacts.append(c);
520  }
521 
522  // update track:
523  if (c) {
524  c->loc = c_shot->Location();
525  c->d_pas = (float) d_pas;
526  c->d_act = (float) d_act;
527 
528  c->UpdateTrack();
529  }
530  }
531  }
532 }
533 
534 Contact*
536 {
538  while (++iter) {
539  Contact* c = iter.value();
540  if (c->GetShip() == s)
541  return c;
542  }
543 
544  return 0;
545 }
546 
547 Contact*
549 {
551  while (++iter) {
552  Contact* c = iter.value();
553  if (c->GetShot() == s)
554  return c;
555  }
556 
557  return 0;
558 }
559 
560 // +--------------------------------------------------------------------+
561 
562 bool
564 {
565  if (tgt && mode != GM && mode != PAS && mode != PST && IsPowerOn()) {
566  if (tgt == target)
567  return true;
568 
569  Contact* c = 0;
570 
571  if (tgt->Type() == SimObject::SIM_SHIP) {
572  c = FindContact((Ship*) tgt);
573  }
574  else {
575  c = FindContact((Shot*) tgt);
576  }
577 
578  return (c != 0 && c->ActReturn() > SENSOR_THRESHOLD && !c->IsProbed());
579  }
580 
581  return false;
582 }
583 
584 // +--------------------------------------------------------------------+
585 
586 const double sensor_lock_threshold = 0.5;
587 
588 struct TargetOffset {
589  static const char* TYPENAME() { return "TargetOffset"; }
590 
592  double offset;
593 
594  TargetOffset() : target(0), offset(10) { }
595  TargetOffset(SimObject* t, double o) : target(t), offset(o) { }
596  int operator< (const TargetOffset& o) const { return offset < o.offset; }
597  int operator<=(const TargetOffset& o) const { return offset <= o.offset; }
598  int operator==(const TargetOffset& o) const { return offset == o.offset; }
599 };
600 
601 SimObject*
602 Sensor::LockTarget(int type, bool closest, bool hostile)
603 {
604  if (!ship || ship->GetEMCON() < 3) {
605  Ignore(target);
606  target = 0;
607  return target;
608  }
609 
610  SimObject* test = 0;
611  ListIter<Contact> contact(ship->ContactList());
612 
613  List<TargetOffset> targets;
614 
615  while (++contact) {
616  if (type == SimObject::SIM_SHIP)
617  test = contact->GetShip();
618  else
619  test = contact->GetShot();
620 
621  if (!test)
622  continue;
623 
624  // do not target own missiles:
625  if (contact->GetShot() && contact->GetShot()->Owner() == ship)
626  continue;
627 
628  double tgt_range = contact->Range(ship);
629 
630  // do not target ships outside of detet range:
631  if (contact->GetShip() && contact->GetShip()->Design()->detet < tgt_range)
632  continue;
633 
634  double d_pas = contact->PasReturn();
635  double d_act = contact->ActReturn();
636  bool vis = contact->Visible(ship) || contact->Threat(ship);
637 
638  if (!vis && d_pas < sensor_lock_threshold && d_act < sensor_lock_threshold)
639  continue;
640 
641  if (closest) {
642  if (hostile && (contact->GetIFF(ship) == 0 || contact->GetIFF(ship) == ship->GetIFF()))
643  continue;
644 
645  targets.append(new(__FILE__,__LINE__) TargetOffset(test, tgt_range));
646  }
647 
648  // clip:
649  else if (contact->InFront(ship)) {
650  double az, el, rng;
651 
652  contact->GetBearing(ship, az, el, rng);
653  az = fabs(az / PI);
654  el = fabs(el / PI);
655 
656  if (az <= 0.2 && el <= 0.2)
657  targets.append(new(__FILE__,__LINE__) TargetOffset(test, az+el));
658  }
659  }
660 
661  targets.sort();
662 
663  if (target) {
664  int index = 100000;
665  int i = 0;
666 
667  if (targets.size() > 0) {
668  ListIter<TargetOffset> iter(targets);
669  while (++iter) {
670  if (iter->target == target) {
671  index = i;
672  break;
673  }
674 
675  i++;
676  }
677 
678  if (index < targets.size()-1)
679  index++;
680  else
681  index = 0;
682 
683  target = targets[index]->target;
684  Observe(target);
685  }
686  }
687  else if (targets.size() > 0) {
688  target = targets[0]->target;
689  Observe(target);
690  }
691  else {
692  target = 0;
693  }
694 
695  targets.destroy();
696 
697  if (target && mode < STD)
698  mode = STD;
699 
700  return target;
701 }
702 
703 // +--------------------------------------------------------------------+
704 
705 SimObject*
707 {
708  Ignore(target);
709  target = 0;
710 
711  if (ship->GetEMCON() < 3)
712  return target;
713 
714  if (!candidate)
715  return target;
716 
717  int type = candidate->Type();
718  SimObject* test = 0;
719  ListIter<Contact> contact(ship->ContactList());
720 
721  while (++contact) {
722  if (type == SimObject::SIM_SHIP)
723  test = contact->GetShip();
724  else
725  test = contact->GetShot();
726 
727  if (test == candidate) {
728  double d_pas = contact->PasReturn();
729  double d_act = contact->ActReturn();
730  bool vis = contact->Visible(ship) || contact->Threat(ship);
731 
732  if (vis || d_pas > sensor_lock_threshold || d_act > sensor_lock_threshold) {
733  target = test;
734  Observe(target);
735  }
736 
737  break;
738  }
739  }
740 
741  if (target && mode < STD)
742  mode = STD;
743 
744  return target;
745 }
746 
747 // +--------------------------------------------------------------------+
748 
749 SimObject*
751 {
752  SimObject* pick = 0;
753  double min_off = 2;
754 
755  ListIter<Contact> contact(ship->ContactList());
756 
757  while (++contact) {
758  SimObject* test = contact->GetShip();
759  double d = contact->PasReturn();
760 
761  if (d < 1) continue;
762 
763  // clip:
764  if (contact->InFront(ship)) {
765  double az, el, rng;
766 
767  contact->GetBearing(ship, az, el, rng);
768  az = fabs(az / PI);
769  el = fabs(el / PI);
770 
771  if (az + el < min_off) {
772  min_off = az + el;
773  pick = test;
774  }
775  }
776  }
777 
778  return pick;
779 }
780 
781 // +--------------------------------------------------------------------+
782 
783 SimObject*
785 {
786  SimObject* pick = 0;
787  double min_off = 2;
788 
789  ListIter<Contact> contact(ship->ContactList());
790 
791  while (++contact) {
792  SimObject* test = contact->GetShip();
793  double d = contact->ActReturn();
794 
795  if (d < 1) continue;
796 
797  if (contact->InFront(ship)) {
798  double az, el, rng;
799 
800  contact->GetBearing(ship, az, el, rng);
801  az = fabs(az / PI);
802  el = fabs(el / PI);
803 
804  if (az + el < min_off) {
805  min_off = az + el;
806  pick = test;
807  }
808  }
809  }
810 
811  return pick;
812 }
813 
814 // +--------------------------------------------------------------------+
815 
816 void
817 Sensor::DoEMCON(int index)
818 {
819  int e = GetEMCONPower(index);
820 
821  if (power_level * 100 > e || emcon != index) {
822  if (e == 0) {
823  PowerOff();
824  }
825  else if (emcon != index) {
826  PowerOn();
827 
828  if (power_level * 100 > e) {
829  SetPowerLevel(e);
830  }
831 
832  if (emcon == 3) {
833  if (GetMode() < PST)
834  SetMode(STD);
835  else
836  SetMode(CST);
837  }
838  else {
839  int m = GetMode();
840  if (m < PST && m > PAS)
842  else if (m == CST)
843  SetMode(PST);
844  }
845  }
846  }
847 
848  emcon = index;
849 }
850