From 8898ad9b25fca6afe2374d293a981db02a83d7e9 Mon Sep 17 00:00:00 2001 From: "FWoltermann@gmail.com" Date: Thu, 31 May 2012 14:46:27 +0000 Subject: Committing the documentation to svn to have it accessible online --- Doc/doxygen/html/_ship_8cpp_source.html | 5428 +++++++++++++++++++++++++++++++ 1 file changed, 5428 insertions(+) create mode 100644 Doc/doxygen/html/_ship_8cpp_source.html (limited to 'Doc/doxygen/html/_ship_8cpp_source.html') diff --git a/Doc/doxygen/html/_ship_8cpp_source.html b/Doc/doxygen/html/_ship_8cpp_source.html new file mode 100644 index 0000000..856f274 --- /dev/null +++ b/Doc/doxygen/html/_ship_8cpp_source.html @@ -0,0 +1,5428 @@ + + + + + +Starshatter_Open: D:/SRC/StarshatterSVN/Stars45/Ship.cpp Source File + + + + + + + + + + + + + +
+
+ + + + + + +
+
Starshatter_Open +
+
Open source Starshatter engine
+
+
+ + + + + +
+
+ +
+
+
+ +
+ + + + +
+ +
+ +
+
+
Ship.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: Ship.cpp
+
7  AUTHOR: John DiCamillo
+
8 
+
9 
+
10  OVERVIEW
+
11  ========
+
12  Starship class
+
13 */
+
14 
+
15 #include "MemDebug.h"
+
16 #include "Ship.h"
+
17 #include "ShipAI.h"
+
18 #include "ShipCtrl.h"
+
19 #include "ShipDesign.h"
+
20 #include "ShipKiller.h"
+
21 #include "Shot.h"
+
22 #include "Drone.h"
+
23 #include "SeekerAI.h"
+
24 #include "HardPoint.h"
+
25 #include "Weapon.h"
+
26 #include "WeaponGroup.h"
+
27 #include "Shield.h"
+
28 #include "ShieldRep.h"
+
29 #include "Computer.h"
+
30 #include "FlightComp.h"
+
31 #include "Drive.h"
+
32 #include "QuantumDrive.h"
+
33 #include "Farcaster.h"
+
34 #include "Thruster.h"
+
35 #include "Power.h"
+
36 #include "FlightDeck.h"
+
37 #include "LandingGear.h"
+
38 #include "Hangar.h."
+
39 #include "Sensor.h"
+
40 #include "Contact.h"
+
41 #include "CombatUnit.h"
+
42 #include "Element.h"
+
43 #include "Instruction.h"
+
44 #include "RadioMessage.h"
+
45 #include "RadioHandler.h"
+
46 #include "RadioTraffic.h"
+
47 #include "NavLight.h"
+
48 #include "NavSystem.h"
+
49 #include "NavAI.h"
+
50 #include "DropShipAI.h"
+
51 #include "Explosion.h"
+
52 #include "MissionEvent.h"
+
53 #include "ShipSolid.h"
+
54 #include "Sim.h"
+
55 #include "SimEvent.h"
+
56 #include "StarSystem.h"
+
57 #include "TerrainRegion.h"
+
58 #include "Terrain.h"
+
59 #include "System.h"
+
60 #include "Component.h"
+
61 #include "KeyMap.h"
+
62 #include "RadioView.h"
+
63 #include "AudioConfig.h"
+
64 #include "CameraDirector.h"
+
65 #include "HUDView.h"
+
66 #include "Random.h"
+
67 #include "RadioVox.h"
+
68 
+
69 #include "NetGame.h"
+
70 #include "NetUtil.h"
+
71 
+
72 #include "MotionController.h"
+
73 #include "Keyboard.h"
+
74 #include "Joystick.h"
+
75 #include "Bolt.h"
+
76 #include "Game.h"
+
77 #include "Solid.h"
+
78 #include "Shadow.h"
+
79 #include "Skin.h"
+
80 #include "Sprite.h"
+
81 #include "Light.h"
+
82 #include "Bitmap.h"
+
83 #include "Button.h"
+
84 #include "Sound.h"
+
85 #include "DataLoader.h"
+
86 
+
87 #include "Parser.h"
+
88 #include "Reader.h"
+
89 
+
90 // +----------------------------------------------------------------------+
+
91 
+
92 static int base_contact_id = 0;
+
93 static double range_min = 0;
+
94 static double range_max = 250e3;
+
95 
+
96 int Ship::control_model = 0; // standard
+
97 int Ship::flight_model = 0; // standard
+
98 int Ship::landing_model = 0; // standard
+
99 double Ship::friendly_fire_level = 1; // 100%
+
100 
+
101 const int HIT_NOTHING = 0;
+
102 const int HIT_HULL = 1;
+
103 const int HIT_SHIELD = 2;
+
104 const int HIT_BOTH = 3;
+
105 const int HIT_TURRET = 4;
+
106 
+
107 // +----------------------------------------------------------------------+
+
108 
+
109 Ship::Ship(const char* ship_name, const char* reg_num, ShipDesign* ship_dsn, int IFF, int cmd_ai, const int* load)
+
110 : IFF_code(IFF), killer(0), throttle(0), augmenter(false), throttle_request(0),
+
111 shield(0), shieldRep(0), main_drive(0), quantum_drive(0), farcaster(0),
+
112 check_fire(false), probe(0), sensor_drone(0), primary(0), secondary(1),
+
113 cmd_chain_index(0), target(0), subtarget(0), radio_orders(0), launch_point(0),
+
114 g_force(0.0f), sensor(0), navsys(0), flcs(0), hangar(0), respawns(0), invulnerable(false),
+
115 thruster(0), decoy(0), ai_mode(2), command_ai_level(cmd_ai), flcs_mode(FLCS_AUTO), loadout(0),
+
116 emcon(3), old_emcon(3), master_caution(false), cockpit(0), gear(0), skin(0),
+
117 auto_repair(true), last_repair_time(0), last_eval_time(0), last_beam_time(0), last_bolt_time(0),
+
118 warp_fov(1), flight_phase(LAUNCH), launch_time(0), carrier(0), dock(0), ff_count(0),
+
119 inbound(0), element(0), director_info("Init"), combat_unit(0), net_control(0),
+
120 track(0), ntrack(0), track_time(0), helm_heading(0.0f), helm_pitch(0.0f),
+
121 altitude_agl(-1.0e6f), transition_time(0.0f), transition_type(TRANSITION_NONE),
+
122 friendly_fire_time(0), ward(0), net_observer_mode(false), orig_elem_index(-1)
+
123 {
+
124  sim = Sim::GetSim();
+
125 
+
126  strcpy_s(name, ship_name);
+
127  if (reg_num && *reg_num)
+
128  strcpy_s(regnum, reg_num);
+
129  else regnum[0] = 0;
+
130 
+
131  design = ship_dsn;
+
132 
+
133  if (!design) {
+
134  char msg[256];
+
135  sprintf_s(msg, "No ship design found for '%s'\n", ship_name);
+
136  Game::Panic(msg);
+
137  }
+
138 
+ +
140 
+
141  radius = design->radius;
+
142  mass = design->mass;
+ +
144  vlimit = design->vlimit;
+
145 
+ +
147  wep_mass = 0.0f;
+
148  wep_resist = 0.0f;
+
149 
+
150  CL = design->CL;
+
151  CD = design->CD;
+
152  stall = design->stall;
+
153 
+ + +
156 
+
157  acs = design->acs;
+
158  pcs = design->acs;
+
159 
+ +
161 
+
162  while (!base_contact_id)
+
163  base_contact_id = rand() % 1000;
+
164 
+
165  contact_id = base_contact_id++;
+
166  int sys_id = 0;
+
167 
+
168  for (int i = 0; i < design->reactors.size(); i++) {
+
169  PowerSource* reactor = new(__FILE__,__LINE__) PowerSource(*design->reactors[i]);
+
170  reactor->SetShip(this);
+
171  reactor->SetID(sys_id++);
+
172  reactors.append(reactor);
+
173  systems.append(reactor);
+
174  }
+
175 
+
176  for (int i = 0; i < design->drives.size(); i++) {
+
177  Drive* drive = new(__FILE__,__LINE__) Drive(*design->drives[i]);
+
178  drive->SetShip(this);
+
179  drive->SetID(sys_id++);
+
180 
+
181  int src_index = drive->GetSourceIndex();
+
182  if (src_index >= 0 && src_index < reactors.size())
+
183  reactors[src_index]->AddClient(drive);
+
184 
+
185  drives.append(drive);
+
186  systems.append(drive);
+
187  }
+
188 
+
189  if (design->quantum_drive) {
+
190  quantum_drive = new(__FILE__,__LINE__) QuantumDrive(*design->quantum_drive);
+
191  quantum_drive->SetShip(this);
+
192  quantum_drive->SetID(sys_id++);
+
193 
+
194  int src_index = quantum_drive->GetSourceIndex();
+
195  if (src_index >= 0 && src_index < reactors.size())
+
196  reactors[src_index]->AddClient(quantum_drive);
+
197 
+
198  quantum_drive->SetShip(this);
+ +
200  }
+
201 
+
202  if (design->farcaster) {
+
203  farcaster = new(__FILE__,__LINE__) Farcaster(*design->farcaster);
+
204  farcaster->SetShip(this);
+
205  farcaster->SetID(sys_id++);
+
206 
+
207  int src_index = farcaster->GetSourceIndex();
+
208  if (src_index >= 0 && src_index < reactors.size())
+
209  reactors[src_index]->AddClient(farcaster);
+
210 
+
211  farcaster->SetShip(this);
+ +
213  }
+
214 
+
215  if (design->thruster) {
+
216  thruster = new(__FILE__,__LINE__) Thruster(*design->thruster);
+
217  thruster->SetShip(this);
+
218  thruster->SetID(sys_id++);
+
219 
+
220  int src_index = thruster->GetSourceIndex();
+
221  if (src_index >= 0 && src_index < reactors.size())
+
222  reactors[src_index]->AddClient(thruster);
+
223 
+
224  thruster->SetShip(this);
+ +
226  }
+
227 
+
228  if (design->shield) {
+
229  shield = new(__FILE__,__LINE__) Shield(*design->shield);
+
230  shield->SetShip(this);
+
231  shield->SetID(sys_id++);
+
232 
+
233  int src_index = shield->GetSourceIndex();
+
234  if (src_index >= 0 && src_index < reactors.size())
+
235  reactors[src_index]->AddClient(shield);
+
236 
+
237  if (design->shield_model) {
+
238  shieldRep = new(__FILE__,__LINE__) ShieldRep;
+ +
240  }
+
241 
+ +
243  }
+
244 
+
245  for (int i = 0; i < design->flight_decks.size(); i++) {
+
246  FlightDeck* deck = new(__FILE__,__LINE__) FlightDeck(*design->flight_decks[i]);
+
247  deck->SetShip(this);
+
248  deck->SetCarrier(this);
+
249  deck->SetID(sys_id++);
+
250  deck->SetIndex(i);
+
251 
+
252  int src_index = deck->GetSourceIndex();
+
253  if (src_index >= 0 && src_index < reactors.size())
+
254  reactors[src_index]->AddClient(deck);
+
255 
+
256  flight_decks.append(deck);
+
257  systems.append(deck);
+
258  }
+
259 
+
260  if (design->flight_decks.size() > 0) {
+
261  if (!hangar) {
+
262  hangar = new(__FILE__,__LINE__) Hangar;
+
263  hangar->SetShip(this);
+
264  }
+
265  }
+
266 
+
267  if (design->squadrons.size() > 0) {
+
268  if (!hangar) {
+
269  hangar = new(__FILE__,__LINE__) Hangar;
+
270  hangar->SetShip(this);
+
271  }
+
272 
+
273  for (int i = 0; i < design->squadrons.size(); i++) {
+
274  ShipSquadron* s = design->squadrons[i];
+
275  hangar->CreateSquadron(s->name, 0, s->design, s->count, GetIFF(), 0, 0, s->avail);
+
276  }
+
277  }
+
278 
+
279  if (design->gear) {
+
280  gear = new(__FILE__,__LINE__) LandingGear(*design->gear);
+
281  gear->SetShip(this);
+
282  gear->SetID(sys_id++);
+
283 
+
284  int src_index = gear->GetSourceIndex();
+
285  if (src_index >= 0 && src_index < reactors.size())
+
286  reactors[src_index]->AddClient(gear);
+
287 
+ +
289  }
+
290 
+
291  if (design->sensor) {
+
292  sensor = new(__FILE__,__LINE__) Sensor(*design->sensor);
+
293  sensor->SetShip(this);
+
294  sensor->SetID(sys_id++);
+
295 
+
296  int src_index = sensor->GetSourceIndex();
+
297  if (src_index >= 0 && src_index < reactors.size())
+
298  reactors[src_index]->AddClient(sensor);
+
299 
+
300  if (IsStarship() || IsStatic() || !strncmp(design->name, "Camera", 6))
+ +
302 
+ +
304  }
+
305 
+
306  int wep_index = 1;
+
307 
+
308  for (int i = 0; i < design->weapons.size(); i++) {
+
309  Weapon* gun = new(__FILE__,__LINE__) Weapon(*design->weapons[i]);
+
310  gun->SetID(sys_id++);
+
311  gun->SetOwner(this);
+
312  gun->SetIndex(wep_index++);
+
313 
+
314  int src_index = gun->GetSourceIndex();
+
315  if (src_index >= 0 && src_index < reactors.size())
+
316  reactors[src_index]->AddClient(gun);
+
317 
+
318  WeaponGroup* group = FindWeaponGroup(gun->Group());
+
319  group->AddWeapon(gun);
+
320  group->SetAbbreviation(gun->Abbreviation());
+
321 
+
322  systems.append(gun);
+
323 
+
324  if (IsDropship() && gun->GetTurret())
+ +
326  else
+ +
328  }
+
329 
+
330  int loadout_size = design->hard_points.size();
+
331 
+
332  if (load && loadout_size > 0) {
+
333  loadout = new(__FILE__,__LINE__) int[loadout_size];
+
334 
+
335  for (int i = 0; i < loadout_size; i++) {
+
336  int mounted_weapon = loadout[i] = load[i];
+
337 
+
338  if (mounted_weapon < 0)
+
339  continue;
+
340 
+
341  Weapon* missile = design->hard_points[i]->CreateWeapon(mounted_weapon);
+
342 
+
343  if (missile) {
+
344  missile->SetID(sys_id++);
+
345  missile->SetOwner(this);
+
346  missile->SetIndex(wep_index++);
+
347 
+
348  WeaponGroup* group = FindWeaponGroup(missile->Group());
+
349  group->AddWeapon(missile);
+
350  group->SetAbbreviation(missile->Abbreviation());
+
351 
+
352  systems.append(missile);
+
353  }
+
354  }
+
355  }
+
356 
+
357  if (weapons.size() > 1) {
+
358  primary = -1;
+
359  secondary = -1;
+
360 
+
361  for (int i = 0; i < weapons.size(); i++) {
+
362  WeaponGroup* group = weapons[i];
+
363  if (group->IsPrimary() && primary < 0) {
+
364  primary = i;
+
365 
+
366  // turrets on fighters are set to point defense by default,
+
367  // this forces the primary turret back to manual control
+ +
369  }
+
370 
+
371  else if (group->IsMissile() && secondary < 0) {
+
372  secondary = i;
+
373  }
+
374  }
+
375 
+
376  if (primary < 0) primary = 0;
+
377  if (secondary < 0) secondary = 1;
+
378 
+
379  if (weapons.size() > 4) {
+
380  ::Print("WARNING: Ship '%s' type '%s' has %d wep groups (max=4)\n",
+
381  Name(), DesignName(), weapons.size());
+
382  }
+
383  }
+
384 
+
385  if (design->decoy) {
+
386  decoy = new(__FILE__,__LINE__) Weapon(*design->decoy);
+
387  decoy->SetOwner(this);
+
388  decoy->SetID(sys_id++);
+
389  decoy->SetIndex(wep_index++);
+
390 
+
391  int src_index = decoy->GetSourceIndex();
+
392  if (src_index >= 0 && src_index < reactors.size())
+
393  reactors[src_index]->AddClient(decoy);
+
394 
+ +
396  }
+
397 
+
398  for (int i = 0; i < design->navlights.size(); i++) {
+
399  NavLight* navlight = new(__FILE__,__LINE__) NavLight(*design->navlights[i]);
+
400  navlight->SetShip(this);
+
401  navlight->SetID(sys_id++);
+
402  navlight->SetOffset(((DWORD) this) << 2);
+
403  navlights.append(navlight);
+
404  systems.append(navlight);
+
405  }
+
406 
+
407  if (design->navsys) {
+
408  navsys = new(__FILE__,__LINE__) NavSystem(*design->navsys);
+
409  navsys->SetShip(this);
+
410  navsys->SetID(sys_id++);
+
411 
+
412  int src_index = navsys->GetSourceIndex();
+
413  if (src_index >= 0 && src_index < reactors.size())
+
414  reactors[src_index]->AddClient(navsys);
+
415 
+ +
417  }
+
418 
+
419  if (design->probe) {
+
420  probe = new(__FILE__,__LINE__) Weapon(*design->probe);
+
421  probe->SetOwner(this);
+
422  probe->SetID(sys_id++);
+
423  probe->SetIndex(wep_index++);
+
424 
+
425  int src_index = probe->GetSourceIndex();
+
426  if (src_index >= 0 && src_index < reactors.size())
+
427  reactors[src_index]->AddClient(probe);
+
428 
+ +
430  }
+
431 
+
432  for (int i = 0; i < design->computers.size(); i++) {
+
433  Computer* comp = 0;
+
434 
+
435  if (design->computers[i]->Subtype() == Computer::FLIGHT) {
+
436  flcs = new(__FILE__,__LINE__) FlightComp(*design->computers[i]);
+
437 
+
438  flcs->SetShip(this);
+ + +
441 
+
442  if (thruster)
+ + +
445  thruster->TransZLimit());
+
446  else
+ +
448  design->trans_y,
+
449  design->trans_z);
+
450 
+
451  comp = flcs;
+
452  }
+
453  else {
+
454  comp = new(__FILE__,__LINE__) Computer(*design->computers[i]);
+
455  }
+
456 
+
457  comp->SetShip(this);
+
458  comp->SetID(sys_id++);
+
459  int src_index = comp->GetSourceIndex();
+
460  if (src_index >= 0 && src_index < reactors.size())
+
461  reactors[src_index]->AddClient(comp);
+
462 
+
463  computers.append(comp);
+
464  systems.append(comp);
+
465  }
+
466 
+
467  radio_orders = new(__FILE__,__LINE__) Instruction("", Point(0,0,0));
+
468 
+
469  // Load Detail Set:
+
470  for (int i = 0; i < DetailSet::MAX_DETAIL; i++) {
+
471  if (design->models[i].size() > 0) {
+
472  Solid* solid = new(__FILE__,__LINE__) ShipSolid(this);
+
473  solid->UseModel(design->models[i].at(0));
+
474  solid->CreateShadows(1);
+
475 
+
476  Point* offset = 0;
+
477  Point* spin = 0;
+
478 
+
479  if (design->offsets[i].size() > 0)
+
480  offset = new(__FILE__,__LINE__) Point(*design->offsets[i].at(0));
+
481 
+
482  if (design->spin_rates.size() > 0)
+
483  spin = new(__FILE__,__LINE__) Point(*design->spin_rates.at(0));
+
484 
+
485  detail_level = detail.DefineLevel(design->feature_size[i], solid, offset, spin);
+
486  }
+
487 
+
488  if (design->models[i].size() > 1) {
+
489  for (int n = 1; n < design->models[i].size(); n++) {
+
490  Solid* solid = new(__FILE__,__LINE__) ShipSolid(this); //Solid;
+
491  solid->UseModel(design->models[i].at(n));
+
492  solid->CreateShadows(1);
+
493 
+
494  Point* offset = 0;
+
495  Point* spin = 0;
+
496 
+
497  if (design->offsets[i].size() > n)
+
498  offset = new(__FILE__,__LINE__) Point(*design->offsets[i].at(n));
+
499 
+
500  if (design->spin_rates.size() > n)
+
501  spin = new(__FILE__,__LINE__) Point(*design->spin_rates.at(n));
+
502 
+
503  detail.AddToLevel(detail_level, solid, offset, spin);
+
504  }
+
505  }
+
506  }
+
507 
+
508  // start with lowest available detail:
+
509  detail_level = 0; // this is highest -> detail.NumLevels()-1);
+ +
511 
+
512  if (design->cockpit_model) {
+
513  cockpit = new(__FILE__,__LINE__) Solid;
+ +
515  cockpit->SetForeground(true);
+
516  }
+
517 
+
518  if (design->main_drive >= 0 && design->main_drive < drives.size())
+ +
520 
+
521  // only use light from drives:
+
522  light = 0;
+
523 
+
524  // setup starship helm stuff:
+
525  if (IsStarship()) {
+ +
527  }
+
528 
+
529  // initialize the AI:
+
530  dir = 0;
+
531  SetControls(0);
+
532 
+
533  for (int i = 0; i < 4; i++) {
+
534  missile_id[i] = 0;
+
535  missile_eta[i] = 0;
+
536  trigger[i] = false;
+
537  }
+
538 }
+
539 
+
540 // +--------------------------------------------------------------------+
+
541 
+ +
543 {
+
544  // the loadout can not be cleared during Destroy, because it
+
545  // is needed after Destroy to create the re-spawned ship
+
546 
+
547  delete [] loadout;
+
548  loadout = 0;
+
549 
+
550  Destroy();
+
551 }
+
552 
+
553 // +--------------------------------------------------------------------+
+
554 
+
555 void
+ +
557 {
+
558  // destroy fighters on deck:
+ +
560  while (++deck) {
+
561  for (int i = 0; i < deck->NumSlots(); i++) {
+
562  Ship* s = deck->GetShip(i);
+
563 
+
564  if (s && !s->IsDying() && !s->IsDead()) {
+
565  if (sim && sim->IsActive()) {
+
566  s->DeathSpiral();
+
567  }
+
568  else {
+ +
570  s->Destroy();
+
571  }
+
572  }
+
573  }
+
574  }
+
575 
+
576  if (element) {
+
577  // mission ending for this ship, evaluate objectives one last time:
+
578  for (int i = 0; i < element->NumObjectives(); i++) {
+
579  Instruction* obj = element->GetObjective(i);
+
580 
+
581  if (obj->Status() <= Instruction::ACTIVE) {
+
582  obj->Evaluate(this);
+
583  }
+
584  }
+
585 
+ +
587  SetElement(0);
+
588  }
+
589 
+
590  delete [] track;
+
591  track = 0;
+
592 
+
593  delete shield;
+
594  shield = 0;
+
595  delete sensor;
+
596  sensor = 0;
+
597  delete navsys;
+
598  navsys = 0;
+
599  delete thruster;
+
600  thruster = 0;
+
601  delete farcaster;
+
602  farcaster = 0;
+
603  delete quantum_drive;
+
604  quantum_drive = 0;
+
605  delete decoy;
+
606  decoy = 0;
+
607  delete probe;
+
608  probe = 0;
+
609  delete gear;
+
610  gear = 0;
+
611 
+
612  main_drive = 0;
+
613  flcs = 0;
+
614 
+
615  // repair queue does not own the systems under repair:
+ +
617 
+
618  navlights.destroy();
+ +
620  computers.destroy();
+
621  weapons.destroy();
+
622  drives.destroy();
+
623  reactors.destroy();
+
624 
+
625  // this is now a list of dangling pointers:
+
626  systems.clear();
+
627 
+
628  delete hangar;
+
629  hangar = 0;
+
630 
+
631  // this also destroys the rep:
+
632  detail.Destroy();
+
633  rep = 0;
+
634 
+ + + +
638 
+
639  delete launch_point;
+
640  launch_point = 0;
+
641 
+
642  delete radio_orders;
+
643  radio_orders = 0;
+
644 
+
645  delete dir;
+
646  dir = 0;
+
647 
+
648  delete killer;
+
649  killer = 0;
+
650 
+
651  // inbound slot is deleted by flight deck:
+
652  inbound = 0;
+
653 
+
654  life = 0;
+
655  Notify();
+
656 }
+
657 
+
658 // +--------------------------------------------------------------------+
+
659 
+
660 void
+ +
662 {
+ + +
665 }
+
666 
+
667 // +--------------------------------------------------------------------+
+
668 
+
669 void
+ +
671 {
+ +
673  Thruster::Close();
+
674 }
+
675 
+
676 void
+ +
678 {
+
679  const float ROLL_SPEED = (float)(PI * 0.1500);
+
680  const float PITCH_SPEED = (float)(PI * 0.0250);
+
681  const float YAW_SPEED = (float)(PI * 0.0250);
+
682 
+
683  drag = design->drag;
+ + + +
687 
+
688  if (IsDying()) {
+
689  drag = 0.0f;
+
690  dr_drg *= 0.25f;
+
691  dp_drg *= 0.25f;
+
692  dy_drg *= 0.25f;
+
693  }
+
694 
+
695  if (flight_model > 0) {
+ +
697  thrust *= 10.0f;
+
698  }
+
699 
+
700  float yaw_air_factor = 1.0f;
+
701 
+
702  if (IsAirborne()) {
+
703  bool grounded = AltitudeAGL() < Radius()/2;
+
704 
+
705  if (flight_model > 0) {
+
706  drag *= 2.0f;
+
707 
+
708  if (gear && gear->GetState() != LandingGear::GEAR_UP)
+
709  drag *= 2.0f;
+
710 
+
711  if (grounded)
+
712  drag *= 3.0f;
+
713  }
+
714 
+
715  else {
+
716  if (Class() != LCA)
+
717  yaw_air_factor = 0.3f;
+
718 
+
719  double rho = GetDensity();
+
720  double speed = Velocity().length();
+
721 
+
722  agility = design->air_factor * rho * speed - wep_resist;
+
723 
+
724  if (grounded && agility < 0)
+
725  agility = 0;
+
726 
+
727  else if (!grounded && agility < 0.5 * design->agility)
+
728  agility = 0.5 * design->agility;
+
729 
+
730  else if (agility > 2 * design->agility)
+
731  agility = 2 * design->agility;
+
732 
+
733  // undercarriage aerodynamic drag
+
734  if (gear && gear->GetState() != LandingGear::GEAR_UP)
+
735  drag *= 5.0f;
+
736 
+
737  // wheel rolling friction
+
738  if (grounded)
+
739  drag *= 10.0f;
+
740 
+
741  // dead engine drag ;-)
+
742  if (thrust < 10)
+
743  drag *= 5.0f;
+
744  }
+
745  }
+
746 
+
747  else {
+ +
749 
+
750  if (agility < 0.5 * design->agility)
+
751  agility = 0.5 * design->agility;
+
752 
+
753  if (flight_model == 0)
+
754  drag = 0.0f;
+
755  }
+
756 
+
757  float rr = (float) (design->roll_rate * PI / 180);
+
758  float pr = (float) (design->pitch_rate * PI / 180);
+
759  float yr = (float) (design->yaw_rate * PI / 180);
+
760 
+
761  if (rr == 0) rr = (float) agility * ROLL_SPEED;
+
762  if (pr == 0) pr = (float) agility * PITCH_SPEED;
+
763  if (yr == 0) yr = (float) agility * YAW_SPEED * yaw_air_factor;
+
764 
+
765  SetAngularRates(rr, pr, yr);
+
766 }
+
767 
+
768 // +--------------------------------------------------------------------+
+
769 
+
770 void
+ +
772 {
+ +
774 
+
775  const double GRAV = 6.673e-11;
+
776 
+
777  if (IsGroundUnit()) {
+
778  // glue buildings to the terrain:
+
779  Point loc = Location();
+
780  Terrain* terrain = region->GetTerrain();
+
781 
+
782  if (terrain) {
+
783  loc.y = terrain->Height(loc.x, loc.z);
+
784  MoveTo(loc);
+
785  }
+
786  }
+
787 
+
788  else if (IsAirborne()) {
+ +
790 
+
791  double m0 = primary->Mass();
+
792  double r = primary->Radius();
+
793 
+
794  SetGravity((float) (GRAV * m0 / (r*r)));
+
795  SetBaseDensity(1.0f);
+
796  }
+
797 
+
798  else {
+
799  SetGravity(0.0f);
+
800  SetBaseDensity(0.0f);
+
801 
+
802  if (IsStarship())
+ +
804  else
+ +
806  }
+
807 }
+
808 
+
809 // +--------------------------------------------------------------------+
+
810 
+
811 int
+ +
813 {
+
814  textures.clear();
+
815 
+
816  for (int d = 0; d < detail.NumLevels(); d++) {
+
817  for (int i = 0; i < detail.NumModels(d); i++) {
+
818  Graphic* g = detail.GetRep(d, i);
+
819 
+
820  if (g->IsSolid()) {
+
821  Solid* solid = (Solid*) g;
+
822  Model* model = solid->GetModel();
+
823 
+
824  if (model) {
+
825  for (int n = 0; n < model->NumMaterials(); n++) {
+
826  //textures.append(model->textures[n]);
+
827  }
+
828  }
+
829  }
+
830  }
+
831  }
+
832 
+
833  return textures.size();
+
834 }
+
835 
+
836 // +--------------------------------------------------------------------+
+
837 
+
838 void
+ +
840 {
+
841  int i = 0;
+
842  SimObject::Activate(scene);
+
843 
+
844  for (i = 0; i < detail.NumModels(detail_level); i++) {
+ +
846  scene.AddGraphic(g);
+
847  }
+
848 
+
849  for (i = 0; i < flight_decks.size(); i++)
+
850  scene.AddLight(flight_decks[i]->GetLight());
+
851 
+
852  if (shieldRep)
+
853  scene.AddGraphic(shieldRep);
+
854 
+
855  if (cockpit) {
+
856  scene.AddForeground(cockpit);
+
857  cockpit->Hide();
+
858  }
+
859 
+
860  Drive* drive = GetDrive();
+
861  if (drive) {
+
862  for (i = 0; i < drive->NumEngines(); i++) {
+
863  Graphic* flare = drive->GetFlare(i);
+
864  if (flare) {
+
865  scene.AddGraphic(flare);
+
866  }
+
867 
+
868  Graphic* trail = drive->GetTrail(i);
+
869  if (trail) {
+
870  scene.AddGraphic(trail);
+
871  }
+
872  }
+
873  }
+
874 
+ +
876  if (thruster) {
+
877  for (i = 0; i < thruster->NumThrusters(); i++) {
+
878  Graphic* flare = thruster->Flare(i);
+
879  if (flare) {
+
880  scene.AddGraphic(flare);
+
881  }
+
882 
+
883  Graphic* trail = thruster->Trail(i);
+
884  if (trail) {
+
885  scene.AddGraphic(trail);
+
886  }
+
887  }
+
888  }
+
889 
+
890  for (int n = 0; n < navlights.size(); n++) {
+
891  NavLight* navlight = navlights[n];
+
892  for (i = 0; i < navlight->NumBeacons(); i++) {
+
893  Graphic* beacon = navlight->Beacon(i);
+
894  if (beacon)
+
895  scene.AddGraphic(beacon);
+
896  }
+
897  }
+
898 
+ +
900  while (++g) {
+
901  ListIter<Weapon> w = g->GetWeapons();
+
902  while (++w) {
+
903  Solid* turret = w->GetTurret();
+
904  if (turret) {
+
905  scene.AddGraphic(turret);
+
906 
+
907  Solid* turret_base = w->GetTurretBase();
+
908  if (turret_base)
+
909  scene.AddGraphic(turret_base);
+
910  }
+
911  if (w->IsMissile()) {
+
912  for (i = 0; i < w->Ammo(); i++) {
+
913  Solid* store = w->GetVisibleStore(i);
+
914  if (store)
+
915  scene.AddGraphic(store);
+
916  }
+
917  }
+
918  }
+
919  }
+
920 
+
921  if (gear && gear->GetState() != LandingGear::GEAR_UP) {
+
922  for (int i = 0; i < gear->NumGear(); i++) {
+
923  scene.AddGraphic(gear->GetGear(i));
+
924  }
+
925  }
+
926 }
+
927 
+
928 void
+ +
930 {
+
931  int i = 0;
+
932  SimObject::Deactivate(scene);
+
933 
+
934  for (i = 0; i < detail.NumModels(detail_level); i++) {
+ +
936  scene.DelGraphic(g);
+
937  }
+
938 
+
939  for (i = 0; i < flight_decks.size(); i++)
+
940  scene.DelLight(flight_decks[i]->GetLight());
+
941 
+
942  if (shieldRep)
+
943  scene.DelGraphic(shieldRep);
+
944 
+
945  if (cockpit)
+
946  scene.DelForeground(cockpit);
+
947 
+
948  Drive* drive = GetDrive();
+
949  if (drive) {
+
950  for (i = 0; i < drive->NumEngines(); i++) {
+
951  Graphic* flare = drive->GetFlare(i);
+
952  if (flare) {
+
953  scene.DelGraphic(flare);
+
954  }
+
955 
+
956  Graphic* trail = drive->GetTrail(i);
+
957  if (trail) {
+
958  scene.DelGraphic(trail);
+
959  }
+
960  }
+
961  }
+
962 
+ +
964  if (thruster) {
+
965  for (i = 0; i < thruster->NumThrusters(); i++) {
+
966  Graphic* flare = thruster->Flare(i);
+
967  if (flare) {
+
968  scene.DelGraphic(flare);
+
969  }
+
970 
+
971  Graphic* trail = thruster->Trail(i);
+
972  if (trail) {
+
973  scene.DelGraphic(trail);
+
974  }
+
975  }
+
976  }
+
977 
+
978  for (int n = 0; n < navlights.size(); n++) {
+
979  NavLight* navlight = navlights[n];
+
980  for (i = 0; i < navlight->NumBeacons(); i++) {
+
981  Graphic* beacon = navlight->Beacon(i);
+
982  if (beacon)
+
983  scene.DelGraphic(beacon);
+
984  }
+
985  }
+
986 
+ +
988  while (++g) {
+
989  ListIter<Weapon> w = g->GetWeapons();
+
990  while (++w) {
+
991  Solid* turret = w->GetTurret();
+
992  if (turret) {
+
993  scene.DelGraphic(turret);
+
994 
+
995  Solid* turret_base = w->GetTurretBase();
+
996  if (turret_base)
+
997  scene.DelGraphic(turret_base);
+
998  }
+
999  if (w->IsMissile()) {
+
1000  for (i = 0; i < w->Ammo(); i++) {
+
1001  Solid* store = w->GetVisibleStore(i);
+
1002  if (store)
+
1003  scene.DelGraphic(store);
+
1004  }
+
1005  }
+
1006  }
+
1007  }
+
1008 
+
1009  if (gear) {
+
1010  for (int i = 0; i < gear->NumGear(); i++) {
+
1011  scene.DelGraphic(gear->GetGear(i));
+
1012  }
+
1013  }
+
1014 }
+
1015 
+
1016 // +--------------------------------------------------------------------+
+
1017 
+
1018 void
+ +
1020 {
+
1021  Point pos = cam.Pos();
+
1022  cam.Clone(s.cam);
+
1023  cam.MoveTo(pos);
+
1024 
+
1025  if (rep)
+ +
1027 
+
1028  if (cockpit)
+ +
1030 }
+
1031 
+
1032 // +--------------------------------------------------------------------+
+
1033 
+
1034 void
+ +
1036 {
+
1037  const int DEFAULT_TRACK_LENGTH = 20; // 10 seconds
+
1038 
+
1039  if (!track) {
+
1040  track = new(__FILE__,__LINE__) Point[DEFAULT_TRACK_LENGTH];
+
1041  }
+
1042 
+
1043  track[0] = Location();
+
1044  ntrack = 1;
+ +
1046 }
+
1047 
+
1048 void
+ +
1050 {
+
1051  const int DEFAULT_TRACK_UPDATE = 500; // milliseconds
+
1052  const int DEFAULT_TRACK_LENGTH = 20; // 10 seconds
+
1053 
+
1054  DWORD time = Game::GameTime();
+
1055 
+
1056  if (!track) {
+
1057  track = new(__FILE__,__LINE__) Point[DEFAULT_TRACK_LENGTH];
+
1058  track[0] = Location();
+
1059  ntrack = 1;
+
1060  track_time = time;
+
1061  }
+
1062 
+
1063  else if (time - track_time > DEFAULT_TRACK_UPDATE) {
+
1064  if (Location() != track[0]) {
+
1065  for (int i = DEFAULT_TRACK_LENGTH-2; i >= 0; i--)
+
1066  track[i+1] = track[i];
+
1067 
+
1068  track[0] = Location();
+
1069  if (ntrack < DEFAULT_TRACK_LENGTH) ntrack++;
+
1070  }
+
1071 
+
1072  track_time = time;
+
1073  }
+
1074 }
+
1075 
+
1076 Point
+
1077 Ship::TrackPoint(int i) const
+
1078 {
+
1079  if (track && i < ntrack)
+
1080  return track[i];
+
1081 
+
1082  return Point();
+
1083 }
+
1084 
+
1085 // +--------------------------------------------------------------------+
+
1086 
+
1087 const char*
+ +
1089 {
+
1090  return design->abrv;
+
1091 }
+
1092 
+
1093 const char*
+ +
1095 {
+
1096  return design->DisplayName();
+
1097 }
+
1098 
+
1099 const char*
+ +
1101 {
+
1102  return design->filename;
+
1103 }
+
1104 
+
1105 const char*
+ +
1107 {
+ +
1109 }
+
1110 
+
1111 const char*
+ +
1113 {
+
1114  return ShipDesign::ClassName(c);
+
1115 }
+
1116 
+
1117 int
+
1118 Ship::ClassForName(const char* name)
+
1119 {
+
1120  return ShipDesign::ClassForName(name);
+
1121 }
+
1122 
+ + +
1125 {
+
1126  return (CLASSIFICATION) design->type;
+
1127 }
+
1128 
+
1129 bool
+ +
1131 {
+
1132  return (design->type & GROUND_UNITS) ? true : false;
+
1133 }
+
1134 
+
1135 bool
+ +
1137 {
+
1138  return (design->type & STARSHIPS) ? true : false;
+
1139 }
+
1140 
+
1141 bool
+ +
1143 {
+
1144  return (design->type & DROPSHIPS) ? true : false;
+
1145 }
+
1146 
+
1147 bool
+ +
1149 {
+
1150  return design->type >= STATION;
+
1151 }
+
1152 
+
1153 bool
+ +
1155 {
+
1156  return ff_count >= 50;
+
1157 }
+
1158 
+
1159 // +--------------------------------------------------------------------+
+
1160 
+
1161 bool
+ +
1163 {
+
1164  if (o) {
+
1165  if (IsRogue())
+
1166  return true;
+
1167 
+
1168  if (o->Type() == SIM_SHIP) {
+
1169  Ship* s = (Ship*) o;
+
1170 
+
1171  if (s->IsRogue())
+
1172  return true;
+
1173 
+
1174  if (GetIFF() == 0) {
+
1175  if (s->GetIFF() > 1)
+
1176  return true;
+
1177  }
+
1178  else {
+
1179  if (s->GetIFF() > 0 && s->GetIFF() != GetIFF())
+
1180  return true;
+
1181  }
+
1182  }
+
1183 
+
1184  else if (o->Type() == SIM_SHOT || o->Type() == SIM_DRONE) {
+
1185  Shot* s = (Shot*) o;
+
1186 
+
1187  if (GetIFF() == 0) {
+
1188  if (s->GetIFF() > 1)
+
1189  return true;
+
1190  }
+
1191  else {
+
1192  if (s->GetIFF() > 0 && s->GetIFF() != GetIFF())
+
1193  return true;
+
1194  }
+
1195  }
+
1196  }
+
1197 
+
1198  return false;
+
1199 }
+
1200 
+
1201 // +--------------------------------------------------------------------+
+
1202 
+
1203 double
+ +
1205 {
+
1206  return design->repair_speed;
+
1207 }
+
1208 
+
1209 int
+ +
1211 {
+
1212  return design->repair_teams;
+
1213 }
+
1214 
+
1215 // +--------------------------------------------------------------------+
+
1216 
+
1217 int
+ +
1219 {
+
1220  // cast-away const:
+
1221  return ((Ship*)this)->ContactList().size();
+
1222 }
+
1223 
+ + +
1226 {
+
1227  if (region)
+
1228  return region->TrackList(GetIFF());
+
1229 
+
1230  static List<Contact> empty_contact_list;
+
1231  return empty_contact_list;
+
1232 }
+
1233 
+
1234 Contact*
+ +
1236 {
+
1237  if (!s) return 0;
+
1238 
+
1239  ListIter<Contact> c_iter = ((Ship*) this)->ContactList();
+
1240  while (++c_iter) {
+
1241  Contact* c = c_iter.value();
+
1242 
+
1243  if (c->GetShip() == s)
+
1244  return c;
+
1245 
+
1246  if (c->GetShot() == s)
+
1247  return c;
+
1248  }
+
1249 
+
1250  return 0;
+
1251 }
+
1252 
+
1253 // +--------------------------------------------------------------------+
+
1254 
+
1255 Ship*
+ +
1257 {
+
1258  Ship* controller = 0;
+
1259 
+
1260  if (carrier) {
+
1261  // are we in same region as carrier?
+
1262  if (carrier->GetRegion() == GetRegion()) {
+
1263  return carrier;
+
1264  }
+
1265 
+
1266  // if not, figure out who our control unit is:
+
1267  else {
+
1268  double distance = 10e6;
+
1269 
+
1270  ListIter<Ship> iter = GetRegion()->Carriers();
+
1271  while (++iter) {
+
1272  Ship* test = iter.value();
+
1273  if (test->GetIFF() == GetIFF()) {
+
1274  double d = Point(Location() - test->Location()).length();
+
1275  if (d < distance) {
+
1276  controller = test;
+
1277  distance = d;
+
1278  }
+
1279  }
+
1280  }
+
1281  }
+
1282  }
+
1283 
+
1284  if (!controller) {
+
1285  if (element && element->GetCommander())
+
1286  controller = element->GetCommander()->GetShip(1);
+
1287  }
+
1288 
+
1289  return controller;
+
1290 }
+
1291 
+
1292 int
+ +
1294 {
+
1295  int result = 0;
+
1296 
+
1297  for (int i = 0; i < flight_decks.size(); i++) {
+
1298  result += flight_decks[i]->GetRecoveryQueue().size();
+
1299  }
+
1300 
+
1301  return result;
+
1302 }
+
1303 
+
1304 int
+ +
1306 {
+
1307  return flight_decks.size();
+
1308 }
+
1309 
+
1310 FlightDeck*
+ +
1312 {
+
1313  if (i >= 0 && i < flight_decks.size())
+
1314  return flight_decks[i];
+
1315 
+
1316  return 0;
+
1317 }
+
1318 
+
1319 // +--------------------------------------------------------------------+
+
1320 
+
1321 void
+ +
1323 {
+
1324  if (phase == ACTIVE && !launch_time) {
+
1325  launch_time = Game::GameTime() + 1;
+
1326  dock = 0;
+
1327 
+
1328  if (element)
+ +
1330  }
+
1331 
+
1332  flight_phase = phase;
+
1333 
+
1334  if (flight_phase == ACTIVE)
+
1335  dock = 0;
+
1336 }
+
1337 
+
1338 void
+ +
1340 {
+
1341  carrier = c;
+
1342  dock = d;
+
1343 
+
1344  if (carrier)
+
1345  Observe(carrier);
+
1346 }
+
1347 
+
1348 void
+ +
1350 {
+
1351  inbound = s;
+
1352 
+
1353  if (inbound && flight_phase == ACTIVE) {
+ +
1355 
+ +
1357 
+
1358  HUDView* hud = HUDView::GetInstance();
+
1359 
+
1360  if (hud && hud->GetShip() == this)
+
1361  hud->SetHUDMode(HUDView::HUD_MODE_ILS);
+
1362  }
+
1363 }
+
1364 
+
1365 void
+ +
1367 {
+
1368  if (carrier && carrier->GetHangar())
+
1369  carrier->GetHangar()->Stow(this);
+
1370 }
+
1371 
+
1372 bool
+ +
1374 {
+ +
1376  return true;
+
1377 
+
1378  return false;
+
1379 }
+
1380 
+
1381 void
+ +
1383 {
+
1384  if (gear && gear->GetState() != LandingGear::GEAR_DOWN) {
+ +
1386  Scene* scene = 0;
+
1387 
+
1388  if (rep)
+
1389  scene = rep->GetScene();
+
1390 
+
1391  if (scene) {
+
1392  for (int i = 0; i < gear->NumGear(); i++) {
+
1393  Solid* g = gear->GetGear(i);
+
1394  if (g) {
+
1395  if (detail_level == 0)
+
1396  scene->DelGraphic(g);
+
1397  else
+
1398  scene->AddGraphic(g);
+
1399  }
+
1400  }
+
1401  }
+
1402  }
+
1403 }
+
1404 
+
1405 void
+ +
1407 {
+
1408  if (gear && gear->GetState() != LandingGear::GEAR_UP)
+ +
1410 }
+
1411 
+
1412 void
+ +
1414 {
+
1415  if (gear) {
+
1416  if (gear->GetState() == LandingGear::GEAR_UP ||
+ +
1418  LowerGear();
+
1419  }
+
1420  else {
+
1421  RaiseGear();
+
1422  }
+
1423  }
+
1424 }
+
1425 
+
1426 void
+ +
1428 {
+
1429  bool enable = false;
+
1430 
+
1431  for (int i = 0; i < navlights.size(); i++) {
+
1432  if (i == 0)
+
1433  enable = !navlights[0]->IsEnabled();
+
1434 
+
1435  if (enable)
+
1436  navlights[i]->Enable();
+
1437  else
+
1438  navlights[i]->Disable();
+
1439  }
+
1440 }
+
1441 
+
1442 // +--------------------------------------------------------------------+
+
1443 
+
1444 int
+ +
1446 {
+
1447  // bounding spheres test:
+
1448  Point delta_loc = Location() - o.Location();
+
1449  if (delta_loc.length() > radius + o.Radius())
+
1450  return 0;
+
1451 
+
1452  if (!o.Rep())
+
1453  return 1;
+
1454 
+
1455  for (int i = 0; i < detail.NumModels(detail_level); i++) {
+
1456  Graphic* g = detail.GetRep(detail_level, i);
+
1457 
+
1458  if (o.Type() == SimObject::SIM_SHIP) {
+
1459  Ship* o_ship = (Ship*) &o;
+
1460  int o_det = o_ship->detail_level;
+
1461 
+
1462  for (int j = 0; j < o_ship->detail.NumModels(o_det); j++) {
+
1463  Graphic* o_g = o_ship->detail.GetRep(o_det, j);
+
1464 
+
1465  if (g->CollidesWith(*o_g))
+
1466  return 1;
+
1467  }
+
1468  }
+
1469  else {
+
1470  // representation collision test (will do bounding spheres first):
+
1471  if (g->CollidesWith(*o.Rep()))
+
1472  return 1;
+
1473  }
+
1474  }
+
1475 
+
1476  return 0;
+
1477 }
+
1478 
+
1479 // +--------------------------------------------------------------------+
+
1480 
+
1481 static DWORD ff_warn_time = 0;
+
1482 
+
1483 int
+
1484 Ship::HitBy(Shot* shot, Point& impact)
+
1485 {
+
1486  if (shot->Owner() == this || IsNetObserver())
+
1487  return HIT_NOTHING;
+
1488 
+
1489  if (shot->IsFlak())
+
1490  return HIT_NOTHING;
+
1491 
+
1492  if (InTransition())
+
1493  return HIT_NOTHING;
+
1494 
+
1495  Point shot_loc = shot->Location();
+
1496  Point delta = shot_loc - Location();
+
1497  double dlen = delta.length();
+
1498 
+
1499  Point hull_impact;
+
1500  int hit_type = HIT_NOTHING;
+
1501  double dscale = 1;
+
1502  float scale = design->explosion_scale;
+
1503  Weapon* wep = 0;
+
1504 
+
1505  if (!shot->IsMissile() && !shot->IsBeam()) {
+
1506  if (dlen > Radius() * 2)
+
1507  return HIT_NOTHING;
+
1508  }
+
1509 
+
1510  if (scale <= 0)
+
1511  scale = design->scale;
+
1512 
+
1513  if (shot->Owner()) {
+
1514  const ShipDesign* owner_design = shot->Owner()->Design();
+
1515  if (owner_design && owner_design->scale < scale)
+
1516  scale = (float) owner_design->scale;
+
1517  }
+
1518 
+
1519 
+
1520  // MISSILE PROCESSING ------------------------------------------------
+
1521 
+
1522  if (shot->IsMissile() && rep) {
+
1523  if (dlen < rep->Radius()) {
+
1524  hull_impact = impact = shot_loc;
+
1525 
+
1526  hit_type = CheckShotIntersection(shot, impact, hull_impact, &wep);
+
1527 
+
1528  if (hit_type) {
+
1529  if (shot->Damage() > 0) {
+
1530  DWORD flash = Explosion::HULL_FLASH;
+
1531 
+
1532  if ((hit_type & HIT_SHIELD) != 0)
+
1533  flash = Explosion::SHIELD_FLASH;
+
1534 
+
1535  sim->CreateExplosion(impact, Velocity(), flash, 0.3f * scale, scale, region);
+
1536  sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 2.0f, scale, region);
+
1537  }
+
1538  }
+
1539  }
+
1540 
+
1541  if (hit_type == HIT_NOTHING && shot->IsArmed()) {
+
1542  SeekerAI* seeker = (SeekerAI*) shot->GetDirector();
+
1543 
+
1544  // if the missile overshot us, take damage proportional to distance
+
1545  double damage_radius = shot->Design()->lethal_radius;
+
1546  if (dlen < (damage_radius + Radius())) {
+
1547  if (seeker && seeker->Overshot()) {
+
1548  dscale = 1.0 - (dlen / (damage_radius + Radius()));
+
1549 
+
1550  if (dscale > 1)
+
1551  dscale = 1;
+
1552 
+
1553  if (ShieldStrength() > 5) {
+
1554  hull_impact = impact = shot_loc;
+
1555 
+
1556  if (shot->Damage() > 0) {
+
1557  if (shieldRep)
+
1558  shieldRep->Hit(impact, shot, shot->Damage()*dscale);
+
1559  sim->CreateExplosion(impact, Velocity(), Explosion::SHIELD_FLASH, 0.20f * scale, scale, region);
+
1560  sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 20.0f * scale, scale, region);
+
1561  }
+
1562 
+
1563  hit_type = HIT_BOTH;
+
1564  }
+
1565  else {
+
1566  hull_impact = impact = shot_loc;
+
1567 
+
1568  if (shot->Damage() > 0) {
+
1569  sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region);
+
1570  sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 20.0f * scale, scale, region);
+
1571  }
+
1572 
+
1573  hit_type = HIT_HULL;
+
1574  }
+
1575  }
+
1576  }
+
1577  }
+
1578  }
+
1579 
+
1580  // ENERGY WEP PROCESSING ---------------------------------------------
+
1581 
+
1582  else {
+
1583  hit_type = CheckShotIntersection(shot, impact, hull_impact, &wep);
+
1584 
+
1585  // impact:
+
1586  if (hit_type) {
+
1587 
+
1588  if (hit_type & HIT_SHIELD) {
+
1589  if (shieldRep)
+
1590  shieldRep->Hit(impact, shot, shot->Damage());
+
1591  sim->CreateExplosion(impact, Velocity(), Explosion::SHIELD_FLASH, 0.20f * scale, scale, region);
+
1592  }
+
1593 
+
1594  else {
+
1595  if (shot->IsBeam())
+
1596  sim->CreateExplosion(impact, Velocity(), Explosion::BEAM_FLASH, 0.30f * scale, scale, region);
+
1597  else
+
1598  sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region);
+
1599 
+
1600  if (IsStarship()) {
+
1601  Point burst_vel = hull_impact - Location();
+
1602  burst_vel.Normalize();
+
1603  burst_vel *= Radius() * 0.5;
+
1604  burst_vel += Velocity();
+
1605 
+
1606  sim->CreateExplosion(hull_impact, burst_vel, Explosion::HULL_BURST, 0.50f * scale, scale, region, this);
+
1607  }
+
1608  }
+
1609  }
+
1610  }
+
1611 
+
1612  // DAMAGE RESOLUTION -------------------------------------------------
+
1613 
+
1614  if (hit_type != HIT_NOTHING && shot->IsArmed()) {
+
1615  double effective_damage = shot->Damage() * dscale;
+
1616 
+
1617  // FRIENDLY FIRE --------------------------------------------------
+
1618 
+
1619  if (shot->Owner()) {
+
1620  Ship* s = (Ship*) shot->Owner();
+
1621 
+
1622  if (!IsRogue() && s->GetIFF() == GetIFF() &&
+
1623  s->GetDirector() && s->GetDirector()->Type() < 1000) {
+
1624  bool was_rogue = s->IsRogue();
+
1625 
+
1626  // only count beam hits once
+
1627  if (shot->Damage() && !shot->HitTarget() && GetFriendlyFireLevel() > 0) {
+
1628  int penalty = 1;
+
1629 
+
1630  if (shot->IsBeam()) penalty = 5;
+
1631  else if (shot->IsDrone()) penalty = 7;
+
1632 
+
1633  if (s->GetTarget() == this) penalty *= 3;
+
1634 
+
1635  s->IncFriendlyFire(penalty);
+
1636  }
+
1637 
+
1638  effective_damage *= GetFriendlyFireLevel();
+
1639 
+
1640  if (Class() > DRONE && s->Class() > DRONE) {
+
1641  if (s->IsRogue() && !was_rogue) {
+
1642  RadioMessage* warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::DECLARE_ROGUE);
+
1643  RadioTraffic::Transmit(warn);
+
1644  }
+
1645  else if (!s->IsRogue() && (Game::GameTime() - ff_warn_time) > 5000) {
+
1646  ff_warn_time = Game::GameTime();
+
1647 
+
1648  RadioMessage* warn = 0;
+
1649  if (s->GetTarget() == this)
+
1650  warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::WARN_TARGETED);
+
1651  else
+
1652  warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::WARN_ACCIDENT);
+
1653 
+
1654  RadioTraffic::Transmit(warn);
+
1655  }
+
1656  }
+
1657  }
+
1658  }
+
1659 
+
1660  if (effective_damage > 0) {
+
1661  if (!shot->IsBeam() && shot->Design()->damage_type == WeaponDesign::DMG_NORMAL)
+
1662  ApplyTorque(shot->Velocity() * (float) effective_damage * 1e-6f);
+
1663 
+
1664  if (!NetGame::IsNetGameClient()) {
+
1665  InflictDamage(effective_damage, shot, hit_type, hull_impact);
+
1666  }
+
1667  }
+
1668  }
+
1669 
+
1670  return hit_type;
+
1671 }
+
1672 
+
1673 static bool CheckRaySphereIntersection(Point loc, double radius, Point Q, Point w, double len)
+
1674 {
+
1675  Point d0 = loc - Q;
+
1676  Point d1 = d0.cross(w);
+
1677  double dlen = d1.length(); // distance of point from line
+
1678 
+
1679  if (dlen > radius) // clean miss
+
1680  return false; // (no impact)
+
1681 
+
1682  // possible collision course...
+
1683  // find the point on the ray that is closest
+
1684  // to the sphere's location:
+
1685  Point closest = Q + w * (d0 * w);
+
1686 
+
1687  // find the leading edge, and it's distance from the location:
+
1688  Point leading_edge = Q + w*len;
+
1689  Point leading_delta = leading_edge - loc;
+
1690  double leading_dist = leading_delta.length();
+
1691 
+
1692  // if the leading edge is not within the sphere,
+
1693  if (leading_dist > radius) {
+
1694  // check to see if the closest point is between the
+
1695  // ray's endpoints:
+
1696  Point delta1 = closest - Q;
+
1697  Point delta2 = leading_edge - Q; // this is w*len
+
1698 
+
1699  // if the closest point is not between the leading edge
+
1700  // and the origin, this ray does not intersect:
+
1701  if (delta1 * delta2 < 0 || delta1.length() > len) {
+
1702  return false;
+
1703  }
+
1704  }
+
1705 
+
1706  return true;
+
1707 }
+
1708 
+
1709 int
+ +
1711 {
+
1712  int hit_type = HIT_NOTHING;
+
1713  Point shot_loc = shot->Location();
+
1714  Point shot_org = shot->Origin();
+
1715  Point shot_vpn = shot_loc - shot_org;
+
1716  double shot_len = shot_vpn.Normalize();
+
1717  double blow_len = shot_len;
+
1718  bool hit_hull = false;
+
1719  bool easy = false;
+
1720 
+
1721  if (shot_len <= 0)
+
1722  return hit_type;
+
1723 
+
1724  if (shot_len < 1000)
+
1725  shot_len = 1000;
+
1726 
+
1727  Point hull_impact;
+
1728  Point shield_impact;
+
1729  Point turret_impact;
+
1730  Point closest;
+
1731  double d0 = 1e9;
+
1732  double d1 = 1e9;
+
1733  double ds = 1e9;
+
1734 
+
1735  if (dir && dir->Type() == SteerAI::FIGHTER) {
+
1736  ShipAI* shipAI = (ShipAI*) dir;
+
1737  easy = shipAI->GetAILevel() < 2;
+
1738  }
+
1739 
+
1740  if (shieldRep && ShieldStrength() > 5) {
+
1741  if (shieldRep->CheckRayIntersection(shot_org, shot_vpn, shot_len, shield_impact)) {
+
1742  hit_type = HIT_SHIELD;
+
1743  closest = shield_impact;
+
1744  d0 = Point(closest - shot_org).length();
+
1745  ds = d0;
+
1746 
+
1747  ipt = shield_impact;
+
1748  }
+
1749  }
+
1750 
+
1751  if (shieldRep && hit_type == HIT_SHIELD && !shot->IsBeam())
+
1752  blow_len = shieldRep->Radius() * 2;
+
1753 
+
1754  for (int i = 0; i < detail.NumModels(detail_level) && !hit_hull; i++) {
+
1755  Solid* s = (Solid*) detail.GetRep(detail_level, i);
+
1756  if (s) {
+
1757  if (easy) {
+
1758  hit_hull = CheckRaySphereIntersection(s->Location(), s->Radius(), shot_org, shot_vpn, shot_len);
+
1759  }
+
1760  else {
+
1761  hit_hull = s->CheckRayIntersection(shot_org, shot_vpn, blow_len, hull_impact)?true:false;
+
1762  }
+
1763  }
+
1764  }
+
1765 
+
1766  if (hit_hull) {
+
1767  if (ShieldStrength() > 5 && !shieldRep)
+
1768  hit_type = HIT_SHIELD;
+
1769 
+
1770  hit_type = hit_type | HIT_HULL;
+
1771  hpt = hull_impact;
+
1772 
+
1773  d1 = Point(hull_impact - shot_org).length();
+
1774 
+
1775  if (d1 < d0) {
+
1776  closest = hull_impact;
+
1777  d0 = d1;
+
1778  }
+
1779  }
+
1780 
+
1781  if (IsStarship() || IsStatic()) {
+
1782  ListIter<WeaponGroup> g_iter = Weapons();
+
1783  while (++g_iter) {
+
1784  WeaponGroup* g = g_iter.value();
+
1785 
+
1786  if (g->GetDesign() && g->GetDesign()->turret_model) {
+
1787  double tsize = g->GetDesign()->turret_model->Radius();
+
1788 
+
1789  ListIter<Weapon> w_iter = g->GetWeapons();
+
1790  while (++w_iter) {
+
1791  Weapon* w = w_iter.value();
+
1792 
+
1793  Point tloc = w->GetTurret()->Location();
+
1794 
+
1795  if (CheckRaySphereIntersection(tloc, tsize, shot_org, shot_vpn, shot_len)) {
+
1796  Point delta = tloc - shot_org;
+
1797  d1 = delta.length();
+
1798 
+
1799  if (d1 < d0) {
+
1800  if (wep) *wep = w;
+
1801  hit_type = hit_type | HIT_TURRET;
+
1802  turret_impact = tloc;
+
1803 
+
1804  d0 = d1;
+
1805 
+
1806  closest = turret_impact;
+
1807  hull_impact = turret_impact;
+
1808  hpt = turret_impact;
+
1809 
+
1810  if (d1 < ds)
+
1811  ipt = turret_impact;
+
1812  }
+
1813  }
+
1814  }
+
1815  }
+
1816  }
+
1817  }
+
1818 
+
1819  // trim beam shots to closest impact point:
+
1820  if (hit_type && shot->IsBeam()) {
+
1821  shot->SetBeamPoints(shot_org, closest);
+
1822  }
+
1823 
+
1824  return hit_type;
+
1825 }
+
1826 
+
1827 // +--------------------------------------------------------------------+
+
1828 
+
1829 void
+
1830 Ship::InflictNetDamage(double damage, Shot* shot)
+
1831 {
+
1832  if (damage > 0 && !IsNetObserver()) {
+
1833  Physical::InflictDamage(damage, 0);
+
1834 
+
1835  // shake by percentage of maximum damage
+
1836  double newshake = 50 * damage/design->integrity;
+
1837  const double MAX_SHAKE = 7;
+
1838 
+
1839  if (shake < MAX_SHAKE) shake += (float) newshake;
+
1840  if (shake > MAX_SHAKE) shake = (float) MAX_SHAKE;
+
1841  }
+
1842 }
+
1843 
+
1844 void
+
1845 Ship::InflictNetSystemDamage(System* system, double damage, BYTE dmg_type)
+
1846 {
+
1847  if (system && damage > 0 && !IsNetObserver()) {
+
1848  bool dmg_normal = dmg_type == WeaponDesign::DMG_NORMAL;
+
1849  bool dmg_power = dmg_type == WeaponDesign::DMG_POWER;
+
1850  bool dmg_emp = dmg_type == WeaponDesign::DMG_EMP;
+
1851 
+
1852  double sys_damage = damage;
+
1853  double avail = system->Availability();
+
1854 
+
1855  if (dmg_normal || system->IsPowerCritical() && dmg_emp) {
+
1856  system->ApplyDamage(sys_damage);
+
1857  master_caution = true;
+
1858 
+
1859  if (system->GetExplosionType() && (avail - system->Availability()) >= 50) {
+
1860  float scale = design->explosion_scale;
+
1861  if (scale <= 0)
+
1862  scale = design->scale;
+
1863 
+
1864  sim->CreateExplosion(system->MountLocation(), Velocity() * 0.7f, system->GetExplosionType(), 0.2f * scale, scale, region, this, system);
+
1865  }
+
1866  }
+
1867  }
+
1868 }
+
1869 
+
1870 void
+
1871 Ship::SetNetSystemStatus(System* system, int status, int power, int reactor, double avail)
+
1872 {
+
1873  if (system && !IsNetObserver()) {
+
1874  if (system->GetPowerLevel() != power)
+
1875  system->SetPowerLevel(power);
+
1876 
+
1877  if (system->GetSourceIndex() != reactor) {
+
1878  System* s = GetSystem(reactor);
+
1879 
+
1880  if (s && s->Type() == System::POWER_SOURCE) {
+
1881  PowerSource* reac = (PowerSource*) s;
+
1882  reac->AddClient(system);
+
1883  }
+
1884  }
+
1885 
+
1886  if (system->Status() != status) {
+
1887  if (status == System::MAINT) {
+
1888  ListIter<Component> comp = system->GetComponents();
+
1889  while (++comp) {
+
1890  Component* c = comp.value();
+
1891 
+
1892  if (c->Status() < Component::NOMINAL && c->Availability() < 75) {
+
1893  if (c->SpareCount() &&
+
1894  c->ReplaceTime() <= 300 &&
+
1895  (c->Availability() < 50 ||
+
1896  c->ReplaceTime() < c->RepairTime())) {
+
1897 
+
1898  c->Replace();
+
1899  }
+
1900 
+
1901  else if (c->Availability() >= 50 || c->NumJerried() < 5) {
+
1902  c->Repair();
+
1903  }
+
1904  }
+
1905  }
+
1906 
+
1907  RepairSystem(system);
+
1908  }
+
1909  }
+
1910 
+
1911  if (system->Availability() < avail) {
+
1912  system->SetNetAvail(avail);
+
1913  }
+
1914  else {
+
1915  system->SetNetAvail(-1);
+
1916  }
+
1917  }
+
1918 }
+
1919 
+
1920 // +----------------------------------------------------------------------+
+
1921 
+ +
1923 {
+
1924  if (w->GetTarget()) {
+
1925  Point tgt = w->GetTarget()->Location();
+
1926  Point obj = test->Location();
+
1927  Point wep = w->MountLocation();
+
1928 
+
1929  Point dir = tgt - wep;
+
1930  double d = dir.Normalize();
+
1931  Point rho = obj - wep;
+
1932  double r = rho.Normalize();
+
1933 
+
1934  // if target is much closer than obstacle,
+
1935  // don't worry about friendly fire...
+
1936  if (d < 1.5 * r)
+
1937  return false;
+
1938 
+
1939  Point dst = dir * r + wep;
+
1940  double err = (obj - dst).length();
+
1941 
+
1942  if (err < test->Radius() * 1.5)
+
1943  return true;
+
1944  }
+
1945 
+
1946  return false;
+
1947 }
+
1948 
+
1949 void
+ +
1951 {
+
1952  // if no weapons, there is no worry about friendly fire...
+
1953  if (weapons.size() < 1)
+
1954  return;
+
1955 
+
1956  // only check once each second
+
1957  if (Game::GameTime() - friendly_fire_time < 1000)
+
1958  return;
+
1959 
+
1960  List<Weapon> w_list;
+
1961  int i, j;
+
1962 
+
1963  // clear the FF blocked flag on all weapons
+
1964  for (i = 0; i < weapons.size(); i++) {
+
1965  WeaponGroup* g = weapons[i];
+
1966 
+
1967  for (j = 0; j < g->NumWeapons(); j++) {
+
1968  Weapon* w = g->GetWeapon(j);
+
1969  w_list.append(w);
+
1970  w->SetBlockedFriendly(false);
+
1971  }
+
1972  }
+
1973 
+
1974  // for each friendly ship within some kind of weapons range,
+
1975  ListIter<Contact> c_iter = ContactList();
+
1976  while (++c_iter) {
+
1977  Contact* c = c_iter.value();
+
1978  Ship* cship = c->GetShip();
+
1979  Shot* cshot = c->GetShot();
+
1980 
+
1981  if (cship && cship != this && (cship->GetIFF() == 0 || cship->GetIFF() == GetIFF())) {
+
1982  double range = (cship->Location() - Location()).length();
+
1983 
+
1984  if (range > 100e3)
+
1985  continue;
+
1986 
+
1987  // check each unblocked weapon to see if it is blocked by that ship
+
1988  ListIter<Weapon> iter = w_list;
+
1989  while (++iter) {
+
1990  Weapon* w = iter.value();
+
1991 
+
1992  if (!w->IsBlockedFriendly())
+ +
1994  }
+
1995  }
+
1996 
+
1997  else if (cshot && cshot->GetIFF() == GetIFF()) {
+
1998  double range = (cshot->Location() - Location()).length();
+
1999 
+
2000  if (range > 30e3)
+
2001  continue;
+
2002 
+
2003  // check each unblocked weapon to see if it is blocked by that shot
+
2004  ListIter<Weapon> iter = w_list;
+
2005  while (++iter) {
+
2006  Weapon* w = iter.value();
+
2007 
+
2008  if (!w->IsBlockedFriendly())
+ +
2010  }
+
2011  }
+
2012  }
+
2013 
+
2014  friendly_fire_time = Game::GameTime() + (DWORD) Random(0, 500);
+
2015 }
+
2016 
+
2017 // +----------------------------------------------------------------------+
+
2018 
+
2019 Ship*
+ +
2021 {
+
2022  if (element)
+
2023  return element->GetShip(1);
+
2024 
+
2025  return (Ship*) this;
+
2026 }
+
2027 
+
2028 int
+ +
2030 {
+
2031  if (element)
+
2032  return element->FindIndex(this);
+
2033 
+
2034  return 0;
+
2035 }
+
2036 
+
2037 int
+ +
2039 {
+
2040  return orig_elem_index;
+
2041 }
+
2042 
+
2043 void
+ +
2045 {
+
2046  element = e;
+
2047 
+
2048  if (element) {
+ +
2050 
+
2051  if (combat_unit) {
+ +
2053  }
+
2054 
+ +
2056  }
+
2057 }
+
2058 
+
2059 void
+ +
2061 {
+
2062  if (pt && !launch_point)
+
2063  launch_point = pt;
+
2064 }
+
2065 
+
2066 void
+ +
2068 {
+
2069  if (GetElementIndex() == 1)
+
2070  element->AddNavPoint(pt, after);
+
2071 }
+
2072 
+
2073 void
+ +
2075 {
+
2076  if (GetElementIndex() == 1)
+
2077  element->DelNavPoint(pt);
+
2078 }
+
2079 
+
2080 void
+ +
2082 {
+
2083  if (GetElementIndex() == 1)
+ +
2085 }
+
2086 
+
2087 // +----------------------------------------------------------------------+
+
2088 
+
2089 bool
+ +
2091 {
+
2092  if (navsys && navsys->AutoNavEngaged())
+
2093  return true;
+
2094 
+
2095  return false;
+
2096 }
+
2097 
+
2098 void
+
2099 Ship::SetAutoNav(bool engage)
+
2100 {
+
2101  if (navsys) {
+
2102  if (navsys->AutoNavEngaged()) {
+
2103  if (!engage)
+ +
2105  }
+
2106  else {
+
2107  if (engage)
+
2108  navsys->EngageAutoNav();
+
2109  }
+
2110 
+
2111  if (sim)
+ +
2113  }
+
2114 }
+
2115 
+
2116 void
+ +
2118 {
+
2119  if (!dir || dir->Type() != ShipCtrl::DIR_TYPE) {
+
2120  const char* msg = "Captain on the bridge";
+
2121  RadioVox* vox = new(__FILE__,__LINE__) RadioVox(0, "1", msg);
+
2122 
+
2123  if (vox) {
+
2124  vox->AddPhrase(msg);
+
2125 
+
2126  if (!vox->Start()) {
+ +
2128  delete vox;
+
2129  }
+
2130  }
+
2131 
+ +
2133  }
+
2134 
+
2135  else {
+
2136  const char* msg = "Exec, you have the conn";
+
2137  RadioVox* vox = new(__FILE__,__LINE__) RadioVox(0, "1", msg);
+
2138 
+
2139  if (vox) {
+
2140  vox->AddPhrase(msg);
+
2141 
+
2142  if (!vox->Start()) {
+ +
2144  delete vox;
+
2145  }
+
2146  }
+
2147 
+
2148  SetControls(0);
+
2149  }
+
2150 }
+
2151 
+
2152 // +----------------------------------------------------------------------+
+
2153 
+
2154 Instruction*
+ +
2156 {
+ +
2158  return launch_point;
+
2159 
+
2160  if (element)
+
2161  return element->GetNextNavPoint();
+
2162 
+
2163  return 0;
+
2164 }
+
2165 
+
2166 int
+ +
2168 {
+
2169  if (element)
+
2170  return element->GetNavIndex(n);
+
2171 
+
2172  return 0;
+
2173 }
+
2174 
+
2175 double
+ +
2177 {
+
2178  double distance = 0;
+
2179 
+
2180  if (n && n->Region()) {
+
2181  Point npt = n->Region()->Location() + n->Location();
+
2182  npt -= GetRegion()->Location();
+
2183  npt = npt.OtherHand(); // convert from map to sim coords
+
2184 
+
2185  distance = Point(npt - Location()).length();
+
2186  }
+
2187 
+
2188  return distance;
+
2189 }
+
2190 
+
2191 void
+ +
2193 {
+
2194  if (navpt && navpt->Status() != status) {
+
2195  if (status == Instruction::COMPLETE) {
+
2196  if (navpt->Action() == Instruction::ASSAULT)
+
2197  ::Print("Completed Assault\n");
+
2198 
+
2199  else if (navpt->Action() == Instruction::STRIKE)
+
2200  ::Print("Completed Strike\n");
+
2201  }
+
2202 
+
2203  navpt->SetStatus(status);
+
2204 
+
2205  if (status == Instruction::COMPLETE)
+ +
2207 
+
2208  if (element) {
+
2209  int index = element->GetNavIndex(navpt);
+
2210 
+
2211  if (index >= 0)
+
2212  NetUtil::SendNavData(false, element, index-1, navpt);
+
2213  }
+
2214  }
+
2215 }
+
2216 
+ + +
2219 {
+
2220  if (element)
+
2221  return element->GetFlightPlan();
+
2222 
+
2223  static List<Instruction> dummy_flight_plan;
+
2224  return dummy_flight_plan;
+
2225 }
+
2226 
+
2227 int
+ +
2229 {
+
2230  if (element)
+
2231  return element->FlightPlanLength();
+
2232 
+
2233  return 0;
+
2234 }
+
2235 
+
2236 // +--------------------------------------------------------------------+
+
2237 
+
2238 void
+ +
2240 {
+
2241  if (ward == s)
+
2242  return;
+
2243 
+
2244  ward = s;
+
2245 
+
2246  if (ward)
+
2247  Observe(ward);
+
2248 }
+
2249 
+
2250 // +--------------------------------------------------------------------+
+
2251 
+
2252 void
+
2253 Ship::SetTarget(SimObject* targ, System* sub, bool from_net)
+
2254 {
+
2255  if (targ && targ->Type() == SimObject::SIM_SHIP) {
+
2256  Ship* targ_ship = (Ship*) targ;
+
2257 
+
2258  if (targ_ship && targ_ship->IsNetObserver())
+
2259  return;
+
2260  }
+
2261 
+
2262  if (target != targ) {
+
2263  // DON'T IGNORE TARGET, BECAUSE IT MAY BE IN THREAT LIST
+
2264  target = targ;
+
2265  if (target) Observe(target);
+
2266 
+
2267  if (sim && target)
+ +
2269  }
+
2270 
+
2271  subtarget = sub;
+
2272 
+
2273  ListIter<WeaponGroup> weapon = weapons;
+
2274  while (++weapon) {
+
2275  if (weapon->GetFiringOrders() != Weapon::POINT_DEFENSE) {
+
2276  weapon->SetTarget(target, subtarget);
+
2277 
+
2278  if (sub || !IsStarship())
+
2279  weapon->SetSweep(Weapon::SWEEP_NONE);
+
2280  else
+
2281  weapon->SetSweep(Weapon::SWEEP_TIGHT);
+
2282  }
+
2283  }
+
2284 
+
2285  if (!from_net && NetGame::GetInstance())
+
2286  NetUtil::SendObjTarget(this);
+
2287 
+
2288  // track engagement:
+
2289  if (target && target->Type() == SimObject::SIM_SHIP) {
+
2290  Element* elem = GetElement();
+
2291  Element* tgt_elem = ((Ship*) target)->GetElement();
+
2292 
+
2293  if (elem)
+
2294  elem->SetAssignment(tgt_elem);
+
2295  }
+
2296 }
+
2297 
+
2298 void
+ +
2300 {
+
2301  target = 0;
+
2302  subtarget = 0;
+
2303 
+ +
2305 }
+
2306 
+
2307 // +--------------------------------------------------------------------+
+
2308 
+
2309 void
+ +
2311 {
+
2312  if (!target || target->Type() != SimObject::SIM_SHIP)
+
2313  return;
+
2314 
+
2315  Ship* tgt = (Ship*) target;
+
2316 
+
2317  if (tgt->IsDropship())
+
2318  return;
+
2319 
+
2320  System* subtgt = 0;
+
2321 
+
2322  ListIter<System> sys = tgt->Systems();
+
2323 
+
2324  if (dir > 0) {
+
2325  int latch = (subtarget == 0);
+
2326  while (++sys) {
+
2327  if (sys->Type() == System::COMPUTER || // computers are not targetable
+
2328  sys->Type() == System::SENSOR) // sensors are not targetable
+
2329  continue;
+
2330 
+
2331  if (sys.value() == subtarget) {
+
2332  latch = 1;
+
2333  }
+
2334 
+
2335  else if (latch) {
+
2336  subtgt = sys.value();
+
2337  break;
+
2338  }
+
2339  }
+
2340  }
+
2341 
+
2342  else {
+
2343  System* prev = 0;
+
2344 
+
2345  while (++sys) {
+
2346  if (sys->Type() == System::COMPUTER || // computers are not targetable
+
2347  sys->Type() == System::SENSOR) // sensors are not targetable
+
2348  continue;
+
2349 
+
2350  if (sys.value() == subtarget) {
+
2351  subtgt = prev;
+
2352  break;
+
2353  }
+
2354 
+
2355  prev = sys.value();
+
2356  }
+
2357 
+
2358  if (!subtarget)
+
2359  subtgt = prev;
+
2360  }
+
2361 
+
2362  SetTarget(tgt, subtgt);
+
2363 }
+
2364 
+
2365 // +--------------------------------------------------------------------+
+
2366 
+
2367 void
+
2368 Ship::ExecFrame(double seconds)
+
2369 {
+
2370  ZeroMemory(trigger, sizeof(trigger));
+
2371  altitude_agl = -1.0e6f;
+
2372 
+
2373  if (flight_phase < LAUNCH) {
+
2374  DockFrame(seconds);
+
2375  return;
+
2376  }
+
2377 
+
2378  if (flight_phase == LAUNCH ||
+
2379  (flight_phase == TAKEOFF && AltitudeAGL() > Radius())) {
+ +
2381  }
+
2382 
+
2383  if (transition_time > 0) {
+
2384  transition_time -= (float) seconds;
+
2385 
+
2386  if (transition_time <= 0) {
+ +
2388  return;
+
2389  }
+
2390 
+
2391  if (rep && IsDying() && killer) {
+
2392  killer->ExecFrame(seconds);
+
2393  }
+
2394  }
+
2395 
+
2396  // observers do not run out of power:
+
2397  if (IsNetObserver()) {
+
2398  for (int i = 0; i < reactors.size(); i++)
+
2399  reactors[i]->SetFuelRange(1e6);
+
2400  }
+
2401 
+
2402  if (IsStatic()) {
+
2403  StatFrame(seconds);
+
2404  return;
+
2405  }
+
2406 
+ +
2408  ExecNavFrame(seconds);
+
2409  ExecEvalFrame(seconds);
+
2410 
+
2411  if (IsAirborne()) {
+
2412  // are we trying to make orbit?
+
2413  if (Location().y >= TERRAIN_ALTITUDE_LIMIT)
+
2414  MakeOrbit();
+
2415  }
+
2416 
+
2417  if (!InTransition()) {
+
2418  ExecSensors(seconds);
+
2419  ExecThrottle(seconds);
+
2420  }
+
2421 
+
2422  else if (IsDropping() || IsAttaining() || IsSkipping()) {
+
2423  throttle = 100;
+
2424  }
+
2425 
+
2426  if (target && target->Life() == 0) {
+
2427  DropTarget();
+
2428  }
+
2429 
+
2430  ExecPhysics(seconds);
+
2431 
+
2432  if (!InTransition()) {
+
2433  UpdateTrack();
+
2434  }
+
2435 
+
2436  // are we docking?
+
2437  if (IsDropship()) {
+
2438  ListIter<Ship> iter = GetRegion()->Carriers();
+
2439 
+
2440  while (++iter) {
+
2441  Ship* carrier_target = iter.value();
+
2442 
+
2443  double range = (Location() - carrier_target->Location()).length();
+
2444  if (range > carrier_target->Radius() * 1.5)
+
2445  continue;
+
2446 
+
2447  if (carrier_target->GetIFF() == GetIFF() || carrier_target->GetIFF() == 0) {
+
2448  for (int i = 0; i < carrier_target->NumFlightDecks(); i++) {
+
2449  if (carrier_target->GetFlightDeck(i)->Recover(this))
+
2450  break;
+
2451  }
+
2452  }
+
2453  }
+
2454  }
+
2455 
+
2456  ExecSystems(seconds);
+
2457  ExecMaintFrame(seconds);
+
2458 
+
2459  if (flight_decks.size() > 0) {
+
2460  Camera* global_cam = CameraDirector::GetInstance()->GetCamera();
+
2461  Point global_cam_loc = global_cam->Pos();
+
2462  bool disable_shadows = false;
+
2463 
+
2464  for (int i = 0; i < flight_decks.size(); i++) {
+
2465  if (flight_decks[i]->ContainsPoint(global_cam_loc))
+
2466  disable_shadows = true;
+
2467  }
+
2468 
+
2469  EnableShadows(!disable_shadows);
+
2470  }
+
2471 
+
2472  if (!_finite(Location().x)) {
+
2473  DropTarget();
+
2474  }
+
2475 
+
2476  if (!IsStatic() && !IsGroundUnit() && GetFlightModel() < 2)
+
2477  CalcFlightPath();
+
2478 }
+
2479 
+
2480 // +--------------------------------------------------------------------+
+
2481 
+
2482 void
+ +
2484 {
+
2485  if (net_observer_mode)
+
2486  return;
+
2487 
+
2488  if (sensor_drone) {
+
2489  sensor_drone = 0;
+
2490  }
+
2491 
+
2492  if (probe) {
+
2493  sensor_drone = (Drone*) probe->Fire();
+
2494 
+
2495  if (sensor_drone)
+ +
2497 
+
2498  else if (sim->GetPlayerShip() == this)
+ +
2500  }
+
2501 }
+
2502 
+
2503 void
+ +
2505 {
+
2506  if (sensor_drone != d) {
+
2507  sensor_drone = d;
+
2508 
+
2509  if (sensor_drone)
+ +
2511  }
+
2512 }
+
2513 
+
2514 void
+
2515 Ship::ExecSensors(double seconds)
+
2516 {
+
2517  // how visible are we?
+
2518  DoEMCON();
+
2519 
+
2520  // what can we see?
+
2521  if (sensor)
+
2522  sensor->ExecFrame(seconds);
+
2523 
+
2524  // can we still see our target?
+
2525  if (target) {
+
2526  int target_found = 0;
+
2527  ListIter<Contact> c_iter = ContactList();
+
2528  while (++c_iter) {
+
2529  Contact* c = c_iter.value();
+
2530 
+
2531  if (target == c->GetShip() || target == c->GetShot()) {
+
2532  target_found = 1;
+
2533 
+
2534  bool vis = c->Visible(this) || c->Threat(this);
+
2535 
+
2536  if (!vis && !c->PasLock() && !c->ActLock())
+
2537  DropTarget();
+
2538  }
+
2539  }
+
2540 
+
2541  if (!target_found)
+
2542  DropTarget();
+
2543  }
+
2544 }
+
2545 
+
2546 // +--------------------------------------------------------------------+
+
2547 
+
2548 void
+
2549 Ship::ExecNavFrame(double seconds)
+
2550 {
+
2551  bool auto_pilot = false;
+
2552 
+
2553  // update director info string:
+ +
2555 
+
2556  if (navsys) {
+
2557  navsys->ExecFrame(seconds);
+
2558 
+
2559  if (navsys->AutoNavEngaged()) {
+
2560  if (dir && dir->Type() == NavAI::DIR_TYPE) {
+
2561  NavAI* navai = (NavAI*) dir;
+
2562 
+
2563  if (navai->Complete()) {
+ + +
2566  }
+
2567  else {
+
2568  auto_pilot = true;
+
2569  }
+
2570  }
+
2571  }
+
2572  }
+
2573 
+
2574  // even if we are not on auto pilot,
+
2575  // have we completed the next navpoint?
+
2576 
+
2577  Instruction* navpt = GetNextNavPoint();
+
2578  if (navpt && !auto_pilot) {
+
2579  if (navpt->Region() == GetRegion()) {
+
2580  double distance = 0;
+
2581 
+
2582  Point npt = navpt->Location();
+
2583 
+
2584  if (navpt->Region())
+
2585  npt += navpt->Region()->Location();
+
2586 
+
2587  Sim* sim = Sim::GetSim();
+
2588  if (sim->GetActiveRegion())
+
2589  npt -= sim->GetActiveRegion()->Location();
+
2590 
+
2591  npt = npt.OtherHand();
+
2592 
+
2593  // distance from self to navpt:
+
2594  distance = Point(npt - Location()).length();
+
2595 
+
2596  if (distance < 10 * Radius())
+ +
2598  }
+
2599  }
+
2600 }
+
2601 
+
2602 // +--------------------------------------------------------------------+
+
2603 
+
2604 void
+
2605 Ship::ExecEvalFrame(double seconds)
+
2606 {
+
2607  // is it already too late?
+
2608  if (life == 0 || integrity < 1) return;
+
2609 
+
2610  const DWORD EVAL_FREQUENCY = 1000; // once every second
+
2611  static DWORD last_eval_frame = 0; // one ship per game frame
+
2612 
+
2613  if (element && element->NumObjectives() > 0 &&
+
2614  Game::GameTime() - last_eval_time > EVAL_FREQUENCY &&
+
2615  last_eval_frame != Game::Frame()) {
+
2616 
+ +
2618  last_eval_frame = Game::Frame();
+
2619 
+
2620  for (int i = 0; i < element->NumObjectives(); i++) {
+
2621  Instruction* obj = element->GetObjective(i);
+
2622 
+
2623  if (obj->Status() <= Instruction::ACTIVE) {
+
2624  obj->Evaluate(this);
+
2625  }
+
2626  }
+
2627  }
+
2628 }
+
2629 
+
2630 // +--------------------------------------------------------------------+
+
2631 
+
2632 void
+
2633 Ship::ExecPhysics(double seconds)
+
2634 {
+
2635  if (net_control) {
+
2636  net_control->ExecFrame(seconds);
+
2637  Thrust(seconds); // drive flare
+
2638  }
+
2639  else {
+
2640  thrust = (float) Thrust(seconds);
+
2641  SetupAgility();
+
2642 
+
2643  if (seconds > 0) {
+
2644  g_force = 0.0f;
+
2645  }
+
2646 
+
2647  if (IsAirborne()) {
+
2648  Point v1 = velocity;
+
2649  AeroFrame(seconds);
+
2650  Point v2 = velocity;
+
2651  Point dv = v2 - v1 + Point(0, g_accel*seconds, 0);
+
2652 
+
2653  if (seconds > 0) {
+
2654  g_force = (float) (dv * cam.vup() / seconds) / 9.8f;
+
2655  }
+
2656  }
+
2657 
+
2658  else if (IsDying() || flight_model < 2) { // standard and relaxed modes
+
2659  Physical::ExecFrame(seconds);
+
2660  }
+
2661 
+
2662  else { // arcade mode
+
2663  Physical::ArcadeFrame(seconds);
+
2664  }
+
2665  }
+
2666 }
+
2667 
+
2668 // +--------------------------------------------------------------------+
+
2669 
+
2670 void
+
2671 Ship::ExecThrottle(double seconds)
+
2672 {
+
2673  double spool = 75 * seconds;
+
2674 
+
2675  if (throttle < throttle_request)
+
2676  if (throttle_request-throttle < spool)
+ +
2678  else
+
2679  throttle += spool;
+
2680 
+
2681  else if (throttle > throttle_request)
+
2682  if (throttle - throttle_request < spool)
+ +
2684  else
+
2685  throttle -= spool;
+
2686 }
+
2687 
+
2688 // +--------------------------------------------------------------------+
+
2689 
+
2690 void
+
2691 Ship::ExecSystems(double seconds)
+
2692 {
+
2693  if (!rep)
+
2694  return;
+
2695 
+
2696  int i;
+
2697 
+
2698  ListIter<System> iter = systems;
+
2699  while (++iter) {
+
2700  System* sys = iter.value();
+
2701 
+
2702  sys->Orient(this);
+
2703 
+
2704  // sensors have already been executed,
+
2705  // they can not be run twice in a frame!
+
2706  if (sys->Type() != System::SENSOR)
+
2707  sys->ExecFrame(seconds);
+
2708  }
+
2709 
+
2710  // hangars and weapon groups are not systems
+
2711  // they must be executed separately from above
+
2712  if (hangar)
+
2713  hangar->ExecFrame(seconds);
+
2714 
+
2715  wep_mass = 0.0f;
+
2716  wep_resist = 0.0f;
+
2717 
+
2718  bool winchester_cycle = false;
+
2719 
+
2720  for (i = 0; i < weapons.size(); i++) {
+
2721  WeaponGroup* w_group = weapons[i];
+
2722  w_group->ExecFrame(seconds);
+
2723 
+
2724  if (w_group->GetTrigger() && w_group->GetFiringOrders() == Weapon::MANUAL) {
+
2725 
+
2726  Weapon* gun = w_group->GetSelected();
+
2727 
+
2728  SimObject* gun_tgt = gun->GetTarget();
+
2729 
+
2730  // if no target has been designated for this
+
2731  // weapon, let it guide on the contact closest
+
2732  // to its boresight. this must be done before
+
2733  // firing the weapon.
+
2734 
+
2735  if (sensor && gun->Guided() && !gun->Design()->beam && !gun_tgt) {
+ +
2737  }
+
2738 
+
2739  gun->Fire();
+
2740 
+
2741  w_group->SetTrigger(false);
+
2742  w_group->CycleWeapon();
+
2743  w_group->CheckAmmo();
+
2744 
+
2745  // was that the last shot from this missile group?
+
2746  if (w_group->IsMissile() && w_group->Ammo() < 1) {
+
2747 
+
2748  // is this the current secondary weapon group?
+
2749  if (weapons[secondary] == w_group) {
+
2750  winchester_cycle = true;
+
2751  }
+
2752  }
+
2753  }
+
2754 
+
2755  wep_mass += w_group->Mass();
+
2756  wep_resist += w_group->Resistance();
+
2757  }
+
2758 
+
2759  // if we just fired the last shot in the current secondary
+
2760  // weapon group, auto cycle to another secondary weapon:
+
2761  if (winchester_cycle) {
+
2762  int old_secondary = secondary;
+
2763 
+
2764  CycleSecondary();
+
2765 
+
2766  // do not winchester-cycle to an A2G missile type,
+
2767  // or a missile that is also out of ammo,
+
2768  // keep going!
+
2769 
+
2770  while (secondary != old_secondary) {
+
2771  Weapon* missile = GetSecondary();
+
2772  if (missile && missile->CanTarget(Ship::GROUND_UNITS))
+
2773  CycleSecondary();
+
2774 
+
2775  else if (weapons[secondary]->Ammo() < 1)
+
2776  CycleSecondary();
+
2777 
+
2778  else
+
2779  break;
+
2780  }
+
2781  }
+
2782 
+
2783  mass = (float) design->mass + wep_mass;
+
2784 
+
2785  if (IsDropship())
+
2786  agility = (float) design->agility - wep_resist;
+
2787 
+
2788  if (shieldRep) {
+
2789  Solid* solid = (Solid*) rep;
+
2790  shieldRep->MoveTo(solid->Location());
+ +
2792 
+
2793  bool bubble = false;
+
2794  if (shield)
+
2795  bubble = shield->ShieldBubble();
+
2796 
+
2797  if (shieldRep->ActiveHits()) {
+
2798  shieldRep->Energize(seconds, bubble);
+
2799  shieldRep->Show();
+
2800  }
+
2801  else {
+
2802  shieldRep->Hide();
+
2803  }
+
2804  }
+
2805 
+
2806  if (cockpit) {
+
2807  Solid* solid = (Solid*) rep;
+
2808 
+
2809  Point cpos = cam.Pos() +
+
2810  cam.vrt() * bridge_vec.x +
+
2811  cam.vpn() * bridge_vec.y +
+
2812  cam.vup() * bridge_vec.z;
+
2813 
+
2814  cockpit->MoveTo(cpos);
+
2815  cockpit->SetOrientation(solid->Orientation());
+
2816  }
+
2817 }
+
2818 
+
2819 // +--------------------------------------------------------------------+
+
2820 
+
2821 void
+
2822 Ship::AeroFrame(double seconds)
+
2823 {
+
2824  float g_save = g_accel;
+
2825 
+
2826  if (Class() == LCA) {
+
2827  lat_thrust = true;
+
2828  SetGravity(0.0f);
+
2829  }
+
2830 
+
2831  if (AltitudeAGL() < Radius()) {
+
2832  SetGravity(0.0f);
+
2833 
+
2834  // on the ground/runway?
+
2835  double bottom = 1e9;
+
2836  double tlevel = Location().y - AltitudeAGL();
+
2837 
+
2838  // taking off or landing?
+
2839  if (flight_phase < ACTIVE || flight_phase > APPROACH) {
+
2840  if (dock)
+
2841  tlevel = dock->MountLocation().y;
+
2842  }
+
2843 
+
2844  if (tlevel < 0)
+
2845  tlevel = 0;
+
2846 
+
2847  if (gear)
+
2848  bottom = gear->GetTouchDown()-1;
+
2849  else
+
2850  bottom = Location().y-6;
+
2851 
+
2852  if (bottom < tlevel)
+
2853  TranslateBy(Point(0, bottom-tlevel, 0));
+
2854  }
+
2855 
+
2856  // MODEL 2: ARCADE
+
2857  if (flight_model >= 2) {
+
2858  Physical::ArcadeFrame(seconds);
+
2859  }
+
2860 
+
2861  // MODEL 1: RELAXED
+
2862  else if (flight_model == 1) {
+
2863  Physical::ExecFrame(seconds);
+
2864  }
+
2865 
+
2866  // MODEL 0: STANDARD
+
2867  else {
+
2868  // apply drag-torque (i.e. turn ship into
+
2869  // velocity vector to minimize drag):
+
2870 
+
2871  Point vnrm = velocity;
+
2872  double v = vnrm.Normalize();
+
2873  double pitch_deflection = vnrm * cam.vup();
+
2874  double yaw_deflection = vnrm * cam.vrt();
+
2875 
+
2876  if (lat_thrust && v < 250) {
+
2877  }
+
2878 
+
2879  else {
+
2880  if (v < 250) {
+
2881  double factor = 1.2 + (250 - v) / 100;
+
2882 
+
2883  ApplyPitch(pitch_deflection * -factor);
+
2884  ApplyYaw(yaw_deflection * factor);
+
2885 
+
2886  dp += (float) (dp_acc * seconds);
+
2887  dy += (float) (dy_acc * seconds);
+
2888  }
+
2889 
+
2890  else {
+
2891  if (fabs(pitch_deflection) > stall) {
+
2892  ApplyPitch(pitch_deflection * -1.2);
+
2893  dp += (float) (dp_acc * seconds);
+
2894  }
+
2895 
+
2896  ApplyYaw(yaw_deflection * 2);
+
2897  dy += (float) (dy_acc * seconds);
+
2898  }
+
2899  }
+
2900 
+
2901  // compute rest of physics:
+
2902  Physical::AeroFrame(seconds);
+
2903  }
+
2904 
+
2905  SetGravity(g_save);
+
2906 }
+
2907 
+
2908 // +--------------------------------------------------------------------+
+
2909 
+
2910 void
+
2911 Ship::LinearFrame(double seconds)
+
2912 {
+
2913  Physical::LinearFrame(seconds);
+
2914 
+
2915  if (!IsAirborne() || Class() != LCA)
+
2916  return;
+
2917 
+
2918  // damp lateral movement in atmosphere:
+
2919 
+
2920  // side-to-side
+
2921  if (!trans_x) {
+
2922  Point transvec = cam.vrt();
+
2923  transvec *= (transvec * velocity) * seconds * 0.5;
+
2924  velocity -= transvec;
+
2925  }
+
2926 
+
2927  // fore-and-aft
+
2928  if (!trans_y && fabs(thrust) < 1.0f) {
+
2929  Point transvec = cam.vpn();
+
2930  transvec *= (transvec * velocity) * seconds * 0.25;
+
2931  velocity -= transvec;
+
2932  }
+
2933 
+
2934  // up-and-down
+
2935  if (!trans_z) {
+
2936  Point transvec = cam.vup();
+
2937  transvec *= (transvec * velocity) * seconds * 0.5;
+
2938  velocity -= transvec;
+
2939  }
+
2940 }
+
2941 
+
2942 // +--------------------------------------------------------------------+
+
2943 
+
2944 void
+
2945 Ship::DockFrame(double seconds)
+
2946 {
+
2947  SelectDetail(seconds);
+
2948 
+
2949  if (sim->GetPlayerShip() == this) {
+
2950  // Make sure the thruster sound is diabled
+
2951  // when the player is on the runway or catapult
+
2952  if (thruster) {
+
2953  thruster->ExecTrans(0,0,0);
+
2954  }
+
2955  }
+
2956 
+
2957  if (rep) {
+
2958  // Update the graphic rep and light sources:
+
2959  // (This is usually done by the physics class,
+
2960  // but when the ship is in dock, we skip the
+
2961  // standard physics processing):
+
2962  rep->MoveTo(cam.Pos());
+ +
2964 
+
2965  if (light)
+
2966  light->MoveTo(cam.Pos());
+
2967 
+
2968  ListIter<System> iter = systems;
+
2969  while (++iter)
+
2970  iter->Orient(this);
+
2971 
+
2972  double spool = 75 * seconds;
+
2973 
+
2974  if (flight_phase == DOCKING) {
+
2975  throttle_request = 0;
+
2976  throttle = 0;
+
2977  }
+
2978 
+
2979  else if (throttle < throttle_request)
+
2980  if (throttle_request-throttle < spool)
+ +
2982  else
+
2983  throttle += spool;
+
2984 
+
2985  else if (throttle > throttle_request)
+
2986  if (throttle - throttle_request < spool)
+ +
2988  else
+
2989  throttle -= spool;
+
2990 
+
2991  // make sure there is power to run the drive:
+
2992  for (int i = 0; i < reactors.size(); i++)
+
2993  reactors[i]->ExecFrame(seconds);
+
2994 
+
2995  // count up weapon ammo for status mfd:
+
2996  for (int i = 0; i < weapons.size(); i++)
+
2997  weapons[i]->ExecFrame(seconds);
+
2998 
+
2999  // show drive flare while on catapult:
+
3000  if (main_drive) {
+ +
3002 
+
3003  if (throttle > 0)
+
3004  main_drive->Thrust(seconds); // show drive flare
+
3005  }
+
3006  }
+
3007 
+
3008  if (cockpit && !cockpit->Hidden()) {
+
3009  Solid* solid = (Solid*) rep;
+
3010 
+
3011  Point cpos = cam.Pos() +
+
3012  cam.vrt() * bridge_vec.x +
+
3013  cam.vpn() * bridge_vec.y +
+
3014  cam.vup() * bridge_vec.z;
+
3015 
+
3016  cockpit->MoveTo(cpos);
+
3017  cockpit->SetOrientation(solid->Orientation());
+
3018  }
+
3019 }
+
3020 
+
3021 // +--------------------------------------------------------------------+
+
3022 
+
3023 void
+
3024 Ship::StatFrame(double seconds)
+
3025 {
+
3026  if (flight_phase != ACTIVE) {
+
3027  flight_phase = ACTIVE;
+
3028  launch_time = Game::GameTime() + 1;
+
3029 
+
3030  if (element)
+ +
3032  }
+
3033 
+
3034  if (IsGroundUnit()) {
+
3035  // glue buildings to the terrain:
+
3036  Point loc = Location();
+
3037  Terrain* terrain = region->GetTerrain();
+
3038 
+
3039  if (terrain) {
+
3040  loc.y = terrain->Height(loc.x, loc.z);
+
3041  MoveTo(loc);
+
3042  }
+
3043  }
+
3044 
+
3045  if (rep) {
+
3046  rep->MoveTo(cam.Pos());
+ +
3048  }
+
3049 
+
3050  if (light) {
+
3051  light->MoveTo(cam.Pos());
+
3052  }
+
3053 
+
3054  ExecSensors(seconds);
+
3055 
+
3056  if (target && target->Life() == 0) {
+
3057  DropTarget();
+
3058  }
+
3059 
+
3060  if (dir) dir->ExecFrame(seconds);
+
3061 
+
3062  SelectDetail(seconds);
+
3063 
+
3064  int i = 0;
+
3065 
+
3066  if (rep) {
+
3067  ListIter<System> iter = systems;
+
3068  while (++iter)
+
3069  iter->Orient(this);
+
3070 
+
3071  for (i = 0; i < reactors.size(); i++)
+
3072  reactors[i]->ExecFrame(seconds);
+
3073 
+
3074  for (i = 0; i < navlights.size(); i++)
+
3075  navlights[i]->ExecFrame(seconds);
+
3076 
+
3077  for (i = 0; i < weapons.size(); i++)
+
3078  weapons[i]->ExecFrame(seconds);
+
3079 
+
3080  if (farcaster) {
+
3081  farcaster->ExecFrame(seconds);
+
3082 
+
3083  if (navlights.size() == 2) {
+
3084  if (farcaster->Charge() > 99) {
+
3085  navlights[0]->Enable();
+
3086  navlights[1]->Disable();
+
3087  }
+
3088  else {
+
3089  navlights[0]->Disable();
+
3090  navlights[1]->Enable();
+
3091  }
+
3092  }
+
3093  }
+
3094 
+
3095  if (shield)
+
3096  shield->ExecFrame(seconds);
+
3097 
+
3098  if (hangar)
+
3099  hangar->ExecFrame(seconds);
+
3100 
+
3101  if (flight_decks.size() > 0) {
+
3102  Camera* global_cam = CameraDirector::GetInstance()->GetCamera();
+
3103  Point global_cam_loc = global_cam->Pos();
+
3104  bool disable_shadows = false;
+
3105 
+
3106  for (i = 0; i < flight_decks.size(); i++) {
+
3107  flight_decks[i]->ExecFrame(seconds);
+
3108 
+
3109  if (flight_decks[i]->ContainsPoint(global_cam_loc))
+
3110  disable_shadows = true;
+
3111  }
+
3112 
+
3113  EnableShadows(!disable_shadows);
+
3114  }
+
3115  }
+
3116 
+
3117  if (shieldRep) {
+
3118  Solid* solid = (Solid*) rep;
+
3119  shieldRep->MoveTo(solid->Location());
+ +
3121 
+
3122  bool bubble = false;
+
3123  if (shield)
+
3124  bubble = shield->ShieldBubble();
+
3125 
+
3126  if (shieldRep->ActiveHits()) {
+
3127  shieldRep->Energize(seconds, bubble);
+
3128  shieldRep->Show();
+
3129  }
+
3130  else {
+
3131  shieldRep->Hide();
+
3132  }
+
3133  }
+
3134 
+
3135  if (!_finite(Location().x)) {
+
3136  DropTarget();
+
3137  }
+
3138 }
+
3139 
+
3140 // +--------------------------------------------------------------------+
+
3141 
+
3142 Graphic*
+ +
3144 {
+
3145  return cockpit;
+
3146 }
+
3147 
+
3148 void
+ +
3150 {
+
3151  if (cockpit) {
+
3152  cockpit->Show();
+
3153 
+ +
3155  while (++g) {
+
3156  ListIter<Weapon> w = g->GetWeapons();
+
3157  while (++w) {
+
3158  Solid* turret = w->GetTurret();
+
3159  if (turret) {
+
3160  turret->Show();
+
3161 
+
3162  Solid* turret_base = w->GetTurretBase();
+
3163  if (turret_base)
+
3164  turret_base->Show();
+
3165  }
+
3166 
+
3167  if (w->IsMissile()) {
+
3168  for (int i = 0; i < w->Ammo(); i++) {
+
3169  Solid* store = w->GetVisibleStore(i);
+
3170  if (store) {
+
3171  store->Show();
+
3172  }
+
3173  }
+
3174  }
+
3175  }
+
3176  }
+
3177  }
+
3178 }
+
3179 
+
3180 void
+ +
3182 {
+
3183  if (cockpit)
+
3184  cockpit->Hide();
+
3185 }
+
3186 
+
3187 // +--------------------------------------------------------------------+
+
3188 
+
3189 void
+
3190 Ship::SelectDetail(double seconds)
+
3191 {
+
3192  detail.ExecFrame(seconds);
+ +
3194 
+
3195  int new_level = detail.GetDetailLevel();
+
3196 
+
3197  if (detail_level != new_level) {
+
3198  Scene* scene = 0;
+
3199 
+
3200  // remove current rep from scene (if necessary):
+
3201  for (int i = 0; i < detail.NumModels(detail_level); i++) {
+
3202  Graphic* g = detail.GetRep(detail_level, i);
+
3203  if (g) {
+
3204  scene = g->GetScene();
+
3205  if (scene)
+
3206  scene->DelGraphic(g);
+
3207  }
+
3208  }
+
3209 
+
3210  // switch to new rep:
+
3211  detail_level = new_level;
+ +
3213 
+
3214  // add new rep to scene (if necessary):
+
3215  if (scene) {
+
3216  for (int i = 0; i < detail.NumModels(detail_level); i++) {
+
3217  Graphic* g = detail.GetRep(detail_level, i);
+
3218  Point s = detail.GetSpin(detail_level, i);
+
3219  Matrix m = cam.Orientation();
+
3220 
+
3221  m.Pitch(s.x);
+
3222  m.Yaw(s.z);
+
3223  m.Roll(s.y);
+
3224 
+
3225  scene->AddGraphic(g);
+
3226  g->MoveTo(cam.Pos() + detail.GetOffset(detail_level, i));
+
3227  g->SetOrientation(m);
+
3228  }
+
3229 
+
3230  // show/hide external stores and landing gear...
+
3231  if (detail.NumLevels() > 0) {
+
3232  if (gear && (gear->GetState() != LandingGear::GEAR_UP)) {
+
3233  for (int i = 0; i < gear->NumGear(); i++) {
+
3234  Solid* g = gear->GetGear(i);
+
3235 
+
3236  if (g) {
+
3237  if (detail_level == 0)
+
3238  scene->DelGraphic(g);
+
3239  else
+
3240  scene->AddGraphic(g);
+
3241  }
+
3242  }
+
3243  }
+
3244 
+ +
3246  while (++g) {
+
3247  ListIter<Weapon> w = g->GetWeapons();
+
3248  while (++w) {
+
3249  Solid* turret = w->GetTurret();
+
3250  if (turret) {
+
3251  if (detail_level == 0)
+
3252  scene->DelGraphic(turret);
+
3253  else
+
3254  scene->AddGraphic(turret);
+
3255 
+
3256  Solid* turret_base = w->GetTurretBase();
+
3257  if (turret_base) {
+
3258  if (detail_level == 0)
+
3259  scene->DelGraphic(turret_base);
+
3260  else
+
3261  scene->AddGraphic(turret_base);
+
3262  }
+
3263  }
+
3264  if (w->IsMissile()) {
+
3265  for (int i = 0; i < w->Ammo(); i++) {
+
3266  Solid* store = w->GetVisibleStore(i);
+
3267  if (store) {
+
3268  if (detail_level == 0)
+
3269  scene->DelGraphic(store);
+
3270  else
+
3271  scene->AddGraphic(store);
+
3272  }
+
3273  }
+
3274  }
+
3275  }
+
3276  }
+
3277  }
+
3278  }
+
3279  }
+
3280 
+
3281  else {
+
3282  int nmodels = detail.NumModels(detail_level);
+
3283 
+
3284  if (nmodels > 1) {
+
3285  for (int i = 0; i < nmodels; i++) {
+
3286  Graphic* g = detail.GetRep(detail_level, i);
+
3287  Point s = detail.GetSpin(detail_level, i);
+
3288  Matrix m = cam.Orientation();
+
3289 
+
3290  m.Pitch(s.x);
+
3291  m.Yaw(s.z);
+
3292  m.Roll(s.y);
+
3293 
+
3294  g->MoveTo(cam.Pos() + detail.GetOffset(detail_level, i));
+
3295  g->SetOrientation(m);
+
3296  }
+
3297  }
+
3298  }
+
3299 }
+
3300 
+
3301 // +--------------------------------------------------------------------+
+
3302 
+
3303 void
+ +
3305 {
+
3306  for (int i = 0; i < detail.NumModels(detail_level); i++) {
+
3307  Graphic* g = detail.GetRep(detail_level, i);
+
3308  g->Show();
+
3309  }
+
3310 
+
3311  if (gear && (gear->GetState() != LandingGear::GEAR_UP)) {
+
3312  for (int i = 0; i < gear->NumGear(); i++) {
+
3313  Solid* g = gear->GetGear(i);
+
3314  if (g) g->Show();
+
3315  }
+
3316  }
+
3317 
+ +
3319  while (++g) {
+
3320  ListIter<Weapon> w = g->GetWeapons();
+
3321  while (++w) {
+
3322  Solid* turret = w->GetTurret();
+
3323  if (turret) {
+
3324  turret->Show();
+
3325 
+
3326  Solid* turret_base = w->GetTurretBase();
+
3327  if (turret_base)
+
3328  turret_base->Show();
+
3329  }
+
3330 
+
3331  if (w->IsMissile()) {
+
3332  for (int i = 0; i < w->Ammo(); i++) {
+
3333  Solid* store = w->GetVisibleStore(i);
+
3334  if (store) {
+
3335  store->Show();
+
3336  }
+
3337  }
+
3338  }
+
3339  }
+
3340  }
+
3341 }
+
3342 
+
3343 void
+ +
3345 {
+
3346  for (int i = 0; i < detail.NumModels(detail_level); i++) {
+
3347  Graphic* g = detail.GetRep(detail_level, i);
+
3348  g->Hide();
+
3349  }
+
3350 
+
3351  if (gear && (gear->GetState() != LandingGear::GEAR_UP)) {
+
3352  for (int i = 0; i < gear->NumGear(); i++) {
+
3353  Solid* g = gear->GetGear(i);
+
3354  if (g) g->Hide();
+
3355  }
+
3356  }
+
3357 
+ +
3359  while (++g) {
+
3360  ListIter<Weapon> w = g->GetWeapons();
+
3361  while (++w) {
+
3362  Solid* turret = w->GetTurret();
+
3363  if (turret) {
+
3364  turret->Hide();
+
3365 
+
3366  Solid* turret_base = w->GetTurretBase();
+
3367  if (turret_base)
+
3368  turret_base->Hide();
+
3369  }
+
3370 
+
3371  if (w->IsMissile()) {
+
3372  for (int i = 0; i < w->Ammo(); i++) {
+
3373  Solid* store = w->GetVisibleStore(i);
+
3374  if (store) {
+
3375  store->Hide();
+
3376  }
+
3377  }
+
3378  }
+
3379  }
+
3380  }
+
3381 }
+
3382 
+
3383 void
+ +
3385 {
+
3386  for (int i = 0; i < detail.NumModels(detail_level); i++) {
+
3387  Graphic* g = detail.GetRep(detail_level, i);
+
3388 
+
3389  if (g->IsSolid()) {
+
3390  Solid* s = (Solid*) g;
+
3391 
+
3392  ListIter<Shadow> iter = s->GetShadows();
+
3393  while (++iter) {
+
3394  Shadow* shadow = iter.value();
+
3395  shadow->SetEnabled(enable);
+
3396  }
+
3397  }
+
3398  }
+
3399 }
+
3400 
+
3401 // +--------------------------------------------------------------------+
+
3402 
+
3403 bool
+ +
3405 {
+
3406  if (obj == ward)
+
3407  ward = 0;
+
3408 
+
3409  if (obj == target) {
+
3410  target = 0;
+
3411  subtarget = 0;
+
3412  }
+
3413 
+
3414  if (obj == carrier) {
+
3415  carrier = 0;
+
3416  dock = 0;
+
3417  inbound = 0;
+
3418  }
+
3419 
+
3420  if (obj->Type() == SimObject::SIM_SHOT ||
+
3421  obj->Type() == SimObject::SIM_DRONE) {
+
3422  Shot* s = (Shot*) obj;
+
3423 
+
3424  if (sensor_drone == s)
+
3425  sensor_drone = 0;
+
3426 
+
3427  if (decoy_list.contains(s))
+
3428  decoy_list.remove(s);
+
3429 
+
3430  if (threat_list.contains(s))
+
3431  threat_list.remove(s);
+
3432  }
+
3433 
+
3434  return SimObserver::Update(obj);
+
3435 }
+
3436 
+
3437 // +--------------------------------------------------------------------+
+
3438 
+
3439 int
+ +
3441 {
+
3442  if (reactors.size() > 0) {
+
3443  PowerSource* reactor = reactors[0];
+
3444  if (reactor)
+
3445  return reactor->Charge();
+
3446  }
+
3447 
+
3448  return 0;
+
3449 }
+
3450 
+
3451 void
+
3452 Ship::SetThrottle(double percent)
+
3453 {
+
3454  throttle_request = percent;
+
3455 
+
3456  if (throttle_request < 0) throttle_request = 0;
+
3457  else if (throttle_request > 100) throttle_request = 100;
+
3458 
+
3459  if (throttle_request < 50)
+
3460  augmenter = false;
+
3461 }
+
3462 
+
3463 void
+ +
3465 {
+
3466  if (throttle <= 50)
+
3467  enable = false;
+
3468 
+
3469  if (main_drive && main_drive->MaxAugmenter() <= 0)
+
3470  enable = false;
+
3471 
+
3472  augmenter = enable;
+
3473 }
+
3474 
+
3475 // +--------------------------------------------------------------------+
+
3476 
+
3477 void
+
3478 Ship::SetTransition(double trans_time, int trans_type, const Point& trans_loc)
+
3479 {
+
3480  transition_time = (float) trans_time;
+
3481  transition_type = trans_type;
+
3482  transition_loc = trans_loc;
+
3483 }
+
3484 
+
3485 void
+ +
3487 {
+ +
3489  SimRegion* dst_rgn = sim->FindNearestTerrainRegion(this);
+
3490 
+
3491  if (dst_rgn &&
+
3492  dst_rgn->GetOrbitalRegion()->Primary() ==
+ +
3494 
+
3495  transition_time = 10.0f;
+ +
3497  transition_loc = Location() + Heading() * (-2*Radius());
+
3498 
+ +
3500  SetControls(0);
+
3501  }
+
3502  }
+
3503 }
+
3504 
+
3505 void
+ +
3507 {
+ +
3509  transition_time = 5.0f;
+ +
3511  transition_loc = Location() + Heading() * (-2*Radius());
+
3512 
+ +
3514  SetControls(0);
+
3515  }
+
3516 }
+
3517 
+
3518 // +--------------------------------------------------------------------+
+
3519 
+
3520 bool
+ +
3522 {
+
3523  if (IsRogue())
+
3524  return true;
+
3525 
+
3526  bool combat = false;
+
3527 
+
3528  ListIter<Contact> c_iter = ContactList();
+
3529  while (++c_iter) {
+
3530  Contact* c = c_iter.value();
+
3531  Ship* cship = c->GetShip();
+
3532  int ciff = c->GetIFF(this);
+
3533  Point delta = c->Location() - Location();
+
3534  double dist = delta.length();
+
3535 
+
3536  if (c->Threat(this) && !cship) {
+
3537  if (IsStarship())
+
3538  combat = dist < 120e3;
+
3539  else
+
3540  combat = dist < 60e3;
+
3541  }
+
3542 
+
3543  else if (cship && ciff > 0 && ciff != GetIFF()) {
+
3544  if (IsStarship() && cship->IsStarship())
+
3545  combat = dist < 120e3;
+
3546  else
+
3547  combat = dist < 60e3;
+
3548  }
+
3549  }
+
3550 
+
3551  return combat;
+
3552 }
+
3553 
+
3554 // +--------------------------------------------------------------------+
+
3555 
+
3556 bool
+ +
3558 {
+
3559  bool go = false;
+
3560  Instruction* navpt = GetNextNavPoint();
+
3561 
+
3562  if (MissionClock() < 10000 || NetGame::IsNetGame())
+
3563  return go;
+
3564 
+
3565  if (navpt) {
+
3566  go = true;
+
3567 
+
3568  if (navpt->Region() != GetRegion())
+
3569  go = false;
+
3570 
+
3571  else if (Point(navpt->Location().OtherHand() - Location()).length() < 30e3)
+
3572  go = false;
+
3573  }
+
3574 
+
3575  if (go)
+
3576  go = !IsInCombat();
+
3577 
+
3578  return go;
+
3579 }
+
3580 
+
3581 void
+ +
3583 {
+
3584  if (CanTimeSkip()) {
+
3585  // go back to regular time before performing the skip:
+ +
3587 
+
3588  transition_time = 7.5f;
+ +
3590  transition_loc = Location() + Heading() * (Velocity().length() * 4);
+
3591  // 2500; //(8*Radius());
+
3592 
+
3593  if (rand() < 16000)
+
3594  transition_loc += BeamLine() * (2.5*Radius());
+
3595  else
+
3596  transition_loc += BeamLine() * (-2 *Radius());
+
3597 
+
3598  if (rand() < 8000)
+
3599  transition_loc += LiftLine() * (-1*Radius());
+
3600  else
+
3601  transition_loc += LiftLine() * (1.8*Radius());
+
3602 
+
3603  SetControls(0);
+
3604  }
+
3605 
+
3606  else if (sim->GetPlayerShip() == this) {
+
3607  SetAutoNav(true);
+
3608  }
+
3609 }
+
3610 
+
3611 // +--------------------------------------------------------------------+
+
3612 
+
3613 void
+
3614 Ship::DropCam(double time, double range)
+
3615 {
+ +
3617 
+
3618  if (time > 0)
+
3619  transition_time = (float) time;
+
3620  else
+
3621  transition_time = 10.0f;
+
3622 
+
3623  Point offset = Heading() * (Velocity().length() * 5);
+
3624  double lateral_offset = 2 * Radius();
+
3625  double vertical_offset = Radius();
+
3626 
+
3627  if (vertical_offset > 300)
+
3628  vertical_offset = 300;
+
3629 
+
3630  if (rand() < 16000)
+
3631  lateral_offset *= -1;
+
3632 
+
3633  if (rand() < 8000)
+
3634  vertical_offset *= -1;
+
3635 
+
3636  offset += BeamLine() * lateral_offset;
+
3637  offset += LiftLine() * vertical_offset;
+
3638 
+
3639  if (range > 0)
+
3640  offset *= range;
+
3641 
+
3642  transition_loc = Location() + offset;
+
3643 }
+
3644 
+
3645 // +--------------------------------------------------------------------+
+
3646 
+
3647 void
+ +
3649 {
+
3650  if (!killer)
+
3651  killer = new(__FILE__,__LINE__) ShipKiller(this);
+
3652 
+
3653  ListIter<System> iter = systems;
+
3654  while (++iter)
+
3655  iter->PowerOff();
+
3656 
+
3657  // transfer arcade velocity to newtonian velocity:
+
3658  if (flight_model >= 2) {
+ +
3660  }
+
3661 
+
3662  if (GetIFF() < 100 && !IsGroundUnit()) {
+ +
3664  }
+
3665 
+ +
3667 
+ +
3669 
+ + +
3672 }
+
3673 
+
3674 // +--------------------------------------------------------------------+
+
3675 
+
3676 void
+ +
3678 {
+
3679  int old_type = transition_type;
+
3680  transition_time = 0.0f;
+ +
3682 
+
3683  switch (old_type) {
+
3684  case TRANSITION_NONE:
+
3685  case TRANSITION_DROP_CAM:
+
3686  default:
+
3687  return;
+
3688 
+
3689  case TRANSITION_DROP_ORBIT: {
+
3690  SetControls(0);
+
3691  SimRegion* dst_rgn = sim->FindNearestTerrainRegion(this);
+
3692  Point dst_loc = Location().OtherHand() * 0.20;
+
3693  dst_loc.x += 6000 * GetElementIndex();
+
3694  dst_loc.z = TERRAIN_ALTITUDE_LIMIT * 0.95;
+
3695  dst_loc += RandomDirection() * 2e3;
+
3696 
+
3697  sim->RequestHyperJump(this, dst_rgn, dst_loc, TRANSITION_DROP_ORBIT);
+
3698 
+
3699  ShipStats* stats = ShipStats::Find(Name());
+
3700  stats->AddEvent(SimEvent::BREAK_ORBIT, dst_rgn->Name());
+
3701  }
+
3702  break;
+
3703 
+
3704  case TRANSITION_MAKE_ORBIT: {
+
3705  SetControls(0);
+
3706  SimRegion* dst_rgn = sim->FindNearestSpaceRegion(this);
+
3707  double dist = 200.0e3 + 10.0e3 * GetElementIndex();
+
3708  Point esc_vec = dst_rgn->GetOrbitalRegion()->Location() -
+
3709  dst_rgn->GetOrbitalRegion()->Primary()->Location();
+
3710 
+
3711  esc_vec.z = -100 * GetElementIndex();
+
3712  esc_vec.Normalize();
+
3713  esc_vec *= -dist;
+
3714  esc_vec += RandomDirection() * 2e3;
+
3715 
+
3716  sim->RequestHyperJump(this, dst_rgn, esc_vec, TRANSITION_MAKE_ORBIT);
+
3717 
+
3718  ShipStats* stats = ShipStats::Find(Name());
+
3719  stats->AddEvent(SimEvent::MAKE_ORBIT, dst_rgn->Name());
+
3720  }
+
3721  break;
+
3722 
+
3723  case TRANSITION_TIME_SKIP: {
+
3724  Instruction* navpt = GetNextNavPoint();
+
3725 
+
3726  if (navpt) {
+
3727  Point delta = navpt->Location().OtherHand() - Location();
+
3728  Point unit = delta; unit.Normalize();
+
3729  Point trans = delta + unit * -20e3;
+
3730  double dist = trans.length();
+
3731  double speed = navpt->Speed();
+
3732 
+
3733  if (speed < 50) speed = 500;
+
3734 
+
3735  double etr = dist / speed;
+
3736 
+
3737  sim->ResolveTimeSkip(etr);
+
3738  }
+
3739  }
+
3740  break;
+
3741 
+ +
3743  SetControls(0);
+ +
3745  break;
+
3746  }
+
3747 
+
3748 }
+
3749 
+
3750 bool
+ +
3752 {
+
3753  if (region)
+
3754  return region->Type() == SimRegion::AIR_SPACE;
+
3755 
+
3756  return false;
+
3757 }
+
3758 
+
3759 double
+ +
3761 {
+
3762  Point heading = Heading();
+
3763  double compass_heading = atan2(fabs(heading.x), heading.z);
+
3764 
+
3765  if (heading.x < 0)
+
3766  compass_heading *= -1;
+
3767 
+
3768  double result = compass_heading + PI;
+
3769 
+
3770  if (result >= 2*PI)
+
3771  result -= 2*PI;
+
3772 
+
3773  return result;
+
3774 }
+
3775 
+
3776 double
+ +
3778 {
+
3779  Point heading = Heading();
+
3780  return asin(heading.y);
+
3781 }
+
3782 
+
3783 double
+ +
3785 {
+
3786  return Location().y;
+
3787 }
+
3788 
+
3789 double
+ +
3791 {
+
3792  if (altitude_agl < -1000) {
+
3793  Ship* pThis = (Ship*) this; // cast-away const
+
3794  Point loc = Location();
+
3795 
+
3796  Terrain* terrain = region->GetTerrain();
+
3797 
+
3798  if (terrain)
+
3799  pThis->altitude_agl = (float) (loc.y - terrain->Height(loc.x, loc.z));
+
3800 
+
3801  else
+
3802  pThis->altitude_agl = (float) loc.y;
+
3803 
+
3804  if (!_finite(altitude_agl)) {
+
3805  pThis->altitude_agl = 0.0f;
+
3806  }
+
3807  }
+
3808 
+
3809  return altitude_agl;
+
3810 }
+
3811 
+
3812 double
+ +
3814 {
+
3815  return g_force;
+
3816 }
+
3817 
+
3818 // +--------------------------------------------------------------------+
+
3819 
+
3820 WeaponGroup*
+
3821 Ship::FindWeaponGroup(const char* name)
+
3822 {
+
3823  WeaponGroup* group = 0;
+
3824 
+ +
3826  while (!group && ++iter)
+
3827  if (!_stricmp(iter->Name(), name))
+
3828  group = iter.value();
+
3829 
+
3830  if (!group) {
+
3831  group = new(__FILE__,__LINE__) WeaponGroup(name);
+
3832  weapons.append(group);
+
3833  }
+
3834 
+
3835  return group;
+
3836 }
+
3837 
+
3838 void
+
3839 Ship::SelectWeapon(int n, int w)
+
3840 {
+
3841  if (n < weapons.size())
+
3842  weapons[n]->SelectWeapon(w);
+
3843 }
+
3844 
+
3845 // +--------------------------------------------------------------------+
+
3846 
+
3847 void
+ +
3849 {
+
3850  if (weapons.isEmpty())
+
3851  return;
+
3852 
+
3853  if (IsDropship() && primary < weapons.size()) {
+
3854  WeaponGroup* p = weapons[primary];
+
3855  Weapon* w = p->GetSelected();
+
3856 
+
3857  if (w && w->GetTurret()) {
+ +
3859  }
+
3860  }
+
3861 
+
3862  int n = primary + 1;
+
3863  while (n != primary) {
+
3864  if (n >= weapons.size())
+
3865  n = 0;
+
3866 
+
3867  if (weapons[n]->IsPrimary()) {
+
3868  weapons[n]->SetFiringOrders(Weapon::MANUAL);
+
3869  break;
+
3870  }
+
3871 
+
3872  n++;
+
3873  }
+
3874 
+
3875  primary = n;
+
3876 }
+
3877 
+
3878 // +--------------------------------------------------------------------+
+
3879 
+
3880 void
+ +
3882 {
+
3883  if (weapons.isEmpty())
+
3884  return;
+
3885 
+
3886  int n = secondary + 1;
+
3887  while (n != secondary) {
+
3888  if (n >= weapons.size())
+
3889  n = 0;
+
3890 
+
3891  if (weapons[n]->IsMissile())
+
3892  break;
+
3893 
+
3894  n++;
+
3895  }
+
3896 
+
3897  secondary = n;
+
3898 
+
3899  // automatically switch sensors to appropriate mode:
+
3900  if (IsAirborne()) {
+
3901  Weapon* missile = GetSecondary();
+
3902  if (missile && missile->CanTarget(Ship::GROUND_UNITS))
+ +
3904  else if (sensor && sensor->GetMode() == Sensor::GM)
+ +
3906  }
+
3907 }
+
3908 
+
3909 int
+
3910 Ship::GetMissileEta(int index) const
+
3911 {
+
3912  if (index >= 0 && index < 4)
+
3913  return missile_eta[index];
+
3914 
+
3915  return 0;
+
3916 }
+
3917 
+
3918 void
+
3919 Ship::SetMissileEta(int id, int eta)
+
3920 {
+
3921  int index = -1;
+
3922 
+
3923  // are we tracking this missile's eta?
+
3924  for (int i = 0; i < 4; i++)
+
3925  if (id == missile_id[i])
+
3926  index = i;
+
3927 
+
3928  // if not, can we find an open slot to track it in?
+
3929  if (index < 0) {
+
3930  for (int i = 0; i < 4 && index < 0; i++) {
+
3931  if (missile_eta[i] == 0) {
+
3932  index = i;
+
3933  missile_id[i] = id;
+
3934  }
+
3935  }
+
3936  }
+
3937 
+
3938  // track the eta:
+
3939  if (index >= 0 && index < 4) {
+
3940  if (eta > 3599)
+
3941  eta = 3599;
+
3942 
+
3943  missile_eta[index] = (BYTE) eta;
+
3944  }
+
3945 }
+
3946 
+
3947 // +--------------------------------------------------------------------+
+
3948 
+
3949 void
+ +
3951 {
+
3952  ListIter<System> iter = systems;
+
3953  while (++iter) {
+
3954  System* s = iter.value();
+
3955  s->DoEMCON(emcon);
+
3956  }
+
3957 
+
3958  old_emcon = emcon;
+
3959 }
+
3960 
+
3961 // +--------------------------------------------------------------------+
+
3962 
+
3963 double
+
3964 Ship::Thrust(double seconds) const
+
3965 {
+
3966  double total_thrust = 0;
+
3967 
+
3968  if (main_drive) {
+
3969  // velocity limiter:
+
3970  Point H = Heading();
+
3971  Point V = Velocity();
+
3972  double vmag = V.Normalize();
+
3973  double eff_throttle = throttle;
+
3974  double thrust_factor = 1;
+
3975  double vfwd = H * V;
+
3976  bool aug_on = main_drive->IsAugmenterOn();
+
3977 
+
3978  if (vmag > vlimit && vfwd > 0) {
+
3979  double vmax = vlimit;
+
3980  if (aug_on)
+
3981  vmax *= 1.5;
+
3982 
+
3983  vfwd = 0.5 * vfwd + 0.5;
+
3984 
+
3985  // reduce drive efficiency at high fwd speed:
+
3986  thrust_factor = (vfwd * pow(vmax,3) / pow(vmag,3)) + (1-vfwd);
+
3987  }
+
3988 
+
3989  if (flcs)
+
3990  eff_throttle = flcs->Throttle();
+
3991 
+
3992  // square-law throttle curve to increase sensitivity
+
3993  // at lower throttle settings:
+
3994  if (flight_model > 1) {
+
3995  eff_throttle /= 100;
+
3996  eff_throttle *= eff_throttle;
+
3997  eff_throttle *= 100;
+
3998  }
+
3999 
+
4000  main_drive->SetThrottle(eff_throttle, augmenter);
+
4001  total_thrust += thrust_factor * main_drive->Thrust(seconds);
+
4002 
+
4003  if (aug_on && shake < 1.5)
+
4004  ((Ship*) this)->shake = 1.5f;
+
4005  }
+
4006 
+
4007  return total_thrust;
+
4008 }
+
4009 
+
4010 // +--------------------------------------------------------------------+
+
4011 
+
4012 void
+ +
4014 {
+
4015  switch (flcs_mode) {
+
4016  case FLCS_MANUAL: SetFLCSMode(FLCS_HELM); break;
+
4017  case FLCS_AUTO: SetFLCSMode(FLCS_MANUAL); break;
+
4018  case FLCS_HELM: SetFLCSMode(FLCS_AUTO); break;
+
4019 
+
4020  default:
+
4021  if (IsStarship())
+
4022  flcs_mode = (BYTE) FLCS_HELM;
+
4023  else
+
4024  flcs_mode = (BYTE) FLCS_AUTO;
+
4025  break;
+
4026  }
+
4027 
+
4028  // reset helm heading to compass heading when switching
+
4029  // back to helm mode from manual mode:
+
4030 
+
4031  if (flcs_mode == FLCS_HELM) {
+
4032  if (IsStarship()) {
+ + +
4035  }
+
4036  else {
+
4037  flcs_mode = (BYTE) FLCS_AUTO;
+
4038  }
+
4039  }
+
4040 }
+
4041 
+
4042 void
+ +
4044 {
+
4045  flcs_mode = (BYTE) mode;
+
4046 
+
4047  if (IsAirborne())
+
4048  flcs_mode = (BYTE) FLCS_MANUAL;
+
4049 
+
4050  if (dir && dir->Type() < SteerAI::SEEKER) {
+
4051  switch (flcs_mode) {
+
4052  case FLCS_MANUAL: director_info = Game::GetText("flcs.manual"); break;
+
4053  case FLCS_AUTO: director_info = Game::GetText("flcs.auto"); break;
+
4054  case FLCS_HELM: director_info = Game::GetText("flcs.helm"); break;
+
4055  default: director_info = Game::GetText("flcs.fault"); break;
+
4056  }
+
4057 
+
4058  if (!flcs || !flcs->IsPowerOn())
+
4059  director_info = Game::GetText("flcs.offline");
+
4060 
+
4061  else if (IsAirborne())
+
4062  director_info = Game::GetText("flcs.atmospheric");
+
4063  }
+
4064 
+
4065  if (flcs)
+
4066  flcs->SetMode(mode);
+
4067 }
+
4068 
+
4069 int
+ +
4071 {
+
4072  return (int) flcs_mode;
+
4073 }
+
4074 
+
4075 void
+ +
4077 {
+
4078  float limit = design->trans_x;
+
4079 
+
4080  if (thruster)
+
4081  limit = (float) thruster->TransXLimit();
+
4082 
+
4083  trans_x = (float) t;
+
4084 
+
4085  if (trans_x) {
+
4086  if (trans_x > limit)
+
4087  trans_x = limit;
+
4088  else if (trans_x < -limit)
+
4089  trans_x = -limit;
+
4090 
+
4091  // reduce thruster efficiency at high fwd speed:
+
4092  double vfwd = cam.vrt() * Velocity();
+
4093  double vmag = fabs(vfwd);
+
4094  if (vmag > vlimit) {
+
4095  if (trans_x > 0 && vfwd > 0 || trans_x < 0 && vfwd < 0)
+
4096  trans_x *= (float) (pow(vlimit,4) / pow(vmag,4));
+
4097  }
+
4098  }
+
4099 }
+
4100 
+
4101 void
+ +
4103 {
+
4104  float limit = design->trans_y;
+
4105 
+
4106  if (thruster)
+
4107  limit = (float) thruster->TransYLimit();
+
4108 
+
4109  trans_y = (float) t;
+
4110 
+
4111  if (trans_y) {
+
4112  double vmag = Velocity().length();
+
4113 
+
4114  if (trans_y > limit)
+
4115  trans_y = limit;
+
4116  else if (trans_y < -limit)
+
4117  trans_y = -limit;
+
4118 
+
4119  // reduce thruster efficiency at high fwd speed:
+
4120  if (vmag > vlimit) {
+
4121  double vfwd = cam.vpn() * Velocity();
+
4122 
+
4123  if (trans_y > 0 && vfwd > 0 || trans_y < 0 && vfwd < 0)
+
4124  trans_y *= (float) (pow(vlimit,4) / pow(vmag,4));
+
4125  }
+
4126  }
+
4127 }
+
4128 
+
4129 void
+ +
4131 {
+
4132  float limit = design->trans_z;
+
4133 
+
4134  if (thruster)
+
4135  limit = (float) thruster->TransZLimit();
+
4136 
+
4137  trans_z = (float) t;
+
4138 
+
4139  if (trans_z) {
+
4140 
+
4141  if (trans_z > limit)
+
4142  trans_z = limit;
+
4143  else if (trans_z < -limit)
+
4144  trans_z = -limit;
+
4145 
+
4146  // reduce thruster efficiency at high fwd speed:
+
4147  double vfwd = cam.vup() * Velocity();
+
4148  double vmag = fabs(vfwd);
+
4149  if (vmag > vlimit) {
+
4150  if (trans_z > 0 && vfwd > 0 || trans_z < 0 && vfwd < 0)
+
4151  trans_z *= (float) (pow(vlimit,4) / pow(vmag,4));
+
4152  }
+
4153  }
+
4154 }
+
4155 
+
4156 void
+ +
4158 {
+
4159  if (flcs)
+
4160  flcs->ExecSubFrame();
+
4161 }
+
4162 
+
4163 // +--------------------------------------------------------------------+
+
4164 
+
4165 void
+ +
4167 {
+
4168  while (h < 0)
+
4169  h += 2*PI;
+
4170 
+
4171  while (h >= 2*PI)
+
4172  h -= 2*PI;
+
4173 
+
4174  helm_heading = (float) h;
+
4175 }
+
4176 
+
4177 void
+ +
4179 {
+
4180  const double PITCH_LIMIT = 80 * DEGREES;
+
4181 
+
4182  if (p < -PITCH_LIMIT)
+
4183  p = -PITCH_LIMIT;
+
4184 
+
4185  else if (p > PITCH_LIMIT)
+
4186  p = PITCH_LIMIT;
+
4187 
+
4188  helm_pitch = (float) p;
+
4189 }
+
4190 
+
4191 void
+ +
4193 {
+
4194  // rotate compass into helm-relative orientation:
+
4195  double compass = CompassHeading() - helm_heading;
+
4196  double turn = y * PI/4;
+
4197 
+
4198  if (compass > PI)
+
4199  compass -= 2*PI;
+
4200  else if (compass < -PI)
+
4201  compass += 2*PI;
+
4202 
+
4203  // if requested turn is more than 170, reject it:
+
4204  if (fabs(compass + turn) > 170*DEGREES)
+
4205  return;
+
4206 
+
4207  SetHelmHeading(helm_heading + turn);
+
4208 }
+
4209 
+
4210 void
+ +
4212 {
+
4213  SetHelmPitch(helm_pitch - p * PI/4);
+
4214 }
+
4215 
+
4216 void
+ +
4218 {
+
4219  if (flight_model == 0) { // standard flight model
+
4220  if (IsAirborne())
+
4221  p *= 0.5;
+
4222 
+
4223  // command for pitch up is negative
+
4224  if (p < 0) {
+
4225  if (alpha > PI/6) {
+
4226  p *= 0.05;
+
4227  }
+
4228  else if (g_force > 12.0) {
+
4229  double limit = 0.5 - (g_force - 12.0)/10.0;
+
4230 
+
4231  if (limit < 0)
+
4232  p = 0;
+
4233  else
+
4234  p *= limit;
+
4235  }
+
4236  }
+
4237 
+
4238  // command for pitch down is positive
+
4239  else if (p > 0) {
+
4240  if (alpha < -PI/8) {
+
4241  p *= 0.05;
+
4242  }
+
4243  else if (g_force < -3) {
+
4244  p *= 0.1;
+
4245  }
+
4246  }
+
4247  }
+
4248 
+ +
4250 }
+
4251 
+
4252 // +--------------------------------------------------------------------+
+
4253 
+
4254 bool
+ +
4256 {
+
4257  bool fired = false;
+
4258 
+
4259  if (n >= 0 && !CheckFire()) {
+
4260  if (n < 4)
+
4261  trigger[n] = true;
+
4262 
+
4263  if (n < weapons.size()) {
+
4264  weapons[n]->SetTrigger(true);
+
4265  fired = weapons[n]->GetTrigger();
+
4266  }
+
4267  }
+
4268 
+
4269  if (!fired && sim->GetPlayerShip() == this)
+ +
4271 
+
4272  return fired;
+
4273 }
+
4274 
+
4275 bool
+ +
4277 {
+
4278  Shot* drone = 0;
+
4279 
+
4280  if (decoy && !CheckFire()) {
+
4281  drone = decoy->Fire();
+
4282 
+
4283  if (drone) {
+
4284  Observe(drone);
+
4285  decoy_list.append(drone);
+
4286  }
+
4287  }
+
4288 
+
4289  if (sim->GetPlayerShip() == this) {
+
4290  if (NetGame::IsNetGame()) {
+
4291  if (decoy && decoy->Ammo() < 1)
+ +
4293  }
+
4294 
+
4295  else if (!drone) {
+ +
4297  }
+
4298  }
+
4299 
+
4300  return drone != 0;
+
4301 }
+
4302 
+
4303 void
+ +
4305 {
+
4306  if (drone) {
+
4307  Observe(drone);
+
4308  decoy_list.append(drone);
+
4309  }
+
4310 }
+
4311 
+
4312 Weapon*
+ +
4314 {
+
4315  if (weapons.size() > primary)
+
4316  return weapons[primary]->GetSelected();
+
4317  return 0;
+
4318 }
+
4319 
+
4320 Weapon*
+ +
4322 {
+
4323  if (weapons.size() > secondary)
+
4324  return weapons[secondary]->GetSelected();
+
4325  return 0;
+
4326 }
+
4327 
+
4328 Weapon*
+ +
4330 {
+
4331  for (int i = 0; i < weapons.size(); i++) {
+
4332  WeaponGroup* g = weapons[i];
+
4333 
+
4334  List<Weapon>& wlist = g->GetWeapons();
+
4335  for (int j = 0; j < wlist.size(); j++) {
+
4336  Weapon* w = wlist[j];
+
4337 
+
4338  if (w->GetIndex() == n) {
+
4339  return w;
+
4340  }
+
4341  }
+
4342  }
+
4343 
+
4344  return 0;
+
4345 }
+
4346 
+
4347 WeaponGroup*
+ +
4349 {
+
4350  if (weapons.size() > primary)
+
4351  return weapons[primary];
+
4352  return 0;
+
4353 }
+
4354 
+
4355 WeaponGroup*
+ +
4357 {
+
4358  if (weapons.size() > secondary)
+
4359  return weapons[secondary];
+
4360  return 0;
+
4361 }
+
4362 
+
4363 WeaponDesign*
+ +
4365 {
+
4366  if (weapons.size() > primary)
+
4367  return (WeaponDesign*) weapons[primary]->GetSelected()->Design();
+
4368  return 0;
+
4369 }
+
4370 
+
4371 WeaponDesign*
+ +
4373 {
+
4374  if (weapons.size() > secondary)
+
4375  return (WeaponDesign*) weapons[secondary]->GetSelected()->Design();
+
4376  return 0;
+
4377 }
+
4378 
+
4379 Weapon*
+ +
4381 {
+
4382  return decoy;
+
4383 }
+
4384 
+
4385 List<Shot>&
+ +
4387 {
+
4388  return decoy_list;
+
4389 }
+
4390 
+
4391 List<Shot>&
+ +
4393 {
+
4394  return threat_list;
+
4395 }
+
4396 
+
4397 void
+ +
4399 {
+
4400  if (!threat_list.contains(s)) {
+
4401  Observe(s);
+
4402  threat_list.append(s);
+
4403  }
+
4404 }
+
4405 
+
4406 void
+ +
4408 {
+
4409  if (threat_list.contains(s)) {
+
4410  threat_list.remove(s);
+
4411  }
+
4412 }
+
4413 
+
4414 bool
+
4415 Ship::GetTrigger(int i) const
+
4416 {
+
4417  if (i >= 0) {
+
4418  if (i < 4)
+
4419  return trigger[i];
+
4420 
+
4421  else if (i < weapons.size())
+
4422  return weapons[i]->GetTrigger();
+
4423  }
+
4424 
+
4425  return false;
+
4426 }
+
4427 
+
4428 void
+ +
4430 {
+
4431  if (i >= 0 && !CheckFire()) {
+
4432  if (i < 4)
+
4433  trigger[i] = true;
+
4434 
+
4435  if (i < weapons.size())
+
4436  weapons[i]->SetTrigger();
+
4437  }
+
4438 }
+
4439 
+
4440 // +--------------------------------------------------------------------+
+
4441 
+
4442 void
+ +
4444 {
+
4445  if (sensor)
+
4446  sensor->SetMode((Sensor::Mode) mode);
+
4447 }
+
4448 
+
4449 int
+ +
4451 {
+
4452  if (sensor)
+
4453  return (int) sensor->GetMode();
+
4454 
+
4455  return 0;
+
4456 }
+
4457 
+
4458 // +--------------------------------------------------------------------+
+
4459 
+
4460 bool
+ +
4462 {
+
4463  if (tgt && sensor)
+
4464  return sensor->IsTracking(tgt);
+
4465 
+
4466  return false;
+
4467 }
+
4468 
+
4469 // +--------------------------------------------------------------------+
+
4470 
+
4471 void
+
4472 Ship::LockTarget(int type, bool closest, bool hostile)
+
4473 {
+
4474  if (sensor)
+
4475  SetTarget(sensor->LockTarget(type, closest, hostile));
+
4476 }
+
4477 
+
4478 // +--------------------------------------------------------------------+
+
4479 
+
4480 void
+ +
4482 {
+
4483  if (sensor)
+
4484  SetTarget(sensor->LockTarget(candidate));
+
4485  else
+
4486  SetTarget(candidate);
+
4487 }
+
4488 
+
4489 // +--------------------------------------------------------------------+
+
4490 
+
4491 double
+
4492 Ship::InflictDamage(double damage, Shot* shot, int hit_type, Point impact)
+
4493 {
+
4494  double damage_applied = 0;
+
4495 
+
4496  if (Game::Paused() || IsNetObserver() || IsInvulnerable())
+
4497  return damage_applied;
+
4498 
+
4499  if (Integrity() == 0) // already dead?
+
4500  return damage_applied;
+
4501 
+
4502  const double MAX_SHAKE = 7;
+
4503  double hull_damage = damage;
+
4504  bool hit_shield = (hit_type & HIT_SHIELD) != 0;
+
4505  bool hit_hull = (hit_type & HIT_HULL) != 0;
+
4506  bool hit_turret = (hit_type & HIT_TURRET) != 0;
+
4507 
+
4508  if (impact == Point(0,0,0))
+
4509  impact = Location();
+
4510 
+
4511  if (hit_shield && ShieldStrength() > 0) {
+
4512  hull_damage = shield->DeflectDamage(shot, damage);
+
4513 
+
4514  if (shot) {
+
4515  if (shot->IsBeam()) {
+ +
4517  if (Game::RealTime() - last_beam_time > 400) {
+ +
4519  s->SetLocation(impact);
+ +
4521  s->Play();
+
4522 
+ +
4524  }
+
4525  }
+
4526  }
+
4527 
+
4528  else {
+ +
4530  if (Game::RealTime() - last_bolt_time > 400) {
+ +
4532  s->SetLocation(impact);
+ +
4534  s->Play();
+
4535 
+ +
4537  }
+
4538  }
+
4539  }
+
4540  }
+
4541  }
+
4542 
+
4543  if (hit_hull) {
+
4544  hull_damage = InflictSystemDamage(hull_damage, shot, impact);
+
4545 
+
4546  int damage_type = WeaponDesign::DMG_NORMAL;
+
4547 
+
4548  if (shot && shot->Design())
+
4549  damage_type = shot->Design()->damage_type;
+
4550 
+
4551  if (damage_type == WeaponDesign::DMG_NORMAL) {
+
4552  damage_applied = hull_damage;
+
4553  Physical::InflictDamage(damage_applied, 0);
+
4554  NetUtil::SendObjDamage(this, damage_applied, shot);
+
4555  }
+
4556  }
+
4557 
+
4558  else if (hit_turret) {
+
4559  hull_damage = InflictSystemDamage(hull_damage, shot, impact) * 0.3;
+
4560 
+
4561  int damage_type = WeaponDesign::DMG_NORMAL;
+
4562 
+
4563  if (shot && shot->Design())
+
4564  damage_type = shot->Design()->damage_type;
+
4565 
+
4566  if (damage_type == WeaponDesign::DMG_NORMAL) {
+
4567  damage_applied = hull_damage;
+
4568  Physical::InflictDamage(damage_applied, 0);
+
4569  NetUtil::SendObjDamage(this, damage_applied, shot);
+
4570  }
+
4571  }
+
4572 
+
4573  // shake by percentage of maximum damage
+
4574  double newshake = 50 * damage/design->integrity;
+
4575 
+
4576  if (shake < MAX_SHAKE) shake += (float) newshake;
+
4577  if (shake > MAX_SHAKE) shake = (float) MAX_SHAKE;
+
4578 
+
4579  // start fires as needed:
+
4580  if ((IsStarship() || IsGroundUnit() || RandomChance(1,3)) && hit_hull && damage_applied > 0) {
+
4581  int old_integrity = (int) ((integrity + damage_applied)/design->integrity * 10);
+
4582  int new_integrity = (int) ((integrity )/design->integrity * 10);
+
4583 
+
4584  if (new_integrity < 5 && new_integrity < old_integrity) {
+
4585  // need accurate hull impact for starships,
+
4586  if (rep) {
+
4587  Point detonation = impact*2 - Location();
+
4588  Point direction = Location() - detonation;
+
4589  double distance = direction.Normalize() * 3;
+
4590  rep->CheckRayIntersection(detonation, direction, distance, impact);
+
4591 
+
4592  // pull fire back into hull a bit:
+
4593  direction = Location() - impact;
+
4594  impact += direction * 0.2;
+
4595 
+
4596  float scale = (float) design->scale;
+
4597 
+
4598  if (IsDropship())
+
4599  sim->CreateExplosion(impact, Velocity(), Explosion::SMOKE_TRAIL, 0.01f * scale, 0.5f * scale, region, this);
+
4600  else
+
4601  sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FIRE, 0.10f * scale, scale, region, this);
+
4602  }
+
4603  }
+
4604  }
+
4605 
+
4606  return damage_applied;
+
4607 }
+
4608 
+
4609 double
+
4610 Ship::InflictSystemDamage(double damage, Shot* shot, Point impact)
+
4611 {
+
4612  if (IsNetObserver())
+
4613  return 0;
+
4614 
+
4615  // find the system that is closest to the impact point:
+
4616  System* system = 0;
+
4617  double distance = 1e6;
+
4618  double blast_radius = 0;
+
4619  int dmg_type = 0;
+
4620 
+
4621  if (shot)
+
4622  dmg_type = shot->Design()->damage_type;
+
4623 
+
4624  bool dmg_normal = dmg_type == WeaponDesign::DMG_NORMAL;
+
4625  bool dmg_power = dmg_type == WeaponDesign::DMG_POWER;
+
4626  bool dmg_emp = dmg_type == WeaponDesign::DMG_EMP;
+
4627  double to_level = 0;
+
4628 
+
4629  if (dmg_power) {
+
4630  to_level = 1 - damage / 1e4;
+
4631 
+
4632  if (to_level < 0)
+
4633  to_level = 0;
+
4634  }
+
4635 
+
4636  // damage caused by weapons applies to closest system:
+
4637  if (shot) {
+
4638  if (shot->IsMissile())
+
4639  blast_radius = 300;
+
4640 
+
4641  ListIter<System> iter = systems;
+
4642  while (++iter) {
+
4643  System* candidate = iter.value();
+
4644  double sysrad = candidate->Radius();
+
4645 
+
4646  if (dmg_power)
+
4647  candidate->DrainPower(to_level);
+
4648 
+
4649  if (sysrad > 0 || dmg_emp && candidate->IsPowerCritical()) {
+
4650  double test_distance = (impact - candidate->MountLocation()).length();
+
4651 
+
4652  if ((test_distance-blast_radius) < sysrad || dmg_emp && candidate->IsPowerCritical()) {
+
4653  if (test_distance < distance) {
+
4654  system = candidate;
+
4655  distance = test_distance;
+
4656  }
+
4657  }
+
4658  }
+
4659  }
+
4660 
+
4661  // if a system was in range of the blast, assess the damage:
+
4662  if (system) {
+
4663  double hull_damage = damage * system->HullProtection();
+
4664  double sys_damage = damage - hull_damage;
+
4665  double avail = system->Availability();
+
4666 
+
4667  if (dmg_normal || system->IsPowerCritical() && dmg_emp) {
+
4668  system->ApplyDamage(sys_damage);
+
4669  NetUtil::SendSysDamage(this, system, sys_damage);
+
4670 
+
4671  master_caution = true;
+
4672 
+
4673  if (dmg_normal) {
+
4674  if (sys_damage < 100)
+
4675  damage -= sys_damage;
+
4676  else
+
4677  damage -= 100;
+
4678  }
+
4679 
+
4680  if (system->GetExplosionType() && (avail - system->Availability()) >= 50) {
+
4681  float scale = design->explosion_scale;
+
4682  if (scale <= 0)
+
4683  scale = design->scale;
+
4684 
+
4685  sim->CreateExplosion(system->MountLocation(), Velocity() * 0.7f, system->GetExplosionType(), 0.2f * scale, scale, region, this, system);
+
4686  }
+
4687  }
+
4688  }
+
4689  }
+
4690 
+
4691  // damage caused by collision applies to all systems:
+
4692  else {
+
4693  // ignore incidental bumps:
+
4694  if (damage < 100)
+
4695  return damage;
+
4696 
+
4697  ListIter<System> iter = systems;
+
4698  while (++iter) {
+
4699  System* sys = iter.value();
+
4700 
+
4701  if (rand() > 24000) {
+
4702  double base_damage = 33.0 + rand()/1000.0;
+
4703  double sys_damage = base_damage * (1.0 - sys->HullProtection());
+
4704  sys->ApplyDamage(sys_damage);
+
4705  NetUtil::SendSysDamage(this, system, sys_damage);
+
4706  damage -= sys_damage;
+
4707 
+
4708  master_caution = true;
+
4709  }
+
4710  }
+
4711 
+
4712  // just in case this ship has lots of systems...
+
4713  if (damage < 0)
+
4714  damage = 0;
+
4715  }
+
4716 
+
4717  // return damage remaining
+
4718  return damage;
+
4719 }
+
4720 
+
4721 // +--------------------------------------------------------------------+
+
4722 
+
4723 int
+ +
4725 {
+
4726  if (!shield) return 0;
+
4727 
+
4728  return (int) shield->ShieldLevel();
+
4729 }
+
4730 
+
4731 int
+ +
4733 {
+
4734  if (design)
+
4735  return (int) (Integrity() / design->integrity * 100);
+
4736 
+
4737  return 10;
+
4738 }
+
4739 
+
4740 // +--------------------------------------------------------------------+
+
4741 
+
4742 System*
+
4743 Ship::GetSystem(int sys_id)
+
4744 {
+
4745  System* s = 0;
+
4746 
+
4747  if (sys_id >= 0) {
+
4748  if (sys_id < systems.size()) {
+
4749  s = systems[sys_id];
+
4750  if (s->GetID() == sys_id)
+
4751  return s;
+
4752  }
+
4753 
+
4754  ListIter<System> iter = systems;
+
4755  while (++iter) {
+
4756  s = iter.value();
+
4757 
+
4758  if (s->GetID() == sys_id)
+
4759  return s;
+
4760  }
+
4761  }
+
4762 
+
4763  return 0;
+
4764 }
+
4765 
+
4766 // +--------------------------------------------------------------------+
+
4767 
+
4768 void
+ +
4770 {
+
4771  if (!repair_queue.contains(sys)) {
+
4772  repair_queue.append(sys);
+
4773  sys->Repair();
+
4774  }
+
4775 }
+
4776 
+
4777 // +--------------------------------------------------------------------+
+
4778 
+
4779 void
+ +
4781 {
+
4782  if (task_index > 0 && task_index < repair_queue.size()) {
+
4783  System* task1 = repair_queue.at(task_index-1);
+
4784  System* task2 = repair_queue.at(task_index);
+
4785 
+
4786  repair_queue.at(task_index-1) = task2;
+
4787  repair_queue.at(task_index) = task1;
+
4788  }
+
4789 }
+
4790 
+
4791 void
+ +
4793 {
+
4794  if (task_index >= 0 && task_index < repair_queue.size()-1) {
+
4795  System* task1 = repair_queue.at(task_index);
+
4796  System* task2 = repair_queue.at(task_index+1);
+
4797 
+
4798  repair_queue.at(task_index) = task2;
+
4799  repair_queue.at(task_index+1) = task1;
+
4800  }
+
4801 }
+
4802 
+
4803 // +--------------------------------------------------------------------+
+
4804 
+
4805 void
+
4806 Ship::ExecMaintFrame(double seconds)
+
4807 {
+
4808  // is it already too late?
+
4809  if (life == 0 || integrity < 1) return;
+
4810 
+
4811  const DWORD REPAIR_FREQUENCY = 5000; // once every five seconds
+
4812  static DWORD last_repair_frame = 0; // one ship per game frame
+
4813 
+
4814  if (auto_repair &&
+
4815  Game::GameTime() - last_repair_time > REPAIR_FREQUENCY &&
+
4816  last_repair_frame != Game::Frame()) {
+
4817 
+ +
4819  last_repair_frame = Game::Frame();
+
4820 
+
4821  ListIter<System> iter = systems;
+
4822  while (++iter) {
+
4823  System* sys = iter.value();
+
4824 
+
4825  if (sys->Status() != System::NOMINAL) {
+
4826  bool started_repairs = false;
+
4827 
+
4828  // emergency power routing:
+
4829  if (sys->Type() == System::POWER_SOURCE && sys->Availability() < 33) {
+
4830  PowerSource* src = (PowerSource*) sys;
+
4831  PowerSource* dst = 0;
+
4832 
+
4833  for (int i = 0; i < reactors.size(); i++) {
+
4834  PowerSource* pwr = reactors[i];
+
4835 
+
4836  if (pwr != src && pwr->Availability() > src->Availability()) {
+
4837  if (!dst ||
+
4838  (pwr->Availability() > dst->Availability() &&
+
4839  pwr->Charge() > dst->Charge()))
+
4840  dst = pwr;
+
4841  }
+
4842  }
+
4843 
+
4844  if (dst) {
+
4845  while (src->Clients().size() > 0) {
+
4846  System* s = src->Clients().at(0);
+
4847  src->RemoveClient(s);
+
4848  dst->AddClient(s);
+
4849  }
+
4850  }
+
4851  }
+
4852 
+
4853  ListIter<Component> comp = sys->GetComponents();
+
4854  while (++comp) {
+
4855  Component* c = comp.value();
+
4856 
+
4857  if (c->Status() < Component::NOMINAL && c->Availability() < 75) {
+
4858  if (c->SpareCount() &&
+
4859  c->ReplaceTime() <= 300 &&
+
4860  (c->Availability() < 50 ||
+
4861  c->ReplaceTime() < c->RepairTime())) {
+
4862 
+
4863  c->Replace();
+
4864  started_repairs = true;
+
4865  }
+
4866 
+
4867  else if (c->Availability() >= 50 || c->NumJerried() < 5) {
+
4868  c->Repair();
+
4869  started_repairs = true;
+
4870  }
+
4871  }
+
4872  }
+
4873 
+
4874  if (started_repairs)
+
4875  RepairSystem(sys);
+
4876  }
+
4877  }
+
4878  }
+
4879 
+
4880  if (repair_queue.size() > 0 && RepairTeams() > 0) {
+
4881  int team = 0;
+ +
4883  while (++iter && team < RepairTeams()) {
+
4884  System* sys = iter.value();
+
4885 
+
4886  sys->ExecMaintFrame(seconds * RepairSpeed());
+
4887  team++;
+
4888 
+
4889  if (sys->Status() != System::MAINT) {
+
4890  iter.removeItem();
+
4891 
+
4892  // emergency power routing (restore):
+
4893  if (sys->Type() == System::POWER_SOURCE &&
+
4894  sys->Status() == System::NOMINAL) {
+
4895  PowerSource* src = (PowerSource*) sys;
+
4896  int isrc = reactors.index(src);
+
4897 
+
4898  for (int i = 0; i < reactors.size(); i++) {
+
4899  PowerSource* pwr = reactors[i];
+
4900 
+
4901  if (pwr != src) {
+
4902  List<System> xfer;
+
4903 
+
4904  for (int j = 0; j < pwr->Clients().size(); j++) {
+
4905  System* s = pwr->Clients().at(j);
+
4906 
+
4907  if (s->GetSourceIndex() == isrc) {
+
4908  xfer.append(s);
+
4909  }
+
4910  }
+
4911 
+
4912  for (int j = 0; j < xfer.size(); j++) {
+
4913  System* s = xfer.at(j);
+
4914  pwr->RemoveClient(s);
+
4915  src->AddClient(s);
+
4916  }
+
4917  }
+
4918  }
+
4919  }
+
4920  }
+
4921  }
+
4922  }
+
4923 }
+
4924 
+
4925 // +--------------------------------------------------------------------+
+
4926 
+
4927 void
+ +
4929 {
+
4930  net_control = net;
+
4931 
+
4932  delete dir;
+
4933  dir = 0;
+
4934 
+
4935  if (!net_control && GetIFF() < 100) {
+
4936  if (IsStatic())
+
4937  dir = 0;
+
4938  else if (IsStarship())
+ +
4940  else
+ +
4942  }
+
4943 }
+
4944 
+
4945 void
+ +
4947 {
+
4948  if (IsDropping() || IsAttaining()) {
+
4949  if (dir && dir->Type() != DropShipAI::DIR_TYPE) {
+
4950  delete dir;
+
4951  dir = new(__FILE__,__LINE__) DropShipAI(this);
+
4952  }
+
4953 
+
4954  return;
+
4955  }
+
4956 
+
4957  else if (IsSkipping()) {
+
4958  if (navsys && sim->GetPlayerShip() == this)
+
4959  navsys->EngageAutoNav();
+
4960  }
+
4961 
+
4962  else if (IsDying() || IsDead()) {
+
4963  if (dir) {
+
4964  delete dir;
+
4965  dir = 0;
+
4966  }
+
4967 
+
4968  if (navsys && navsys->AutoNavEngaged()) {
+ +
4970  }
+
4971 
+
4972  return;
+
4973  }
+
4974 
+
4975  else if (life == 0) {
+
4976  if (dir || navsys) {
+
4977  ::Print("Warning: dying ship '%' still has not been destroyed!\n", name);
+
4978  delete dir;
+
4979  dir = 0;
+
4980 
+
4981  if (navsys && navsys->AutoNavEngaged())
+ +
4983  }
+
4984 
+
4985  return;
+
4986  }
+
4987 
+
4988  if (navsys && navsys->AutoNavEngaged()) {
+
4989  NavAI* nav = 0;
+
4990 
+
4991  if (dir) {
+
4992  if (dir->Type() != NavAI::DIR_TYPE) {
+
4993  delete dir;
+
4994  dir = 0;
+
4995  }
+
4996  else {
+
4997  nav = (NavAI*) dir;
+
4998  }
+
4999  }
+
5000 
+
5001  if (!nav) {
+
5002  nav = new(__FILE__,__LINE__) NavAI(this);
+
5003  dir = nav;
+
5004  return;
+
5005  }
+
5006 
+
5007  else if (!nav->Complete()) {
+
5008  return;
+
5009  }
+
5010  }
+
5011 
+
5012  if (dir) {
+
5013  delete dir;
+
5014  dir = 0;
+
5015  }
+
5016 
+
5017  if (m) {
+ +
5019  m->Acquire();
+
5020  dir = new(__FILE__,__LINE__) ShipCtrl(this, m);
+
5021  director_info = Game::GetText("flcs.auto");
+
5022  }
+
5023  else if (GetIFF() < 100) {
+
5024  if (IsStatic())
+ +
5026 
+
5027  else if (IsStarship() && !IsAirborne())
+ +
5029 
+
5030  else
+ +
5032  }
+
5033 }
+
5034 
+
5035 // +--------------------------------------------------------------------+
+
5036 
+
5037 Color
+ +
5039 {
+
5040  Color c;
+
5041 
+
5042  switch (iff) {
+
5043  case 0: // NEUTRAL, NON-COMBAT
+
5044  c = Color(192,192,192);
+
5045  break;
+
5046 
+
5047  case 1: // TERELLIAN ALLIANCE
+
5048  c = Color(70,70,220);
+
5049  break;
+
5050 
+
5051  case 2: // MARAKAN HEGEMONY
+
5052  c = Color(220,20,20);
+
5053  break;
+
5054 
+
5055  case 3: // BROTHERHOOD OF IRON
+
5056  c = Color(200,180,20);
+
5057  break;
+
5058 
+
5059  case 4: // ZOLON EMPIRE
+
5060  c = Color(20,200,20);
+
5061  break;
+
5062 
+
5063  case 5:
+
5064  c = Color(128, 0, 128);
+
5065  break;
+
5066 
+
5067  case 6:
+
5068  c = Color(40,192,192);
+
5069  break;
+
5070 
+
5071  default:
+
5072  c = Color(128,128,128);
+
5073  break;
+
5074  }
+
5075 
+
5076  return c;
+
5077 }
+
5078 
+
5079 Color
+ +
5081 {
+
5082  return IFFColor(IFF_code);
+
5083 }
+
5084 
+
5085 // +--------------------------------------------------------------------+
+
5086 
+
5087 void
+ +
5089 {
+
5090  IFF_code = iff;
+
5091 
+
5092  if (hangar)
+
5093  hangar->SetAllIFF(iff);
+
5094 
+
5095  DropTarget();
+
5096 
+
5097  if (dir && dir->Type() >= 1000) {
+
5098  SteerAI* ai = (SteerAI*) dir;
+
5099  ai->DropTarget();
+
5100  }
+
5101 }
+
5102 
+
5103 // +--------------------------------------------------------------------+
+
5104 
+
5105 void
+ +
5107 {
+
5108  bool rogue = IsRogue();
+
5109 
+
5110  ff_count = r ? 1000 : 0;
+
5111 
+
5112  if (!rogue && IsRogue()) {
+
5113  Print("Ship '%s' has been made rogue\n", Name());
+
5114  }
+
5115  else if (rogue && !IsRogue()) {
+
5116  Print("Ship '%s' is no longer rogue\n", Name());
+
5117  }
+
5118 }
+
5119 
+
5120 void
+ +
5122 {
+
5123  bool rogue = IsRogue();
+
5124 
+
5125  ff_count = f;
+
5126 
+
5127  if (!rogue && IsRogue()) {
+
5128  Print("Ship '%s' has been made rogue with ff_count = %d\n", Name(), ff_count);
+
5129  }
+
5130  else if (rogue && !IsRogue()) {
+
5131  Print("Ship '%s' is no longer rogue\n", Name());
+
5132  }
+
5133 }
+
5134 
+
5135 void
+ +
5137 {
+
5138  if (f > 0) {
+
5139  bool rogue = IsRogue();
+
5140 
+
5141  ff_count += f;
+
5142 
+
5143  if (!rogue && IsRogue()) {
+
5144  Print("Ship '%s' has been made rogue with ff_count = %d\n", Name(), ff_count);
+
5145  }
+
5146  }
+
5147 }
+
5148 
+
5149 // +--------------------------------------------------------------------+
+
5150 
+
5151 void
+
5152 Ship::SetEMCON(int e, bool from_net)
+
5153 {
+
5154  if (e < 1) emcon = 1;
+
5155  else if (e > 3) emcon = 3;
+
5156  else emcon = (BYTE) e;
+
5157 
+
5158  if (emcon != old_emcon && !from_net && NetGame::GetInstance())
+
5159  NetUtil::SendObjEmcon(this);
+
5160 }
+
5161 
+
5162 double
+
5163 Ship::PCS() const
+
5164 {
+
5165  double e_factor = design->e_factor[emcon-1];
+
5166 
+
5167  if (IsAirborne() && !IsGroundUnit()) {
+
5168  if (AltitudeAGL() < 40)
+
5169  return 0;
+
5170 
+
5171  if (AltitudeAGL() < 200) {
+
5172  double clutter = AltitudeAGL() / 200;
+
5173  return clutter * e_factor;
+
5174  }
+
5175  }
+
5176 
+
5177  return e_factor * pcs;
+
5178 }
+
5179 
+
5180 double
+
5181 Ship::ACS() const
+
5182 {
+
5183  if (IsAirborne() && !IsGroundUnit()) {
+
5184  if (AltitudeAGL() < 40)
+
5185  return 0;
+
5186 
+
5187  if (AltitudeAGL() < 200) {
+
5188  double clutter = AltitudeAGL() / 200;
+
5189  return clutter * acs;
+
5190  }
+
5191  }
+
5192 
+
5193  return acs;
+
5194 }
+
5195 
+
5196 DWORD
+ +
5198 {
+
5199  if (launch_time > 0)
+
5200  return Game::GameTime() + 1 - launch_time;
+
5201 
+
5202  return 0;
+
5203 }
+
5204 
+
5205 // +----------------------------------------------------------------------+
+
5206 
+
5207 Instruction*
+ +
5209 {
+
5210  return radio_orders;
+
5211 }
+
5212 
+
5213 void
+ +
5215 {
+
5216  if (radio_orders) {
+
5217  radio_orders->SetAction(0);
+ + +
5220  }
+
5221 }
+
5222 
+
5223 void
+ +
5225 {
+
5226  if (!msg) return;
+
5227 
+
5228  static RadioHandler rh;
+
5229 
+
5230  if (rh.ProcessMessage(msg, this))
+
5231  rh.AcknowledgeMessage(msg, this);
+
5232 }
+
5233 
+
5234 // +----------------------------------------------------------------------+
+
5235 
+
5236 int
+ +
5238 {
+
5239  return Value(design->type);
+
5240 }
+
5241 
+
5242 // +----------------------------------------------------------------------+
+
5243 
+
5244 int
+
5245 Ship::Value(int type)
+
5246 {
+
5247  int value = 0;
+
5248 
+
5249  switch (type) {
+
5250  case DRONE: value = 10; break;
+
5251  case FIGHTER: value = 20; break;
+
5252  case ATTACK: value = 40; break;
+
5253  case LCA: value = 50; break;
+
5254 
+
5255  case COURIER: value = 100; break;
+
5256  case CARGO: value = 100; break;
+
5257  case CORVETTE: value = 100; break;
+
5258  case FREIGHTER: value = 250; break;
+
5259  case FRIGATE: value = 200; break;
+
5260  case DESTROYER: value = 500; break;
+
5261  case CRUISER: value = 800; break;
+
5262  case BATTLESHIP: value = 1000; break;
+
5263  case CARRIER: value = 1500; break;
+
5264  case DREADNAUGHT: value = 1500; break;
+
5265 
+
5266  case STATION: value = 2500; break;
+
5267  case FARCASTER: value = 5000; break;
+
5268 
+
5269  case MINE: value = 20; break;
+
5270  case COMSAT: value = 200; break;
+
5271  case DEFSAT: value = 300; break;
+
5272  case SWACS: value = 500; break;
+
5273 
+
5274  case BUILDING: value = 100; break;
+
5275  case FACTORY: value = 250; break;
+
5276  case SAM: value = 100; break;
+
5277  case EWR: value = 200; break;
+
5278  case C3I: value = 500; break;
+
5279  case STARBASE: value = 2000; break;
+
5280 
+
5281  default: value = 100; break;
+
5282  }
+
5283 
+
5284  return value;
+
5285 }
+
5286 
+
5287 // +----------------------------------------------------------------------+
+
5288 
+
5289 double
+ +
5291 {
+
5292  int i = 0;
+
5293  double value = 0;
+
5294 
+
5295  for (i = 0; i < reactors.size(); i++) {
+
5296  const PowerSource* r = reactors[i];
+
5297  value += r->Value();
+
5298  }
+
5299 
+
5300  for (i = 0; i < drives.size(); i++) {
+
5301  const Drive* d = drives[i];
+
5302  value += d->Value();
+
5303  }
+
5304 
+
5305  for (i = 0; i < weapons.size(); i++) {
+
5306  const WeaponGroup* w = weapons[i];
+
5307  value += w->Value();
+
5308  }
+
5309 
+
5310  return value;
+
5311 }
+
+
+ + + + -- cgit v1.1