Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Thruster.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: Thruster.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Weapon class
13 */
14 
15 #include "MemDebug.h"
16 #include "Thruster.h"
17 #include "Component.h"
18 #include "Drive.h"
19 #include "FlightComp.h"
20 #include "SystemDesign.h"
21 #include "Ship.h"
22 #include "ShipDesign.h"
23 #include "Sim.h"
24 #include "CameraDirector.h"
25 #include "AudioConfig.h"
26 #include "Random.h"
27 
28 #include "Light.h"
29 #include "Bitmap.h"
30 #include "Sound.h"
31 #include "DataLoader.h"
32 #include "Bolt.h"
33 #include "Sprite.h"
34 #include "Game.h"
35 
36 // +----------------------------------------------------------------------+
37 
38 static Sound* thruster_resource = 0;
39 static Sound* thruster_sound = 0;
40 
41 extern Bitmap* drive_flare_bitmap[8];
42 extern Bitmap* drive_trail_bitmap[8];
43 
44 #define CLAMP(x, a, b) if (x < (a)) x = (a); else if (x > (b)) x = (b);
45 
46 // +----------------------------------------------------------------------+
47 
48 ThrusterPort::ThrusterPort(int t, const Point& l, DWORD f, float s)
49 : type(t), loc(l), flare(0), trail(0), fire(f), burn(0), scale(s)
50 { }
51 
53 {
56 }
57 
58 // +----------------------------------------------------------------------+
59 
60 static int sys_value = 2;
61 
62 // +----------------------------------------------------------------------+
63 
64 Thruster::Thruster(int dtype, double max_thrust, float flare_scale)
65 : System(DRIVE, dtype, "Thruster", sys_value,
66 max_thrust, max_thrust, max_thrust),
67 ship(0), thrust(1.0f), scale(flare_scale),
68 avail_x(1.0f), avail_y(1.0f), avail_z(1.0f)
69 {
70  name = Game::GetText("sys.thruster");
71  abrv = Game::GetText("sys.thruster.abrv");
72 
74 
75  ZeroMemory(burn, sizeof(burn));
76 
77  emcon_power[0] = 50;
78  emcon_power[1] = 50;
79  emcon_power[2] = 100;
80 }
81 
82 // +----------------------------------------------------------------------+
83 
85 : System(t), ship(0),
86 thrust(1.0f), scale(t.scale),
87 avail_x(1.0f), avail_y(1.0f), avail_z(1.0f)
88 {
90  Mount(t);
91 
92  ZeroMemory(burn, sizeof(burn));
93 
94  if (subtype != Drive::STEALTH) {
95  for (int i = 0; i < t.ports.size(); i++) {
96  ThrusterPort* p = t.ports[i];
97  CreatePort(p->type, p->loc, p->fire, p->scale);
98  }
99  }
100 }
101 
102 // +--------------------------------------------------------------------+
103 
105 {
106  ports.destroy();
107 
108  if (thruster_sound && thruster_sound->IsPlaying()) {
109  thruster_sound->Stop();
110  }
111 }
112 
113 // +--------------------------------------------------------------------+
114 
115 void
117 {
118  static int initialized = 0;
119  if (initialized) return;
120 
121  DataLoader* loader = DataLoader::GetLoader();
122 
123  const int SOUND_FLAGS = Sound::LOCALIZED |
124  Sound::LOC_3D |
125  Sound::LOOP |
127 
128  loader->SetDataPath("Sounds/");
129  loader->LoadSound("thruster.wav", thruster_resource, SOUND_FLAGS);
130  loader->SetDataPath(0);
131 
132  if (thruster_resource)
133  thruster_resource->SetMaxDistance(15.0e3f);
134 
135  initialized = 1;
136 }
137 
138 void
140 {
141  delete thruster_resource;
142  thruster_resource = 0;
143 
144  if (thruster_sound) {
145  thruster_sound->Stop();
146  thruster_sound->Release();
147  }
148 }
149 
150 // +--------------------------------------------------------------------+
151 
152 void
154 {
155  System::Orient(rep);
156 
157  bool hide_all = false;
158  if (!ship || (ship->IsAirborne() && ship->Class() != Ship::LCA)) {
159  hide_all = true;
160  }
161 
162  if (ship->Rep() && ship->Rep()->Hidden()) {
163  hide_all = true;
164  }
165 
166  if (thrust <= 0) {
167  hide_all = true;
168  }
169 
170  const Matrix& orientation = rep->Cam().Orientation();
171  Point ship_loc = rep->Location();
172 
173  for (int i = 0; i < ports.size(); i++) {
174  ThrusterPort* p = ports[i];
175 
176  Point projector = (p->loc * orientation) + ship_loc;
177 
178  if (p->flare)
179  p->flare->MoveTo(projector);
180 
181  if (p->trail) {
182  double intensity = p->burn;
183 
184  if (intensity > 0.5 && !hide_all) {
185  Bolt* t = (Bolt*) p->trail;
186  double len = -50 * p->scale * intensity;
187 
188  t->Show();
189 
190  switch (p->type) {
191  case LEFT: t->SetEndPoints(projector, projector + rep->Cam().vrt() * len); break;
192  case RIGHT: t->SetEndPoints(projector, projector - rep->Cam().vrt() * len); break;
193  case AFT: t->SetEndPoints(projector, projector + rep->Cam().vpn() * len); break;
194  case FORE: t->SetEndPoints(projector, projector - rep->Cam().vpn() * len); break;
195  case BOTTOM: t->SetEndPoints(projector, projector + rep->Cam().vup() * len); break;
196  case TOP: t->SetEndPoints(projector, projector - rep->Cam().vup() * len); break;
197  default: t->Hide(); break;
198  }
199  }
200  else {
201  p->trail->Hide();
202  if (p->flare)
203  p->flare->Hide();
204  }
205  }
206  }
207 }
208 
209 // +--------------------------------------------------------------------+
210 
211 void
212 Thruster::ExecFrame(double seconds)
213 {
214  System::ExecFrame(seconds);
215 
216  if (ship) {
217  double rr = 0, pr = 0, yr = 0;
218  double rd = 0, pd = 0, yd = 0;
219 
220  double agility = 1;
221  double stability = 1;
222 
223  FlightComp* flcs = ship->GetFLCS();
224 
225  if (flcs) {
226  if (!flcs->IsPowerOn() || flcs->Status() < DEGRADED) {
227  agility = 0.3;
228  stability = 0.0;
229  }
230  }
231 
232  // check for thruster damage here:
233  if (components.size() >= 3) {
234  int stat = components[0]->Status();
235  if (stat == Component::NOMINAL)
236  avail_x = 1.0f;
237  else if (stat == Component::DEGRADED)
238  avail_x = 0.5f;
239  else
240  avail_x = 0.0f;
241 
242  stat = components[1]->Status();
243  if (stat == Component::NOMINAL)
244  avail_z = 1.0f;
245  else if (stat == Component::DEGRADED)
246  avail_z = 0.5f;
247  else
248  avail_z = 0.0f;
249 
250  stat = components[2]->Status();
251  if (stat == Component::NOMINAL)
252  avail_y = 1.0f;
253  else if (stat == Component::DEGRADED)
254  avail_y = 0.5f;
255  else
256  avail_y = 0.0f;
257  }
258 
259  // thrust limited by power distribution:
261  energy = 0.0f;
262 
263  if (thrust < 0)
264  thrust = 0.0f;
265 
266  agility *= thrust;
267  stability *= thrust;
268 
269  rr = roll_rate * agility * avail_y;
270  pr = pitch_rate * agility * avail_y;
271  yr = yaw_rate * agility * avail_x;
272 
273  rd = roll_drag * stability * avail_y;
274  pd = pitch_drag * stability * avail_y;
275  yd = yaw_drag * stability * avail_x;
276 
277  ship->SetAngularRates(rr,pr,yr);
278  ship->SetAngularDrag (rd,pd,yd);
279  }
280 }
281 
282 // +--------------------------------------------------------------------+
283 
284 void
286 {
287  const double ROLL_SPEED = PI * 0.0400;
288  const double PITCH_SPEED = PI * 0.0250;
289  const double YAW_SPEED = PI * 0.0250;
290 
291  ship = s;
292 
293  if (ship) {
295 
296  trans_x = design->trans_x;
297  trans_y = design->trans_y;
298  trans_z = design->trans_z;
299 
300  roll_drag = design->roll_drag;
301  pitch_drag = design->pitch_drag;
302  yaw_drag = design->yaw_drag;
303 
304  roll_rate = (float) (design->roll_rate * PI / 180);
305  pitch_rate = (float) (design->pitch_rate * PI / 180);
306  yaw_rate = (float) (design->yaw_rate * PI / 180);
307 
308  double agility = design->agility;
309 
310  if (roll_rate == 0) roll_rate = (float) (agility * ROLL_SPEED);
311  if (pitch_rate == 0) pitch_rate = (float) (agility * PITCH_SPEED);
312  if (yaw_rate == 0) yaw_rate = (float) (agility * YAW_SPEED);
313  }
314 }
315 
316 // +--------------------------------------------------------------------+
317 
318 double
320 {
321  return trans_x * avail_x;
322 }
323 
324 double
326 {
327  return trans_y * avail_y;
328 }
329 
330 double
332 {
333  return trans_z * avail_z;
334 }
335 
336 // +--------------------------------------------------------------------+
337 
338 void
339 Thruster::ExecTrans(double x, double y, double z)
340 {
341  if (!ship || (ship->IsAirborne() && ship->Class() != Ship::LCA)) {
342  if (thruster_sound && thruster_sound->IsPlaying())
343  thruster_sound->Stop();
344 
345  for (int i = 0; i < ports.size(); i++) {
346  ThrusterPort* p = ports[i];
347  if (p->flare) p->flare->Hide();
348  if (p->trail) p->trail->Hide();
349  }
350 
351  return;
352  }
353 
354  bool sound_on = false;
355  bool show_flare = true;
356 
357  if (ship->Rep() && ship->Rep()->Hidden())
358  show_flare = false;
359 
360  if (ship->Class() == Ship::LCA &&
361  ship->IsAirborne() &&
362  ship->Velocity().length() < 250 &&
363  ship->AltitudeAGL() > ship->Radius()/2) {
364 
365  sound_on = true;
366  IncBurn(BOTTOM, TOP);
367  }
368 
369  else if (!ship->IsAirborne()) {
370  double tx_limit = ship->Design()->trans_x;
371  double ty_limit = ship->Design()->trans_y;
372  double tz_limit = ship->Design()->trans_z;
373 
374  if (x < -0.15 * tx_limit) IncBurn(RIGHT, LEFT);
375  else if (x > 0.15 * tx_limit) IncBurn(LEFT, RIGHT);
376  else DecBurn(LEFT, RIGHT);
377 
378  if (y < -0.15 * ty_limit) IncBurn(FORE, AFT);
379  else if (y > 0.15 * ty_limit) IncBurn(AFT, FORE);
380  else DecBurn(FORE, AFT);
381 
382  if (z < -0.15 * tz_limit) IncBurn(TOP, BOTTOM);
383  else if (z > 0.15 * tz_limit) IncBurn(BOTTOM, TOP);
384  else DecBurn(TOP, BOTTOM);
385 
386  double r, p, y;
387  ship->GetAngularThrust(r, p, y);
388 
389  // Roll seems to have the opposite sign from
390  // the pitch and yaw thrust factors. Not sure why.
391 
392  if (r > 0) IncBurn(ROLL_L, ROLL_R);
393  else if (r < 0) IncBurn(ROLL_R, ROLL_L);
394  else DecBurn(ROLL_R, ROLL_L);
395 
396  if (y < 0) IncBurn(YAW_L, YAW_R);
397  else if (y > 0) IncBurn(YAW_R, YAW_L);
398  else DecBurn(YAW_R, YAW_L);
399 
400  if (p < 0) IncBurn(PITCH_D, PITCH_U);
401  else if (p > 0) IncBurn(PITCH_U, PITCH_D);
402  else DecBurn(PITCH_U, PITCH_D);
403  }
404 
405  else {
406  for (int i = 0; i < 12; i++) {
407  burn[i] -= 0.1f;
408  if (burn[i] < 0)
409  burn[i] = 0.0f;
410  }
411  }
412 
413  for (int i = 0; i < ports.size(); i++) {
414  ThrusterPort* p = ports[i];
415 
416  if (p->fire) {
417  p->burn = 0;
418 
419  int flag = 1;
420 
421  for (int n = 0; n < 12; n++) {
422  if ((p->fire & flag) != 0 && burn[n] > p->burn)
423  p->burn = burn[n];
424 
425  flag <<= 1;
426  }
427  }
428 
429  else {
430  p->burn = burn[p->type];
431  }
432 
433  if (p->burn > 0 && thrust > 0) {
434  sound_on = true;
435 
436  if (show_flare) {
437  Sprite* flare_rep = (Sprite*) p->flare;
438  if (flare_rep) {
439  flare_rep->Show();
440  flare_rep->SetShade(1);
441  }
442 
443  if (p->trail) {
444  Bolt* t = (Bolt*) p->trail;
445  t->Show();
446  t->SetShade(1);
447  }
448  }
449  }
450  else {
451  if (p->flare) p->flare->Hide();
452  if (p->trail) p->trail->Hide();
453  }
454  }
455 
456  // thruster sound:
457  if (ship && ship == Sim::GetSim()->GetPlayerShip() && ports.size() > 0) {
459 
460  // no sound when paused!
461  if (!Game::Paused() && cam_dir && cam_dir->GetCamera()) {
462  if (!thruster_sound) {
463  if (thruster_resource)
464  thruster_sound = thruster_resource->Duplicate();
465  }
466 
467  if (thruster_sound) {
468  if (sound_on) {
469  Point cam_loc = cam_dir->GetCamera()->Pos();
470  double dist = (ship->Location() - cam_loc).length();
471 
472  long max_vol = AudioConfig::EfxVolume();
473  long volume = -2000;
474 
475  if (volume > max_vol)
476  volume = max_vol;
477 
478  if (dist < thruster_sound->GetMaxDistance()) {
479  thruster_sound->SetLocation(ship->Location());
480  thruster_sound->SetVolume(volume);
481  thruster_sound->Play();
482  }
483  else if (thruster_sound->IsPlaying()) {
484  thruster_sound->Stop();
485  }
486  }
487  else if (thruster_sound->IsPlaying()) {
488  thruster_sound->Stop();
489  }
490  }
491  }
492  }
493 
494  ship->SetTransX(x * thrust);
495  ship->SetTransY(y * thrust);
496  ship->SetTransZ(z * thrust);
497 }
498 
499 // +--------------------------------------------------------------------+
500 
501 void
502 Thruster::AddPort(int type, const Point& loc, DWORD fire, float flare_scale)
503 {
504  if (flare_scale == 0) flare_scale = scale;
505  ThrusterPort* port = new(__FILE__,__LINE__) ThrusterPort(type, loc, fire, flare_scale);
506  ports.append(port);
507 }
508 
509 void
510 Thruster::CreatePort(int type, const Point& loc, DWORD fire, float flare_scale)
511 {
512  Bitmap* flare_bmp = drive_flare_bitmap[subtype];
513  Bitmap* trail_bmp = drive_trail_bitmap[subtype];
514 
515  if (subtype != Drive::STEALTH) {
516  Sprite* flare_rep = new(__FILE__,__LINE__) Sprite(flare_bmp);
517  flare_rep->Scale(flare_scale * 0.667f);
518  flare_rep->SetShade(0);
519 
520  Bolt* trail_rep = new(__FILE__,__LINE__) Bolt(flare_scale * 30, flare_scale * 8, trail_bmp, true);
521 
522  ThrusterPort* port = new(__FILE__,__LINE__) ThrusterPort(type, loc, fire, flare_scale);
523  port->flare = flare_rep;
524  port->trail = trail_rep;
525  ports.append(port);
526  }
527 }
528 
529 // +--------------------------------------------------------------------+
530 
531 int
533 {
534  return ports.size();
535 }
536 
537 Graphic*
538 Thruster::Flare(int engine) const
539 {
540  if (engine >= 0 && engine < ports.size())
541  return ports[engine]->flare;
542 
543  return 0;
544 }
545 
546 Graphic*
547 Thruster::Trail(int engine) const
548 {
549  if (engine >= 0 && engine < ports.size())
550  return ports[engine]->trail;
551 
552  return 0;
553 }
554 
555 // +--------------------------------------------------------------------+
556 
557 void
558 Thruster::IncBurn(int inc, int dec)
559 {
560  burn[inc] += 0.1f;
561  if (burn[inc] > 1)
562  burn[inc] = 1.0f;
563 
564  burn[dec] -= 0.1f;
565  if (burn[dec] < 0)
566  burn[dec] = 0.0f;
567 }
568 
569 void
570 Thruster::DecBurn(int a, int b)
571 {
572  burn[a] -= 0.1f;
573  if (burn[a] < 0)
574  burn[a] = 0.0f;
575 
576  burn[b] -= 0.1f;
577  if (burn[b] < 0)
578  burn[b] = 0.0f;
579 }
580 
581 // +----------------------------------------------------------------------+
582 
583 double
584 Thruster::GetRequest(double seconds) const
585 {
586  if (!power_on)
587  return 0;
588 
589  for (int i = 0; i < 12; i++)
590  if (burn[i] != 0)
591  return power_level * sink_rate * seconds;
592 
593  return 0;
594 }
595