Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
MissionEvent.cpp
Go to the documentation of this file.
1 /* Project Starshatter 5.0
2  Destroyer Studios LLC
3  Copyright © 1997-2007. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: MissionEvent.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Events for mission scripting
13 */
14 
15 #include "MemDebug.h"
16 #include "MissionEvent.h"
17 #include "Mission.h"
18 #include "StarSystem.h"
19 #include "Galaxy.h"
20 #include "Starshatter.h"
21 #include "StarServer.h"
22 #include "Ship.h"
23 #include "ShipDesign.h"
24 #include "Element.h"
25 #include "DisplayView.h"
26 #include "HUDView.h"
27 #include "Instruction.h"
28 #include "QuantumDrive.h"
29 #include "Sim.h"
30 #include "AudioConfig.h"
31 #include "CameraDirector.h"
32 #include "RadioMessage.h"
33 #include "RadioTraffic.h"
34 #include "Weapon.h"
35 #include "WeaponGroup.h"
36 #include "Player.h"
37 #include "Campaign.h"
38 #include "CombatGroup.h"
39 
40 #include "NetData.h"
41 #include "NetUtil.h"
42 
43 #include "Game.h"
44 #include "DataLoader.h"
45 #include "Font.h"
46 #include "FontMgr.h"
47 #include "Sound.h"
48 #include "ParseUtil.h"
49 #include "FormatUtil.h"
50 #include "Random.h"
51 
52 const char* FormatGameTime();
53 
54 // +--------------------------------------------------------------------+
55 
57 : id(0), status(PENDING), time(0), delay(0), event(0), event_nparams(0),
58 event_chance(100), trigger(0), trigger_nparams(0), sound(0)
59 {
60  ZeroMemory(event_param, sizeof(event_param));
61  ZeroMemory(trigger_param, sizeof(trigger_param));
62 }
63 
65 {
66  if (sound) {
67  sound->Stop();
68  sound->Release();
69  }
70 }
71 
72 // +--------------------------------------------------------------------+
73 
74 void
75 MissionEvent::ExecFrame(double seconds)
76 {
77  Sim* sim = Sim::GetSim();
78 
79  if (!sim) {
80  status = PENDING;
81  return;
82  }
83 
84  if (status == PENDING)
85  CheckTrigger();
86 
87  if (status == ACTIVE) {
88  if (delay > 0)
89  delay -= seconds;
90 
91  else
92  Execute();
93  }
94 }
95 
96 // +--------------------------------------------------------------------+
97 
98 void
100 {
101  if (status == PENDING) {
102  if (event_chance > 0 && event_chance < 100) {
103  if (Random(0, 100) < event_chance)
104  status = ACTIVE;
105  else
106  status = SKIPPED;
107  }
108 
109  else {
110  status = ACTIVE;
111  }
112 
113  if (status == SKIPPED) {
115  }
116  }
117 }
118 
119 void
121 {
122  if (status == PENDING) {
123  status = SKIPPED;
124  }
125 }
126 
127 // +--------------------------------------------------------------------+
128 
129 bool
131 {
132  Sim* sim = Sim::GetSim();
133 
134  if (time > 0 && time > sim->MissionClock())
135  return false;
136 
137  switch (trigger) {
138  case TRIGGER_TIME: {
139  if (time <= sim->MissionClock())
140  Activate();
141  }
142  break;
143 
144  case TRIGGER_DAMAGE: {
145  Ship* ship = sim->FindShip(trigger_ship);
146  if (ship) {
147  double damage = 100.0 * (ship->Design()->integrity - ship->Integrity()) /
148  (ship->Design()->integrity);
149 
150  if (damage >= trigger_param[0])
151  Activate();
152  }
153  }
154  break;
155 
156  case TRIGGER_DETECT: {
157  Ship* ship = sim->FindShip(trigger_ship);
158  Ship* tgt = sim->FindShip(trigger_target);
159 
160  if (ship && tgt) {
161  if (ship->FindContact(tgt))
162  Activate();
163  }
164  else {
165  Skip();
166  }
167  }
168  break;
169 
170  case TRIGGER_RANGE: {
171  Ship* ship = sim->FindShip(trigger_ship);
172  Ship* tgt = sim->FindShip(trigger_target);
173 
174  if (ship && tgt) {
175  double range = (ship->Location() - tgt->Location()).length();
176  double min_range = 0;
177  double max_range = 1e12;
178 
179  if (trigger_param[0] > 0)
180  min_range = trigger_param[0];
181  else
182  max_range = -trigger_param[0];
183 
184  if (range < min_range || range > max_range)
185  Activate();
186  }
187  else {
188  Skip();
189  }
190  }
191  break;
192 
193  case TRIGGER_SHIPS_LEFT: {
194  int alive = 0;
195  int count = 0;
196  int iff = -1;
197  int nparams = NumTriggerParams();
198 
199  if (nparams > 0) count = TriggerParam(0);
200  if (nparams > 1) iff = TriggerParam(1);
201 
202  ListIter<SimRegion> iter = sim->GetRegions();
203  while (++iter) {
204  SimRegion* rgn = iter.value();
205 
206  ListIter<Ship> s_iter = rgn->Ships();
207  while (++s_iter) {
208  Ship* ship = s_iter.value();
209 
210  if (ship->Type() >= Ship::STATION)
211  continue;
212 
213  if (ship->Life() == 0 && ship->RespawnCount() < 1)
214  continue;
215 
216  if (iff < 0 || ship->GetIFF() == iff)
217  alive++;
218  }
219  }
220 
221  if (alive <= count)
222  Activate();
223  }
224  break;
225 
226  case TRIGGER_EVENT_ALL: {
227  bool all = true;
228  int nparams = NumTriggerParams();
229  for (int i = 0; all && i < nparams; i++) {
230  int trigger_id = TriggerParam(i);
231 
232  ListIter<MissionEvent> iter = sim->GetEvents();
233  while (++iter) {
234  MissionEvent* e = iter.value();
235  if (e->EventID() == trigger_id) {
236  if (e->Status() != COMPLETE)
237  all = false;
238  break;
239  }
240 
241  else if (e->EventID() == -trigger_id) {
242  if (e->Status() == COMPLETE)
243  all = false;
244  break;
245  }
246  }
247  }
248 
249  if (all)
250  Activate();
251  }
252  break;
253 
254  case TRIGGER_EVENT_ANY: {
255  bool any = false;
256  int nparams = NumTriggerParams();
257  for (int i = 0; !any && i < nparams; i++) {
258  int trigger_id = TriggerParam(i);
259 
260  ListIter<MissionEvent> iter = sim->GetEvents();
261  while (++iter) {
262  MissionEvent* e = iter.value();
263  if (e->EventID() == trigger_id) {
264  if (e->Status() == COMPLETE)
265  any = true;
266  break;
267  }
268  }
269  }
270 
271  if (any)
272  Activate();
273  }
274  break;
275  }
276 
277  return status == ACTIVE;
278 }
279 
280 // +--------------------------------------------------------------------+
281 
282 void
284 {
286  HUDView* hud = HUDView::GetInstance();
287  Sim* sim = Sim::GetSim();
288  Ship* player = sim->GetPlayerShip();
289  Ship* ship = 0;
290  Ship* src = 0;
291  Ship* tgt = 0;
292  Element* elem = 0;
293  int pan = 0;
294  bool end_mission = false;
295 
296  if (event_ship.length())
297  ship = sim->FindShip(event_ship);
298  else
299  ship = player;
300 
301  if (event_source.length())
302  src = sim->FindShip(event_source);
303 
304  if (event_target.length())
305  tgt = sim->FindShip(event_target);
306 
307  if (ship)
308  elem = ship->GetElement();
309 
310  else if (event_ship.length()) {
311  elem = sim->FindElement(event_ship);
312 
313  if (elem)
314  ship = elem->GetShip(1);
315  }
316 
317  // expire the delay, if any remains
318  delay = 0;
319 
320  // fire the event action
321  switch (event) {
322  case MESSAGE:
323  if (event_message.length() > 0) {
324  if (ship) {
325  RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(ship, src, event_param[0]);
326  msg->SetInfo(event_message);
327  msg->SetChannel(ship->GetIFF());
328  if (tgt)
329  msg->AddTarget(tgt);
331  }
332 
333  else if (elem) {
334  RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, src, event_param[0]);
335  msg->SetInfo(event_message);
336  msg->SetChannel(elem->GetIFF());
337  if (tgt)
338  msg->AddTarget(tgt);
340  }
341  }
342 
343  if (event_sound.length() > 0) {
344  pan = event_param[0];
345  }
346  break;
347 
348  case OBJECTIVE:
349  if (elem) {
350  if (event_param[0]) {
351  elem->ClearInstructions();
352  elem->ClearObjectives();
353  }
354 
355  Instruction* obj = new(__FILE__,__LINE__) Instruction(event_param[0], 0);
356  obj->SetTarget(event_target);
357  elem->AddObjective(obj);
358 
359  if (elem->Contains(player)) {
360  HUDView* hud = HUDView::GetInstance();
361 
362  if (hud)
363  hud->ShowHUDInst();
364  }
365  }
366  break;
367 
368  case INSTRUCTION:
369  if (elem) {
370  if (event_param[0])
371  elem->ClearInstructions();
372 
374 
375  if (elem->Contains(player) && event_message.length() > 0) {
376  HUDView* hud = HUDView::GetInstance();
377 
378  if (hud)
379  hud->ShowHUDInst();
380  }
381  }
382  break;
383 
384  case IFF:
385  if (elem) {
386  elem->SetIFF(event_param[0]);
387  }
388 
389  else if (ship) {
390  ship->SetIFF(event_param[0]);
391  }
392  break;
393 
394  case DAMAGE:
395  if (ship) {
396  ship->InflictDamage(event_param[0]);
397 
398  if (ship->Integrity() < 1) {
400  ship->DeathSpiral();
401  Print(" %s Killed By Scripted Event %d (%s)\n", (const char*) ship->Name(), id, FormatGameTime());
402  }
403  }
404  else {
405  Print(" EVENT %d: Could not apply damage to ship '%s' (not found).\n", id, (const char*) event_ship);
406  }
407  break;
408 
409  case JUMP:
410  if (ship) {
411  SimRegion* rgn = sim->FindRegion(event_target);
412 
413  if (rgn && ship->GetRegion() != rgn) {
414  if (rgn->IsOrbital()) {
415  QuantumDrive* quantum_drive = ship->GetQuantumDrive();
416  if (quantum_drive) {
417  quantum_drive->SetDestination(rgn, Point(0,0,0));
418  quantum_drive->Engage(true); // request immediate jump
419  }
420 
421  else if (ship->IsAirborne()) {
422  ship->MakeOrbit();
423  }
424  }
425 
426  else {
427  ship->DropOrbit();
428  }
429  }
430 
431  }
432  break;
433 
434  case HOLD:
435  if (elem)
436  elem->SetHoldTime(event_param[0]);
437  break;
438 
439  case SKIP: {
440  for (int i = 0; i < event_nparams; i++) {
441  int skip_id = event_param[i];
442 
443  ListIter<MissionEvent> iter = sim->GetEvents();
444  while (++iter) {
445  MissionEvent* e = iter.value();
446  if (e->EventID() == skip_id) {
447  if (e->status != COMPLETE)
448  e->status = SKIPPED;
449  }
450  }
451  }
452  }
453  break;
454 
455  case END_MISSION:
456  Print(" END MISSION By Scripted Event %d (%s)\n", id, FormatGameTime());
457  end_mission = true;
458  break;
459 
460  //
461  // NOTE: CUTSCENE EVENTS DO NOT APPLY IN MULTIPLAYER
462  //
463  case BEGIN_SCENE:
464  Print(" ------------------------------------\n");
465  Print(" Begin Cutscene '%s'\n", event_message.data());
466  stars->BeginCutscene();
467  break;
468 
469  case END_SCENE:
470  Print(" End Cutscene '%s'\n", event_message.data());
471  Print(" ------------------------------------\n");
472  stars->EndCutscene();
473  break;
474 
475  case CAMERA:
476  if (stars->InCutscene()) {
478 
479  if (!cam_dir->GetShip())
480  cam_dir->SetShip(player);
481 
482  switch (event_param[0]) {
483  case 1:
484  if (cam_dir->GetMode() != CameraDirector::MODE_COCKPIT)
486  break;
487 
488  case 2:
489  if (cam_dir->GetMode() != CameraDirector::MODE_CHASE)
491  break;
492 
493  case 3:
494  if (cam_dir->GetMode() != CameraDirector::MODE_ORBIT)
496  break;
497 
498  case 4:
499  if (cam_dir->GetMode() != CameraDirector::MODE_TARGET)
501  break;
502  }
503 
504  if (event_target.length()) {
505  ::Print("Mission Event %d: setting camera target to %s\n", id, (const char*) event_target);
506  Ship* s_tgt = 0;
507 
508  if (event_target.indexOf("body:") < 0)
509  s_tgt = sim->FindShip(event_target);
510 
511  if (s_tgt) {
512  ::Print(" found ship %s\n", s_tgt->Name());
513  cam_dir->SetViewOrbital(0);
514 
515  if (cam_dir->GetViewObject() != s_tgt) {
516 
517  if (event_param[0] == 6) {
518  s_tgt->DropCam(event_param[1], event_param[2]);
519  cam_dir->SetShip(s_tgt);
520  cam_dir->SetMode(CameraDirector::MODE_DROP, 0);
521  }
522  else {
523  Ship* cam_ship = cam_dir->GetShip();
524 
525  if (cam_ship && cam_ship->IsDropCam()) {
526  cam_ship->CompleteTransition();
527  }
528 
529  if (cam_dir->GetShip() != sim->GetPlayerShip())
530  cam_dir->SetShip(sim->GetPlayerShip());
531  cam_dir->SetViewObject(s_tgt, true); // immediate, no transition
532  }
533  }
534  }
535 
536  else {
537  const char* body_name = event_target.data();
538 
539  if (!strncmp(body_name, "body:", 5))
540  body_name += 5;
541 
542  Orbital* orb = sim->FindOrbitalBody(body_name);
543 
544  if (orb) {
545  ::Print(" found body %s\n", orb->Name());
546  cam_dir->SetViewOrbital(orb);
547  }
548  }
549  }
550 
551  if (event_param[0] == 3) {
553  }
554 
555  else if (event_param[0] == 5) {
557  }
558  }
559  break;
560 
561  case VOLUME:
562  if (stars->InCutscene()) {
563  AudioConfig* audio_cfg = AudioConfig::GetInstance();
564 
565  audio_cfg->SetEfxVolume(event_param[0]);
566  audio_cfg->SetWrnVolume(event_param[0]);
567  }
568  break;
569 
570  case DISPLAY:
571  if (stars->InCutscene()) {
572  DisplayView* disp_view = DisplayView::GetInstance();
573 
574  if (disp_view) {
575  Color color;
576  color.Set(event_param[0]);
577 
579 
580  if (event_message.contains('$')) {
581  Campaign* campaign = Campaign::GetCampaign();
583  CombatGroup* group = campaign->GetPlayerGroup();
584 
585  if (user) {
586  event_message = FormatTextReplace(event_message, "$NAME", user->Name().data());
587  event_message = FormatTextReplace(event_message, "$RANK", Player::RankName(user->Rank()));
588  }
589 
590  if (group) {
592  }
593 
594  if (event_message.contains("$TIME")) {
595  char timestr[32];
596  FormatDayTime(timestr, campaign->GetTime(), true);
597  event_message = FormatTextReplace(event_message, "$TIME", timestr);
598  }
599  }
600 
601  disp_view->AddText( event_message,
603  color,
604  event_rect,
605  event_point.y,
606  event_point.x,
607  event_point.z);
608 
609  }
610 
611  else if (event_target.length()) {
612  DataLoader* loader = DataLoader::GetLoader();
613 
614  if (loader) {
615  loader->SetDataPath(0);
616  loader->LoadBitmap(event_target, image, 0, true);
617  }
618 
619  if (image.Width() && image.Height())
620  disp_view->AddImage( &image,
621  color,
623  event_rect,
624  event_point.y,
625  event_point.x,
626  event_point.z);
627  }
628  }
629  }
630  break;
631 
632  case FIRE_WEAPON:
633  if (ship) {
634  // fire single weapon:
635  if (event_param[0] >= 0) {
636  ship->FireWeapon(event_param[0]);
637  }
638 
639  // fire all weapons:
640  else {
641  ListIter<WeaponGroup> g_iter = ship->Weapons();
642  while (++g_iter) {
643  ListIter<Weapon> w_iter = g_iter->GetWeapons();
644  while (++w_iter) {
645  Weapon* w = w_iter.value();
646  w->Fire();
647  }
648  }
649  }
650  }
651  break;
652 
653  default:
654  break;
655  }
656 
658 
659  if (!silent && !sound && event_sound.length()) {
660  DataLoader* loader = DataLoader::GetLoader();
661  bool use_fs = loader->IsFileSystemEnabled();
662  DWORD flags = pan ? Sound::LOCKED|Sound::LOCALIZED :
664 
665  loader->UseFileSystem(true);
666  loader->SetDataPath("Sounds/");
667  loader->LoadSound(event_sound, sound, flags);
668  loader->SetDataPath(0);
669 
670  if (!sound) {
671  loader->SetDataPath("Mods/Sounds/");
672  loader->LoadSound(event_sound, sound, flags);
673  loader->SetDataPath(0);
674  }
675 
676  if (!sound) {
677  loader->LoadSound(event_sound, sound, flags);
678  }
679 
680  loader->UseFileSystem(use_fs);
681 
682  // fire and forget:
683  if (sound) {
684  if (sound->GetFlags() & Sound::STREAMED) {
685  sound->SetFlags(flags | sound->GetFlags());
687  sound->Play();
688  }
689  else {
690  sound->SetFlags(flags);
692  sound->SetPan(pan);
695  sound->Play();
696  }
697  }
698  }
699 
700  status = COMPLETE;
701 
702  if (end_mission) {
704 
705  if (stars) {
706  stars->EndMission();
707  }
708 
709  else if (server) {
710  // end mission event uses event_target member
711  // to forward server to next mission in the chain:
712  if (event_target.length())
713  server->SetNextMission(event_target);
714 
716  }
717  }
718 }
719 
720 // +--------------------------------------------------------------------+
721 
722 Text
724 {
725 
726  Text result;
727  char buffer[8];
728 
729  if (trigger_param[0] == 0) {
730  // nothing
731  }
732 
733  else if (trigger_param[1] == 0) {
734  sprintf_s(buffer, "%d", trigger_param[0]);
735  result = buffer;
736  }
737 
738  else {
739  result = "(";
740 
741  for (int i = 0; i < 8; i++) {
742  if (trigger_param[i] == 0)
743  break;
744 
745  if (i < 7 && trigger_param[i+1] != 0)
746  sprintf_s(buffer, "%d, ", trigger_param[i]);
747  else
748  sprintf_s(buffer, "%d", trigger_param[i]);
749 
750  result += buffer;
751  }
752 
753  result += ")";
754  }
755 
756  return result;
757 }
758 
759 // +--------------------------------------------------------------------+
760 
761 int
762 MissionEvent::EventParam(int index) const
763 {
764  if (index >= 0 && index < NumEventParams())
765  return event_param[index];
766 
767  return 0;
768 }
769 
770 int
772 {
773  return event_nparams;
774 }
775 
776 // +--------------------------------------------------------------------+
777 
778 int
780 {
781  if (index >= 0 && index < NumTriggerParams())
782  return trigger_param[index];
783 
784  return 0;
785 }
786 
787 int
789 {
790  return trigger_nparams;
791 }
792 
793 // +--------------------------------------------------------------------+
794 
795 static const char* event_names[] = {
796  "Message",
797  "Objective",
798  "Instruction",
799  "IFF",
800  "Damage",
801  "Jump",
802  "Hold",
803  "Skip",
804  "Exit",
805 
806  "BeginScene",
807  "Camera",
808  "Volume",
809  "Display",
810  "Fire",
811  "EndScene"
812 };
813 
814 static const char* trigger_names[] = {
815  "Time",
816  "Damage",
817  "Destroyed",
818  "Jump",
819  "Launch",
820  "Dock",
821  "Navpoint",
822  "Event",
823  "Skipped",
824  "Target",
825  "Ships Left",
826  "Detect",
827  "Range",
828  "Event (ALL)",
829  "Event (ANY)"
830 };
831 
832 const char*
834 {
835  return event_names[event];
836 }
837 
838 const char*
840 {
841  return event_names[n];
842 }
843 
844 int
846 {
847  for (int i = 0; i < NUM_EVENTS; i++)
848  if (!_stricmp(n, event_names[i]))
849  return i;
850 
851  return 0;
852 }
853 
854 const char*
856 {
857  return trigger_names[trigger];
858 }
859 
860 const char*
862 {
863  return trigger_names[n];
864 }
865 
866 int
868 {
869  for (int i = 0; i < NUM_TRIGGERS; i++)
870  if (!_stricmp(n, trigger_names[i]))
871  return i;
872 
873  return 0;
874 }