Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Campaign.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright (C) 1997-2006. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: Campaign.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Campaign defines a strategic military scenario.
13 */
14 
15 #include "MemDebug.h"
16 #include "Campaign.h"
17 #include "CampaignPlanStrategic.h"
18 #include "CampaignPlanAssignment.h"
19 #include "CampaignPlanEvent.h"
20 #include "CampaignPlanMission.h"
21 #include "CampaignPlanMovement.h"
23 #include "CampaignSaveGame.h"
24 #include "Combatant.h"
25 #include "CombatAction.h"
26 #include "CombatEvent.h"
27 #include "CombatGroup.h"
28 #include "CombatRoster.h"
29 #include "CombatUnit.h"
30 #include "CombatZone.h"
31 #include "Galaxy.h"
32 #include "Mission.h"
33 #include "StarSystem.h"
34 #include "Starshatter.h"
35 #include "Player.h"
36 
37 #include "Game.h"
38 #include "Bitmap.h"
39 #include "DataLoader.h"
40 #include "ParseUtil.h"
41 #include "Random.h"
42 #include "FormatUtil.h"
43 
44 
45 const int TIME_NEVER = (int) 1e9;
46 const int ONE_DAY = (int) 24 * 3600;
47 
48 // +====================================================================+
49 
51 : mission(0), start(0), type(0), id(0), min_rank(0), max_rank(0),
52 action_id(0), action_status(0), exec_once(0),
53 start_before(TIME_NEVER), start_after(0)
54 { }
55 
57 {
58  delete mission;
59 }
60 
61 bool
63 {
64  Campaign* campaign = Campaign::GetCampaign();
65  Player* player = Player::GetCurrentPlayer();
66  CombatGroup* player_group = campaign->GetPlayerGroup();
67 
68  if (campaign->GetTime() < start_after)
69  return false;
70 
71  if (campaign->GetTime() > start_before)
72  return false;
73 
74  if (region.length() && player_group->GetRegion() != region)
75  return false;
76 
77  if (min_rank && player->Rank() < min_rank)
78  return false;
79 
80  if (max_rank && player->Rank() > max_rank)
81  return false;
82 
83  if (exec_once < 0)
84  return false;
85 
86  if (exec_once > 0)
87  exec_once = -1;
88 
89  return true;
90 }
91 
92 // +====================================================================+
93 
95 : mission_type(0), group_type(0), index(0)
96 { }
97 
99 {
100  missions.destroy();
101 }
102 
103 // +====================================================================+
104 
105 static List<Campaign> campaigns;
106 static Campaign* current_campaign = 0;
107 
108 // +--------------------------------------------------------------------+
109 
110 Campaign::Campaign(int id, const char* n)
111 : campaign_id(id), name(n), mission_id(-1), mission(0), net_mission(0),
112 scripted(false), sequential(false), time(0), startTime(0), loadTime(0),
113 player_group(0), player_unit(0), status(CAMPAIGN_INIT), lockout(0),
114 loaded_from_savegame(false)
115 {
116  ZeroMemory(path, sizeof(path));
117  Load();
118 }
119 
120 Campaign::Campaign(int id, const char* n, const char* p)
121 : campaign_id(id), name(n), mission_id(-1), mission(0), net_mission(0),
122 scripted(false), sequential(false), time(0), startTime(0), loadTime(0),
123 player_group(0), player_unit(0), status(CAMPAIGN_INIT), lockout(0),
124 loaded_from_savegame(false)
125 {
126  ZeroMemory(path, sizeof(path));
127  strncpy(path, p, sizeof(path));
128  Load();
129 }
130 
131 // +--------------------------------------------------------------------+
132 
134 {
135  for (int i = 0; i < NUM_IMAGES; i++)
136  image[i].ClearImage();
137 
138  delete net_mission;
139 
140  actions.destroy();
141  events.destroy();
142  missions.destroy();
143  templates.destroy();
144  planners.destroy();
145  zones.destroy();
147 }
148 
149 // +--------------------------------------------------------------------+
150 
151 void
153 {
154  Campaign* c = 0;
155  DataLoader* loader = DataLoader::GetLoader();
156 
157  for (int i = 1; i < 100; i++) {
158  char path[256];
159  sprintf_s(path, "Campaigns/%02d/", i);
160 
161  loader->UseFileSystem(true);
162  loader->SetDataPath(path);
163 
164  if (loader->FindFile("campaign.def")) {
165  char txt[256];
166  sprintf_s(txt, "Dynamic Campaign %02d", i);
167  c = new(__FILE__,__LINE__) Campaign(i, txt);
168 
169  if (c) {
170  campaigns.insertSort(c);
171  }
172  }
173  }
174 
175  c = new(__FILE__,__LINE__) Campaign(SINGLE_MISSIONS, "Single Missions");
176  if (c) {
177  campaigns.insertSort(c);
178  current_campaign = c;
179  }
180 
181  c = new(__FILE__,__LINE__) Campaign(MULTIPLAYER_MISSIONS, "Multiplayer Missions");
182  if (c) {
183  campaigns.insertSort(c);
184  }
185 
186  c = new(__FILE__,__LINE__) Campaign(CUSTOM_MISSIONS, "Custom Missions");
187  if (c) {
188  campaigns.insertSort(c);
189  }
190 }
191 
192 void
194 {
195  Print("Campaign::Close() - destroying all campaigns\n");
196  current_campaign = 0;
197  campaigns.destroy();
198 }
199 
200 Campaign*
202 {
203  return current_campaign;
204 }
205 
206 Campaign*
207 Campaign::SelectCampaign(const char* name)
208 {
209  Campaign* c = 0;
210  ListIter<Campaign> iter = campaigns;
211 
212  while (++iter && !c) {
213  if (!_stricmp(iter->Name(), name))
214  c = iter.value();
215  }
216 
217  if (c) {
218  Print("Campaign: Selected '%s'\n", c->Name());
219  current_campaign = c;
220  }
221  else {
222  Print("Campaign: could not find '%s'\n", name);
223  }
224 
225  return c;
226 }
227 
228 Campaign*
229 Campaign::CreateCustomCampaign(const char* name, const char* path)
230 {
231  int id = 0;
232 
233  if (name && *name && path && *path) {
234  ListIter<Campaign> iter = campaigns;
235 
236  while (++iter) {
237  Campaign* c = iter.value();
238  if (c->GetCampaignId() >= id)
239  id = c->GetCampaignId() + 1;
240 
241  if (!strcmp(c->Name(), name)) {
242  Print("Campaign: custom campaign '%s' already exists.\n", name);
243  return 0;
244  }
245  }
246  }
247 
248  if (id == 0)
249  id = CUSTOM_MISSIONS + 1;
250 
251  Campaign* c = new(__FILE__,__LINE__) Campaign(id, name, path);
252  Print("Campaign: created custom campaign %d '%s'\n", id, name);
253  campaigns.append(c);
254 
255  return c;
256 }
257 
260 {
261  return campaigns;
262 }
263 
264 int
266 {
267  int result = 0;
268 
269  for (int i = 0; i < campaigns.size(); i++) {
270  Campaign* c = campaigns.at(i);
271 
272  if (c->IsDynamic() && c->GetCampaignId() > result) {
273  result = c->GetCampaignId();
274  }
275  }
276 
277  return result;
278 }
279 
280 // +--------------------------------------------------------------------+
281 
284 {
285  CombatEvent* result = 0;
286 
287  if (!events.isEmpty())
288  result = events.last();
289 
290  return result;
291 }
292 
293 // +--------------------------------------------------------------------+
294 
295 int
297 {
298  int result = 0;
299 
300  for (int i = 0; i < events.size(); i++)
301  if (/*events[i]->Source() != CombatEvent::TACNET &&*/ !events[i]->Visited())
302  result++;
303 
304  return result;
305 }
306 
307 // +--------------------------------------------------------------------+
308 
309 void
311 {
312  missions.destroy();
313  planners.destroy();
315  events.destroy();
316  actions.destroy();
317 
318  player_group = 0;
319  player_unit = 0;
320 
321  updateTime = time;
322 }
323 
324 // +--------------------------------------------------------------------+
325 
326 void
328 {
329  // first, unload any existing data:
330  Unload();
331 
332  if (!path[0]) {
333  // then load the campaign from files:
334  switch (campaign_id) {
335  case SINGLE_MISSIONS: strcpy_s(path, "Missions/"); break;
336  case CUSTOM_MISSIONS: strcpy_s(path, "Mods/Missions/"); break;
337  case MULTIPLAYER_MISSIONS: strcpy_s(path, "Multiplayer/"); break;
338  default: sprintf_s(path, "Campaigns/%02d/", campaign_id); break;
339  }
340  }
341 
342  DataLoader* loader = DataLoader::GetLoader();
343  loader->UseFileSystem(true);
344  loader->SetDataPath(path);
345  systems.clear();
346 
347  if (loader->FindFile("zones.def"))
348  zones.append(CombatZone::Load("zones.def"));
349 
350  for (int i = 0; i < zones.size(); i++) {
351  Text s = zones[i]->System();
352  bool found = false;
353 
354  for (int n = 0; !found && n < systems.size(); n++) {
355  if (s == systems[n]->Name())
356  found = true;
357  }
358 
359  if (!found)
361  }
362 
364 
365  if (loader->FindFile("campaign.def"))
366  LoadCampaign(loader);
367 
368  if (campaign_id == CUSTOM_MISSIONS) {
369  loader->SetDataPath(path);
370  LoadCustomMissions(loader);
371  }
372  else {
373  bool found = false;
374 
375  if (loader->FindFile("missions.def")) {
376  loader->SetDataPath(path);
377  LoadMissionList(loader);
378  found = true;
379  }
380 
381  if (loader->FindFile("templates.def")) {
382  loader->SetDataPath(path);
383  LoadTemplateList(loader);
384  found = true;
385  }
386 
387  if (!found) {
388  loader->SetDataPath(path);
389  LoadCustomMissions(loader);
390  }
391  }
392 
393  loader->UseFileSystem(true);
394  loader->SetDataPath(path);
395 
396  if (loader->FindFile("image.pcx")) {
397  loader->LoadBitmap("image.pcx", image[ 0]);
398  loader->LoadBitmap("selected.pcx", image[ 1]);
399  loader->LoadBitmap("unavail.pcx", image[ 2]);
400  loader->LoadBitmap("banner.pcx", image[ 3]);
401  }
402 
403  loader->SetDataPath(0);
405 }
406 
407 void
409 {
411 
414 
415  startTime = Stardate();
417  lockout = 0;
418 
419  for (int i = 0; i < NUM_IMAGES; i++)
420  image[i].ClearImage();
421 
422  Clear();
423 
424  zones.destroy();
425 }
426 
427 void
429 {
430  BYTE* block = 0;
431  const char* filename = "campaign.def";
432 
433  loader->UseFileSystem(true);
434  loader->LoadBuffer(filename, block, true);
436  Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
437 
438  Term* term = parser.ParseTerm();
439 
440  if (!term) {
441  return;
442  }
443  else {
444  TermText* file_type = term->isText();
445  if (!file_type || file_type->value() != "CAMPAIGN") {
446  return;
447  }
448  }
449 
450  do {
451  delete term; term = 0;
452  term = parser.ParseTerm();
453 
454  if (term) {
455  TermDef* def = term->isDef();
456  if (def) {
457  if (def->name()->value() == "name") {
458  if (!def->term() || !def->term()->isText()) {
459  ::Print("WARNING: name missing in '%s/%s'\n", loader->GetDataPath(), filename);
460  }
461  else {
462  name = def->term()->isText()->value();
464  }
465  }
466  else if (def->name()->value() == "desc") {
467  if (!def->term() || !def->term()->isText()) {
468  ::Print("WARNING: description missing in '%s/%s'\n", loader->GetDataPath(), filename);
469  }
470  else {
471  description = def->term()->isText()->value();
473  }
474  }
475  else if (def->name()->value() == "situation") {
476  if (!def->term() || !def->term()->isText()) {
477  ::Print("WARNING: situation missing in '%s/%s'\n", loader->GetDataPath(), filename);
478  }
479  else {
480  situation = def->term()->isText()->value();
482  }
483  }
484  else if (def->name()->value() == "orders") {
485  if (!def->term() || !def->term()->isText()) {
486  ::Print("WARNING: orders missing in '%s/%s'\n", loader->GetDataPath(), filename);
487  }
488  else {
489  orders = def->term()->isText()->value();
491  }
492  }
493  else if (def->name()->value() == "scripted") {
494  if (def->term() && def->term()->isBool()) {
495  scripted = def->term()->isBool()->value();
496  }
497  }
498  else if (def->name()->value() == "sequential") {
499  if (def->term() && def->term()->isBool()) {
500  sequential = def->term()->isBool()->value();
501  }
502  }
503  else if (full && def->name()->value() == "combatant") {
504  if (!def->term() || !def->term()->isStruct()) {
505  ::Print("WARNING: combatant struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
506  }
507  else {
508  TermStruct* val = def->term()->isStruct();
509 
510  char name[64];
511  int iff = 0;
512  CombatGroup* force = 0;
513  CombatGroup* clone = 0;
514 
515  ZeroMemory(name, sizeof(name));
516 
517  for (int i = 0; i < val->elements()->size(); i++) {
518  TermDef* pdef = val->elements()->at(i)->isDef();
519  if (pdef) {
520  if (pdef->name()->value() == "name") {
521  GetDefText(name, pdef, filename);
522 
523  force = CombatRoster::GetInstance()->GetForce(name);
524 
525  if (force)
526  clone = force->Clone(false); // shallow copy
527  }
528 
529  else if (pdef->name()->value() == "group") {
530  ParseGroup(pdef->term()->isStruct(), force, clone, filename);
531  }
532  }
533  }
534 
535  loader->SetDataPath(path);
536  Combatant* c = new(__FILE__,__LINE__) Combatant(name, clone);
537  if (c) {
538  combatants.append(c);
539  }
540  else {
541  Unload();
542  return;
543  }
544  }
545  }
546  else if (full && def->name()->value() == "action") {
547  if (!def->term() || !def->term()->isStruct()) {
548  ::Print("WARNING: action struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
549  }
550  else {
551  TermStruct* val = def->term()->isStruct();
552  ParseAction(val, filename);
553  }
554  }
555  }
556  }
557  }
558  while (term);
559 
560  loader->ReleaseBuffer(block);
561 }
562 
563 // +--------------------------------------------------------------------+
564 
565 void
567 CombatGroup* force,
568 CombatGroup* clone,
569 const char* filename)
570 {
571  if (!val) {
572  ::Print("invalid combat group in campaign %s\n", name.data());
573  return;
574  }
575 
576  int type = 0;
577  int id = 0;
578 
579  for (int i = 0; i < val->elements()->size(); i++) {
580  TermDef* pdef = val->elements()->at(i)->isDef();
581  if (pdef) {
582  if (pdef->name()->value() == "type") {
583  char type_name[64];
584  GetDefText(type_name, pdef, filename);
585  type = CombatGroup::TypeFromName(type_name);
586  }
587 
588  else if (pdef->name()->value() == "id") {
589  GetDefNumber(id, pdef, filename);
590  }
591  }
592  }
593 
594  if (type && id) {
595  CombatGroup* g = force->FindGroup(type, id);
596 
597  // found original group, now clone it over
598  if (g && g->GetParent()) {
599  CombatGroup* parent = CloneOver(force, clone, g->GetParent());
600  parent->AddComponent(g->Clone());
601  }
602  }
603 }
604 
605 // +--------------------------------------------------------------------+
606 
607 void
608 Campaign::ParseAction(TermStruct* val, const char* filename)
609 {
610  if (!val) {
611  ::Print("invalid action in campaign %s\n", name.data());
612  return;
613  }
614 
615  int id = 0;
616  int type = 0;
617  int subtype = 0;
618  int opp_type = -1;
619  int team = 0;
620  int source = 0;
621  Vec3 loc(0.0f, 0.0f, 0.0f);
622  Text system;
623  Text region;
624  Text file;
625  Text image;
626  Text scene;
627  Text text;
628  int count = 1;
629  int start_before = TIME_NEVER;
630  int start_after = 0;
631  int min_rank = 0;
632  int max_rank = 100;
633  int delay = 0;
634  int probability = 100;
635 
636  int asset_type = 0;
637  int asset_id = 0;
638  int target_type = 0;
639  int target_id = 0;
640  int target_iff = 0;
641 
642  CombatAction* action = 0;
643 
644  for (int i = 0; i < val->elements()->size(); i++) {
645  TermDef* pdef = val->elements()->at(i)->isDef();
646  if (pdef) {
647  if (pdef->name()->value() == "id") {
648  GetDefNumber(id, pdef, filename);
649  }
650  else if (pdef->name()->value() == "type") {
651  char txt[64];
652  GetDefText(txt, pdef, filename);
653  type = CombatAction::TypeFromName(txt);
654  }
655  else if (pdef->name()->value() == "subtype") {
656  if (pdef->term()->isNumber()) {
657  GetDefNumber(subtype, pdef, filename);
658  }
659 
660  else if (pdef->term()->isText()) {
661  char txt[64];
662  GetDefText(txt, pdef, filename);
663 
664  if (type == CombatAction::MISSION_TEMPLATE) {
665  subtype = Mission::TypeFromName(txt);
666  }
667  else if (type == CombatAction::COMBAT_EVENT) {
668  subtype = CombatEvent::TypeFromName(txt);
669  }
670  else if (type == CombatAction::INTEL_EVENT) {
671  subtype = Intel::IntelFromName(txt);
672  }
673  }
674  }
675  else if (pdef->name()->value() == "opp_type") {
676  if (pdef->term()->isNumber()) {
677  GetDefNumber(opp_type, pdef, filename);
678  }
679 
680  else if (pdef->term()->isText()) {
681  char txt[64];
682  GetDefText(txt, pdef, filename);
683 
684  if (type == CombatAction::MISSION_TEMPLATE) {
685  opp_type = Mission::TypeFromName(txt);
686  }
687  }
688  }
689  else if (pdef->name()->value() == "source") {
690  char txt[64];
691  GetDefText(txt, pdef, filename);
692  source = CombatEvent::SourceFromName(txt);
693  }
694  else if (pdef->name()->value() == "team") {
695  GetDefNumber(team, pdef, filename);
696  }
697  else if (pdef->name()->value() == "iff") {
698  GetDefNumber(team, pdef, filename);
699  }
700  else if (pdef->name()->value() == "count") {
701  GetDefNumber(count, pdef, filename);
702  }
703  else if (pdef->name()->value().contains("before")) {
704  if (pdef->term()->isNumber()) {
705  GetDefNumber(start_before, pdef, filename);
706  }
707  else {
708  GetDefTime(start_before, pdef, filename);
709  start_before -= ONE_DAY;
710  }
711  }
712  else if (pdef->name()->value().contains("after")) {
713  if (pdef->term()->isNumber()) {
714  GetDefNumber(start_after, pdef, filename);
715  }
716  else {
717  GetDefTime(start_after, pdef, filename);
718  start_after -= ONE_DAY;
719  }
720  }
721  else if (pdef->name()->value() == "min_rank") {
722  if (pdef->term()->isNumber()) {
723  GetDefNumber(min_rank, pdef, filename);
724  }
725  else {
726  char rank_name[64];
727  GetDefText(rank_name, pdef, filename);
728  min_rank = Player::RankFromName(rank_name);
729  }
730  }
731  else if (pdef->name()->value() == "max_rank") {
732  if (pdef->term()->isNumber()) {
733  GetDefNumber(max_rank, pdef, filename);
734  }
735  else {
736  char rank_name[64];
737  GetDefText(rank_name, pdef, filename);
738  max_rank = Player::RankFromName(rank_name);
739  }
740  }
741  else if (pdef->name()->value() == "delay") {
742  GetDefNumber(delay, pdef, filename);
743  }
744  else if (pdef->name()->value() == "probability") {
745  GetDefNumber(probability, pdef, filename);
746  }
747  else if (pdef->name()->value() == "asset_type") {
748  char type_name[64];
749  GetDefText(type_name, pdef, filename);
750  asset_type = CombatGroup::TypeFromName(type_name);
751  }
752  else if (pdef->name()->value() == "target_type") {
753  char type_name[64];
754  GetDefText(type_name, pdef, filename);
755  target_type = CombatGroup::TypeFromName(type_name);
756  }
757  else if (pdef->name()->value() == "location" ||
758  pdef->name()->value() == "loc") {
759  GetDefVec(loc, pdef, filename);
760  }
761  else if (pdef->name()->value() == "system" ||
762  pdef->name()->value() == "sys") {
763  GetDefText(system, pdef, filename);
764  }
765  else if (pdef->name()->value() == "region" ||
766  pdef->name()->value() == "rgn" ||
767  pdef->name()->value() == "zone") {
768  GetDefText(region, pdef, filename);
769  }
770  else if (pdef->name()->value() == "file") {
771  GetDefText(file, pdef, filename);
772  }
773  else if (pdef->name()->value() == "image") {
774  GetDefText(image, pdef, filename);
775  }
776  else if (pdef->name()->value() == "scene") {
777  GetDefText(scene, pdef, filename);
778  }
779  else if (pdef->name()->value() == "text") {
780  GetDefText(text, pdef, filename);
781  text = Game::GetText(text);
782  }
783  else if (pdef->name()->value() == "asset_id") {
784  GetDefNumber(asset_id, pdef, filename);
785  }
786  else if (pdef->name()->value() == "target_id") {
787  GetDefNumber(target_id, pdef, filename);
788  }
789  else if (pdef->name()->value() == "target_iff") {
790  GetDefNumber(target_iff, pdef, filename);
791  }
792 
793  else if (pdef->name()->value() == "asset_kill") {
794  if (!action)
795  action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
796 
797  if (action) {
798  char txt[64];
799  GetDefText(txt, pdef, filename);
800  action->AssetKills().append(new (__FILE__,__LINE__) Text(txt));
801  }
802  }
803 
804  else if (pdef->name()->value() == "target_kill") {
805  if (!action)
806  action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
807 
808  if (action) {
809  char txt[64];
810  GetDefText(txt, pdef, filename);
811  action->TargetKills().append(new (__FILE__,__LINE__) Text(txt));
812  }
813  }
814 
815  else if (pdef->name()->value() == "req") {
816  if (!action)
817  action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
818 
819  if (!pdef->term() || !pdef->term()->isStruct()) {
820  ::Print("WARNING: action req struct missing in '%s'\n", filename);
821  }
822  else if (action) {
823  TermStruct* val2 = pdef->term()->isStruct();
824 
825  int act = 0;
826  int stat = CombatAction::COMPLETE;
827  bool not = false;
828 
829  Combatant* c1 = 0;
830  Combatant* c2 = 0;
831  int comp = 0;
832  int score = 0;
833  int intel = 0;
834  int gtype = 0;
835  int gid = 0;
836 
837  for (int i = 0; i < val2->elements()->size(); i++) {
838  TermDef* pdef2 = val2->elements()->at(i)->isDef();
839  if (pdef2) {
840  if (pdef2->name()->value() == "action") {
841  GetDefNumber(act, pdef2, filename);
842  }
843  else if (pdef2->name()->value() == "status") {
844  char txt[64];
845  GetDefText(txt, pdef2, filename);
846  stat = CombatAction::StatusFromName(txt);
847  }
848  else if (pdef2->name()->value() == "not") {
849  GetDefBool(not, pdef2, filename);
850  }
851 
852  else if (pdef2->name()->value() == "c1") {
853  char txt[64];
854  GetDefText(txt, pdef2, filename);
855  c1 = GetCombatant(txt);
856  }
857  else if (pdef2->name()->value() == "c2") {
858  char txt[64];
859  GetDefText(txt, pdef2, filename);
860  c2 = GetCombatant(txt);
861  }
862  else if (pdef2->name()->value() == "comp") {
863  char txt[64];
864  GetDefText(txt, pdef2, filename);
865  comp = CombatActionReq::CompFromName(txt);
866  }
867  else if (pdef2->name()->value() == "score") {
868  GetDefNumber(score, pdef2, filename);
869  }
870  else if (pdef2->name()->value() == "intel") {
871  if (pdef2->term()->isNumber()) {
872  GetDefNumber(intel, pdef2, filename);
873  }
874  else if (pdef2->term()->isText()) {
875  char txt[64];
876  GetDefText(txt, pdef2, filename);
877  intel = Intel::IntelFromName(txt);
878  }
879  }
880  else if (pdef2->name()->value() == "group_type") {
881  char type_name[64];
882  GetDefText(type_name, pdef2, filename);
883  gtype = CombatGroup::TypeFromName(type_name);
884  }
885  else if (pdef2->name()->value() == "group_id") {
886  GetDefNumber(gid, pdef2, filename);
887  }
888  }
889  }
890 
891  if (act)
892  action->AddRequirement(act, stat, not);
893 
894  else if (gtype)
895  action->AddRequirement(c1, gtype, gid, comp, score, intel);
896 
897  else
898  action->AddRequirement(c1, c2, comp, score);
899  }
900  }
901  }
902  }
903 
904  if (!action)
905  action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
906 
907  if (action) {
908  action->SetSource(source);
909  action->SetOpposingType(opp_type);
910  action->SetLocation(loc);
911  action->SetSystem(system);
912  action->SetRegion(region);
913  action->SetFilename(file);
914  action->SetImageFile(image);
915  action->SetSceneFile(scene);
916  action->SetCount(count);
917  action->SetStartAfter(start_after);
918  action->SetStartBefore(start_before);
919  action->SetMinRank(min_rank);
920  action->SetMaxRank(max_rank);
921  action->SetDelay(delay);
922  action->SetProbability(probability);
923 
924  action->SetAssetId(asset_id);
925  action->SetAssetType(asset_type);
926  action->SetTargetId(target_id);
927  action->SetTargetIFF(target_iff);
928  action->SetTargetType(target_type);
929  action->SetText(text);
930 
931  actions.append(action);
932  }
933 }
934 
935 // +--------------------------------------------------------------------+
936 
939 {
940  CombatGroup* orig_parent = group->GetParent();
941 
942  if (orig_parent) {
943  CombatGroup* clone_parent = clone->FindGroup(orig_parent->Type(), orig_parent->GetID());
944 
945  if (!clone_parent)
946  clone_parent = CloneOver(force, clone, orig_parent);
947 
948  CombatGroup* new_clone = clone->FindGroup(group->Type(), group->GetID());
949 
950  if (!new_clone) {
951  new_clone = group->Clone(false);
952  clone_parent->AddComponent(new_clone);
953  }
954 
955  return new_clone;
956  }
957  else {
958  return clone;
959  }
960 }
961 
962 // +--------------------------------------------------------------------+
963 
964 void
966 {
967  bool ok = true;
968  BYTE* block = 0;
969  const char* filename = "Missions.def";
970 
971  loader->UseFileSystem(true);
972  loader->LoadBuffer(filename, block, true);
974  Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
975 
976  Term* term = parser.ParseTerm();
977 
978  if (!term) {
979  return;
980  }
981  else {
982  TermText* file_type = term->isText();
983  if (!file_type || file_type->value() != "MISSIONLIST") {
984  ::Print("WARNING: invalid mission list file '%s'\n", filename);
985  term->print(10);
986  return;
987  }
988  }
989 
990  do {
991  delete term; term = 0;
992  term = parser.ParseTerm();
993 
994  if (term) {
995  TermDef* def = term->isDef();
996  if (def) {
997  if (def->name()->value() == "mission") {
998  if (!def->term() || !def->term()->isStruct()) {
999  ::Print("WARNING: mission struct missing in '%s'\n", filename);
1000  }
1001  else {
1002  TermStruct* val = def->term()->isStruct();
1003 
1004  int id = 0;
1005  Text name;
1006  Text desc;
1007  char script[256];
1008  char system[256];
1009  char region[256];
1010  int start = 0;
1011  int type = 0;
1012 
1013  ZeroMemory(script, sizeof(script));
1014 
1015  strcpy_s(system, "Unknown");
1016  strcpy_s(region, "Unknown");
1017 
1018  for (int i = 0; i < val->elements()->size(); i++) {
1019  TermDef* pdef = val->elements()->at(i)->isDef();
1020  if (pdef) {
1021  if (pdef->name()->value() == "id")
1022  GetDefNumber(id, pdef, filename);
1023 
1024  else if (pdef->name()->value() == "name") {
1025  GetDefText(name, pdef, filename);
1026  name = Game::GetText(name);
1027  }
1028 
1029  else if (pdef->name()->value() == "desc") {
1030  GetDefText(desc, pdef, filename);
1031  if (desc.length() > 0 && desc.length() < 32)
1032  desc = Game::GetText(desc);
1033  }
1034 
1035  else if (pdef->name()->value() == "start")
1036  GetDefTime(start, pdef, filename);
1037 
1038  else if (pdef->name()->value() == "system")
1039  GetDefText(system, pdef, filename);
1040 
1041  else if (pdef->name()->value() == "region")
1042  GetDefText(region, pdef, filename);
1043 
1044  else if (pdef->name()->value() == "script")
1045  GetDefText(script, pdef, filename);
1046 
1047  else if (pdef->name()->value() == "type") {
1048  char typestr[64];
1049  GetDefText(typestr, pdef, filename);
1050  type = Mission::TypeFromName(typestr);
1051  }
1052  }
1053  }
1054 
1055  MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
1056  if (info) {
1057  info->id = id;
1058  info->name = name;
1059  info->description = desc;
1060  info->system = system;
1061  info->region = region;
1062  info->script = script;
1063  info->start = start;
1064  info->type = type;
1065  info->mission = 0;
1066 
1067  info->script.setSensitive(false);
1068 
1069  missions.append(info);
1070  }
1071  else {
1072  ok = false;
1073  }
1074  }
1075  }
1076  }
1077  }
1078  }
1079  while (term);
1080 
1081  loader->ReleaseBuffer(block);
1082 
1083  if (!ok)
1084  Unload();
1085 }
1086 
1087 void
1089 {
1090  bool ok = true;
1091  List<Text> files;
1092  loader->UseFileSystem(true);
1093  loader->ListFiles("*.*", files);
1094 
1095  for (int i = 0; i < files.size(); i++) {
1096  Text file = *files[i];
1097  file.setSensitive(false);
1098 
1099  if (file.contains(".def")) {
1100  BYTE* block = 0;
1101  const char* filename = file.data();
1102 
1103  loader->UseFileSystem(true);
1104  loader->LoadBuffer(filename, block, true);
1106 
1107  if (strstr((const char*) block, "MISSION") == (const char*) block) {
1108  Text name;
1109  Text desc;
1110  Text system = "Unknown";
1111  Text region = "Unknown";
1112  int start = 0;
1113  int type = 0;
1114  int msn_id = 0;
1115 
1116  Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
1117  Term* term = parser.ParseTerm();
1118 
1119  if (!term) {
1120  Print("ERROR: could not parse '%s'\n", filename);
1121  continue;
1122  }
1123  else {
1124  TermText* file_type = term->isText();
1125  if (!file_type || file_type->value() != "MISSION") {
1126  Print("ERROR: invalid mission file '%s'\n", filename);
1127  term->print(10);
1128  continue;
1129  }
1130  }
1131 
1132  do {
1133  delete term; term = 0;
1134  term = parser.ParseTerm();
1135 
1136  if (term) {
1137  TermDef* def = term->isDef();
1138  if (def) {
1139  if (def->name()->value() == "name") {
1140  GetDefText(name, def, filename);
1141  name = Game::GetText(name);
1142  }
1143 
1144  else if (def->name()->value() == "type") {
1145  char typestr[64];
1146  GetDefText(typestr, def, filename);
1147  type = Mission::TypeFromName(typestr);
1148  }
1149 
1150  else if (def->name()->value() == "id")
1151  GetDefNumber(msn_id, def, filename);
1152 
1153  else if (def->name()->value() == "desc") {
1154  GetDefText(desc, def, filename);
1155  if (desc.length() > 0 && desc.length() < 32)
1156  desc = Game::GetText(desc);
1157  }
1158 
1159  else if (def->name()->value() == "system")
1160  GetDefText(system, def, filename);
1161 
1162  else if (def->name()->value() == "region")
1163  GetDefText(region, def, filename);
1164 
1165  else if (def->name()->value() == "start")
1166  GetDefTime(start, def, filename);
1167  }
1168  }
1169  }
1170  while (term);
1171 
1172  loader->ReleaseBuffer(block);
1173 
1174  if (strstr(filename, "custom") == filename) {
1175  sscanf_s(filename+6, "%d", &msn_id);
1176 
1177  if (msn_id <= i)
1178  msn_id = i+1;
1179  }
1180  else if (msn_id < 1) {
1181  msn_id = i+1;
1182  }
1183 
1184  MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
1185  if (info) {
1186  info->id = msn_id;
1187  info->name = name;
1188  info->type = type;
1189  info->description = desc;
1190  info->system = system;
1191  info->region = region;
1192  info->script = filename;
1193  info->start = start;
1194  info->mission = 0;
1195 
1196  info->script.setSensitive(false);
1197 
1198  missions.append(info);
1199  }
1200  else {
1201  ok = false;
1202  }
1203  }
1204  else {
1205  Print("Invalid Custom Mission File: '%s'\n", filename);
1206  }
1207 
1208  loader->ReleaseBuffer(block);
1209  }
1210  }
1211 
1212  files.destroy();
1213 
1214  if (!ok)
1215  Unload();
1216  else
1217  missions.sort();
1218 }
1219 
1220 void
1222 {
1223  BYTE* block = 0;
1224  const char* filename = "Templates.def";
1225 
1226  loader->UseFileSystem(true);
1227  loader->LoadBuffer(filename, block, true);
1229  Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
1230 
1231  Term* term = parser.ParseTerm();
1232 
1233  if (!term) {
1234  return;
1235  }
1236  else {
1237  TermText* file_type = term->isText();
1238  if (!file_type || file_type->value() != "TEMPLATELIST") {
1239  ::Print("WARNING: invalid template list file '%s'\n", filename);
1240  term->print(10);
1241  return;
1242  }
1243  }
1244 
1245  do {
1246  delete term; term = 0;
1247  term = parser.ParseTerm();
1248 
1249  if (term) {
1250  TermDef* def = term->isDef();
1251  if (def) {
1252  if (def->name()->value() == "mission") {
1253  if (!def->term() || !def->term()->isStruct()) {
1254  ::Print("WARNING: mission struct missing in '%s'\n", filename);
1255  }
1256  else {
1257  TermStruct* val = def->term()->isStruct();
1258 
1259  char name[256];
1260  char script[256];
1261  char region[256];
1262  int id = 0;
1263  int msn_type = 0;
1264  int grp_type = 0;
1265 
1266  int min_rank = 0;
1267  int max_rank = 0;
1268  int action_id = 0;
1269  int action_status = 0;
1270  int exec_once = 0;
1271  int start_before = TIME_NEVER;
1272  int start_after = 0;
1273 
1274  name[0] = 0;
1275  script[0] = 0;
1276  region[0] = 0;
1277 
1278  for (int i = 0; i < val->elements()->size(); i++) {
1279  TermDef* pdef = val->elements()->at(i)->isDef();
1280  if (pdef) {
1281  if (pdef->name()->value() == "id")
1282  GetDefNumber(id, pdef, filename);
1283 
1284  else if (pdef->name()->value() == "name")
1285  GetDefText(name, pdef, filename);
1286 
1287  else if (pdef->name()->value() == "script")
1288  GetDefText(script, pdef, filename);
1289 
1290  else if (pdef->name()->value() == "rgn" || pdef->name()->value() == "region")
1291  GetDefText(region, pdef, filename);
1292 
1293  else if (pdef->name()->value() == "type") {
1294  char typestr[64];
1295  GetDefText(typestr, pdef, filename);
1296  msn_type = Mission::TypeFromName(typestr);
1297  }
1298 
1299  else if (pdef->name()->value() == "group") {
1300  char typestr[64];
1301  GetDefText(typestr, pdef, filename);
1302  grp_type = CombatGroup::TypeFromName(typestr);
1303  }
1304 
1305  else if (pdef->name()->value() == "min_rank")
1306  GetDefNumber(min_rank, pdef, filename);
1307 
1308  else if (pdef->name()->value() == "max_rank")
1309  GetDefNumber(max_rank, pdef, filename);
1310 
1311  else if (pdef->name()->value() == "action_id")
1312  GetDefNumber(action_id, pdef, filename);
1313 
1314  else if (pdef->name()->value() == "action_status")
1315  GetDefNumber(action_status, pdef, filename);
1316 
1317  else if (pdef->name()->value() == "exec_once")
1318  GetDefNumber(exec_once, pdef, filename);
1319 
1320  else if (pdef->name()->value().contains("before")) {
1321  if (pdef->term()->isNumber()) {
1322  GetDefNumber(start_before, pdef, filename);
1323  }
1324  else {
1325  GetDefTime(start_before, pdef, filename);
1326  start_before -= ONE_DAY;
1327  }
1328  }
1329  else if (pdef->name()->value().contains("after")) {
1330  if (pdef->term()->isNumber()) {
1331  GetDefNumber(start_after, pdef, filename);
1332  }
1333  else {
1334  GetDefTime(start_after, pdef, filename);
1335  start_after -= ONE_DAY;
1336  }
1337  }
1338  }
1339  }
1340 
1341  MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
1342  if (info) {
1343  info->id = id;
1344  info->name = name;
1345  info->script = script;
1346  info->region = region;
1347  info->type = msn_type;
1348  info->min_rank = min_rank;
1349  info->max_rank = max_rank;
1350  info->action_id = action_id;
1351  info->action_status = action_status;
1352  info->exec_once = exec_once;
1353  info->start_before = start_before;
1354  info->start_after = start_after;
1355 
1356  info->script.setSensitive(false);
1357 
1358  TemplateList* templist = GetTemplateList(msn_type, grp_type);
1359 
1360  if (!templist) {
1361  templist = new(__FILE__,__LINE__) TemplateList;
1362  templist->mission_type = msn_type;
1363  templist->group_type = grp_type;
1364  templates.append(templist);
1365  }
1366 
1367  templist->missions.append(info);
1368  }
1369  }
1370  }
1371  }
1372  }
1373  }
1374  while (term);
1375 
1376  loader->ReleaseBuffer(block);
1377 }
1378 
1379 // +--------------------------------------------------------------------+
1380 
1381 void
1383 {
1384  if (planners.size() > 0)
1385  planners.destroy();
1386 
1387  CampaignPlan* p;
1388 
1389  // PLAN EVENT MUST BE FIRST PLANNER:
1390  p = new(__FILE__,__LINE__) CampaignPlanEvent(this);
1391  if (p)
1392  planners.append(p);
1393 
1394  p = new(__FILE__,__LINE__) CampaignPlanStrategic(this);
1395  if (p)
1396  planners.append(p);
1397 
1398  p = new(__FILE__,__LINE__) CampaignPlanAssignment(this);
1399  if (p)
1400  planners.append(p);
1401 
1402  p = new(__FILE__,__LINE__) CampaignPlanMovement(this);
1403  if (p)
1404  planners.append(p);
1405 
1406  p = new(__FILE__,__LINE__) CampaignPlanMission(this);
1407  if (p)
1408  planners.append(p);
1409 
1410  if (lockout > 0 && planners.size()) {
1412  while (++plan)
1413  plan->SetLockout(lockout);
1414  }
1415 }
1416 
1417 // +--------------------------------------------------------------------+
1418 
1419 int
1421 {
1422  int iff = 1;
1423 
1424  if (player_group)
1425  iff = player_group->GetIFF();
1426 
1427  return iff;
1428 }
1429 
1430 void
1432 {
1433  if (player_group != pg) {
1434  ::Print("Campaign::SetPlayerGroup(%s)\n", pg ? pg->Name().data() : "0");
1435 
1436  // should verify that the player group is
1437  // actually part of this campaign, first!
1438 
1439  player_group = pg;
1440  player_unit = 0;
1441 
1442  // need to regenerate missions when changing
1443  // player combat group:
1444  if (IsDynamic()) {
1445  ::Print(" destroying mission list...\n");
1446  missions.destroy();
1447  }
1448  }
1449 }
1450 
1451 void
1453 {
1454  if (player_unit != unit) {
1455  ::Print("Campaign::SetPlayerUnit(%s)\n", unit ? unit->Name().data() : "0");
1456 
1457  // should verify that the player unit is
1458  // actually part of this campaign, first!
1459 
1460  player_unit = unit;
1461 
1462  if (unit)
1463  player_group = unit->GetCombatGroup();
1464 
1465  // need to regenerate missions when changing
1466  // player combat unit:
1467  if (IsDynamic()) {
1468  ::Print(" destroying mission list...\n");
1469  missions.destroy();
1470  }
1471  }
1472 }
1473 
1474 // +--------------------------------------------------------------------+
1475 
1476 CombatZone*
1477 Campaign::GetZone(const char* rgn)
1478 {
1480  while (++z) {
1481  if (z->HasRegion(rgn))
1482  return z.value();
1483  }
1484 
1485  return 0;
1486 }
1487 
1488 StarSystem*
1489 Campaign::GetSystem(const char* sys)
1490 {
1491  return Galaxy::GetInstance()->GetSystem(sys);
1492 }
1493 
1494 Combatant*
1495 Campaign::GetCombatant(const char* cname)
1496 {
1498  while (++iter) {
1499  Combatant* c = iter.value();
1500  if (!strcmp(c->Name(), cname))
1501  return c;
1502  }
1503 
1504  return 0;
1505 }
1506 
1507 // +--------------------------------------------------------------------+
1508 
1509 Mission*
1511 {
1512  return GetMission(mission_id);
1513 }
1514 
1515 Mission*
1517 {
1518  if (id < 0) {
1519  ::Print("ERROR - Campaign::GetMission(%d) invalid mission id\n", id);
1520  return 0;
1521  }
1522 
1523  if (mission && mission->Identity() == id) {
1524  return mission;
1525  }
1526 
1527  MissionInfo* info = 0;
1528  for (int i = 0; !info && i < missions.size(); i++)
1529  if (missions[i]->id == id)
1530  info = missions[i];
1531 
1532  if (info) {
1533  if (!info->mission) {
1534  ::Print("Campaign::GetMission(%d) loading mission...\n", id);
1535  info->mission = new(__FILE__,__LINE__) Mission(id, info->script, path);
1536  if (info->mission)
1537  info->mission->Load();
1538  }
1539 
1540  if (IsDynamic()) {
1541  if (info->mission) {
1542  if (!_stricmp(info->mission->Situation(), "Unknown")) {
1543  ::Print("Campaign::GetMission(%d) generating sitrep...\n", id);
1544  CampaignSituationReport sitrep(this, info->mission);
1545  sitrep.GenerateSituationReport();
1546  }
1547  }
1548  else {
1549  ::Print("Campaign::GetMission(%d) could not find/load mission.\n", id);
1550  }
1551  }
1552 
1553  return info->mission;
1554  }
1555 
1556  return 0;
1557 }
1558 
1559 Mission*
1560 Campaign::GetMissionByFile(const char* filename)
1561 {
1562  if (!filename || !*filename) {
1563  ::Print("ERROR - Campaign::GetMissionByFile() invalid filename\n");
1564  return 0;
1565  }
1566 
1567  int id = 0;
1568  int maxid = 0;
1569  MissionInfo* info = 0;
1570 
1571  for (int i = 0; !info && i < missions.size(); i++) {
1572  MissionInfo* m = missions[i];
1573 
1574  if (m->id > maxid)
1575  maxid = m->id;
1576 
1577  if (m->script == filename)
1578  info = m;
1579  }
1580 
1581  if (info) {
1582  id = info->id;
1583 
1584  if (!info->mission) {
1585  ::Print("Campaign::GetMission(%d) loading mission...\n", id);
1586  info->mission = new(__FILE__,__LINE__) Mission(id, info->script, path);
1587  if (info->mission)
1588  info->mission->Load();
1589  }
1590 
1591  if (IsDynamic()) {
1592  if (info->mission) {
1593  if (!_stricmp(info->mission->Situation(), "Unknown")) {
1594  ::Print("Campaign::GetMission(%d) generating sitrep...\n", id);
1595  CampaignSituationReport sitrep(this, info->mission);
1596  sitrep.GenerateSituationReport();
1597  }
1598  }
1599  else {
1600  ::Print("Campaign::GetMission(%d) could not find/load mission.\n", id);
1601  }
1602  }
1603  }
1604 
1605  else {
1606  info = new(__FILE__,__LINE__) MissionInfo;
1607  if (info) {
1608  info->id = maxid+1;
1609  info->name = "New Custom Mission";
1610  info->script = filename;
1611  info->mission = new(__FILE__,__LINE__) Mission(info->id, info->script, "Mods/Missions/");
1612  info->mission->SetName(info->name);
1613 
1614  info->script.setSensitive(false);
1615 
1616  missions.append(info);
1617  }
1618  }
1619 
1620  return info->mission;
1621 }
1622 
1623 MissionInfo*
1625 {
1626  int id = 0;
1627  int maxid = 0;
1628  MissionInfo* info = 0;
1629 
1631  maxid = 10;
1632 
1633  for (int i = 0; !info && i < missions.size(); i++) {
1634  MissionInfo* m = missions[i];
1635 
1636  if (m->id > maxid)
1637  maxid = m->id;
1638  }
1639 
1640  char filename[64];
1641  sprintf_s(filename, "custom%03d.def", maxid+1);
1642 
1643  info = new(__FILE__,__LINE__) MissionInfo;
1644  if (info) {
1645  info->id = maxid+1;
1646  info->name = "New Custom Mission";
1647  info->script = filename;
1648  info->mission = new(__FILE__,__LINE__) Mission(info->id, filename, path);
1649  info->mission->SetName(info->name);
1650 
1651  info->script.setSensitive(false);
1652 
1653  missions.append(info);
1654  }
1655 
1656  return info;
1657 }
1658 
1659 void
1661 {
1662  if (id < 0) {
1663  ::Print("ERROR - Campaign::DeleteMission(%d) invalid mission id\n", id);
1664  return;
1665  }
1666 
1667  MissionInfo* m = 0;
1668  int index = -1;
1669 
1670  for (int i = 0; !m && i < missions.size(); i++) {
1671  if (missions[i]->id == id) {
1672  m = missions[i];
1673  index = i;
1674  }
1675  }
1676 
1677  if (m) {
1678  char full_path[256];
1679 
1680  if (path[strlen(path)-1] == '/')
1681  sprintf_s(full_path, "%s%s", path, m->script.data());
1682  else
1683  sprintf_s(full_path, "%s/%s", path, m->script.data());
1684 
1685  DeleteFile(full_path);
1686  Load();
1687  }
1688 
1689  else {
1690  ::Print("ERROR - Campaign::DeleteMission(%d) could not find mission\n", id);
1691  }
1692 }
1693 
1694 MissionInfo*
1696 {
1697  if (id < 0) {
1698  ::Print("ERROR - Campaign::GetMissionInfo(%d) invalid mission id\n", id);
1699  return 0;
1700  }
1701 
1702  MissionInfo* m = 0;
1703  for (int i = 0; !m && i < missions.size(); i++)
1704  if (missions[i]->id == id)
1705  m = missions[i];
1706 
1707  if (m) {
1708  if (!m->mission) {
1709  m->mission = new(__FILE__,__LINE__) Mission(id, m->script);
1710  if (m->mission)
1711  m->mission->Load();
1712  }
1713 
1714  return m;
1715  }
1716 
1717  else {
1718  ::Print("ERROR - Campaign::GetMissionInfo(%d) could not find mission\n", id);
1719  }
1720 
1721  return 0;
1722 }
1723 
1724 void
1726 {
1727  if (mission && mission == net_mission) {
1728  delete net_mission;
1729  net_mission = 0;
1730  }
1731 
1732  mission = 0;
1733 
1734  if (id >= 0 && id < missions.size()) {
1735  MissionInfo* m = missions[id];
1736  delete m->mission;
1737  m->mission = 0;
1738  }
1739 }
1740 
1741 void
1742 Campaign::LoadNetMission(int id, const char* net_mission_script)
1743 {
1744  if (mission && mission == net_mission) {
1745  delete net_mission;
1746  net_mission = 0;
1747  }
1748 
1749  mission_id = id;
1750  mission = new(__FILE__,__LINE__) Mission(id);
1751 
1752  if (mission && mission->ParseMission(net_mission_script))
1753  mission->Validate();
1754 
1755  net_mission = mission;
1756 }
1757 
1758 // +--------------------------------------------------------------------+
1759 
1760 CombatAction*
1761 Campaign::FindAction(int action_id)
1762 {
1764  while (++iter) {
1765  CombatAction* a = iter.value();
1766 
1767  if (a->Identity() == action_id)
1768  return a;
1769  }
1770 
1771  return 0;
1772 }
1773 
1774 // +--------------------------------------------------------------------+
1775 
1776 MissionInfo*
1777 Campaign::FindMissionTemplate(int mission_type, CombatGroup* player_group)
1778 {
1779  MissionInfo* info = 0;
1780 
1781  if (!player_group)
1782  return info;
1783 
1784  TemplateList* templates = GetTemplateList(mission_type, player_group->Type());
1785 
1786  if (!templates || !templates->missions.size())
1787  return info;
1788 
1789  int tries = 0;
1790  int msize = templates->missions.size();
1791 
1792  while (!info && tries < msize) {
1793  // get next template:
1794  int index = templates->index;
1795 
1796  if (index >= msize)
1797  index = 0;
1798 
1799  info = templates->missions[index];
1800  templates->index = index + 1;
1801  tries++;
1802 
1803  // validate the template:
1804  if (info) {
1805  if (info->action_id) {
1806  CombatAction* a = FindAction(info->action_id);
1807 
1808  if (a && a->Status() != info->action_status)
1809  info = 0;
1810  }
1811 
1812  if (info && !info->IsAvailable()) {
1813  info = 0;
1814  }
1815  }
1816  }
1817 
1818  return info;
1819 }
1820 
1821 // +--------------------------------------------------------------------+
1822 
1823 TemplateList*
1824 Campaign::GetTemplateList(int msn_type, int grp_type)
1825 {
1826  for (int i = 0; i < templates.size(); i++) {
1827  if (templates[i]->mission_type == msn_type &&
1828  templates[i]->group_type == grp_type)
1829  return templates[i];
1830  }
1831 
1832  return 0;
1833 }
1834 
1835 // +--------------------------------------------------------------------+
1836 
1837 void
1839 {
1840  ::Print("Campaign::SetMissionId(%d)\n", id);
1841 
1842  if (id > 0)
1843  mission_id = id;
1844  else
1845  ::Print(" retaining mission id = %d\n", mission_id);
1846 }
1847 
1848 // +--------------------------------------------------------------------+
1849 
1850 double
1852 {
1853  return StarSystem::Stardate();
1854 }
1855 
1856 // +--------------------------------------------------------------------+
1857 
1858 void
1860 {
1861  if (player_group || !g) return;
1862 
1863  if (g->Type() == type && !g->IsReserve() && g->Value() > 0) {
1864  player_group = g;
1865  player_unit = 0;
1866  return;
1867  }
1868 
1869  for (int i = 0; i < g->GetComponents().size(); i++)
1870  SelectDefaultPlayerGroup(g->GetComponents()[i], type);
1871 }
1872 
1873 // +--------------------------------------------------------------------+
1874 
1875 void
1877 {
1878  // if this is a new campaign,
1879  // create combatants from roster and template:
1880  if (IsDynamic() && combatants.isEmpty()) {
1881  DataLoader* loader = DataLoader::GetLoader();
1882  loader->SetDataPath(path);
1883  LoadCampaign(loader, true);
1884  }
1885 
1887 
1888  // load scripted missions:
1889  if (IsScripted() && actions.isEmpty()) {
1890  DataLoader* loader = DataLoader::GetLoader();
1891  loader->SetDataPath(path);
1892  LoadCampaign(loader, true);
1893 
1895  while (++m) {
1896  GetMission(m->id);
1897  }
1898  }
1899 
1900  CheckPlayerGroup();
1901 }
1902 
1903 void
1905 {
1906  ::Print("Campaign::Start()\n");
1907 
1908  Prep();
1909 
1910  // create planners:
1911  CreatePlanners();
1913 }
1914 
1915 void
1917 {
1918  if (InCutscene())
1919  return;
1920 
1921  time = Stardate() - startTime;
1922 
1923  if (status < CAMPAIGN_ACTIVE)
1924  return;
1925 
1926  if (IsDynamic()) {
1927  bool completed = false;
1929  while (++m) {
1930  if (m->mission && m->mission->IsComplete()) {
1931  ::Print("Campaign::ExecFrame() completed mission %d '%s'\n", m->id, m->name.data());
1932  completed = true;
1933  }
1934  }
1935 
1936  if (completed) {
1937  ::Print("Campaign::ExecFrame() destroying mission list after completion...\n");
1938  missions.destroy();
1939 
1941  time += 10 * 3600;
1942  else
1943  time += 20 * 3600;
1944 
1946  }
1947  else {
1948  m.reset();
1949 
1950  while (++m) {
1951  if (m->start < time && !m->mission->IsActive()) {
1952  MissionInfo* info = m.removeItem();
1953 
1954  if (info)
1955  ::Print("Campaign::ExecFrame() deleting expired mission %d start: %d current: %d\n",
1956  info->id,
1957  info->start,
1958  (int) time);
1959 
1960  delete info;
1961  }
1962  }
1963  }
1964 
1965  // PLAN EVENT MUST BE FIRST PLANNER:
1966  if (loaded_from_savegame && planners.size() > 0) {
1968  plan_event->ExecScriptedEvents();
1969 
1970  loaded_from_savegame = false;
1971  }
1972 
1974  while (++plan) {
1975  CheckPlayerGroup();
1976  plan->ExecFrame();
1977  }
1978 
1979  CheckPlayerGroup();
1980 
1981  // Auto save game AFTER planners have run!
1982  // This is done to ensure that campaign status
1983  // is properly recorded after winning or losing
1984  // the campaign.
1985 
1986  if (completed) {
1987  CampaignSaveGame save(this);
1988  save.SaveAuto();
1989  }
1990  }
1991  else {
1992  // PLAN EVENT MUST BE FIRST PLANNER:
1993  if (planners.size() > 0) {
1995  plan_event->ExecScriptedEvents();
1996  }
1997  }
1998 }
1999 
2000 // +--------------------------------------------------------------------+
2001 
2002 void
2004 {
2005  lockout = seconds;
2006 }
2007 
2008 void
2010 {
2011  if (!player_group || player_group->IsReserve() || player_group->CalcValue() < 1) {
2012  int player_iff = GetPlayerIFF();
2013  player_group = 0;
2014 
2015  CombatGroup* force = 0;
2016  for (int i = 0; i < combatants.size() && !force; i++) {
2017  if (combatants[i]->GetIFF() == player_iff) {
2018  force = combatants[i]->GetForce();
2019  }
2020  }
2021 
2022  if (force) {
2023  force->CalcValue();
2025 
2026  if (!player_group)
2028  }
2029  }
2030 
2031  if (player_unit && player_unit->GetValue() < 1)
2032  SetPlayerUnit(0);
2033 }
2034 
2035 // +--------------------------------------------------------------------+
2036 
2037 void FPU2Extended();
2038 void FPURestore();
2039 
2040 void
2042 {
2043  Mission* m = GetMission();
2044 
2045  if (m) {
2046  ::Print("\n\nCampaign Start Mission - %d. '%s'\n", m->Identity(), m->Name());
2047 
2048  if (!scripted) {
2049  FPU2Extended();
2050 
2051  double gtime = (double) Game::GameTime() / 1000.0;
2052  double base = startTime + m->Start() - 15 - gtime;
2053 
2055 
2056  double current_time = Stardate() - startTime;
2057 
2058  char buffer[32];
2059  FormatDayTime(buffer, current_time);
2060  ::Print(" current time: %s\n", buffer);
2061 
2062  FormatDayTime(buffer, m->Start());
2063  ::Print(" mission start: %s\n", buffer);
2064  ::Print("\n");
2065  }
2066  }
2067 }
2068 
2069 void
2071 {
2072  ::Print("Campaign::RollbackMission()\n");
2073 
2074  Mission* m = GetMission();
2075 
2076  if (m) {
2077  if (!scripted) {
2078  FPU2Extended();
2079 
2080  double gtime = (double) Game::GameTime() / 1000.0;
2081  double base = startTime + m->Start() - 60 - gtime;
2082 
2084 
2085  double current_time = Stardate() - startTime;
2086  ::Print(" mission start: %d\n", m->Start());
2087  ::Print(" current time: %d\n", (int) current_time);
2088  }
2089 
2090  m->SetActive(false);
2091  m->SetComplete(false);
2092  }
2093 }
2094 
2095 // +--------------------------------------------------------------------+
2096 
2097 bool
2099 {
2101  return stars ? stars->InCutscene() : false;
2102 }
2103 
2104 bool
2106 {
2107  return campaign_id >= DYNAMIC_CAMPAIGN &&
2109 }
2110 
2111 bool
2113 {
2114  return campaign_id == TRAINING_CAMPAIGN;
2115 }
2116 
2117 bool
2119 {
2120  return scripted;
2121 }
2122 
2123 bool
2125 {
2126  return sequential;
2127 }
2128 
2129 // +--------------------------------------------------------------------+
2130 
2131 static CombatGroup* FindGroup(CombatGroup* g, int type, int id)
2132 {
2133  if (g->Type() == type && g->GetID() == id)
2134  return g;
2135 
2136  CombatGroup* result = 0;
2137 
2138  ListIter<CombatGroup> subgroup = g->GetComponents();
2139  while (++subgroup && !result)
2140  result = FindGroup(subgroup.value(), type, id);
2141 
2142  return result;
2143 }
2144 
2145 CombatGroup*
2146 Campaign::FindGroup(int iff, int type, int id)
2147 {
2148  CombatGroup* result = 0;
2149 
2150  ListIter<Combatant> combatant = combatants;
2151  while (++combatant && !result) {
2152  if (combatant->GetIFF() == iff) {
2153  result = ::FindGroup(combatant->GetForce(), type, id);
2154  }
2155  }
2156 
2157  return result;
2158 }
2159 
2160 // +--------------------------------------------------------------------+
2161 
2162 static void FindGroups(CombatGroup* g, int type, CombatGroup* near_group,
2163 List<CombatGroup>& groups)
2164 {
2165  if (g->Type() == type && g->IntelLevel() > Intel::RESERVE) {
2166  if (!near_group || g->GetAssignedZone() == near_group->GetAssignedZone())
2167  groups.append(g);
2168  }
2169 
2170  ListIter<CombatGroup> subgroup = g->GetComponents();
2171  while (++subgroup)
2172  FindGroups(subgroup.value(), type, near_group, groups);
2173 }
2174 
2175 CombatGroup*
2176 Campaign::FindGroup(int iff, int type, CombatGroup* near_group)
2177 {
2178  CombatGroup* result = 0;
2179  List<CombatGroup> groups;
2180 
2181  ListIter<Combatant> combatant = combatants;
2182  while (++combatant) {
2183  if (combatant->GetIFF() == iff) {
2184  FindGroups(combatant->GetForce(), type, near_group, groups);
2185  }
2186  }
2187 
2188  if (groups.size() > 0) {
2189  int index = (int) Random(0, groups.size());
2190  if (index >= groups.size()) index = groups.size() - 1;
2191  result = groups[index];
2192  }
2193 
2194  return result;
2195 }
2196 
2197 // +--------------------------------------------------------------------+
2198 
2199 static void FindStrikeTargets(CombatGroup* g, CombatGroup* strike_group,
2200 List<CombatGroup>& groups)
2201 {
2202  if (!strike_group || !strike_group->GetAssignedZone()) return;
2203 
2204  if (g->IsStrikeTarget() && g->IntelLevel() > Intel::RESERVE) {
2205  if (strike_group->GetAssignedZone() == g->GetAssignedZone() ||
2206  strike_group->GetAssignedZone()->HasRegion(g->GetRegion()))
2207  groups.append(g);
2208  }
2209 
2210  ListIter<CombatGroup> subgroup = g->GetComponents();
2211  while (++subgroup)
2212  FindStrikeTargets(subgroup.value(), strike_group, groups);
2213 }
2214 
2215 CombatGroup*
2217 {
2218  CombatGroup* result = 0;
2219 
2220  List<CombatGroup> groups;
2221 
2222  ListIter<Combatant> combatant = GetCombatants();
2223  while (++combatant) {
2224  if (combatant->GetIFF() != 0 && combatant->GetIFF() != iff) {
2225  FindStrikeTargets(combatant->GetForce(), strike_group, groups);
2226  }
2227  }
2228 
2229  if (groups.size() > 0) {
2230  int index = rand() % groups.size();
2231  result = groups[index];
2232  }
2233 
2234  return result;
2235 }
2236 
2237 // +--------------------------------------------------------------------+
2238 
2239 void
2241 {
2243  while (++iter) {
2244  CombatAction* a = iter.value();
2245 
2246  if (a->IsAvailable())
2248  }
2249 
2250  updateTime = time;
2251 }
2252 
2253 // +--------------------------------------------------------------------+
2254 
2255 int
2257 {
2258  int score_us = 0;
2259  int score_them = 0;
2260 
2261  if (player_group) {
2262  int iff = player_group->GetIFF();
2263 
2265  while (++iter) {
2266  Combatant* c = iter.value();
2267 
2268  if (iff <= 1) {
2269  if (c->GetIFF() <= 1)
2270  score_us += c->Score();
2271  else
2272  score_them += c->Score();
2273  }
2274 
2275  else {
2276  if (c->GetIFF() <= 1)
2277  score_them += c->Score();
2278  else
2279  score_us += c->Score();
2280  }
2281  }
2282  }
2283 
2284  return score_us - score_them;
2285 }
2286 
2287 // +--------------------------------------------------------------------+
2288 
2289 void
2291 {
2292  status = s;
2293 
2294  // record the win in player profile:
2295  if (status == CAMPAIGN_SUCCESS) {
2296  Player* player = Player::GetCurrentPlayer();
2297 
2298  if (player)
2300  }
2301 
2302  if (status > CAMPAIGN_ACTIVE) {
2303  ::Print("Campaign::SetStatus() destroying mission list at campaign end\n");
2304  missions.destroy();
2305  }
2306 }
2307 
2308 // +--------------------------------------------------------------------+
2309 
2310 static void GetCombatUnits(CombatGroup* g, List<CombatUnit>& units)
2311 {
2312  if (g) {
2313  ListIter<CombatUnit> unit = g->GetUnits();
2314  while (++unit) {
2315  CombatUnit* u = unit.value();
2316 
2317  if (u->Count() - u->DeadCount() > 0)
2318  units.append(u);
2319  }
2320 
2322  while (++comp) {
2323  CombatGroup* g2 = comp.value();
2324 
2325  if (!g2->IsReserve())
2326  GetCombatUnits(g2, units);
2327  }
2328  }
2329 }
2330 
2331 int
2333 {
2334  units.clear();
2335 
2337  while (++iter) {
2338  Combatant* c = iter.value();
2339 
2340  if (iff < 0 || c->GetIFF() == iff) {
2341  GetCombatUnits(c->GetForce(), units);
2342  }
2343  }
2344 
2345  return units.size();
2346 }