Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
CombatGroup.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright (C) 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: CombatGroup.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  An element in the dynamic campaign
13 */
14 
15 #include "MemDebug.h"
16 #include "CombatGroup.h"
17 #include "CombatUnit.h"
18 #include "CombatZone.h"
19 #include "Combatant.h"
20 #include "CombatAssignment.h"
21 #include "Campaign.h"
22 #include "ShipDesign.h"
23 #include "Ship.h"
24 
25 #include "Game.h"
26 #include "DataLoader.h"
27 #include "ParseUtil.h"
28 
29 // +----------------------------------------------------------------------+
30 
31 CombatGroup::CombatGroup(int t, int n, const char* s, int iff_code, int e, CombatGroup* p)
32 : type(t), id(n), name(s), iff(iff_code), enemy_intel(e),
33 parent(p), value(0), plan_value(0), unit_index(0), combatant(0),
34 expanded(false), sorties(0), kills(0), points(0),
35 current_zone(0), assigned_zone(0), zone_lock(false)
36 {
37  if (parent)
38  parent->AddComponent(this);
39 }
40 
42 {
43  assignments.destroy();
44  components.destroy();
45  units.destroy();
46 }
47 
48 // +--------------------------------------------------------------------+
49 
50 void
52 {
53  if (g) {
54  g->parent = this;
55  components.append(g);
56  }
57 }
58 
59 // +--------------------------------------------------------------------+
60 
61 bool
63 {
64  switch (type) {
65  case CARRIER_GROUP:
66  case BATTLE_GROUP:
67  case DESTROYER_SQUADRON:
68  case ATTACK_SQUADRON:
69  case FIGHTER_SQUADRON:
70  case INTERCEPT_SQUADRON:
71  case LCA_SQUADRON:
72  return ((CombatGroup*) this)->CalcValue() > 0;
73  }
74 
75  return false;
76 }
77 
78 bool
80 {
81  // neutral / non-combatants are not *strategic* targets
82  // for any combatant:
83  if (iff < 1 || iff >= 100)
84  return false;
85 
86  // civilian / non-combatant are not strategic targets:
87  if (type == PASSENGER ||
88  type == PRIVATE ||
89  type == MEDICAL ||
90  type == HABITAT)
91  return false;
92 
93  // must have units of our own to be targetable:
94  if (units.size() < 1)
95  return false;
96 
97  return ((CombatGroup*) this)->CalcValue() > 0;
98 }
99 
100 bool
102 {
103  if (type >= SUPPORT)
104  return ((CombatGroup*) this)->CalcValue() > 0;
105 
106  return false;
107 }
108 
109 bool
111 {
112  if (type < BATTALION ||
113  type == MINEFIELD || // assault, not strike
114  type == PASSENGER ||
115  type == PRIVATE ||
116  type == MEDICAL ||
117  type == HABITAT)
118  return false;
119 
120  return ((CombatGroup*) this)->CalcValue() > 0;
121 }
122 
123 // +--------------------------------------------------------------------+
124 
125 bool
127 {
128  switch (type) {
129  case CARRIER_GROUP:
130  case BATTLE_GROUP:
131  case DESTROYER_SQUADRON:
132  case ATTACK_SQUADRON:
133  case FIGHTER_SQUADRON:
134  case INTERCEPT_SQUADRON:
135  case LCA_SQUADRON:
136  case COURIER:
137  case MEDICAL:
138  case SUPPLY:
139  case REPAIR:
140  case FREIGHT:
141  case PASSENGER:
142  case PRIVATE:
143  return true;
144  }
145 
146  return false;
147 }
148 
149 // +--------------------------------------------------------------------+
150 
151 bool
153 {
154  switch (type) {
155  case WING:
156  case INTERCEPT_SQUADRON:
157  case FIGHTER_SQUADRON:
158  case ATTACK_SQUADRON:
159  return true;
160  }
161 
162  return false;
163 }
164 
165 bool
167 {
168  switch (type) {
169  case DESTROYER_SQUADRON:
170  case BATTLE_GROUP:
171  case CARRIER_GROUP:
172  return true;
173  }
174 
175  return false;
176 }
177 
178 // +--------------------------------------------------------------------+
179 
180 bool
182 {
183  if (enemy_intel <= Intel::RESERVE)
184  return true;
185 
186  if (parent)
187  return parent->IsReserve();
188 
189  return false;
190 }
191 
192 // +--------------------------------------------------------------------+
193 
194 const int*
196 {
197  static int p[8];
198 
199  ZeroMemory(p, sizeof(p));
200 
201  switch (type) {
202  //case FLEET:
204  p[1] = BATTLE_GROUP;
205  p[2] = CARRIER_GROUP;
206  p[3] = ATTACK_SQUADRON;
207  break;
208 
209  case BATTLE_GROUP: p[0] = BATTLE_GROUP;
210  p[1] = DESTROYER_SQUADRON;
211  p[2] = CARRIER_GROUP;
212  p[3] = ATTACK_SQUADRON;
213  break;
214 
215  case CARRIER_GROUP: p[0] = ATTACK_SQUADRON;
216  p[1] = BATTLE_GROUP;
217  p[2] = DESTROYER_SQUADRON;
218  p[3] = CARRIER_GROUP;
219  break;
220 
221  //case WING:
222  case LCA_SQUADRON:
223  case ATTACK_SQUADRON:
224  case INTERCEPT_SQUADRON:
226  p[1] = FIGHTER_SQUADRON;
227  break;
228 
229  //case BATTALION:
230  case STATION: p[0] = BATTLE_GROUP;
231  p[1] = CARRIER_GROUP;
232  break;
233 
234  case STARBASE:
235  case BATTERY:
236  case MISSILE: p[0] = ATTACK_SQUADRON;
237  p[1] = FIGHTER_SQUADRON;
238  break;
239 
240  //case C3I:
241  case MINEFIELD:
242  case COMM_RELAY:
243  case EARLY_WARNING:
244  case FWD_CONTROL_CTR:
245  case ECM: p[0] = ATTACK_SQUADRON;
246  p[1] = FIGHTER_SQUADRON;
247  p[2] = DESTROYER_SQUADRON;
248  break;
249 
250  //case SUPPORT:
251  case COURIER:
252  case MEDICAL:
253  case SUPPLY:
254  case REPAIR: p[0] = DESTROYER_SQUADRON;
255  p[1] = BATTLE_GROUP;
256  p[2] = ATTACK_SQUADRON;
257  break;
258 
259  //case CIVILIAN:
260 
261  //case WAR_PRODuCTION:
262  case FACTORY:
263  case REFINERY:
264  case RESOURCE: p[0] = ATTACK_SQUADRON;
265  p[1] = FIGHTER_SQUADRON;
266  break;
267 
268  //case INFRASTRUCTURE:
269  case TRANSPORT:
270  case NETWORK:
271  case HABITAT:
272  case STORAGE: p[0] = ATTACK_SQUADRON;
273  p[1] = FIGHTER_SQUADRON;
274  break;
275 
276  //case NON_COM:
277  case FREIGHT:
278  case PASSENGER:
279  case PRIVATE: p[0] = DESTROYER_SQUADRON;
280  p[1] = ATTACK_SQUADRON;
281  break;
282  }
283 
284  return p;
285 }
286 
287 // +--------------------------------------------------------------------+
288 
289 const int*
291 {
292  static int p[8];
293 
294  ZeroMemory(p, sizeof(p));
295 
296  switch (type) {
297  //case FLEET:
298  case CARRIER_GROUP:
299  case BATTLE_GROUP:
300  case DESTROYER_SQUADRON:
301 
302  //case WING:
303  case LCA_SQUADRON:
304  case ATTACK_SQUADRON:
305  case INTERCEPT_SQUADRON:
306  case FIGHTER_SQUADRON: break;
307 
308  //case BATTALION:
309  case STATION: p[0] = BATTLE_GROUP;
310  p[1] = CARRIER_GROUP;
311  p[2] = DESTROYER_SQUADRON;
312  break;
313  case STARBASE:
314  case MINEFIELD:
315  case BATTERY:
316  case MISSILE: p[0] = FIGHTER_SQUADRON;
317  p[1] = INTERCEPT_SQUADRON;
318  break;
319 
320  //case C3I:
321  case COMM_RELAY:
322  case EARLY_WARNING:
323  case FWD_CONTROL_CTR:
324  case ECM: p[0] = FIGHTER_SQUADRON;
325  p[1] = INTERCEPT_SQUADRON;
326  break;
327 
328  //case SUPPORT:
329  case COURIER:
330  case MEDICAL:
331  case SUPPLY:
332  case REPAIR: p[0] = DESTROYER_SQUADRON;
333  p[1] = BATTLE_GROUP;
334  p[2] = ATTACK_SQUADRON;
335  break;
336 
337  //case CIVILIAN:
338 
339  //case WAR_PRODuCTION:
340  case FACTORY:
341  case REFINERY:
342  case RESOURCE: p[0] = FIGHTER_SQUADRON;
343  p[1] = INTERCEPT_SQUADRON;
344  break;
345 
346  //case INFRASTRUCTURE:
347  case TRANSPORT:
348  case NETWORK:
349  case HABITAT:
350  case STORAGE: p[0] = FIGHTER_SQUADRON;
351  p[1] = INTERCEPT_SQUADRON;
352  break;
353 
354  //case NON_COM:
355  case FREIGHT:
356  case PASSENGER:
357  case PRIVATE: p[0] = DESTROYER_SQUADRON;
358  p[1] = BATTLE_GROUP;
359  break;
360  }
361 
362  return p;
363 }
364 
365 // +--------------------------------------------------------------------+
366 
369 {
370  CombatGroup* result = 0;
371 
372  if (type == t && (n < 0 || id == n))
373  result = this;
374 
375  ListIter<CombatGroup> group = components;
376  while (!result && ++group) {
377  result = group->FindGroup(t, n);
378  }
379 
380  return result;
381 }
382 
383 // +--------------------------------------------------------------------+
384 
387 {
388  CombatGroup* clone = new(__FILE__,__LINE__)
389  CombatGroup(type, id, name, iff, enemy_intel);
390 
391  clone->combatant = combatant;
392  clone->region = region;
393  clone->location = location;
394  clone->value = value;
395  clone->expanded = expanded;
396 
397  for (int i = 0; i < units.size(); i++) {
398  CombatUnit* u = new(__FILE__,__LINE__) CombatUnit(*units[i]);
399  u->SetCombatGroup(clone);
400  clone->units.append(u);
401  }
402 
403  if (deep) {
404  for (int i = 0; i < components.size(); i++) {
405  CombatGroup* g = components[i]->Clone(deep);
406  clone->AddComponent(g);
407 
408  if (g->Type() == FIGHTER_SQUADRON ||
409  g->Type() == INTERCEPT_SQUADRON ||
410  g->Type() == ATTACK_SQUADRON ||
411  g->Type() == LCA_SQUADRON) {
412 
413  if (units.size() > 0) {
414  CombatUnit* carrier = units[0];
415 
416  for (int u = 0; u < g->GetUnits().size(); u++) {
417  CombatUnit* unit = g->GetUnits()[u];
418 
419  if (unit->Type() >= Ship::FIGHTER ||
420  unit->Type() <= Ship::LCA) {
421  unit->SetCarrier(carrier);
422  unit->SetRegion(carrier->GetRegion());
423  }
424  }
425  }
426  }
427  }
428  }
429 
430  return clone;
431 }
432 
433 // +--------------------------------------------------------------------+
434 
435 const char*
436 CombatGroup::GetOrdinal() const
437 {
438  static char ordinal[16];
439 
440  int last_two_digits = id % 100;
441 
442  if (last_two_digits > 10 && last_two_digits < 20) {
443  sprintf_s(ordinal, "ordinal.%d", last_two_digits);
444  Text suffix = Game::GetText(ordinal);
445 
446  if (suffix != ordinal)
447  sprintf_s(ordinal, "%d%s", id, suffix.data());
448  else
449  sprintf_s(ordinal, "%dth", id);
450  }
451  else {
452  int last_digit = last_two_digits % 10;
453  sprintf_s(ordinal, "ordinal.%d", last_digit);
454  Text suffix = Game::GetText(ordinal);
455  if (suffix != ordinal)
456  sprintf_s(ordinal, "%d%s", id, suffix.data());
457  else if (last_digit == 1)
458  sprintf_s(ordinal, "%dst", id);
459  else if (last_digit == 2)
460  sprintf_s(ordinal, "%dnd", id);
461  else if (last_digit == 3)
462  sprintf_s(ordinal, "%drd", id);
463  else
464  sprintf_s(ordinal, "%dth", id);
465  }
466 
467  return ordinal;
468 }
469 
470 const char*
472 {
473  static char desc[256];
474  static char name_desc[256];
475 
476  if (name.length())
477  sprintf_s(name_desc, " \"%s\"", (const char*) name);
478  else
479  name_desc[0] = 0;
480 
481  switch (type) {
482  case FORCE: strcpy_s(desc, (const char*) name); break;
483 
484  case FLEET: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FLEET").data(), name_desc); break;
485  case CARRIER_GROUP: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.CARRIER_GROUP").data(), name_desc); break;
486  case BATTLE_GROUP: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTLE_GROUP").data(), name_desc); break;
487  case DESTROYER_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.DESTROYER_SQUADRON").data(), name_desc); break;
488 
489  case WING: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.WING").data(), name_desc); break;
490  case ATTACK_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.ATTACK_SQUADRON").data(), name_desc); break;
491  case FIGHTER_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FIGHTER_SQUADRON").data(), name_desc); break;
492  case INTERCEPT_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.INTERCEPT_SQUADRON").data(), name_desc); break;
493  case LCA_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.LCA_SQUADRON").data(), name_desc); break;
494 
495  case BATTALION: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTALION").data(), name_desc); break;
496  case STATION: sprintf_s(desc, "%s %s", Game::GetText("CombatGroup.STATION").data(), name.data()); break;
497  case STARBASE: sprintf_s(desc, "%s %d%s", Game::GetText("CombatGroup.STARBASE").data(), id, name_desc); break;
498  case MINEFIELD: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MINEFIELD").data(), name_desc); break;
499  case BATTERY: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTERY").data(), name_desc); break;
500  case MISSILE: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MISSILE").data(), name_desc); break;
501 
502  case C3I: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.C3I").data(), name_desc); break;
503  case COMM_RELAY: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.COMM_RELAY").data(), name_desc); break;
504  case EARLY_WARNING: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.EARLY_WARNING").data(), name_desc); break;
505  case FWD_CONTROL_CTR: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FWD_CONTROL_CTR").data(), name_desc); break;
506  case ECM: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.ECM").data(), name_desc); break;
507 
508  case SUPPORT: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.SUPPORT").data(), name_desc); break;
509  case COURIER: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.COURIER").data(), name_desc); break;
510  case SUPPLY: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.SUPPLY").data(), name_desc); break;
511  case REPAIR: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.REPAIR").data(), name_desc); break;
512  case MEDICAL: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MEDICAL").data(), name_desc); break;
513 
514  case CIVILIAN:
515  case WAR_PRODUCTION:
516  case FACTORY:
517  case REFINERY:
518  case RESOURCE: strcpy_s(desc, (const char*) name); break;
519 
520  case INFRASTRUCTURE:
521  case TRANSPORT:
522  case NETWORK:
523  case HABITAT:
524  case STORAGE:
525  case FREIGHT:
526  case PASSENGER:
527  case PRIVATE: strcpy_s(desc, (const char*) name); break;
528 
529  default: sprintf_s(desc, "%s%s", Game::GetText("CombatGroup.default").data(), name_desc); break;
530  }
531 
532  return desc;
533 }
534 
535 const char*
537 {
538  static char desc[256];
539 
540  switch (type) {
541  case FORCE: strcpy_s(desc, (const char*) name); break;
542 
543  case FLEET: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FLEET").data()); break;
544  case CARRIER_GROUP: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.CARRIER_GROUP").data()); break;
545  case BATTLE_GROUP: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTLE_GROUP").data()); break;
546  case DESTROYER_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.DESTROYER_SQUADRON").data()); break;
547 
548  case WING: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.WING").data()); break;
549  case ATTACK_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.ATTACK_SQUADRON").data()); break;
550  case FIGHTER_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FIGHTER_SQUADRON").data()); break;
551  case INTERCEPT_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.INTERCEPT_SQUADRON").data()); break;
552  case LCA_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.LCA_SQUADRON").data()); break;
553 
554  case BATTALION: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTALION").data()); break;
555  case STATION: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.STATION").data()); break;
556  case STARBASE: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.STARBASE").data()); break;
557  case MINEFIELD: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.MINEFIELD").data()); break;
558  case BATTERY: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTERY").data()); break;
559 
560  case C3I: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.C3I").data()); break;
561  case COMM_RELAY: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.COMM_RELAY").data()); break;
562  case EARLY_WARNING: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.EARLY_WARNING").data()); break;
563  case FWD_CONTROL_CTR: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FWD_CONTROL_CTR").data()); break;
564  case ECM: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.ECM").data()); break;
565 
566  case SUPPORT: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.SUPPORT").data()); break;
567  case COURIER: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.COURIER").data()); break;
568  case MEDICAL: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.MEDICAL").data()); break;
569  case SUPPLY: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.SUPPLY").data()); break;
570  case REPAIR: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.REPAIR").data()); break;
571 
572  case CIVILIAN:
573  case WAR_PRODUCTION:
574  case FACTORY:
575  case REFINERY:
576  case RESOURCE: strcpy_s(desc, (const char*) name); break;
577 
578  case INFRASTRUCTURE:
579  case TRANSPORT:
580  case NETWORK:
581  case HABITAT:
582  case STORAGE:
583  case FREIGHT:
584  case PASSENGER:
585  case PRIVATE: strcpy_s(desc, (const char*) name); break;
586 
587  default: sprintf_s(desc, "%s", Game::GetText("CombatGroup.abrv.default").data()); break;
588  }
589 
590  return desc;
591 }
592 
593 // +--------------------------------------------------------------------+
594 
595 double
597 {
598  double t = 0;
599 
600  ListIter<CombatUnit> unit = ((CombatGroup*) this)->units;
601  while (++unit)
602  if (unit->GetNextJumpTime() > t)
603  t = unit->GetNextJumpTime();
604 
605  return t;
606 }
607 
608 // +--------------------------------------------------------------------+
609 
610 void
612 {
613  location = loc;
614 }
615 
616 // +--------------------------------------------------------------------+
617 
618 void
620 {
621  assigned_system = s;
622  assigned_zone = 0;
623  zone_lock = false;
624 
625  ListIter<CombatGroup> iter = components;
626  while (++iter) {
627  CombatGroup* g = iter.value();
628  g->SetAssignedSystem(s);
629  }
630 }
631 
632 void
634 {
635  assigned_zone = z;
636 
637  if (!assigned_zone)
638  zone_lock = false;
639 
640  ListIter<CombatGroup> iter = components;
641  while (++iter) {
642  CombatGroup* g = iter.value();
643  g->SetAssignedZone(z);
644  }
645 }
646 
647 void
649 {
650  if (!zone_lock)
651  assigned_zone = 0;
652 
653  ListIter<CombatGroup> iter = components;
654  while (++iter) {
655  CombatGroup* g = iter.value();
656  g->ClearUnlockedZones();
657  }
658 }
659 
660 void
662 {
663  if (!assigned_zone)
664  zone_lock = false;
665  else
666  zone_lock = lock;
667 
668  if (zone_lock)
669  assigned_system = Text();
670 
671  ListIter<CombatGroup> iter = components;
672  while (++iter) {
673  CombatGroup* g = iter.value();
674  g->SetZoneLock(lock);
675  }
676 }
677 
678 // +--------------------------------------------------------------------+
679 
680 void
682 {
683  if (n < Intel::RESERVE || n > Intel::TRACKED) return;
684 
685  enemy_intel = n;
686 
687  // if this group has been discovered, the entire
688  // branch of the OOB tree must be exposed. Otherwise,
689  // no missions would ever be planned against this
690  // combat group.
691 
692  if (n > Intel::SECRET) {
693  CombatGroup* p = parent;
694  while (p) {
695  if (p->enemy_intel < Intel::KNOWN)
696  p->enemy_intel = Intel::KNOWN;
697 
698  p = p->parent;
699  }
700  }
701 }
702 
703 // +--------------------------------------------------------------------+
704 
705 int
707 {
708  int val = 0;
709 
710  ListIter<CombatUnit> unit = units;
711  while (++unit)
712  val += unit->GetValue();
713 
714  ListIter<CombatGroup> comp = components;
715  while (++comp)
716  val += comp->CalcValue();
717 
718  value = val;
719  return value;
720 }
721 
722 int
724 {
725  int n = 0;
726 
727  CombatGroup* g = (CombatGroup*) this;
728 
729  ListIter<CombatUnit> unit = g->units;
730  while (++unit)
731  n += unit->Count() - unit->DeadCount();
732 
733  CombatGroup* pThis = ((CombatGroup*) this);
734  pThis->live_comp.clear();
735  ListIter<CombatGroup> iter = g->components;
736  while (++iter) {
737  CombatGroup* comp = iter.value();
738 
739  if (!comp->IsReserve()) {
740  int unit_count = comp->CountUnits();
741  if (unit_count > 0)
742  pThis->live_comp.append(comp);
743 
744  n += unit_count;
745  }
746  }
747 
748  return n;
749 }
750 
751 // +--------------------------------------------------------------------+
752 
753 void
755 {
756  assignments.destroy();
757 
758  ListIter<CombatGroup> comp = components;
759  while (++comp)
760  comp->ClearAssignments();
761 }
762 
763 // +--------------------------------------------------------------------+
764 
767 {
768  CombatGroup* p = GetParent();
769 
770  while (p != 0 &&
772  p->Type() != CombatGroup::STATION &&
773  p->Type() != CombatGroup::STARBASE)
774  p = p->GetParent();
775 
776  if (p && p->GetUnits().size())
777  return p;
778 
779  return 0;
780 }
781 
782 CombatUnit*
784 {
785  CombatUnit* result = 0;
786  List<CombatUnit> live;
787 
788  ListIter<CombatUnit> unit = units;
789  while (++unit) {
790  if (unit->Count() - unit->DeadCount() > 0)
791  live.append(unit.value());
792  }
793 
794  if (live.size() > 0) {
795  int ntries = 5;
796  while (!result && ntries-- > 0) {
797  int index = rand() % live.size();
798  result = live[index];
799 
800  int ship_class = result->GetShipClass();
801  if (ship_class >= Ship::CRUISER &&
802  ship_class <= Ship::FARCASTER)
803  result = 0;
804  }
805  }
806 
807  if (!result) {
808  ListIter<CombatGroup> comp = components;
809  while (++comp && !result) {
810  CombatUnit* u = comp->GetRandomUnit();
811  if (u)
812  result = u;
813  }
814  }
815 
816  return result;
817 }
818 
819 CombatUnit*
821 {
822  int tmp_index = unit_index;
823  unit_index = 0;
824  CombatUnit* result = GetNextUnit();
825  unit_index = tmp_index;
826 
827  return result;
828 }
829 
830 CombatUnit*
832 {
833  if (units.size() > 0) {
834  List<CombatUnit> live;
835 
836  ListIter<CombatUnit> unit = units;
837  while (++unit) {
838  if (unit->Count() - unit->DeadCount() > 0)
839  live.append(unit.value());
840  }
841 
842  if (live.size() > 0) {
843  return live[unit_index++ % live.size()];
844  }
845  }
846 
847  if (components.size() > 0) {
848  return components[unit_index % components.size()]->GetNextUnit();
849  }
850 
851  return 0;
852 }
853 
854 CombatUnit*
855 CombatGroup::FindUnit(const char* name)
856 {
857  if (units.size() > 0) {
858  ListIter<CombatUnit> iter = units;
859  while (++iter) {
860  CombatUnit* unit = iter.value();
861  if (unit->Name() == name) {
862  if (unit->Count() - unit->DeadCount() > 0)
863  return unit;
864  else
865  return 0;
866  }
867  }
868  }
869 
870  return 0;
871 }
872 
873 void
875 {
876  region = rgn;
877 
878  ListIter<CombatGroup> comp = components;
879  while (++comp)
880  comp->AssignRegion(rgn);
881 
882  ListIter<CombatUnit> unit = units;
883  while (++unit)
884  unit->SetRegion(rgn);
885 }
886 
887 // +--------------------------------------------------------------------+
888 
889 static const char* group_name[] = {
890  "",
891  "force",
892  "wing",
893  "intercept_squadron",
894  "fighter_squadron",
895  "attack_squadron",
896  "lca_squadron",
897  "fleet",
898  "destroyer_squadron",
899  "battle_group",
900  "carrier_group",
901  "battalion",
902  "minefield",
903  "battery",
904  "missile",
905  "station",
906  "starbase",
907  "c3i",
908  "comm_relay",
909  "early_warning",
910  "fwd_control_ctr",
911  "ecm",
912  "support",
913  "courier",
914  "medical",
915  "supply",
916  "repair",
917  "civilian",
918  "war_production",
919  "factory",
920  "refinery",
921  "resource",
922  "infrastructure",
923  "transport",
924  "network",
925  "habitat",
926  "storage",
927  "non_com",
928  "freight",
929  "passenger",
930  "private"
931 };
932 
933 // +--------------------------------------------------------------------+
934 
935 int
936 CombatGroup::TypeFromName(const char* type_name)
937 {
938  for (int i = FORCE; i < PRIVATE; i++)
939  if (!_stricmp(type_name, group_name[i]))
940  return i;
941 
942  return 0;
943 }
944 
945 const char*
947 {
948  return group_name[type];
949 }
950 
951 // +--------------------------------------------------------------------+
952 
953 int ShipClassFromName(const char* type_name)
954 {
955  return Ship::ClassForName(type_name);
956 }
957 
958 // +--------------------------------------------------------------------+
959 
960 #define GET_DEF_BOOL(n) if (pdef->name()->value()==(#n)) GetDefBool((n), pdef, filename)
961 #define GET_DEF_TEXT(n) if (pdef->name()->value()==(#n)) GetDefText((n), pdef, filename)
962 #define GET_DEF_NUM(n) if (pdef->name()->value()==(#n)) GetDefNumber((n), pdef, filename)
963 #define GET_DEF_VEC(n) if (pdef->name()->value()==(#n)) GetDefVec((n), pdef, filename)
964 
966 CombatGroup::LoadOrderOfBattle(const char* filename, int team, Combatant* combatant)
967 {
968  CombatGroup* force = 0;
969  DataLoader* loader = DataLoader::GetLoader();
970  BYTE* block;
971  loader->LoadBuffer(filename, block, true);
972 
973  Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
974  Term* term = parser.ParseTerm();
975 
976  if (!term) {
977  Print("ERROR: could not parse order of battle '%s'\n", filename);
978  return 0;
979  }
980  else {
981  TermText* file_type = term->isText();
982  if (!file_type || file_type->value() != "ORDER_OF_BATTLE") {
983  Print("ERROR: invalid Order of Battle file '%s'\n", filename);
984  term->print(10);
985  return 0;
986  }
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() == "group") {
998  if (!def->term() || !def->term()->isStruct()) {
999  Print("WARNING: group struct missing in '%s'\n", filename);
1000  }
1001  else {
1002  TermStruct* val = def->term()->isStruct();
1003 
1004  char name[256];
1005  char type[64];
1006  char intel[64];
1007  char region[64];
1008  char system[64];
1009  char parent_type[64];
1010  int parent_id = 0;
1011  int id = 0;
1012  int iff = -1;
1013  Vec3 loc = Vec3(1.0e9f,0.0f,0.0f);
1014 
1015  List<CombatUnit> unit_list;
1016  char unit_name[64];
1017  char unit_regnum[16];
1018  char unit_design[64];
1019  char unit_skin[64];
1020  int unit_class = 0;
1021  int unit_count = 1;
1022  int unit_dead = 0;
1023  int unit_damage = 0;
1024  int unit_heading= 0;
1025  int unit_index = 0;
1026 
1027  *name = 0;
1028  *type = 0;
1029  *intel = 0;
1030  *region = 0;
1031  *system = 0;
1032  *parent_type = 0;
1033  *unit_name = 0;
1034  *unit_regnum = 0;
1035  *unit_design = 0;
1036  *unit_skin = 0;
1037 
1038  strcpy_s(intel, "KNOWN");
1039 
1040  // all groups in this OOB default to the IFF of the main force
1041  if (force)
1042  iff = force->GetIFF();
1043 
1044  for (int i = 0; i < val->elements()->size(); i++) {
1045  TermDef* pdef = val->elements()->at(i)->isDef();
1046  if (pdef && (iff < 0 || team < 0 || iff == team)) {
1047  GET_DEF_TEXT(name);
1048  else GET_DEF_TEXT(type);
1049  else GET_DEF_TEXT(intel);
1050  else GET_DEF_TEXT(region);
1051  else GET_DEF_TEXT(system);
1052  else GET_DEF_VEC(loc);
1053  else GET_DEF_TEXT(parent_type);
1054  else GET_DEF_NUM(parent_id);
1055  else GET_DEF_NUM(iff);
1056  else GET_DEF_NUM(id);
1057  else GET_DEF_NUM(unit_index);
1058 
1059  else if ((iff == team || team < 0) && pdef->name()->value() == "unit") {
1060  if (!pdef->term() || !pdef->term()->isStruct()) {
1061  Print("WARNING: unit struct missing for group '%s' in '%s'\n", name, filename);
1062  }
1063  else {
1064  TermStruct* val = pdef->term()->isStruct();
1065 
1066  char unit_region[64];
1067  char design[256];
1068  Vec3 unit_loc = Vec3(1.0e9f,0.0f,0.0f);
1069  unit_count = 1;
1070 
1071  ZeroMemory(unit_region, sizeof(unit_region));
1072  ZeroMemory(design, sizeof(design));
1073 
1074  for (int i = 0; i < val->elements()->size(); i++) {
1075  TermDef* pdef = val->elements()->at(i)->isDef();
1076  if (pdef) {
1077  if (pdef->name()->value() == "name") {
1078  GetDefText(unit_name, pdef, filename);
1079  }
1080  else if (pdef->name()->value() == "regnum") {
1081  GetDefText(unit_regnum, pdef, filename);
1082  }
1083  else if (pdef->name()->value() == "region") {
1084  GetDefText(unit_region, pdef, filename);
1085  }
1086  else if (pdef->name()->value() == "loc") {
1087  GetDefVec(unit_loc, pdef, filename);
1088  }
1089  else if (pdef->name()->value() == "type") {
1090  char typestr[32];
1091  GetDefText(typestr, pdef, filename);
1092  unit_class = ShipDesign::ClassForName(typestr);
1093  }
1094  else if (pdef->name()->value() == "design") {
1095  GetDefText(unit_design, pdef, filename);
1096  }
1097  else if (pdef->name()->value() == "skin") {
1098  GetDefText(unit_skin, pdef, filename);
1099  }
1100  else if (pdef->name()->value() == "count") {
1101  GetDefNumber(unit_count, pdef, filename);
1102  }
1103  else if (pdef->name()->value() == "dead_count") {
1104  GetDefNumber(unit_dead, pdef, filename);
1105  }
1106  else if (pdef->name()->value() == "damage") {
1107  GetDefNumber(unit_damage, pdef, filename);
1108  }
1109  else if (pdef->name()->value() == "heading") {
1110  GetDefNumber(unit_heading, pdef, filename);
1111  }
1112  }
1113  }
1114 
1115  if (!ShipDesign::CheckName(unit_design)) {
1116  Print("ERROR: invalid design '%s' for unit '%s' in '%s'\n", unit_design, unit_name, filename);
1117  return 0;
1118  }
1119 
1120  CombatUnit* cu = new(__FILE__,__LINE__) CombatUnit(unit_name, unit_regnum, unit_class, unit_design, unit_count, iff);
1121  cu->SetRegion(unit_region);
1122  cu->SetSkin(unit_skin);
1123  cu->MoveTo(unit_loc);
1124  cu->Kill(unit_dead);
1125  cu->SetSustainedDamage(unit_damage);
1126  cu->SetHeading(unit_heading * DEGREES);
1127  unit_list.append(cu);
1128  }
1129  }
1130  }
1131  } // elements
1132 
1133  if (iff >= 0 && (iff == team || team < 0)) {
1134  CombatGroup* parent_group = 0;
1135 
1136  if (force) {
1137  parent_group = force->FindGroup(TypeFromName(parent_type), parent_id);
1138  }
1139 
1140  CombatGroup* g = new(__FILE__,__LINE__)
1141  CombatGroup(TypeFromName(type), id, name, iff, Intel::IntelFromName(intel), parent_group);
1142 
1143  g->region = region;
1144  g->combatant = combatant;
1145  g->unit_index = unit_index;
1146 
1147  if (loc.x >= 1e9) {
1148  if (parent_group)
1149  g->location = parent_group->location;
1150  else
1151  g->location = Vec3(0,0,0);
1152  }
1153  else {
1154  g->location = loc;
1155  }
1156 
1157  if (unit_list.size()) {
1158  unit_list[0]->SetLeader(true);
1159 
1160  ListIter<CombatUnit> u = unit_list;
1161  while (++u) {
1162  u->SetCombatGroup(g);
1163 
1164  if (u->GetRegion().length() < 1) {
1165  u->SetRegion(g->GetRegion());
1166  u->MoveTo(g->Location());
1167  }
1168 
1169  if (parent_group &&
1170  (u->Type() == Ship::FIGHTER ||
1171  u->Type() == Ship::ATTACK)) {
1172 
1173  CombatUnit* carrier = 0;
1174  CombatGroup* p = parent_group;
1175 
1176  while (p && !carrier) {
1177  if (p->units.size() && p->units[0]->Type() == Ship::CARRIER) {
1178  carrier = p->units[0];
1179  u->SetCarrier(carrier);
1180  u->SetRegion(carrier->GetRegion());
1181  }
1182 
1183  p = p->parent;
1184  }
1185  }
1186  }
1187 
1188  g->units.append(unit_list);
1189  }
1190 
1191  if (!force)
1192  force = g;
1193  } // iff == team?
1194  } // group-struct
1195  } // group
1196  } // def
1197  } // term
1198  }
1199  while (term);
1200 
1201  loader->ReleaseBuffer(block);
1202  Print("Order of Battle Loaded (%s).\n", force ? force->Name().data() : "unknown force");
1203 
1204  if (force)
1205  force->CalcValue();
1206 
1207  return force;
1208 }
1209 
1210 // +--------------------------------------------------------------------+
1211 
1212 void
1213 CombatGroup::MergeOrderOfBattle(BYTE* block, const char* filename, int team, Combatant* combatant, Campaign* campaign)
1214 {
1215  CombatGroup* force = 0;
1216 
1217  Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
1218  Term* term = parser.ParseTerm();
1219 
1220  if (!term) {
1221  Print("ERROR: could not parse order of battle '%s'\n", filename);
1222  return;
1223  }
1224  else {
1225  TermText* file_type = term->isText();
1226  if (!file_type || file_type->value() != "SAVEGAME") {
1227  Print("ERROR: invalid Save Game file '%s'\n", filename);
1228  term->print(10);
1229  return;
1230  }
1231  }
1232 
1233 
1234  do {
1235  delete term; term = 0;
1236  term = parser.ParseTerm();
1237 
1238  if (term) {
1239  TermDef* def = term->isDef();
1240  if (def) {
1241  if (def->name()->value() == "group") {
1242  if (!def->term() || !def->term()->isStruct()) {
1243  Print("WARNING: group struct missing in '%s'\n", filename);
1244  }
1245  else {
1246  TermStruct* val = def->term()->isStruct();
1247 
1248  char name[256];
1249  char type[64];
1250  char intel[64];
1251  char region[64];
1252  char system[64];
1253  char zone[64];
1254  bool zone_locked = false;
1255  int id = 0;
1256  int iff = -1;
1257  int sorties = -1;
1258  int kills = -1;
1259  int points = -1;
1260  Vec3 loc = Vec3(1.0e9f,0.0f,0.0f);
1261 
1262  List<CombatUnit> unit_list;
1263  char unit_name[64];
1264  char unit_regnum[16];
1265  char unit_design[64];
1266  int unit_class = 0;
1267  int unit_count = 1;
1268  int unit_dead = 0;
1269  int unit_damage = 0;
1270  int unit_heading= 0;
1271  int unit_index = 0;
1272 
1273  *name = 0;
1274  *type = 0;
1275  *intel = 0;
1276  *region = 0;
1277  *system = 0;
1278  *zone = 0;
1279  *unit_name = 0;
1280  *unit_regnum = 0;
1281  *unit_design = 0;
1282 
1283  strcpy_s(intel, "KNOWN");
1284 
1285  // all groups in this OOB default to the IFF of the main force
1286  if (force)
1287  iff = force->GetIFF();
1288 
1289  for (int i = 0; i < val->elements()->size(); i++) {
1290  TermDef* pdef = val->elements()->at(i)->isDef();
1291  if (pdef && (iff < 0 || team < 0 || iff == team)) {
1292  GET_DEF_TEXT(name);
1293  else GET_DEF_TEXT(type);
1294  else GET_DEF_TEXT(intel);
1295  else GET_DEF_TEXT(region);
1296  else GET_DEF_TEXT(system);
1297  else GET_DEF_TEXT(zone);
1298  else GET_DEF_BOOL(zone_locked);
1299  else GET_DEF_VEC(loc);
1300  else GET_DEF_NUM(iff);
1301  else GET_DEF_NUM(id);
1302  else GET_DEF_NUM(sorties);
1303  else GET_DEF_NUM(kills);
1304  else GET_DEF_NUM(points);
1305  else GET_DEF_NUM(unit_index);
1306 
1307  else if ((iff == team || team < 0) && pdef->name()->value() == "unit") {
1308  if (!pdef->term() || !pdef->term()->isStruct()) {
1309  Print("WARNING: unit struct missing for group '%s' in '%s'\n", name, filename);
1310  }
1311  else {
1312  TermStruct* val = pdef->term()->isStruct();
1313 
1314  char unit_region[64];
1315  char design[256];
1316  Vec3 unit_loc=Vec3(0.0f,0.0f,0.0f);
1317  unit_count = 1;
1318 
1319  ZeroMemory(unit_region, sizeof(unit_region));
1320  ZeroMemory(design, sizeof(design));
1321 
1322  for (int i = 0; i < val->elements()->size(); i++) {
1323  TermDef* pdef = val->elements()->at(i)->isDef();
1324  if (pdef) {
1325  if (pdef->name()->value() == "name") {
1326  GetDefText(unit_name, pdef, filename);
1327  }
1328  else if (pdef->name()->value() == "regnum") {
1329  GetDefText(unit_regnum, pdef, filename);
1330  }
1331  else if (pdef->name()->value() == "region") {
1332  GetDefText(unit_region, pdef, filename);
1333  }
1334  else if (pdef->name()->value() == "loc") {
1335  GetDefVec(unit_loc, pdef, filename);
1336  }
1337  else if (pdef->name()->value() == "type") {
1338  char typestr[32];
1339  GetDefText(typestr, pdef, filename);
1340  unit_class = ShipDesign::ClassForName(typestr);
1341  }
1342  else if (pdef->name()->value() == "design") {
1343  GetDefText(unit_design, pdef, filename);
1344  }
1345  else if (pdef->name()->value() == "count") {
1346  GetDefNumber(unit_count, pdef, filename);
1347  }
1348  else if (pdef->name()->value() == "dead_count") {
1349  GetDefNumber(unit_dead, pdef, filename);
1350  }
1351  else if (pdef->name()->value() == "damage") {
1352  GetDefNumber(unit_damage, pdef, filename);
1353  }
1354  else if (pdef->name()->value() == "heading") {
1355  GetDefNumber(unit_heading, pdef, filename);
1356  }
1357  }
1358  }
1359 
1360  if (!ShipDesign::CheckName(unit_design)) {
1361  Print("ERROR: invalid design '%s' for unit '%s' in '%s'\n", unit_design, unit_name, filename);
1362  return;
1363  }
1364 
1365  if (force) {
1366  CombatUnit* cu = new(__FILE__,__LINE__) CombatUnit(unit_name, unit_regnum, unit_class, unit_design, unit_count, iff);
1367  cu->SetRegion(unit_region);
1368  cu->MoveTo(unit_loc);
1369  cu->Kill(unit_dead);
1370  cu->SetSustainedDamage(unit_damage);
1371  cu->SetHeading(unit_heading * DEGREES);
1372  unit_list.append(cu);
1373  }
1374  }
1375  }
1376  }
1377  } // elements
1378 
1379  if (iff >= 0 && (iff == team || team < 0)) {
1380  // have we found the force group we are looking for yet?
1381  if (!force && !_stricmp(name, combatant->Name())) {
1382  force = combatant->GetForce();
1383  }
1384 
1385  else {
1386  if (!force)
1387  continue;
1388 
1389  // if we already have a force, and we find a second one,
1390  // it must be the start of a different combatant.
1391  // So don't process any further:
1392  if (TypeFromName(type) == CombatGroup::FORCE) {
1393  break;
1394  }
1395  }
1396 
1397  CombatGroup* g = force->FindGroup(TypeFromName(type), id);
1398 
1399  if (!g) {
1400  ::Print("WARNING: unexpected combat group %s %d '%s' in '%s'\n", type, id, name, filename);
1401  continue;
1402  }
1403 
1404  g->region = region;
1405  g->combatant = combatant;
1406  g->location = loc;
1407  g->enemy_intel = Intel::IntelFromName(intel);
1408  g->unit_index = unit_index;
1409 
1410  if (*zone) {
1411  CombatZone* combat_zone = campaign->GetZone(zone);
1412 
1413  if (combat_zone) {
1414  g->SetAssignedZone(combat_zone);
1415  g->SetZoneLock(zone_locked);
1416  }
1417  else {
1418  ::Print("WARNING: could not find combat zone '%s' for group %s %d '%s' in '%s'\n", zone, type, id, name, filename);
1419  }
1420  }
1421  else if (*system) {
1422  g->SetAssignedSystem(system);
1423  }
1424 
1425  if (sorties >= 0) g->SetSorties(sorties);
1426  if (kills >= 0) g->SetKills(kills);
1427  if (points >= 0) g->SetPoints(points);
1428 
1429  if (unit_list.size()) {
1430  ListIter<CombatUnit> u_iter = unit_list;
1431  while (++u_iter) {
1432  CombatUnit* load_unit = u_iter.value();
1433  CombatUnit* u = g->FindUnit(load_unit->Name());
1434 
1435  if (u) {
1436  if (load_unit->GetRegion().length() > 0) {
1437  u->SetRegion(load_unit->GetRegion());
1438  u->MoveTo(load_unit->Location());
1439  }
1440  else {
1441  u->SetRegion(g->GetRegion());
1442  u->MoveTo(g->Location());
1443  }
1444  u->SetDeadCount(load_unit->DeadCount());
1445  u->SetSustainedDamage(load_unit->GetSustainedDamage());
1446  u->SetHeading(load_unit->GetHeading());
1447  }
1448  }
1449 
1450  unit_list.destroy();
1451  }
1452 
1453  if (!force)
1454  force = g;
1455  } // iff == team?
1456  } // group-struct
1457  } // group
1458  } // def
1459  } // term
1460  }
1461  while (term);
1462 
1463  Print("Order of Battle Loaded (%s).\n", force ? force->Name().data() : "unknown force");
1464 
1465  if (force)
1466  force->CalcValue();
1467 }
1468 
1469 // +--------------------------------------------------------------------+
1470 
1472 {
1473  char buffer[64];
1474 
1475  if (fabs(n) < 1000)
1476  sprintf_s(buffer, "%d", (int) n);
1477 
1478  else if (fabs(n) < 1e6) {
1479  int nn = (int) n / 1000;
1480  sprintf_s(buffer, "%de3", nn);
1481  }
1482 
1483  else
1484  sprintf_s(buffer, "%g", n);
1485 
1486  return buffer;
1487 }
1488 
1489 void
1491 {
1492  int type = u->Type();
1493 
1494  if (type == 0 && u->GetDesign())
1495  type = u->GetDesign()->type;
1496 
1497  fprintf(f, "\n unit: {");
1498  fprintf(f, " name: \"%s\",", u->Name().data());
1499  fprintf(f, " type: \"%s\",", Ship::ClassName(type));
1500  fprintf(f, " design: \"%s\",", u->DesignName().data());
1501 
1502  if (u->Count() > 1) {
1503  fprintf(f, " count: %d,", u->Count());
1504  }
1505  else {
1506  fprintf(f, " regnum:\"%s\",", u->Registry().data());
1507  }
1508 
1509  if (u->GetRegion().length() > 0) {
1510  fprintf(f, " region:\"%s\",", u->GetRegion().data());
1511 
1512  Text x = FormatNumber(u->Location().x);
1513  Text y = FormatNumber(u->Location().y);
1514  Text z = FormatNumber(u->Location().z);
1515 
1516  fprintf(f, " loc:(%s, %s, %s),", x.data(), y.data(), z.data());
1517  }
1518 
1519  fprintf(f, " dead_count: %d, damage: %d, heading: %d },",
1520  (int) u->DeadCount(),
1521  (int) u->GetSustainedDamage(),
1522  (int) (u->GetHeading() / DEGREES));
1523 }
1524 
1525 void
1527 {
1528  fprintf(f, "group: {");
1529  fprintf(f, " type: %s,", CombatGroup::NameFromType(g->Type()));
1530  fprintf(f, " id: %d,", g->GetID());
1531  fprintf(f, " name: \"%s\",", g->Name().data());
1532  fprintf(f, " intel: %s,", Intel::NameFromIntel(g->IntelLevel()));
1533  fprintf(f, " iff: %d,", g->GetIFF());
1534  fprintf(f, " unit_index: %d,", g->UnitIndex());
1535 
1536  if (g->GetRegion().length()) {
1537  fprintf(f, " region:\"%s\",", g->GetRegion().data());
1538  }
1539 
1540  if (g->GetAssignedSystem().length()) {
1541  fprintf(f, " system: \"%s\",", g->GetAssignedSystem().data());
1542  }
1543 
1544  if (g->GetAssignedZone()) {
1545  fprintf(f, " zone: \"%s\",", g->GetAssignedZone()->Name().data());
1546  if (g->IsZoneLocked()) {
1547  fprintf(f, " zone_locked: true,");
1548  }
1549  }
1550 
1551  Text x = FormatNumber(g->Location().x);
1552  Text y = FormatNumber(g->Location().y);
1553  Text z = FormatNumber(g->Location().z);
1554 
1555  fprintf(f, " loc: (%s, %s, %s),", x.data(), y.data(), z.data());
1556 
1557  CombatGroup* parent = g->GetParent();
1558  if (parent) {
1559  fprintf(f, " parent_type:%s,", CombatGroup::NameFromType(parent->Type()));
1560  fprintf(f, " parent_id:%d,", parent->GetID());
1561  }
1562 
1563  fprintf(f, " sorties: %d,", g->Sorties());
1564  fprintf(f, " kills: %d,", g->Kills());
1565  fprintf(f, " points: %d,", g->Points());
1566 
1567  ListIter<CombatUnit> u = g->GetUnits();
1568  while (++u) {
1569  SaveCombatUnit(f, u.value());
1570  }
1571 
1572  fprintf(f, " }\n");
1573 
1575  while (++c) {
1576  SaveCombatGroup(f, c.value());
1577  }
1578 }
1579 
1580 void
1581 CombatGroup::SaveOrderOfBattle(const char* filename, CombatGroup* force)
1582 {
1583  FILE* f;
1584  ::fopen_s(&f, filename, "a+");
1585 
1586  if (f) {
1587  SaveCombatGroup(f, force);
1588  fprintf(f, "\n");
1589  fclose(f);
1590  }
1591 }