Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
MissionTemplate.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: Mission.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Mission Template classes
13 */
14 
15 #include "MemDebug.h"
16 #include "MissionTemplate.h"
17 #include "MissionEvent.h"
18 #include "StarSystem.h"
19 #include "Galaxy.h"
20 #include "Callsign.h"
21 #include "Campaign.h"
22 #include "Combatant.h"
23 #include "CombatGroup.h"
24 #include "CombatUnit.h"
25 #include "Starshatter.h"
26 #include "Ship.h"
27 #include "ShipDesign.h"
28 #include "Element.h"
29 #include "Instruction.h"
30 #include "Random.h"
31 
32 #include "Game.h"
33 #include "DataLoader.h"
34 #include "ParseUtil.h"
35 
36 // +--------------------------------------------------------------------+
37 
38 MissionTemplate::MissionTemplate(int identity, const char* fname, const char* pname)
39 : Mission(identity, fname, pname)
40 {
41 }
42 
44 {
46  aliases.destroy();
47 }
48 
49 // +--------------------------------------------------------------------+
50 
51 void
53 {
54  if (elem) {
55  elements.append(elem);
56  aliases.append(new(__FILE__,__LINE__) MissionAlias(elem->Name(), elem));
57  }
58 }
59 
60 bool
62 {
63  bool result = false;
64 
65  if (elem && !elem->GetCombatUnit()) {
66  if (elem->IsDropship()) {
67  Text callsign = MapCallsign(elem->Name(), elem->GetIFF());
68 
69  if (callsign.length())
70  elem->SetName(callsign);
71  }
72 
73  ListIter<Instruction> obj = elem->Objectives();
74  while (++obj) {
75  Instruction* i = obj.value();
76  if (strlen(i->TargetName())) {
77  // find a callsign, only if one already exists:
78  Text callsign = MapCallsign(i->TargetName(), -1);
79  if (callsign.length())
80  i->SetTarget(callsign.data());
81  }
82  }
83 
84  ListIter<Instruction> nav = elem->NavList();
85  while (++nav) {
86  Instruction* i = nav.value();
87  if (strlen(i->TargetName())) {
88  // find a callsign, only if one already exists:
89  Text callsign = MapCallsign(i->TargetName(), -1);
90  if (callsign.length())
91  i->SetTarget(callsign.data());
92  }
93  }
94 
95  CombatGroup* g = FindCombatGroup(elem->GetIFF(), elem->GetDesign());
96 
97  if (g) {
98  CombatUnit* u = g->GetNextUnit();
99 
100  if (u) {
101  elem->SetCombatGroup(g);
102  elem->SetCombatUnit(u);
103  }
104  }
105 
106  if (elem->GetCombatUnit()) {
107  MissionElement* cmdr = FindElement(elem->Commander());
108  if (cmdr)
109  elem->SetCommander(cmdr->Name());
110 
111  MissionElement* sqdr = FindElement(elem->Squadron());
112  if (sqdr)
113  elem->SetSquadron(sqdr->Name());
114 
115  if (!elem->IsDropship()) {
116  aliases.append(new(__FILE__,__LINE__) MissionAlias(elem->Name(), elem));
117  elem->SetName(elem->GetCombatUnit()->Name());
118  }
119 
120  result = true;
121  }
122  }
123 
124  return result;
125 }
126 
127 Text
129 {
130  Text result = name;
131  int len = name.length();
132 
133  if (len) {
134  MissionElement* elem = 0;
135 
136  // single ship from an element (e.g. "Alpha 1")?
137  if (isdigit(name[len-1]) && isspace(name[len-2])) {
138  Text elem_name = name.substring(0, len-2);
139 
140  elem = FindElement(elem_name);
141  if (elem)
142  result = elem->Name() + name.substring(len-2, 2);
143  }
144 
145  // full element name
146  // (also used to try again if single-ship search fails)
147  if (!elem) {
148  elem = FindElement(name);
149 
150  if (elem)
151  result = elem->Name();
152  }
153  }
154 
155  return result;
156 }
157 
158 // +--------------------------------------------------------------------+
159 
160 bool
162 {
163  bool result = false;
164 
165  if (event) {
166  event->event_ship = MapShip(event->event_ship);
167  event->event_source = MapShip(event->event_source);
168  event->event_target = MapShip(event->event_target);
169  event->trigger_ship = MapShip(event->trigger_ship);
170  event->trigger_target = MapShip(event->trigger_target);
171 
172  result = true;
173  }
174 
175  return result;
176 }
177 
178 // +--------------------------------------------------------------------+
179 
180 Text
181 MissionTemplate::MapCallsign(const char* name, int iff)
182 {
183  for (int i = 0; i < callsigns.size(); i++) {
184  if (callsigns[i]->Name() == name)
185  return callsigns[i]->Callsign();
186  }
187 
188  if (iff >= 0) {
189  const char* callsign = Callsign::GetCallsign(iff);
190  MissionCallsign* mc = new(__FILE__,__LINE__) MissionCallsign(callsign, name);
191  callsigns.append(mc);
192 
193  return mc->Callsign();
194  }
195 
196  return name;
197 }
198 
199 // +--------------------------------------------------------------------+
200 
201 static void SelectCombatGroups(CombatGroup* g, const ShipDesign* d, List<CombatGroup>& list)
202 {
203  if (g->IntelLevel() <= Intel::RESERVE)
204  return;
205 
206  if (g->GetUnits().size() > 0) {
207  for (int i = 0; i < g->GetUnits().size(); i++) {
208  CombatUnit* u = g->GetUnits().at(i);
209  if (u->GetDesign() == d && u->Count() - u->DeadCount() > 0) {
210  list.append(g);
211  }
212  }
213  }
214 
215  ListIter<CombatGroup> subgroup = g->GetComponents();
216  while (++subgroup)
217  SelectCombatGroups(subgroup.value(), d, list);
218 }
219 
222 {
223  CombatGroup* result = 0;
224  Campaign* campaign = Campaign::GetCampaign();
225  List<CombatGroup> group_list;
226  static int combat_group_index = 0;
227 
228  if (campaign) {
229  ListIter<Combatant> combatant = campaign->GetCombatants();
230  while (++combatant && !result) {
231  if (combatant->GetIFF() == iff) {
232  ::SelectCombatGroups(combatant->GetForce(), d, group_list);
233  }
234  }
235 
236  if (group_list.size() > 0)
237  result = group_list[combat_group_index++ % group_list.size()];
238  }
239 
240  return result;
241 }
242 
243 // +--------------------------------------------------------------------+
244 
247 {
248  Text name = n;
249 
251  while (++c_iter) {
252  MissionCallsign* c = c_iter.value();
253  if (c->Name() == name) {
254  name = c->Callsign();
255  break;
256  }
257  }
258 
260  while (++a_iter) {
261  MissionAlias* a = a_iter.value();
262  if (a->Name() == name)
263  return a->Element();
264  }
265 
267  while (++e_iter) {
268  MissionElement* elem = e_iter.value();
269  if (elem->Name() == name)
270  return elem;
271  }
272 
273  return 0;
274 }
275 
276 // +--------------------------------------------------------------------+
277 
278 bool
279 MissionTemplate::Load(const char* fname, const char* pname)
280 {
281  ok = false;
282 
283  if (fname)
284  strcpy_s(filename, fname);
285 
286  if (pname)
287  strcpy_s(path, pname);
288 
289  if (!filename[0]) {
290  Print("\nCan't Load Mission Template, script unspecified.\n");
291  return ok;
292  }
293 
294  Print("\nLoad Mission Template: '%s'\n", filename);
295 
296  int max_ships = (int) 1e6;
297 
298  DataLoader* loader = DataLoader::GetLoader();
299  bool old_fs = loader->IsFileSystemEnabled();
300  BYTE* block = 0;
301 
302  loader->UseFileSystem(true);
303  loader->SetDataPath(path);
304  loader->LoadBuffer(filename, block, true);
305  loader->SetDataPath(0);
306  loader->UseFileSystem(old_fs);
307 
308  Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
309 
310  Term* term = parser.ParseTerm();
311 
312  if (!term) {
313  Print("ERROR: could not parse '%s'\n", filename);
314  return ok;
315  }
316  else {
317  TermText* file_type = term->isText();
318  if (!file_type || file_type->value() != "MISSION_TEMPLATE") {
319  Print("ERROR: invalid MISSION TEMPLATE file '%s'\n", filename);
320  term->print(10);
321  return ok;
322  }
323  }
324 
325  ok = true;
326 
327  char target_name[256];
328  char ward_name[256];
329 
330  target_name[0] = 0;
331  ward_name[0] = 0;
332 
333  do {
334  delete term; term = 0;
335  term = parser.ParseTerm();
336 
337  if (term) {
338  TermDef* def = term->isDef();
339  if (def) {
340  Text defname = def->name()->value();
341 
342  if (defname == "name")
343  GetDefText(name, def, filename);
344 
345  else if (defname == "type") {
346  char typestr[64];
347  GetDefText(typestr, def, filename);
348  type = TypeFromName(typestr);
349  }
350 
351  else if (defname == "system") {
352  char sysname[64];
353  GetDefText(sysname, def, filename);
354 
355  Campaign* campaign = Campaign::GetCampaign();
356 
357  if (campaign) {
358  Galaxy* galaxy = Galaxy::GetInstance();
359 
360  if (galaxy) {
361  star_system = galaxy->GetSystem(sysname);
362  }
363  }
364  }
365 
366  else if (defname == "degrees")
367  GetDefBool(degrees, def, filename);
368 
369  else if (defname == "region")
370  GetDefText(region, def, filename);
371 
372  else if (defname == "objective")
374 
375  else if (defname == "sitrep")
376  GetDefText(sitrep, def, filename);
377 
378  else if (defname == "start")
379  GetDefTime(start, def, filename);
380 
381  else if (defname == "team")
382  GetDefNumber(team, def, filename);
383 
384  else if (defname == "target")
385  GetDefText(target_name, def, filename);
386 
387  else if (defname == "ward")
388  GetDefText(ward_name, def, filename);
389 
390  else if ((defname == "alias")) {
391  if (!def->term() || !def->term()->isStruct()) {
392  Print("WARNING: alias struct missing in '%s'\n", filename);
393  ok = false;
394  }
395  else {
396  TermStruct* val = def->term()->isStruct();
397  ParseAlias(val);
398  }
399  }
400 
401  else if ((defname == "callsign")) {
402  if (!def->term() || !def->term()->isStruct()) {
403  Print("WARNING: callsign struct missing in '%s'\n", filename);
404  ok = false;
405  }
406  else {
407  TermStruct* val = def->term()->isStruct();
408  ParseCallsign(val);
409  }
410  }
411 
412  else if (defname == "optional") {
413  if (!def->term() || !def->term()->isStruct()) {
414  Print("WARNING: optional group struct missing in '%s'\n", filename);
415  ok = false;
416  }
417  else {
418  TermStruct* val = def->term()->isStruct();
419  ParseOptional(val);
420  }
421  }
422 
423  else if (defname == "element") {
424  if (!def->term() || !def->term()->isStruct()) {
425  Print("WARNING: element struct missing in '%s'\n", filename);
426  ok = false;
427  }
428  else {
429  TermStruct* val = def->term()->isStruct();
430  MissionElement* elem = ParseElement(val);
431  if (MapElement(elem)) {
432  AddElement(elem);
433  }
434  else {
435  Print("WARNING: failed to map element %s '%s' in '%s'\n",
436  elem->GetDesign() ? elem->GetDesign()->name : "NO DSN",
437  elem->Name().data(),
438  filename);
439  val->print();
440  Print("\n");
441  delete elem;
442  ok = false;
443  }
444  }
445  }
446 
447  else if (defname == "event") {
448  if (!def->term() || !def->term()->isStruct()) {
449  Print("WARNING: event struct missing in '%s'\n", filename);
450  ok = false;
451  }
452  else {
453  TermStruct* val = def->term()->isStruct();
454  MissionEvent* event = ParseEvent(val);
455 
456  if (MapEvent(event))
457  AddEvent(event);
458  }
459  }
460  } // def
461  } // term
462  }
463  while (term);
464 
465  loader->ReleaseBuffer(block);
466 
467  if (ok) {
468  CheckObjectives();
469 
470  if (target_name[0])
471  target = FindElement(target_name);
472 
473  if (ward_name[0])
474  ward = FindElement(ward_name);
475 
476  Print("Mission Template Loaded.\n\n");
477  }
478 
479  return ok;
480 }
481 
482 // +--------------------------------------------------------------------+
483 
484 void
486 {
487  Text name;
488  Text design;
489  Text code;
490  Text elem_name;
491  int iff = -1;
492  int player = 0;
493  RLoc* rloc = 0;
494  bool use_loc = false;
495  Vec3 loc;
496 
497  for (int i = 0; i < val->elements()->size(); i++) {
498  TermDef* pdef = val->elements()->at(i)->isDef();
499  if (pdef) {
500  Text defname = pdef->name()->value();
501  defname.setSensitive(false);
502 
503  if (defname == "name")
504  GetDefText(name, pdef, filename);
505 
506  else if (defname == "elem")
507  GetDefText(elem_name, pdef, filename);
508 
509  else if (defname == "code")
510  GetDefText(code, pdef, filename);
511 
512  else if (defname == "design")
513  GetDefText(design, pdef, filename);
514 
515  else if (defname == "iff")
516  GetDefNumber(iff, pdef, filename);
517 
518  else if (defname == "loc") {
519  loc;
520  GetDefVec(loc, pdef, filename);
521  use_loc = true;
522  }
523 
524  else if (defname == "rloc") {
525  if (pdef->term()->isStruct()) {
526  rloc = ParseRLoc(pdef->term()->isStruct());
527  }
528  }
529 
530  else if (defname == "player") {
531  GetDefNumber(player, pdef, filename);
532 
533  if (player && !code.length())
534  code = "player";
535  }
536  }
537  }
538 
539  MissionElement* elem = 0;
540 
541  // now find element and create alias:
542  if (name.length()) {
543  for (int i = 0; i < aliases.size(); i++)
544  if (aliases[i]->Name() == name)
545  return;
546 
547  // by element name?
548  if (elem_name.length()) {
549  elem = FindElement(elem_name);
550  }
551 
552  // by special code?
553  else if (code.length()) {
554  code.toLower();
555  Campaign* campaign = Campaign::GetCampaign();
556 
557  if (code == "player") {
558  for (int i = 0; !elem && i < elements.size(); i++) {
559  MissionElement* e = elements[i];
560  if (e->Player() > 0) {
561  elem = e;
562  }
563  }
564  }
565 
566  else if (campaign && code == "player_carrier") {
567  CombatGroup* player_group = campaign->GetPlayerGroup();
568 
569  if (player_group) {
570  CombatGroup* carrier = player_group->FindCarrier();
571 
572  if (carrier) {
573  elem = FindElement(carrier->Name());
574  }
575  }
576  }
577 
578  else if (campaign && code == "player_squadron") {
579  CombatGroup* player_group = player_squadron;
580 
581  if (!player_group)
582  player_group = campaign->GetPlayerGroup();
583 
584  if (player_group &&
585  (player_group->Type() == CombatGroup::INTERCEPT_SQUADRON ||
586  player_group->Type() == CombatGroup::FIGHTER_SQUADRON ||
587  player_group->Type() == CombatGroup::ATTACK_SQUADRON)) {
588  elem = FindElement(player_group->Name());
589  }
590  }
591 
592  else if (campaign && code == "strike_target") {
593  CombatGroup* player_group = campaign->GetPlayerGroup();
594 
595  if (player_group) {
596  CombatGroup* strike_target = campaign->FindStrikeTarget(player_group->GetIFF(), player_group);
597 
598  if (strike_target) {
599  elem = FindElement(strike_target->Name());
600  }
601  }
602  }
603  }
604 
605  // by design and team?
606  else {
607  MissionElement* first_match = 0;
608 
609  for (int i = 0; !elem && i < elements.size(); i++) {
610  MissionElement* e = elements[i];
611  if (e->GetIFF() == iff && design == e->GetDesign()->name) {
612  // do we already have an alias for this element?
613  bool found = false;
614  for (int a = 0; !found && a < aliases.size(); a++)
615  if (aliases[a]->Element() == e)
616  found = true;
617 
618  if (!found)
619  elem = e;
620 
621  else if (!first_match)
622  first_match = e;
623  }
624  }
625 
626  if (first_match && !elem)
627  elem = first_match;
628  }
629 
630  if (elem) {
631  if (rloc) elem->SetRLoc(*rloc);
632  else if (use_loc) elem->SetLocation(loc);
633 
634  delete rloc;
635 
636  aliases.append(new(__FILE__,__LINE__) MissionAlias(name, elem));
637  }
638  else {
639  ::Print("WARNING: Could not resolve mission alias '%s'\n", (const char*) name);
640  ok = false;
641  }
642  }
643 
644  if (!elem || !ok) return;
645 
646  // re-parse the struct, dealing with stuff
647  // that needs to be attached to the element:
648  for (int i = 0; i < val->elements()->size(); i++) {
649  TermDef* pdef = val->elements()->at(i)->isDef();
650  if (pdef) {
651  Text defname = pdef->name()->value();
652  defname.setSensitive(false);
653 
654  if (defname == "objective") {
655  if (!pdef->term() || !pdef->term()->isStruct()) {
656  Print("WARNING: order struct missing for element '%s' in '%s'\n", (const char*) elem->Name(), filename);
657  ok = false;
658  }
659  else {
660  TermStruct* val = pdef->term()->isStruct();
661  Instruction* obj = ParseInstruction(val, elem);
662  elem->Objectives().append(obj);
663  }
664  }
665 
666  else if (defname == "instr") {
667  Text* obj = new(__FILE__,__LINE__) Text;
668  if (GetDefText(*obj, pdef, filename))
669  elem->Instructions().append(obj);
670  }
671 
672  else if (defname == "order" || defname == "navpt") {
673  if (!pdef->term() || !pdef->term()->isStruct()) {
674  Print("WARNING: order struct missing for element '%s' in '%s'\n", (const char*) elem->Name(), filename);
675  ok = false;
676  }
677  else {
678  TermStruct* val = pdef->term()->isStruct();
679  Instruction* npt = ParseInstruction(val, elem);
680  elem->NavList().append(npt);
681  }
682  }
683 
684  else if (defname == "loadout") {
685  if (!pdef->term() || !pdef->term()->isStruct()) {
686  Print("WARNING: loadout struct missing for element '%s' in '%s'\n", (const char*) elem->Name(), filename);
687  ok = false;
688  }
689  else {
690  TermStruct* val = pdef->term()->isStruct();
691  ParseLoadout(val, elem);
692  }
693  }
694  }
695  }
696 }
697 
698 // +--------------------------------------------------------------------+
699 
700 void
702 {
703  Text name;
704  int iff = -1;
705 
706  for (int i = 0; i < val->elements()->size(); i++) {
707  TermDef* pdef = val->elements()->at(i)->isDef();
708  if (pdef) {
709  Text defname = pdef->name()->value();
710  defname.setSensitive(false);
711 
712  if (defname == "name")
713  GetDefText(name, pdef, filename);
714 
715  else if (defname == "iff")
716  GetDefNumber(iff, pdef, filename);
717  }
718  }
719 
720  if (name.length() > 0 && iff >= 0)
721  MapCallsign(name, iff);
722 }
723 
724 // +--------------------------------------------------------------------+
725 
726 bool
728 {
729  int n = 0;
730  int min = 0;
731  int max = 1000;
732  int skip = 0;
733  int total = val->elements()->size();
734 
735  for (int i = 0; i < val->elements()->size(); i++) {
736  TermDef* pdef = val->elements()->at(i)->isDef();
737  if (pdef) {
738  Text defname = pdef->name()->value();
739  defname.setSensitive(false);
740 
741  if (defname == "min") {
742  GetDefNumber(min, pdef, filename);
743  total--;
744  }
745 
746  else if (defname == "max") {
747  GetDefNumber(max, pdef, filename);
748  total--;
749  }
750 
751 
752  else if ((defname == "optional")) {
753  bool select;
754 
755  if (n >= max)
756  select = false;
757  else if (total - n - skip <= min)
758  select = true;
759  else
760  select = RandomChance();
761 
762  if (select) {
763  if (!pdef->term() || !pdef->term()->isStruct()) {
764  Print("WARNING: optional group struct missing in '%s'\n", filename);
765  ok = false;
766  skip++;
767  }
768  else {
769  TermStruct* val = pdef->term()->isStruct();
770  if (ParseOptional(val))
771  n++;
772  else
773  skip++;
774  }
775  }
776  else {
777  skip++;
778  }
779  }
780 
781  else if (defname == "element") {
782  bool select;
783 
784  if (n >= max)
785  select = false;
786  else if (total - n - skip <= min)
787  select = true;
788  else
789  select = RandomChance();
790 
791  if (select) {
792  if (!pdef->term() || !pdef->term()->isStruct()) {
793  Print("WARNING: element struct missing in '%s'\n", filename);
794  ok = false;
795  skip++;
796  }
797  else {
798  TermStruct* es = pdef->term()->isStruct();
799  MissionElement* elem = ParseElement(es);
800  if (MapElement(elem)) {
801  AddElement(elem);
802  n++;
803  }
804  else {
805  delete elem;
806  skip++;
807  }
808  }
809  }
810  else {
811  skip++;
812  }
813  }
814  }
815  }
816 
817  return n > 0 && n >= min;
818 }
819 
820 // +--------------------------------------------------------------------+
821 
822 void
824 {
826  while (++iter) {
827  MissionElement* elem = iter.value();
828 
829  ListIter<Instruction> obj = elem->Objectives();
830  while (++obj) {
831  Instruction* o = obj.value();
832  Text tgt = o->TargetName();
833 
834  MissionElement* tgt_elem = 0;
835 
836  if (tgt.length()) {
837  tgt_elem = FindElement(tgt);
838 
839  if (!tgt_elem)
840  obj.removeItem();
841  else
842  o->SetTarget(tgt_elem->Name());
843  }
844  }
845  }
846 }