Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Mission.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: Mission.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Mission classes
13 */
14 
15 #include "MemDebug.h"
16 #include "Mission.h"
17 #include "MissionEvent.h"
18 #include "StarSystem.h"
19 #include "Galaxy.h"
20 #include "Starshatter.h"
21 #include "Ship.h"
22 #include "ShipDesign.h"
23 #include "Element.h"
24 #include "Instruction.h"
25 #include "WeaponDesign.h"
26 #include "Sim.h"
27 
28 #include "Game.h"
29 #include "DataLoader.h"
30 #include "ParseUtil.h"
31 #include "FormatUtil.h"
32 #include "Random.h"
33 #include "Skin.h"
34 
35 // +--------------------------------------------------------------------+
36 
37 Mission::Mission(int identity, const char* fname, const char* pname)
38 : id(identity), type(0), team(1), ok(false), active(false), complete(false),
39 star_system(0), start(33 * 3600), stardate(0), target(0), ward(0),
40 current(0), degrees(false)
41 {
42  objective = Game::GetText("Mission.unspecified");
43  sitrep = Game::GetText("Mission.unknown");
44 
45  if (fname)
46  strcpy_s(filename, fname);
47  else
48  ZeroMemory(filename, sizeof(filename));
49 
50  if (pname)
51  strcpy_s(path, pname);
52  else
53  strcpy_s(path, "Missions/");
54 }
55 
57 {
58  ::Print("Mission::~Mission() id = %d name = '%s'\n", id, name.data());
59  elements.destroy();
60  events.destroy();
61 }
62 
63 // +--------------------------------------------------------------------+
64 
65 const char*
67 {
68  return subtitles;
69 }
70 
71 // +--------------------------------------------------------------------+
72 
73 void
75 {
76  if (elem)
77  elements.append(elem);
78 }
79 
80 // +--------------------------------------------------------------------+
81 
83 Mission::FindElement(const char* name)
84 {
86  while (++iter) {
87  MissionElement* elem = iter.value();
88 
89  if (elem->Name() == name)
90  return elem;
91  }
92 
93  return 0;
94 }
95 
96 // +--------------------------------------------------------------------+
97 
98 void
100 {
101  if (elem_index > 0 && elem_index < elements.size()) {
102  MissionElement* elem1 = elements.at(elem_index-1);
103  MissionElement* elem2 = elements.at(elem_index);
104 
105  elements.at(elem_index-1) = elem2;
106  elements.at(elem_index) = elem1;
107  }
108 }
109 
110 void
112 {
113  if (elem_index >= 0 && elem_index < elements.size()-1) {
114  MissionElement* elem1 = elements.at(elem_index);
115  MissionElement* elem2 = elements.at(elem_index+1);
116 
117  elements.at(elem_index) = elem2;
118  elements.at(elem_index+1) = elem1;
119  }
120 }
121 
122 // +--------------------------------------------------------------------+
123 
124 void
126 {
127  if (event_index > 0 && event_index < events.size()) {
128  MissionEvent* event1 = events.at(event_index-1);
129  MissionEvent* event2 = events.at(event_index);
130 
131  events.at(event_index-1) = event2;
132  events.at(event_index) = event1;
133  }
134 }
135 
136 void
138 {
139  if (event_index >= 0 && event_index < events.size()-1) {
140  MissionEvent* event1 = events.at(event_index);
141  MissionEvent* event2 = events.at(event_index+1);
142 
143  events.at(event_index) = event2;
144  events.at(event_index+1) = event1;
145  }
146 }
147 
148 // +--------------------------------------------------------------------+
149 
150 void
152 {
153  if (star_system != s) {
154  star_system = s;
155 
156  if (!system_list.contains(s))
157  system_list.append(s);
158  }
159 }
160 
161 void
163 {
164  star_system = 0;
165  system_list.clear();
166 }
167 
168 // +--------------------------------------------------------------------+
169 
170 void
172 {
174  while (++elem) {
175  MissionElement* element = elem.value();
176  if (element == player_element)
177  element->player = 1;
178  else
179  element->player = 0;
180  }
181 }
182 
185 {
186  MissionElement* p = 0;
187 
189  while (++elem) {
190  if (elem->player > 0)
191  p = elem.value();
192  }
193 
194  return p;
195 }
196 
197 // +--------------------------------------------------------------------+
198 
200 Mission::FindEvent(int event_type) const
201 {
202  Mission* pThis = (Mission*) this;
203  ListIter<MissionEvent> iter = pThis->events;
204  while (++iter) {
205  MissionEvent* event = iter.value();
206 
207  if (event->Event() == event_type)
208  return event;
209  }
210 
211  return 0;
212 }
213 
214 void
216 {
217  if (event)
218  events.append(event);
219 }
220 
221 // +--------------------------------------------------------------------+
222 
223 bool
224 Mission::Load(const char* fname, const char* pname)
225 {
226  ok = false;
227 
228  if (fname)
229  strcpy_s(filename, fname);
230 
231  if (pname)
232  strcpy_s(path, pname);
233 
234  if (!filename[0]) {
235  Print("\nCan't Load Mission, script unspecified.\n");
236  return ok;
237  }
238 
239  // wipe existing mission before attempting to load...
240  elements.destroy();
241  events.destroy();
242 
243  Print("\nLoad Mission: '%s'\n", filename);
244 
245  DataLoader* loader = DataLoader::GetLoader();
246  bool old_fs = loader->IsFileSystemEnabled();
247  BYTE* block = 0;
248 
249  loader->UseFileSystem(true);
250  loader->SetDataPath(path);
251  loader->LoadBuffer(filename, block, true);
252  loader->SetDataPath(0);
253  loader->UseFileSystem(old_fs);
254 
255  ok = ParseMission((const char*) block);
256 
257  loader->ReleaseBuffer(block);
258  Print("Mission Loaded.\n\n");
259 
260  if (ok)
261  Validate();
262 
263  return ok;
264 }
265 
266 // +--------------------------------------------------------------------+
267 
268 bool
269 Mission::ParseMission(const char* block)
270 {
271  Parser parser(new(__FILE__,__LINE__) BlockReader(block));
272  Term* term = parser.ParseTerm();
273  char err[256];
274 
275  if (!term) {
276  sprintf_s(err, "ERROR: could not parse '%s'\n", filename);
277  AddError(err);
278  return ok;
279  }
280  else {
281  TermText* file_type = term->isText();
282  if (!file_type || file_type->value() != "MISSION") {
283  sprintf_s(err, "ERROR: invalid mission file '%s'\n", filename);
284  AddError(err);
285  term->print(10);
286  return ok;
287  }
288  }
289 
290  ok = true;
291 
292  char target_name[256];
293  char ward_name[256];
294 
295  target_name[0] = 0;
296  ward_name[0] = 0;
297 
298  do {
299  delete term; term = 0;
300  term = parser.ParseTerm();
301 
302  if (term) {
303  TermDef* def = term->isDef();
304  if (def) {
305  Text defname = def->name()->value();
306  defname.setSensitive(false);
307 
308  if (defname == "name") {
309  GetDefText(name, def, filename);
311  }
312 
313  else if (defname == "desc") {
314  GetDefText(desc, def, filename);
315  if (desc.length() > 0 && desc.length() < 32)
317  }
318 
319  else if (defname == "type") {
320  char typestr[64];
321  GetDefText(typestr, def, filename);
322  type = TypeFromName(typestr);
323  }
324 
325  else if (defname == "system") {
326  char sysname[64];
327  GetDefText(sysname, def, filename);
328 
329  Galaxy* galaxy = Galaxy::GetInstance();
330 
331  if (galaxy) {
332  SetStarSystem(galaxy->GetSystem(sysname));
333  }
334  }
335 
336  else if (defname == "degrees")
337  GetDefBool(degrees, def, filename);
338 
339  else if (defname == "region")
340  GetDefText(region, def, filename);
341 
342  else if (defname == "objective") {
344  if (objective.length() > 0 && objective.length() < 32)
346  }
347 
348  else if (defname == "sitrep") {
349  GetDefText(sitrep, def, filename);
350  if (sitrep.length() > 0 && sitrep.length() < 32)
352  }
353 
354  else if (defname == "subtitles") {
355  Text subtitles_path;
356  DataLoader* loader = DataLoader::GetLoader();
357  BYTE* block = 0;
358 
359  GetDefText(subtitles_path, def, filename);
360  loader->SetDataPath(0);
361  loader->LoadBuffer(subtitles_path, block, true);
362 
363  subtitles = Text("\n") + (const char*) block;
364 
365  loader->ReleaseBuffer(block);
366  }
367 
368  else if (defname == "start")
369  GetDefTime(start, def, filename);
370 
371  else if (defname == "stardate")
373 
374  else if (defname == "team")
375  GetDefNumber(team, def, filename);
376 
377  else if (defname == "target")
378  GetDefText(target_name, def, filename);
379 
380  else if (defname == "ward")
381  GetDefText(ward_name, def, filename);
382 
383  else if ((defname == "element") ||
384  (defname == "ship") ||
385  (defname == "station")) {
386 
387  if (!def->term() || !def->term()->isStruct()) {
388  sprintf_s(err, "ERROR: element struct missing in '%s'\n", filename);
389  AddError(err);
390  }
391  else {
392  TermStruct* val = def->term()->isStruct();
393  MissionElement* elem = ParseElement(val);
394  AddElement(elem);
395  }
396  }
397 
398  else if (defname == "event") {
399  if (!def->term() || !def->term()->isStruct()) {
400  sprintf_s(err, "ERROR: event struct missing in '%s'\n", filename);
401  AddError(err);
402  }
403  else {
404  TermStruct* val = def->term()->isStruct();
405  MissionEvent* event = ParseEvent(val);
406  AddEvent(event);
407  }
408  }
409  } // def
410  } // term
411  }
412  while (term);
413 
414  if (ok) {
415  if (target_name[0])
416  target = FindElement(target_name);
417 
418  if (ward_name[0])
419  ward = FindElement(ward_name);
420  }
421 
422  return ok;
423 }
424 
425 // +--------------------------------------------------------------------+
426 
427 bool
429 {
430  Validate();
431 
432  if (!filename[0] || !path[0]) {
433  AddError(Game::GetText("Mission.error.no-file"));
434  return ok;
435  }
436 
437  Text content = Serialize();
438 
439  if (content.length() < 8) {
440  AddError(Game::GetText("Mission.error.no-serial"));
441  return ok;
442  }
443 
444  if (!_stricmp(path, "mods/missions/")) {
445  CreateDirectory("Mods", 0);
446  CreateDirectory("Mods/Missions", 0);
447  }
448 
449  else if (!_stricmp(path, "multiplayer/")) {
450  CreateDirectory("Multiplayer", 0);
451  }
452 
453  char fname[256];
454  sprintf_s(fname, "%s%s", path, filename);
455  FILE* f;
456  fopen_s(&f, fname, "w");
457  if (f) {
458  fwrite(content.data(), content.length(), 1, f);
459  fclose(f);
460  }
461 
462  return ok;
463 }
464 
465 // +--------------------------------------------------------------------+
466 
467 void
469 {
470  char err[256];
471 
472  ok = true;
473 
474  if (elements.isEmpty()) {
475  sprintf_s(err, Game::GetText("Mission.error.no-elem").data(), filename);
476  AddError(err);
477  }
478  else {
479  bool found_player = false;
480 
481  for (int i = 0; i < elements.size(); i++) {
482  MissionElement* elem = elements.at(i);
483 
484  if (elem->Name().length() < 1) {
485  sprintf_s(err, Game::GetText("Mission.error.unnamed-elem").data(), filename);
486  AddError(err);
487  }
488 
489  if (elem->Player() > 0) {
490  if (!found_player) {
491  found_player = true;
492 
493  if (elem->Region() != GetRegion()) {
494  sprintf_s(err, Game::GetText("Mission.error.wrong-sector").data(),
495  elem->Name().data(),
496  GetRegion());
497  AddError(err);
498  }
499  }
500  else {
501  sprintf_s(err, Game::GetText("Mission.error.extra-player").data(),
502  elem->Name().data(),
503  filename);
504  AddError(err);
505  }
506  }
507  }
508 
509  if (!found_player) {
510  sprintf_s(err, Game::GetText("Mission.error.no-player").data(), filename);
511  AddError(err);
512  }
513  }
514 }
515 
516 void
518 {
519  ::Print(err);
520  errmsg += err;
521 
522  ok = false;
523 }
524 
525 // +--------------------------------------------------------------------+
526 
527 #define MSN_CHECK(x) if (!_stricmp(n, #x)) result = Mission::x;
528 
529 int
530 Mission::TypeFromName(const char* n)
531 {
532  int result = -1;
533 
535  else MSN_CHECK(SWEEP)
536  else MSN_CHECK(INTERCEPT)
537  else MSN_CHECK(AIR_PATROL)
538  else MSN_CHECK(AIR_SWEEP)
540  else MSN_CHECK(STRIKE)
541  else MSN_CHECK(ASSAULT)
542  else MSN_CHECK(DEFEND)
543  else MSN_CHECK(ESCORT)
547  else MSN_CHECK(INTEL)
548  else MSN_CHECK(SCOUT)
549  else MSN_CHECK(RECON)
550  else MSN_CHECK(BLOCKADE)
551  else MSN_CHECK(FLEET)
552  else MSN_CHECK(BOMBARDMENT)
553  else MSN_CHECK(FLIGHT_OPS)
554  else MSN_CHECK(TRANSPORT)
555  else MSN_CHECK(CARGO)
556  else MSN_CHECK(TRAINING)
557  else MSN_CHECK(OTHER)
558 
559  if (result < PATROL) {
560  for (int i = PATROL; i <= OTHER && result < PATROL; i++) {
561  if (!_stricmp(n, RoleName(i))) {
562  result = i;
563  }
564  }
565  }
566 
567  return result;
568 }
569 
570 // +--------------------------------------------------------------------+
571 
572 static int elem_id = 351;
573 
576 {
577  Text design;
578  Text skin_name;
579  Text role_name;
580  int deck = 1;
581  char err[256];
582 
583  MissionElement* element = new(__FILE__,__LINE__) MissionElement();
584  element->rgn_name = region;
585  element->elem_id = elem_id++;
586 
587  current = element;
588 
589  for (int i = 0; i < val->elements()->size(); i++) {
590  TermDef* pdef = val->elements()->at(i)->isDef();
591  if (pdef) {
592  Text defname = pdef->name()->value();
593  defname.setSensitive(false);
594 
595  if (defname == "name")
596  GetDefText(element->name, pdef, filename);
597 
598  else if (defname == "carrier")
599  GetDefText(element->carrier, pdef, filename);
600 
601  else if (defname == "commander")
602  GetDefText(element->commander, pdef, filename);
603 
604  else if (defname == "squadron")
605  GetDefText(element->squadron, pdef, filename);
606 
607  else if (defname == "path")
608  GetDefText(element->path, pdef, filename);
609 
610  else if (defname == "design") {
611  GetDefText(design, pdef, filename);
612  element->design = ShipDesign::Get(design, element->path);
613 
614  if (!element->design) {
615  sprintf_s(err, Game::GetText("Mission.error.unknown-ship").data(), design.data(), filename);
616  AddError(err);
617  }
618  }
619 
620  else if (defname == "skin") {
621  if (!element->design) {
622  sprintf_s(err, Game::GetText("Mission.error.out-of-order").data(), filename);
623  AddError(err);
624  }
625 
626  else if (pdef->term()->isText()) {
627  GetDefText(skin_name, pdef, filename);
628  element->skin = element->design->FindSkin(skin_name);
629  }
630 
631  else if (pdef->term()->isStruct()) {
632  sprintf_s(err, Game::GetText("Mission.error.bad-skin").data(), filename);
633  AddError(err);
634  }
635  }
636 
637  else if (defname == "mission") {
638  GetDefText(role_name, pdef, filename);
639  element->mission_role = TypeFromName(role_name);
640  }
641 
642  else if (defname == "intel") {
643  GetDefText(role_name, pdef, filename);
644  element->intel = Intel::IntelFromName(role_name);
645  }
646 
647  else if (defname == "loc") {
648  Vec3 loc;
649  GetDefVec(loc, pdef, filename);
650  element->SetLocation(loc);
651  }
652 
653  else if (defname == "rloc") {
654  if (pdef->term()->isStruct()) {
655  RLoc* rloc = ParseRLoc(pdef->term()->isStruct());
656  element->SetRLoc(*rloc);
657  delete rloc;
658  }
659  }
660 
661  else if (defname.indexOf("head") == 0) {
662  if (pdef->term()->isArray()) {
663  Vec3 head;
664  GetDefVec(head, pdef, filename);
665  if (degrees) head.z *= (float) DEGREES;
666  element->heading = head.z;
667  }
668  else if (pdef->term()->isNumber()) {
669  double heading = 0;
670  GetDefNumber(heading, pdef, filename);
671  if (degrees) heading *= DEGREES;
672  element->heading = heading;
673  }
674  }
675 
676  else if (defname == "region" || defname == "rgn")
677  GetDefText(element->rgn_name, pdef, filename);
678 
679  else if (defname == "iff")
680  GetDefNumber(element->IFF_code, pdef, filename);
681 
682  else if (defname == "count")
683  GetDefNumber(element->count, pdef, filename);
684 
685  else if (defname == "maint_count")
686  GetDefNumber(element->maint_count, pdef, filename);
687 
688  else if (defname == "dead_count")
689  GetDefNumber(element->dead_count, pdef, filename);
690 
691  else if (defname == "player")
692  GetDefNumber(element->player, pdef, filename);
693 
694  else if (defname == "alert")
695  GetDefBool(element->alert, pdef, filename);
696 
697  else if (defname == "playable")
698  GetDefBool(element->playable, pdef, filename);
699 
700  else if (defname == "rogue")
701  GetDefBool(element->rogue, pdef, filename);
702 
703  else if (defname == "invulnerable")
704  GetDefBool(element->invulnerable, pdef, filename);
705 
706  else if (defname == "command_ai")
707  GetDefNumber(element->command_ai, pdef, filename);
708 
709  else if (defname.indexOf("respawn") == 0)
710  GetDefNumber(element->respawns, pdef, filename);
711 
712  else if (defname.indexOf("hold") == 0)
713  GetDefNumber(element->hold_time, pdef, filename);
714 
715  else if (defname.indexOf("zone") == 0) {
716  if (pdef->term() && pdef->term()->isBool()) {
717  bool locked = false;
718  GetDefBool(locked, pdef, filename);
719  element->zone_lock = locked;
720  }
721  else {
722  GetDefNumber(element->zone_lock, pdef, filename);
723  }
724  }
725 
726  else if (defname == "objective") {
727  if (!pdef->term() || !pdef->term()->isStruct()) {
728  sprintf_s(err, Game::GetText("Mission.error.no-objective").data(), element->name.data(), filename);
729  AddError(err);
730  }
731  else {
732  TermStruct* val = pdef->term()->isStruct();
733  Instruction* obj = ParseInstruction(val, element);
734  element->objectives.append(obj);
735  }
736  }
737 
738  else if (defname == "instr") {
739  Text* obj = new(__FILE__,__LINE__) Text;
740  if (GetDefText(*obj, pdef, filename))
741  element->instructions.append(obj);
742  else
743  delete obj;
744  }
745 
746  else if (defname == "ship") {
747  if (!pdef->term() || !pdef->term()->isStruct()) {
748  sprintf_s(err, Game::GetText("Mission.error.no-ship").data(), element->name.data(), filename);
749  AddError(err);
750  }
751  else {
752  TermStruct* val = pdef->term()->isStruct();
753  MissionShip* s = ParseShip(val, element);
754  element->ships.append(s);
755 
756  if (s->Integrity() < 0 && element->design)
757  s->SetIntegrity(element->design->integrity);
758  }
759  }
760 
761  else if (defname == "order" || defname == "navpt") {
762  if (!pdef->term() || !pdef->term()->isStruct()) {
763  sprintf_s(err, Game::GetText("Mission.error.no-navpt").data(), element->name.data(), filename);
764  AddError(err);
765  }
766  else {
767  TermStruct* val = pdef->term()->isStruct();
768  Instruction* npt = ParseInstruction(val, element);
769  element->navlist.append(npt);
770  }
771  }
772 
773  else if (defname == "loadout") {
774  if (!pdef->term() || !pdef->term()->isStruct()) {
775  sprintf_s(err, Game::GetText("Mission.error.no-loadout").data(), element->name.data(), filename);
776  AddError(err);
777  }
778  else {
779  TermStruct* val = pdef->term()->isStruct();
780  ParseLoadout(val, element);
781  }
782  }
783  }
784  }
785 
786  if (element->name.length() < 1) {
787  sprintf_s(err, Game::GetText("Mission.error.unnamed-elem").data(), filename);
788  AddError(err);
789  }
790 
791  else if (element->design == 0) {
792  sprintf_s(err, Game::GetText("Mission.error.unknown-ship").data(), element->name.data(), filename);
793  AddError(err);
794  }
795 
796  current = 0;
797 
798  return element;
799 }
800 
803 {
804  MissionEvent* event = new(__FILE__,__LINE__) MissionEvent;
805  Text event_name;
806  Text trigger_name;
807  static int event_id = 1;
808  static double event_time = 0;
809 
810  for (int i = 0; i < val->elements()->size(); i++) {
811  TermDef* pdef = val->elements()->at(i)->isDef();
812  if (pdef) {
813  Text defname = pdef->name()->value();
814  defname.setSensitive(false);
815 
816  if (defname == "event") {
817  GetDefText(event_name, pdef, filename);
818  event->event = MissionEvent::EventForName(event_name);
819  }
820 
821  else if (defname == "trigger") {
822  GetDefText(trigger_name, pdef, filename);
823  event->trigger = MissionEvent::TriggerForName(trigger_name);
824  }
825 
826  else if (defname == "id")
827  GetDefNumber(event_id, pdef, filename);
828 
829  else if (defname == "time")
830  GetDefNumber(event_time, pdef, filename);
831 
832  else if (defname == "delay")
833  GetDefNumber(event->delay, pdef, filename);
834 
835  else if (defname == "event_param" || defname == "param" || defname == "color") {
836  ZeroMemory(event->event_param, sizeof(event->event_param));
837 
838  if (pdef->term()->isNumber()) {
839  GetDefNumber(event->event_param[0], pdef, filename);
840  event->event_nparams = 1;
841  }
842 
843  else if (pdef->term()->isArray()) {
844  std::vector<float> plist;
845  GetDefArray(plist, pdef, filename);
846 
847  for (int i = 0; i < 10 && i < (int)plist.size(); i++) {
848  float f = plist[i];
849  event->event_param[i] = (int) f;
850  event->event_nparams = i + 1;
851  }
852  }
853  }
854 
855  else if (defname == "trigger_param") {
856  ZeroMemory(event->trigger_param, sizeof(event->trigger_param));
857 
858  if (pdef->term()->isNumber()) {
859  GetDefNumber(event->trigger_param[0], pdef, filename);
860  event->trigger_nparams = 1;
861  }
862 
863  else if (pdef->term()->isArray()) {
864  std::vector<float> plist;
865  GetDefArray(plist, pdef, filename);
866 
867  for (int i = 0; i < 10 && i < (int)plist.size(); i++) {
868  float f = plist[i];
869  event->trigger_param[i] = (int) f;
870  event->trigger_nparams = i + 1;
871  }
872  }
873  }
874 
875  else if (defname == "event_ship" || defname == "ship")
876  GetDefText(event->event_ship, pdef, filename);
877 
878  else if (defname == "event_source" || defname == "source" || defname == "font")
879  GetDefText(event->event_source, pdef, filename);
880 
881  else if (defname == "event_target" || defname == "target" || defname == "image")
882  GetDefText(event->event_target, pdef, filename);
883 
884  else if (defname == "event_message" || defname == "message") {
885  Text raw_msg;
886  GetDefText(raw_msg, pdef, filename);
887  raw_msg = Game::GetText(raw_msg);
888  event->event_message = FormatTextEscape(raw_msg);
889  }
890 
891  else if (defname == "event_chance" || defname == "chance")
892  GetDefNumber(event->event_chance, pdef, filename);
893 
894  else if (defname == "event_sound" || defname == "sound")
895  GetDefText(event->event_sound, pdef, filename);
896 
897  else if (defname == "loc" || defname == "vec" || defname == "fade")
898  GetDefVec(event->event_point, pdef, filename);
899 
900  else if (defname == "rect")
901  GetDefRect(event->event_rect, pdef, filename);
902 
903  else if (defname == "trigger_ship")
904  GetDefText(event->trigger_ship, pdef, filename);
905 
906  else if (defname == "trigger_target")
907  GetDefText(event->trigger_target, pdef, filename);
908  }
909  }
910 
911  event->id = event_id++;
912  event->time = event_time;
913  return event;
914 }
915 
918 {
919  MissionShip* msn_ship = new(__FILE__,__LINE__) MissionShip;
920 
921  Text name;
922  Text skin_name;
923  Text regnum;
924  Text region;
925  char err[256];
926  Vec3 loc(-1.0e9f, -1.0e9f, -1.0e9f);
927  Vec3 vel(-1.0e9f, -1.0e9f, -1.0e9f);
928  int respawns = -1;
929  double heading = -1e9;
930  double integrity = -1;
931  int ammo[16];
932  int fuel[4];
933  int i;
934 
935  for (i = 0; i < 16; i++)
936  ammo[i] = -10;
937 
938  for (i = 0; i < 4; i++)
939  fuel[i] = -10;
940 
941  for (i = 0; i < val->elements()->size(); i++) {
942  TermDef* pdef = val->elements()->at(i)->isDef();
943  if (pdef) {
944  Text defname = pdef->name()->value();
945  defname.setSensitive(false);
946 
947  if (defname == "name")
948  GetDefText(name, pdef, filename);
949 
950  else if (defname == "skin") {
951  if (!element || !element->design) {
952  sprintf_s(err, Game::GetText("Mission.error.out-of-order").data(), filename);
953  AddError(err);
954  }
955 
956  else if (pdef->term()->isText()) {
957  GetDefText(skin_name, pdef, filename);
958  msn_ship->skin = element->design->FindSkin(skin_name);
959  }
960 
961  else if (pdef->term()->isStruct()) {
962  sprintf_s(err, Game::GetText("Mission.error.bad-skin").data(), filename);
963  AddError(err);
964  }
965  }
966 
967  else if (defname == "regnum")
968  GetDefText(regnum, pdef, filename);
969 
970  else if (defname == "region")
971  GetDefText(region, pdef, filename);
972 
973  else if (defname == "loc")
974  GetDefVec(loc, pdef, filename);
975 
976  else if (defname == "velocity")
977  GetDefVec(vel, pdef, filename);
978 
979  else if (defname == "respawns")
980  GetDefNumber(respawns, pdef, filename);
981 
982  else if (defname == "heading") {
983  if (pdef->term()->isArray()) {
984  Vec3 h;
985  GetDefVec(h, pdef, filename);
986  if (degrees) h.z *= (float) DEGREES;
987  heading = h.z;
988  }
989  else if (pdef->term()->isNumber()) {
990  double h = 0;
991  GetDefNumber(h, pdef, filename);
992  if (degrees) h *= DEGREES;
993  heading = h;
994  }
995  }
996 
997  else if (defname == "integrity")
998  GetDefNumber(integrity, pdef, filename);
999 
1000  else if (defname == "ammo")
1001  GetDefArray(ammo, 16, pdef, filename);
1002 
1003  else if (defname == "fuel")
1004  GetDefArray(fuel, 4, pdef, filename);
1005  }
1006  }
1007 
1008  msn_ship->SetName(name);
1009  msn_ship->SetRegNum(regnum);
1010  msn_ship->SetRegion(region);
1011  msn_ship->SetIntegrity(integrity);
1012 
1013  if (loc.x > -1e9)
1014  msn_ship->SetLocation(loc);
1015 
1016  if (vel.x > -1e9)
1017  msn_ship->SetVelocity(vel);
1018 
1019  if (respawns > -1)
1020  msn_ship->SetRespawns(respawns);
1021 
1022  if (heading > -1e9)
1023  msn_ship->SetHeading(heading);
1024 
1025  if (ammo[0] > -10)
1026  msn_ship->SetAmmo(ammo);
1027 
1028  if (fuel[0] > -10)
1029  msn_ship->SetFuel(fuel);
1030 
1031  return msn_ship;
1032 }
1033 
1034 Instruction*
1036 {
1037  int order = Instruction::VECTOR;
1038  int status = Instruction::PENDING;
1039  int formation = 0;
1040  int speed = 0;
1041  int priority = 1;
1042  int farcast = 0;
1043  int hold = 0;
1044  int emcon = 0;
1045  Vec3 loc(0,0,0);
1046  RLoc* rloc = 0;
1047  Text order_name;
1048  Text status_name;
1049  Text order_rgn_name;
1050  Text tgt_name;
1051  Text tgt_desc;
1052 
1053  for (int i = 0; i < val->elements()->size(); i++) {
1054  TermDef* pdef = val->elements()->at(i)->isDef();
1055  if (pdef) {
1056  Text defname = pdef->name()->value();
1057  defname.setSensitive(false);
1058 
1059  if (defname == "cmd") {
1060  GetDefText(order_name, pdef, filename);
1061 
1062  for (int cmd = 0; cmd < Instruction::NUM_ACTIONS; cmd++)
1063  if (!_stricmp(order_name, Instruction::ActionName(cmd)))
1064  order = cmd;
1065  }
1066 
1067  else if (defname == "status") {
1068  GetDefText(status_name, pdef, filename);
1069 
1070  for (int n = 0; n < Instruction::NUM_STATUS; n++)
1071  if (!_stricmp(status_name, Instruction::StatusName(n)))
1072  status = n;
1073  }
1074 
1075  else if (defname == "loc") {
1076  GetDefVec(loc, pdef, filename);
1077  }
1078 
1079  else if (defname == "rloc") {
1080  if (pdef->term()->isStruct())
1081  rloc = ParseRLoc(pdef->term()->isStruct());
1082  }
1083 
1084  else if (defname == "rgn") {
1085  GetDefText(order_rgn_name, pdef, filename);
1086  }
1087  else if (defname == "speed") {
1088  GetDefNumber(speed, pdef, filename);
1089  }
1090  else if (defname == "formation") {
1091  GetDefNumber(formation, pdef, filename);
1092  }
1093  else if (defname == "emcon") {
1094  GetDefNumber(emcon, pdef, filename);
1095  }
1096  else if (defname == "priority") {
1097  GetDefNumber(priority, pdef, filename);
1098  }
1099  else if (defname == "farcast") {
1100  if (pdef->term()->isBool()) {
1101  bool f = false;
1102  GetDefBool(f, pdef, filename);
1103  farcast = f;
1104  }
1105  else {
1106  GetDefNumber(farcast, pdef, filename);
1107  }
1108  }
1109  else if (defname == "tgt") {
1110  GetDefText(tgt_name, pdef, filename);
1111  }
1112  else if (defname == "tgt_desc") {
1113  GetDefText(tgt_desc, pdef, filename);
1114  }
1115  else if (defname.indexOf("hold") == 0) {
1116  GetDefNumber(hold, pdef, filename);
1117  }
1118  }
1119  }
1120 
1121  Text rgn;
1122 
1123  if (order_rgn_name.length() > 0)
1124  rgn = order_rgn_name;
1125 
1126  else if (element->navlist.size() > 0)
1127  rgn = element->navlist[element->navlist.size()-1]->RegionName();
1128 
1129  else
1130  rgn = region;
1131 
1132  if (tgt_desc.length() && tgt_name.length())
1133  tgt_desc = tgt_desc + " " + tgt_name;
1134 
1135  Instruction* instr = new(__FILE__,__LINE__) Instruction(rgn, loc, order);
1136 
1137  instr->SetStatus(status);
1138  instr->SetEMCON(emcon);
1139  instr->SetFormation(formation);
1140  instr->SetSpeed(speed);
1141  instr->SetTarget(tgt_name);
1142  instr->SetTargetDesc(tgt_desc);
1143  instr->SetPriority(priority-1);
1144  instr->SetFarcast(farcast);
1145  instr->SetHoldTime(hold);
1146 
1147  if (rloc) {
1148  instr->GetRLoc() = *rloc;
1149  delete rloc;
1150  }
1151 
1152  return instr;
1153 }
1154 
1155 void
1157 {
1158  int ship = -1;
1159  int stations[16];
1160  Text name;
1161 
1162  ZeroMemory(stations, sizeof(stations));
1163 
1164  for (int i = 0; i < val->elements()->size(); i++) {
1165  TermDef* pdef = val->elements()->at(i)->isDef();
1166  if (pdef) {
1167  Text defname = pdef->name()->value();
1168  defname.setSensitive(false);
1169 
1170  if (defname == "ship") {
1171  GetDefNumber(ship, pdef, filename);
1172  }
1173  else if (defname == "name") {
1174  GetDefText(name, pdef, filename);
1175  }
1176  else if (defname == "stations") {
1177  GetDefArray(stations, 16, pdef, filename);
1178  }
1179  }
1180  }
1181 
1182  MissionLoad* load = new(__FILE__,__LINE__) MissionLoad(ship);
1183 
1184  if (name.length())
1185  load->SetName(name);
1186 
1187  for (int i = 0; i < 16; i++)
1188  load->SetStation(i, stations[i]);
1189 
1190  element->loadouts.append(load);
1191 }
1192 
1193 RLoc*
1195 {
1196  Vec3 base_loc;
1197  RLoc* rloc = new(__FILE__,__LINE__) RLoc;
1198  RLoc* ref = 0;
1199 
1200  double dex = 0;
1201  double dex_var = 5e3;
1202  double az = 0;
1203  double az_var = 3.1415;
1204  double el = 0;
1205  double el_var = 0.1;
1206 
1207  for (int i = 0; i < val->elements()->size(); i++) {
1208  TermDef* pdef = val->elements()->at(i)->isDef();
1209  if (pdef) {
1210  Text defname = pdef->name()->value();
1211  defname.setSensitive(false);
1212 
1213  if (defname == "dex") {
1214  GetDefNumber(dex, pdef, filename);
1215  rloc->SetDistance(dex);
1216  }
1217  else if (defname == "dex_var") {
1218  GetDefNumber(dex_var, pdef, filename);
1219  rloc->SetDistanceVar(dex_var);
1220  }
1221  else if (defname == "az") {
1222  GetDefNumber(az, pdef, filename);
1223  if (degrees) az *= DEGREES;
1224  rloc->SetAzimuth(az);
1225  }
1226  else if (defname == "az_var") {
1227  GetDefNumber(az_var, pdef, filename);
1228  if (degrees) az_var *= DEGREES;
1229  rloc->SetAzimuthVar(az_var);
1230  }
1231  else if (defname == "el") {
1232  GetDefNumber(el, pdef, filename);
1233  if (degrees) el *= DEGREES;
1234  rloc->SetElevation(el);
1235  }
1236  else if (defname == "el_var") {
1237  GetDefNumber(el_var, pdef, filename);
1238  if (degrees) el_var *= DEGREES;
1239  rloc->SetElevationVar(el_var);
1240  }
1241  else if (defname == "loc") {
1242  GetDefVec(base_loc, pdef, filename);
1243  rloc->SetBaseLocation(base_loc);
1244  }
1245 
1246  else if (defname == "ref") {
1247  Text refstr;
1248  GetDefText(refstr, pdef, filename);
1249 
1250  int sep = refstr.indexOf(':');
1251 
1252  if (sep >= 0) {
1253  Text elem_name = refstr.substring(0, sep);
1254  Text nav_name = refstr.substring(sep+1, refstr.length());
1255  MissionElement* elem = 0;
1256 
1257  if (elem_name == "this")
1258  elem = current;
1259  else
1260  elem = FindElement(elem_name);
1261 
1262  if (elem && elem->NavList().size() > 0) {
1263  int index = atoi(nav_name)-1;
1264  if (index < 0)
1265  index = 0;
1266  else if (index >= elem->NavList().size())
1267  index = elem->NavList().size()-1;
1268 
1269  ref = &elem->NavList()[index]->GetRLoc();
1270  rloc->SetReferenceLoc(ref);
1271  }
1272  else {
1273  ::Print("Warning: no ref found for rloc '%s' in elem '%s'\n", refstr.data(), current->Name().data());
1274  rloc->SetBaseLocation(RandomPoint());
1275  }
1276  }
1277  else {
1278  MissionElement* elem = 0;
1279 
1280  if (refstr == "this")
1281  elem = current;
1282  else
1283  elem = FindElement(refstr);
1284 
1285  if (elem) {
1286  ref = &elem->GetRLoc();
1287  rloc->SetReferenceLoc(ref);
1288  }
1289  else {
1290  ::Print("Warning: no ref found for rloc '%s' in elem '%s'\n", refstr.data(), current->Name().data());
1291  rloc->SetBaseLocation(RandomPoint());
1292  }
1293  }
1294  }
1295  }
1296  }
1297 
1298  return rloc;
1299 }
1300 
1301 const char*
1303 {
1304  switch (role) {
1305  case PATROL: return "Patrol";
1306  case SWEEP: return "Sweep";
1307  case INTERCEPT: return "Intercept";
1308  case AIR_PATROL: return "Airborne Patrol";
1309  case AIR_SWEEP: return "Airborne Sweep";
1310  case AIR_INTERCEPT: return "Airborne Intercept";
1311  case STRIKE: return "Strike";
1312  case ASSAULT: return "Assault";
1313  case DEFEND: return "Defend";
1314  case ESCORT: return "Escort";
1315  case ESCORT_FREIGHT: return "Freight Escort";
1316  case ESCORT_SHUTTLE: return "Shuttle Escort";
1317  case ESCORT_STRIKE: return "Strike Escort";
1318  case INTEL: return "Intel";
1319  case SCOUT: return "Scout";
1320  case RECON: return "Recon";
1321  case BLOCKADE: return "Blockade";
1322  case FLEET: return "Fleet";
1323  case BOMBARDMENT: return "Attack";
1324  case FLIGHT_OPS: return "Flight Ops";
1325  case TRANSPORT: return "Transport";
1326  case CARGO: return "Cargo";
1327  case TRAINING: return "Training";
1328  default:
1329  case OTHER: return "Misc";
1330  }
1331 }
1332 
1333 // +--------------------------------------------------------------------+
1334 
1335 Text
1336 Mission::Serialize(const char* player_elem, int player_index)
1337 {
1338  Text s = "MISSION\n\nname: \"";
1339  s += SafeString(Name());
1340 
1341  if (desc.length()) {
1342  s += "\"\ndesc: \"";
1343  s += SafeString(desc);
1344  }
1345 
1346  s += "\"\ntype: \"";
1347  s += SafeString(TypeName());
1348 
1349  ListIter<StarSystem> sys_iter = system_list;
1350  while (++sys_iter) {
1351  StarSystem* sys = sys_iter.value();
1352 
1353  if (sys != star_system) {
1354  s += "\"\nsystem: \"";
1355  s += sys->Name();
1356  }
1357  }
1358 
1359  s += "\"\nsystem: \"";
1360  if (GetStarSystem())
1361  s += SafeString(GetStarSystem()->Name());
1362  else
1363  s += "null";
1364 
1366 
1367  Sim* sim = Sim::GetSim();
1368  if (sim && sim->GetElements().size() > 0)
1369  iter = sim->GetMissionElements();
1370 
1371  s += "\"\n";
1372 
1373  bool region_set = false;
1374  if (player_elem && *player_elem) {
1375  // set the mission region to that of the active player
1376  while (++iter) {
1377  MissionElement* e = iter.value();
1378  if (e->Name() == player_elem) {
1379  char buf[32];
1380  sprintf_s(buf, "team: %d\n", e->GetIFF());
1381  s += buf;
1382 
1383  s += "region: \"";
1384  s += SafeString(e->Region());
1385  s += "\"\n\n";
1386 
1387  region_set = true;
1388  break;
1389  }
1390  }
1391 
1392  iter.reset();
1393  }
1394 
1395  if (!region_set) {
1396  s += "region: \"";
1397  s += SafeString(GetRegion());
1398  s += "\"\n\n";
1399  }
1400 
1401  if (Objective() && *Objective()) {
1402  s += "objective: \"";
1403  s += SafeString(Objective());
1404  s += "\"\n\n";
1405  }
1406 
1407  if (Situation() && *Situation()) {
1408  s += "sitrep: \"";
1409  s += SafeString(Situation());
1410  s += "\"\n\n";
1411  }
1412 
1413  char buffer[256];
1414  FormatTime(buffer, Start());
1415 
1416  s += "start: \"";
1417  s += buffer;
1418  s += "\"\n\n";
1419  s += "degrees: true\n\n";
1420 
1421  while (++iter) {
1422  MissionElement* elem = iter.value();
1423 
1424  s += "element: {\n";
1425  s += " name: \"";
1426  s += SafeString(elem->Name());
1427  s += "\"\n";
1428 
1429  if (elem->Path().length()) {
1430  s += " path: \"";
1431  s += SafeString(elem->Path());
1432  s += "\"\n";
1433  }
1434 
1435  if (elem->GetDesign()) {
1436  s += " design: \"";
1437  s += SafeString(elem->GetDesign()->name);
1438  s += "\"\n";
1439  }
1440 
1441  if (elem->GetSkin()) {
1442  s += " skin: \"";
1443  s += SafeString(elem->GetSkin()->Name());
1444  s += "\"\n";
1445  }
1446 
1447  if (elem->Squadron().length()) {
1448  s += " squadron: \"";
1449  s += SafeString(elem->Squadron());
1450  s += "\"\n";
1451  }
1452 
1453  if (elem->Carrier().length()) {
1454  s += " carrier: \"";
1455  s += SafeString(elem->Carrier());
1456  s += "\"\n";
1457  }
1458 
1459  if (elem->Commander().length()) {
1460  s += " commander: \"";
1461  s += SafeString(elem->Commander());
1462  s += "\"\n";
1463  }
1464 
1465  s += " mission: \"";
1466  s += elem->RoleName();
1467  s += "\"\n\n";
1468 
1469  if (elem->IntelLevel()) {
1470  s += " intel: \"";
1471  s += Intel::NameFromIntel(elem->IntelLevel());
1472  s += "\"\n";
1473  }
1474 
1475  sprintf_s(buffer, " count: %d\n", elem->Count());
1476  s += buffer;
1477 
1478  if (elem->MaintCount()) {
1479  sprintf_s(buffer, " maint_count: %d\n", elem->MaintCount());
1480  s += buffer;
1481  }
1482 
1483  if (elem->DeadCount()) {
1484  sprintf_s(buffer, " dead_count: %d\n", elem->DeadCount());
1485  s += buffer;
1486  }
1487 
1488  if (elem->RespawnCount()) {
1489  sprintf_s(buffer, " respawn_count: %d\n", elem->RespawnCount());
1490  s += buffer;
1491  }
1492 
1493  if (elem->HoldTime()) {
1494  sprintf_s(buffer, " hold_time: %d\n", elem->HoldTime());
1495  s += buffer;
1496  }
1497 
1498  if (elem->ZoneLock()) {
1499  sprintf_s(buffer, " zone_lock: %d\n", elem->ZoneLock());
1500  s += buffer;
1501  }
1502 
1503  if (elem->IsAlert()) {
1504  s += " alert: true\n";
1505  }
1506 
1507  if (!elem->IsSquadron()) {
1508  sprintf_s(buffer, " command_ai:%d\n", elem->CommandAI());
1509  s += buffer;
1510  }
1511 
1512  sprintf_s(buffer, " iff: %d\n", elem->GetIFF());
1513  s += buffer;
1514 
1515  if (player_elem) {
1516  if (elem->Name() == player_elem) {
1517  if (player_index < 1)
1518  player_index = 1;
1519 
1520  sprintf_s(buffer, " player: %d\n", player_index);
1521  s += buffer;
1522  }
1523  }
1524 
1525  else {
1526  if (elem->Player()) {
1527  sprintf_s(buffer, " player: %d\n", elem->Player());
1528  s += buffer;
1529  }
1530  }
1531 
1532  if (!elem->IsSquadron()) {
1533  if (elem->IsPlayable())
1534  s += " playable: true\n";
1535  else
1536  s += " playable: false\n";
1537  }
1538 
1539 
1540  s += " region: \"";
1541  s += elem->Region();
1542  s += "\"\n";
1543 
1544  sprintf_s(buffer, " loc: (%.0f, %.0f, %.0f)\n",
1545  elem->Location().x,
1546  elem->Location().y,
1547  elem->Location().z);
1548  s += buffer;
1549 
1550  if (elem->Heading() != 0) {
1551  sprintf_s(buffer, " head: %d\n", (int) (elem->Heading()/DEGREES));
1552  s += buffer;
1553  }
1554 
1555  if (elem->Loadouts().size()) {
1556  s += "\n";
1557 
1558  ListIter<MissionLoad> load_iter = elem->Loadouts();
1559  while (++load_iter) {
1560  MissionLoad* load = load_iter.value();
1561 
1562  sprintf_s(buffer, " loadout: { ship: %d, ", load->GetShip());
1563  s += buffer;
1564 
1565  if (load->GetName().length()) {
1566  s += "name: \"";
1567  s += SafeString(load->GetName());
1568  s += "\" }\n";
1569  }
1570  else {
1571  s += "stations: (";
1572 
1573  for (int i = 0; i < 16; i++) {
1574  sprintf_s(buffer, "%d", load->GetStation(i));
1575  s += buffer;
1576 
1577  if (i < 15)
1578  s += ", ";
1579  }
1580 
1581  s += ") }\n";
1582  }
1583  }
1584  }
1585 
1586  if (elem->Objectives().size()) {
1587  s += "\n";
1588 
1589  ListIter<Instruction> obj_iter = elem->Objectives();
1590  while (++obj_iter) {
1591  Instruction* inst = obj_iter.value();
1592 
1593  s += " objective: { cmd: ";
1594  s += Instruction::ActionName(inst->Action());
1595  s += ", tgt: \"";
1596  s += SafeString(inst->TargetName());
1597  s += "\" }\n";
1598  }
1599  }
1600 
1601  if (elem->NavList().size()) {
1602  s += "\n";
1603 
1604  ListIter<Instruction> nav_iter = elem->NavList();
1605  while (++nav_iter) {
1606  Instruction* inst = nav_iter.value();
1607 
1608  s += " navpt: { cmd: ";
1609  s += Instruction::ActionName(inst->Action());
1610  s += ", status: ";
1611  s += Instruction::StatusName(inst->Status());
1612 
1613  if (inst->TargetName() && *inst->TargetName()) {
1614  s += ", tgt: \"";
1615  s += SafeString(inst->TargetName());
1616  s += "\"";
1617  }
1618 
1619  sprintf_s(buffer, ", loc: (%.0f, %.0f, %.0f), speed: %d",
1620  inst->Location().x,
1621  inst->Location().y,
1622  inst->Location().z,
1623  inst->Speed());
1624  s += buffer;
1625 
1626  if (inst->RegionName() && *inst->RegionName()) {
1627  s += ", rgn: \"";
1628  s += inst->RegionName();
1629  s += "\"";
1630  }
1631 
1632  if (inst->HoldTime()) {
1633  sprintf_s(buffer, ", hold: %d", (int) inst->HoldTime());
1634  s += buffer;
1635  }
1636 
1637  if (inst->Farcast()) {
1638  s += ", farcast: true";
1639  }
1640 
1641  if (inst->Formation() > Instruction::DIAMOND) {
1642  sprintf_s(buffer, ", formation: %d", (int) inst->Formation());
1643  s += buffer;
1644  }
1645 
1646  if (inst->Priority() > Instruction::PRIMARY) {
1647  sprintf_s(buffer, ", priority: %d", (int) inst->Priority());
1648  s += buffer;
1649  }
1650 
1651  s += " }\n";
1652  }
1653  }
1654 
1655  if (elem->Instructions().size()) {
1656  s += "\n";
1657 
1658  ListIter<Text> i_iter = elem->Instructions();
1659  while (++i_iter) {
1660  s += " instr: \"";
1661  s += SafeString(*i_iter.value());
1662  s += "\"\n";
1663  }
1664  }
1665 
1666  if (elem->Ships().size()) {
1667  ListIter<MissionShip> s_iter = elem->Ships();
1668  while (++s_iter) {
1669  MissionShip* ship = s_iter.value();
1670 
1671  s += "\n ship: {\n";
1672 
1673  if (ship->Name().length()) {
1674  s += " name: \"";
1675  s += SafeString(ship->Name());
1676  s += "\"\n";
1677  }
1678 
1679  if (ship->RegNum().length()) {
1680  s += " regnum: \"";
1681  s += SafeString(ship->RegNum());
1682  s += "\"\n";
1683  }
1684 
1685  if (ship->Region().length()) {
1686  s += " region: \"";
1687  s += SafeString(ship->Region());
1688  s += "\"\n";
1689  }
1690 
1691  if (fabs(ship->Location().x) < 1e9) {
1692  sprintf_s(buffer, " loc: (%.0f, %.0f, %.0f),\n",
1693  ship->Location().x,
1694  ship->Location().y,
1695  ship->Location().z);
1696  s += buffer;
1697  }
1698 
1699  if (fabs(ship->Velocity().x) < 1e9) {
1700  sprintf_s(buffer, " velocity: (%.1f, %.1f, %.1f),\n",
1701  ship->Velocity().x,
1702  ship->Velocity().y,
1703  ship->Velocity().z);
1704  s += buffer;
1705  }
1706 
1707  if (ship->Respawns() > -1) {
1708  sprintf_s(buffer, " respawns: %d,\n", ship->Respawns());
1709  s += buffer;
1710  }
1711 
1712  if (ship->Heading() > -1e9) {
1713  sprintf_s(buffer, " heading: %d,\n", (int) (ship->Heading()/DEGREES));
1714  s += buffer;
1715  }
1716 
1717  if (ship->Integrity() > -1) {
1718  sprintf_s(buffer, " integrity: %d,\n", (int) ship->Integrity());
1719  s += buffer;
1720  }
1721 
1722  if (ship->Decoys() > -1) {
1723  sprintf_s(buffer, " decoys: %d,\n", ship->Decoys());
1724  s += buffer;
1725  }
1726 
1727  if (ship->Probes() > -1) {
1728  sprintf_s(buffer, " probes: %d,\n", ship->Probes());
1729  s += buffer;
1730  }
1731 
1732  if (ship->Ammo()[0] > -10) {
1733  s += "\n ammo: (";
1734 
1735  for (int i = 0; i < 16; i++) {
1736  sprintf_s(buffer, "%d", ship->Ammo()[i]);
1737  s += buffer;
1738 
1739  if (i < 15)
1740  s += ", ";
1741  }
1742 
1743  s += ")\n";
1744  }
1745 
1746  if (ship->Fuel()[0] > -10) {
1747  s += "\n fuel: (";
1748 
1749  for (int i = 0; i < 4; i++) {
1750  sprintf_s(buffer, "%d", ship->Fuel()[i]);
1751  s += buffer;
1752 
1753  if (i < 3)
1754  s += ", ";
1755  }
1756 
1757  s += ")\n";
1758  }
1759 
1760  s += " }\n";
1761  }
1762  }
1763 
1764  s += "}\n\n";
1765  }
1766 
1768  while (++iter2) {
1769  MissionEvent* event = iter2.value();
1770 
1771  s += "event: {\n";
1772 
1773  s += " id: ";
1774  sprintf_s(buffer, "%d", event->EventID());
1775  s += buffer;
1776  s += ",\n time: ";
1777  sprintf_s(buffer, "%.1f", event->Time());
1778  s += buffer;
1779  s += ",\n delay: ";
1780  sprintf_s(buffer, "%.1f", event->Delay());
1781  s += buffer;
1782  s += ",\n event: ";
1783  s += event->EventName();
1784  s += "\n";
1785 
1786  if (event->EventShip().length()) {
1787  s += " event_ship: \"";
1788  s += SafeString(event->EventShip());
1789  s += "\"\n";
1790  }
1791 
1792  if (event->EventSource().length()) {
1793  s += " event_source: \"";
1794  s += SafeString(event->EventSource());
1795  s += "\"\n";
1796  }
1797 
1798  if (event->EventTarget().length()) {
1799  s += " event_target: \"";
1800  s += SafeString(event->EventTarget());
1801  s += "\"\n";
1802  }
1803 
1804  if (event->EventSound().length()) {
1805  s += " event_sound: \"";
1806  s += SafeString(event->EventSound());
1807  s += "\"\n";
1808  }
1809 
1810  if (event->EventMessage().length()) {
1811  s += " event_message: \"";
1812  s += SafeString(event->EventMessage());
1813  s += "\"\n";
1814  }
1815 
1816  if (event->EventParam()) {
1817  sprintf_s(buffer, "%d", event->EventParam());
1818  s += " event_param: ";
1819  s += buffer;
1820  s += "\n";
1821  }
1822 
1823  if (event->EventChance()) {
1824  sprintf_s(buffer, "%d", event->EventChance());
1825  s += " event_chance: ";
1826  s += buffer;
1827  s += "\n";
1828  }
1829 
1830  s += " trigger: \"";
1831  s += event->TriggerName();
1832  s += "\"\n";
1833 
1834  if (event->TriggerShip().length()) {
1835  s += " trigger_ship: \"";
1836  s += SafeString(event->TriggerShip());
1837  s += "\"\n";
1838  }
1839 
1840  if (event->TriggerTarget().length()) {
1841  s += " trigger_target: \"";
1842  s += SafeString(event->TriggerTarget());
1843  s += "\"\n";
1844  }
1845 
1846  Text param_str = event->TriggerParamStr();
1847 
1848  if (param_str.length()) {
1849  s += " trigger_param: ";
1850  s += param_str;
1851  s += "\n";
1852  }
1853 
1854  s += "}\n\n";
1855  }
1856 
1857  s += "// EOF\n";
1858 
1859  return s;
1860 }
1861 
1862 // +====================================================================+
1863 
1864 static int elem_idkey = 1;
1865 
1867 : id (elem_idkey++),
1868 elem_id(0), design(0), skin(0), count(1), maint_count(0), dead_count(0),
1869 IFF_code(0), player(0), alert(false), playable(false), rogue(false), invulnerable(false),
1870 respawns(0), hold_time(0), zone_lock(0), heading(0), mission_role(Mission::OTHER),
1871 intel(Intel::SECRET), command_ai(1), combat_group(0), combat_unit(0)
1872 {
1873 }
1874 
1876 {
1877  ships.destroy();
1878  objectives.destroy();
1880  navlist.destroy();
1881  loadouts.destroy();
1882 }
1883 
1884 Text
1886 {
1887  if (design)
1888  return design->abrv;
1889 
1890  return "UNK";
1891 }
1892 
1893 Text
1895 {
1896  if (index < 0 || index >= ships.size()) {
1897  if (count > 1) {
1898  char sname[256];
1899  sprintf_s(sname, "%s %d", (const char*) name, index+1);
1900  return sname;
1901  }
1902  else {
1903  return name;
1904  }
1905  }
1906 
1907  return ships.at(index)->Name();
1908 }
1909 
1910 Text
1912 {
1913  if (index < 0 || index >= ships.size()) {
1914  return Text();
1915  }
1916 
1917  return ships.at(index)->RegNum();
1918 }
1919 
1920 Text
1922 {
1924 }
1925 
1926 Color
1928 {
1929  return Ship::IFFColor(IFF_code);
1930 }
1931 
1932 bool
1934 {
1935  int design_type = 0;
1936  if (GetDesign())
1937  design_type = GetDesign()->type;
1938 
1939  return design_type >= Ship::STATION;
1940 }
1941 
1942 bool
1944 {
1945  int design_type = 0;
1946  if (GetDesign())
1947  design_type = GetDesign()->type;
1948 
1949  return (design_type & Ship::GROUND_UNITS) ? true : false;
1950 }
1951 
1952 bool
1954 {
1955  int design_type = 0;
1956  if (GetDesign())
1957  design_type = GetDesign()->type;
1958 
1959  return (design_type & Ship::STARSHIPS) ? true : false;
1960 }
1961 
1962 bool
1964 {
1965  int design_type = 0;
1966  if (GetDesign())
1967  design_type = GetDesign()->type;
1968 
1969  return (design_type & Ship::DROPSHIPS) ? true : false;
1970 }
1971 
1972 bool
1974 {
1975  const ShipDesign* design = GetDesign();
1976  if (design && design->flight_decks.size() > 0)
1977  return true;
1978 
1979  return false;
1980 }
1981 
1982 bool
1984 {
1985  if (carrier.length() > 0)
1986  return true;
1987 
1988  return false;
1989 }
1990 
1991 // +--------------------------------------------------------------------+
1992 
1993 Point
1995 {
1996  MissionElement* pThis = (MissionElement*) this;
1997  return pThis->rloc.Location();
1998 }
1999 
2000 void
2002 {
2003  rloc.SetBaseLocation(l);
2004  rloc.SetReferenceLoc(0);
2005  rloc.SetDistance(0);
2006 }
2007 
2008 void
2010 {
2011  rloc = r;
2012 }
2013 
2014 // +----------------------------------------------------------------------+
2015 
2016 void
2018 {
2019  if (pt && !navlist.contains(pt)) {
2020  if (afterPoint) {
2021  int index = navlist.index(afterPoint);
2022 
2023  if (index > -1)
2024  navlist.insert(pt, index+1);
2025  else
2026  navlist.append(pt);
2027  }
2028 
2029  else {
2030  navlist.append(pt);
2031  }
2032  }
2033 }
2034 
2035 void
2037 {
2038  if (pt)
2039  delete navlist.remove(pt);
2040 }
2041 
2042 void
2044 {
2045  navlist.destroy();
2046 }
2047 
2048 // +----------------------------------------------------------------------+
2049 
2050 int
2052 {
2053  int index = 0;
2054 
2055  if (navlist.size() > 0) {
2057  while (++navpt) {
2058  index++;
2059  if (navpt.value() == n)
2060  return index;
2061  }
2062  }
2063 
2064  return 0;
2065 }
2066 
2067 // +====================================================================+
2068 
2069 MissionLoad::MissionLoad(int s, const char* n)
2070 : ship(s)
2071 {
2072  for (int i = 0; i < 16; i++)
2073  load[i] = -1; // default: no weapon mounted
2074 
2075  if (n)
2076  name = n;
2077 }
2078 
2080 {
2081 }
2082 
2083 // +--------------------------------------------------------------------+
2084 
2085 int
2087 {
2088  return ship;
2089 }
2090 
2091 void
2093 {
2094  ship = s;
2095 }
2096 
2097 Text
2099 {
2100  return name;
2101 }
2102 
2103 void
2105 {
2106  name = n;
2107 }
2108 
2109 int*
2111 {
2112  return load;
2113 }
2114 
2115 int
2117 {
2118  if (index >= 0 && index < 16)
2119  return load[index];
2120 
2121  return 0;
2122 }
2123 
2124 void
2125 MissionLoad::SetStation(int index, int selection)
2126 {
2127  if (index >= 0 && index < 16)
2128  load[index] = selection;
2129 }
2130 
2131 
2132 // +====================================================================+
2133 
2135 : loc(-1e9, -1e9, -1e9), respawns(0), heading(0), integrity(100),
2136 decoys(-10), probes(-10), skin(0)
2137 {
2138  for (int i = 0; i < 16; i++)
2139  ammo[i] = -10;
2140 
2141  for (int i = 0; i < 4; i++)
2142  fuel[i] = -10;
2143 }
2144 
2145 void
2147 {
2148  if (a) {
2149  for (int i = 0; i < 16; i++)
2150  ammo[i] = a[i];
2151  }
2152 }
2153 
2154 void
2156 {
2157  if (f) {
2158  for (int i = 0; i < 4; i++)
2159  fuel[i] = f[i];
2160  }
2161 }