From d17521c8b9085a91d08fecfd0b51bbbf7b1dccac Mon Sep 17 00:00:00 2001 From: "milo24x7@gmail.com" Date: Sun, 7 Jul 2013 22:08:49 +0000 Subject: Updated open source license declaration and fixed some formatting issues. --- Stars45/CombatGroup.cpp | 3206 ++++++++++++++++++++++++----------------------- 1 file changed, 1615 insertions(+), 1591 deletions(-) (limited to 'Stars45/CombatGroup.cpp') diff --git a/Stars45/CombatGroup.cpp b/Stars45/CombatGroup.cpp index 5ddda20..a8cf988 100644 --- a/Stars45/CombatGroup.cpp +++ b/Stars45/CombatGroup.cpp @@ -1,1591 +1,1615 @@ -/* Project Starshatter 4.5 - Destroyer Studios LLC - Copyright (C) 1997-2004. All Rights Reserved. - - SUBSYSTEM: Stars.exe - FILE: CombatGroup.cpp - AUTHOR: John DiCamillo - - - OVERVIEW - ======== - An element in the dynamic campaign -*/ - -#include "MemDebug.h" -#include "CombatGroup.h" -#include "CombatUnit.h" -#include "CombatZone.h" -#include "Combatant.h" -#include "CombatAssignment.h" -#include "Campaign.h" -#include "ShipDesign.h" -#include "Ship.h" - -#include "Game.h" -#include "DataLoader.h" -#include "ParseUtil.h" - -// +----------------------------------------------------------------------+ - -CombatGroup::CombatGroup(int t, int n, const char* s, int iff_code, int e, CombatGroup* p) -: type(t), id(n), name(s), iff(iff_code), enemy_intel(e), -parent(p), value(0), plan_value(0), unit_index(0), combatant(0), -expanded(false), sorties(0), kills(0), points(0), -current_zone(0), assigned_zone(0), zone_lock(false) -{ - if (parent) - parent->AddComponent(this); -} - -CombatGroup::~CombatGroup() -{ - assignments.destroy(); - components.destroy(); - units.destroy(); -} - -// +--------------------------------------------------------------------+ - -void -CombatGroup::AddComponent(CombatGroup* g) -{ - if (g) { - g->parent = this; - components.append(g); - } -} - -// +--------------------------------------------------------------------+ - -bool -CombatGroup::IsAssignable() const -{ - switch (type) { - case CARRIER_GROUP: - case BATTLE_GROUP: - case DESTROYER_SQUADRON: - case ATTACK_SQUADRON: - case FIGHTER_SQUADRON: - case INTERCEPT_SQUADRON: - case LCA_SQUADRON: - return ((CombatGroup*) this)->CalcValue() > 0; - } - - return false; -} - -bool -CombatGroup::IsTargetable() const -{ - // neutral / non-combatants are not *strategic* targets - // for any combatant: - if (iff < 1 || iff >= 100) - return false; - - // civilian / non-combatant are not strategic targets: - if (type == PASSENGER || - type == PRIVATE || - type == MEDICAL || - type == HABITAT) - return false; - - // must have units of our own to be targetable: - if (units.size() < 1) - return false; - - return ((CombatGroup*) this)->CalcValue() > 0; -} - -bool -CombatGroup::IsDefensible() const -{ - if (type >= SUPPORT) - return ((CombatGroup*) this)->CalcValue() > 0; - - return false; -} - -bool -CombatGroup::IsStrikeTarget() const -{ - if (type < BATTALION || - type == MINEFIELD || // assault, not strike - type == PASSENGER || - type == PRIVATE || - type == MEDICAL || - type == HABITAT) - return false; - - return ((CombatGroup*) this)->CalcValue() > 0; -} - -// +--------------------------------------------------------------------+ - -bool -CombatGroup::IsMovable() const -{ - switch (type) { - case CARRIER_GROUP: - case BATTLE_GROUP: - case DESTROYER_SQUADRON: - case ATTACK_SQUADRON: - case FIGHTER_SQUADRON: - case INTERCEPT_SQUADRON: - case LCA_SQUADRON: - case COURIER: - case MEDICAL: - case SUPPLY: - case REPAIR: - case FREIGHT: - case PASSENGER: - case PRIVATE: - return true; - } - - return false; -} - -// +--------------------------------------------------------------------+ - -bool -CombatGroup::IsFighterGroup() const -{ - switch (type) { - case WING: - case INTERCEPT_SQUADRON: - case FIGHTER_SQUADRON: - case ATTACK_SQUADRON: - return true; - } - - return false; -} - -bool -CombatGroup::IsStarshipGroup() const -{ - switch (type) { - case DESTROYER_SQUADRON: - case BATTLE_GROUP: - case CARRIER_GROUP: - return true; - } - - return false; -} - -// +--------------------------------------------------------------------+ - -bool -CombatGroup::IsReserve() const -{ - if (enemy_intel <= Intel::RESERVE) - return true; - - if (parent) - return parent->IsReserve(); - - return false; -} - -// +--------------------------------------------------------------------+ - -const int* -CombatGroup::PreferredAttacker(int type) -{ - static int p[8]; - - ZeroMemory(p, sizeof(p)); - - switch (type) { - //case FLEET: - case DESTROYER_SQUADRON: p[0] = DESTROYER_SQUADRON; - p[1] = BATTLE_GROUP; - p[2] = CARRIER_GROUP; - p[3] = ATTACK_SQUADRON; - break; - - case BATTLE_GROUP: p[0] = BATTLE_GROUP; - p[1] = DESTROYER_SQUADRON; - p[2] = CARRIER_GROUP; - p[3] = ATTACK_SQUADRON; - break; - - case CARRIER_GROUP: p[0] = ATTACK_SQUADRON; - p[1] = BATTLE_GROUP; - p[2] = DESTROYER_SQUADRON; - p[3] = CARRIER_GROUP; - break; - - //case WING: - case LCA_SQUADRON: - case ATTACK_SQUADRON: - case INTERCEPT_SQUADRON: - case FIGHTER_SQUADRON: p[0] = INTERCEPT_SQUADRON; - p[1] = FIGHTER_SQUADRON; - break; - - //case BATTALION: - case STATION: p[0] = BATTLE_GROUP; - p[1] = CARRIER_GROUP; - break; - - case STARBASE: - case BATTERY: - case MISSILE: p[0] = ATTACK_SQUADRON; - p[1] = FIGHTER_SQUADRON; - break; - - //case C3I: - case MINEFIELD: - case COMM_RELAY: - case EARLY_WARNING: - case FWD_CONTROL_CTR: - case ECM: p[0] = ATTACK_SQUADRON; - p[1] = FIGHTER_SQUADRON; - p[2] = DESTROYER_SQUADRON; - break; - - //case SUPPORT: - case COURIER: - case MEDICAL: - case SUPPLY: - case REPAIR: p[0] = DESTROYER_SQUADRON; - p[1] = BATTLE_GROUP; - p[2] = ATTACK_SQUADRON; - break; - - //case CIVILIAN: - - //case WAR_PRODuCTION: - case FACTORY: - case REFINERY: - case RESOURCE: p[0] = ATTACK_SQUADRON; - p[1] = FIGHTER_SQUADRON; - break; - - //case INFRASTRUCTURE: - case TRANSPORT: - case NETWORK: - case HABITAT: - case STORAGE: p[0] = ATTACK_SQUADRON; - p[1] = FIGHTER_SQUADRON; - break; - - //case NON_COM: - case FREIGHT: - case PASSENGER: - case PRIVATE: p[0] = DESTROYER_SQUADRON; - p[1] = ATTACK_SQUADRON; - break; - } - - return p; -} - -// +--------------------------------------------------------------------+ - -const int* -CombatGroup::PreferredDefender(int type) -{ - static int p[8]; - - ZeroMemory(p, sizeof(p)); - - switch (type) { - //case FLEET: - case CARRIER_GROUP: - case BATTLE_GROUP: - case DESTROYER_SQUADRON: - - //case WING: - case LCA_SQUADRON: - case ATTACK_SQUADRON: - case INTERCEPT_SQUADRON: - case FIGHTER_SQUADRON: break; - - //case BATTALION: - case STATION: p[0] = BATTLE_GROUP; - p[1] = CARRIER_GROUP; - p[2] = DESTROYER_SQUADRON; - break; - case STARBASE: - case MINEFIELD: - case BATTERY: - case MISSILE: p[0] = FIGHTER_SQUADRON; - p[1] = INTERCEPT_SQUADRON; - break; - - //case C3I: - case COMM_RELAY: - case EARLY_WARNING: - case FWD_CONTROL_CTR: - case ECM: p[0] = FIGHTER_SQUADRON; - p[1] = INTERCEPT_SQUADRON; - break; - - //case SUPPORT: - case COURIER: - case MEDICAL: - case SUPPLY: - case REPAIR: p[0] = DESTROYER_SQUADRON; - p[1] = BATTLE_GROUP; - p[2] = ATTACK_SQUADRON; - break; - - //case CIVILIAN: - - //case WAR_PRODuCTION: - case FACTORY: - case REFINERY: - case RESOURCE: p[0] = FIGHTER_SQUADRON; - p[1] = INTERCEPT_SQUADRON; - break; - - //case INFRASTRUCTURE: - case TRANSPORT: - case NETWORK: - case HABITAT: - case STORAGE: p[0] = FIGHTER_SQUADRON; - p[1] = INTERCEPT_SQUADRON; - break; - - //case NON_COM: - case FREIGHT: - case PASSENGER: - case PRIVATE: p[0] = DESTROYER_SQUADRON; - p[1] = BATTLE_GROUP; - break; - } - - return p; -} - -// +--------------------------------------------------------------------+ - -CombatGroup* -CombatGroup::FindGroup(int t, int n) -{ - CombatGroup* result = 0; - - if (type == t && (n < 0 || id == n)) - result = this; - - ListIter group = components; - while (!result && ++group) { - result = group->FindGroup(t, n); - } - - return result; -} - -// +--------------------------------------------------------------------+ - -CombatGroup* -CombatGroup::Clone(bool deep) -{ - CombatGroup* clone = new(__FILE__,__LINE__) - CombatGroup(type, id, name, iff, enemy_intel); - - clone->combatant = combatant; - clone->region = region; - clone->location = location; - clone->value = value; - clone->expanded = expanded; - - for (int i = 0; i < units.size(); i++) { - CombatUnit* u = new(__FILE__,__LINE__) CombatUnit(*units[i]); - u->SetCombatGroup(clone); - clone->units.append(u); - } - - if (deep) { - for (int i = 0; i < components.size(); i++) { - CombatGroup* g = components[i]->Clone(deep); - clone->AddComponent(g); - - if (g->Type() == FIGHTER_SQUADRON || - g->Type() == INTERCEPT_SQUADRON || - g->Type() == ATTACK_SQUADRON || - g->Type() == LCA_SQUADRON) { - - if (units.size() > 0) { - CombatUnit* carrier = units[0]; - - for (int u = 0; u < g->GetUnits().size(); u++) { - CombatUnit* unit = g->GetUnits()[u]; - - if (unit->Type() >= Ship::FIGHTER || - unit->Type() <= Ship::LCA) { - unit->SetCarrier(carrier); - unit->SetRegion(carrier->GetRegion()); - } - } - } - } - } - } - - return clone; -} - -// +--------------------------------------------------------------------+ - -const char* -CombatGroup::GetOrdinal() const -{ - static char ordinal[16]; - - int last_two_digits = id % 100; - - if (last_two_digits > 10 && last_two_digits < 20) { - sprintf_s(ordinal, "ordinal.%d", last_two_digits); - Text suffix = Game::GetText(ordinal); - - if (suffix != ordinal) - sprintf_s(ordinal, "%d%s", id, suffix.data()); - else - sprintf_s(ordinal, "%dth", id); - } - else { - int last_digit = last_two_digits % 10; - sprintf_s(ordinal, "ordinal.%d", last_digit); - Text suffix = Game::GetText(ordinal); - if (suffix != ordinal) - sprintf_s(ordinal, "%d%s", id, suffix.data()); - else if (last_digit == 1) - sprintf_s(ordinal, "%dst", id); - else if (last_digit == 2) - sprintf_s(ordinal, "%dnd", id); - else if (last_digit == 3) - sprintf_s(ordinal, "%drd", id); - else - sprintf_s(ordinal, "%dth", id); - } - - return ordinal; -} - -const char* -CombatGroup::GetDescription() const -{ - static char desc[256]; - static char name_desc[256]; - - if (name.length()) - sprintf_s(name_desc, " \"%s\"", (const char*) name); - else - name_desc[0] = 0; - - switch (type) { - case FORCE: strcpy_s(desc, (const char*) name); break; - - case FLEET: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FLEET").data(), name_desc); break; - case CARRIER_GROUP: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.CARRIER_GROUP").data(), name_desc); break; - case BATTLE_GROUP: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTLE_GROUP").data(), name_desc); break; - case DESTROYER_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.DESTROYER_SQUADRON").data(), name_desc); break; - - case WING: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.WING").data(), name_desc); break; - case ATTACK_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.ATTACK_SQUADRON").data(), name_desc); break; - case FIGHTER_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FIGHTER_SQUADRON").data(), name_desc); break; - case INTERCEPT_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.INTERCEPT_SQUADRON").data(), name_desc); break; - case LCA_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.LCA_SQUADRON").data(), name_desc); break; - - case BATTALION: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTALION").data(), name_desc); break; - case STATION: sprintf_s(desc, "%s %s", Game::GetText("CombatGroup.STATION").data(), name.data()); break; - case STARBASE: sprintf_s(desc, "%s %d%s", Game::GetText("CombatGroup.STARBASE").data(), id, name_desc); break; - case MINEFIELD: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MINEFIELD").data(), name_desc); break; - case BATTERY: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTERY").data(), name_desc); break; - case MISSILE: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MISSILE").data(), name_desc); break; - - case C3I: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.C3I").data(), name_desc); break; - case COMM_RELAY: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.COMM_RELAY").data(), name_desc); break; - case EARLY_WARNING: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.EARLY_WARNING").data(), name_desc); break; - case FWD_CONTROL_CTR: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FWD_CONTROL_CTR").data(), name_desc); break; - case ECM: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.ECM").data(), name_desc); break; - - case SUPPORT: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.SUPPORT").data(), name_desc); break; - case COURIER: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.COURIER").data(), name_desc); break; - case SUPPLY: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.SUPPLY").data(), name_desc); break; - case REPAIR: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.REPAIR").data(), name_desc); break; - case MEDICAL: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MEDICAL").data(), name_desc); break; - - case CIVILIAN: - case WAR_PRODUCTION: - case FACTORY: - case REFINERY: - case RESOURCE: strcpy_s(desc, (const char*) name); break; - - case INFRASTRUCTURE: - case TRANSPORT: - case NETWORK: - case HABITAT: - case STORAGE: - case FREIGHT: - case PASSENGER: - case PRIVATE: strcpy_s(desc, (const char*) name); break; - - default: sprintf_s(desc, "%s%s", Game::GetText("CombatGroup.default").data(), name_desc); break; - } - - return desc; -} - -const char* -CombatGroup::GetShortDescription() const -{ - static char desc[256]; - - switch (type) { - case FORCE: strcpy_s(desc, (const char*) name); break; - - case FLEET: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FLEET").data()); break; - case CARRIER_GROUP: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.CARRIER_GROUP").data()); break; - case BATTLE_GROUP: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTLE_GROUP").data()); break; - case DESTROYER_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.DESTROYER_SQUADRON").data()); break; - - case WING: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.WING").data()); break; - case ATTACK_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.ATTACK_SQUADRON").data()); break; - case FIGHTER_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FIGHTER_SQUADRON").data()); break; - case INTERCEPT_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.INTERCEPT_SQUADRON").data()); break; - case LCA_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.LCA_SQUADRON").data()); break; - - case BATTALION: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTALION").data()); break; - case STATION: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.STATION").data()); break; - case STARBASE: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.STARBASE").data()); break; - case MINEFIELD: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.MINEFIELD").data()); break; - case BATTERY: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTERY").data()); break; - - case C3I: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.C3I").data()); break; - case COMM_RELAY: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.COMM_RELAY").data()); break; - case EARLY_WARNING: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.EARLY_WARNING").data()); break; - case FWD_CONTROL_CTR: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FWD_CONTROL_CTR").data()); break; - case ECM: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.ECM").data()); break; - - case SUPPORT: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.SUPPORT").data()); break; - case COURIER: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.COURIER").data()); break; - case MEDICAL: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.MEDICAL").data()); break; - case SUPPLY: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.SUPPLY").data()); break; - case REPAIR: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.REPAIR").data()); break; - - case CIVILIAN: - case WAR_PRODUCTION: - case FACTORY: - case REFINERY: - case RESOURCE: strcpy_s(desc, (const char*) name); break; - - case INFRASTRUCTURE: - case TRANSPORT: - case NETWORK: - case HABITAT: - case STORAGE: - case FREIGHT: - case PASSENGER: - case PRIVATE: strcpy_s(desc, (const char*) name); break; - - default: sprintf_s(desc, "%s", Game::GetText("CombatGroup.abrv.default").data()); break; - } - - return desc; -} - -// +--------------------------------------------------------------------+ - -double -CombatGroup::GetNextJumpTime() const -{ - double t = 0; - - ListIter unit = ((CombatGroup*) this)->units; - while (++unit) - if (unit->GetNextJumpTime() > t) - t = unit->GetNextJumpTime(); - - return t; -} - -// +--------------------------------------------------------------------+ - -void -CombatGroup::MoveTo(const Point& loc) -{ - location = loc; -} - -// +--------------------------------------------------------------------+ - -void -CombatGroup::SetAssignedSystem(const char* s) -{ - assigned_system = s; - assigned_zone = 0; - zone_lock = false; - - ListIter iter = components; - while (++iter) { - CombatGroup* g = iter.value(); - g->SetAssignedSystem(s); - } -} - -void -CombatGroup::SetAssignedZone(CombatZone* z) -{ - assigned_zone = z; - - if (!assigned_zone) - zone_lock = false; - - ListIter iter = components; - while (++iter) { - CombatGroup* g = iter.value(); - g->SetAssignedZone(z); - } -} - -void -CombatGroup::ClearUnlockedZones() -{ - if (!zone_lock) - assigned_zone = 0; - - ListIter iter = components; - while (++iter) { - CombatGroup* g = iter.value(); - g->ClearUnlockedZones(); - } -} - -void -CombatGroup::SetZoneLock(bool lock) -{ - if (!assigned_zone) - zone_lock = false; - else - zone_lock = lock; - - if (zone_lock) - assigned_system = Text(); - - ListIter iter = components; - while (++iter) { - CombatGroup* g = iter.value(); - g->SetZoneLock(lock); - } -} - -// +--------------------------------------------------------------------+ - -void -CombatGroup::SetIntelLevel(int n) -{ - if (n < Intel::RESERVE || n > Intel::TRACKED) return; - - enemy_intel = n; - - // if this group has been discovered, the entire - // branch of the OOB tree must be exposed. Otherwise, - // no missions would ever be planned against this - // combat group. - - if (n > Intel::SECRET) { - CombatGroup* p = parent; - while (p) { - if (p->enemy_intel < Intel::KNOWN) - p->enemy_intel = Intel::KNOWN; - - p = p->parent; - } - } -} - -// +--------------------------------------------------------------------+ - -int -CombatGroup::CalcValue() -{ - int val = 0; - - ListIter unit = units; - while (++unit) - val += unit->GetValue(); - - ListIter comp = components; - while (++comp) - val += comp->CalcValue(); - - value = val; - return value; -} - -int -CombatGroup::CountUnits() const -{ - int n = 0; - - CombatGroup* g = (CombatGroup*) this; - - ListIter unit = g->units; - while (++unit) - n += unit->Count() - unit->DeadCount(); - - CombatGroup* pThis = ((CombatGroup*) this); - pThis->live_comp.clear(); - ListIter iter = g->components; - while (++iter) { - CombatGroup* comp = iter.value(); - - if (!comp->IsReserve()) { - int unit_count = comp->CountUnits(); - if (unit_count > 0) - pThis->live_comp.append(comp); - - n += unit_count; - } - } - - return n; -} - -// +--------------------------------------------------------------------+ - -void -CombatGroup::ClearAssignments() -{ - assignments.destroy(); - - ListIter comp = components; - while (++comp) - comp->ClearAssignments(); -} - -// +--------------------------------------------------------------------+ - -CombatGroup* -CombatGroup::FindCarrier() -{ - CombatGroup* p = GetParent(); - - while (p != 0 && - p->Type() != CombatGroup::CARRIER_GROUP && - p->Type() != CombatGroup::STATION && - p->Type() != CombatGroup::STARBASE) - p = p->GetParent(); - - if (p && p->GetUnits().size()) - return p; - - return 0; -} - -CombatUnit* -CombatGroup::GetRandomUnit() -{ - CombatUnit* result = 0; - List live; - - ListIter unit = units; - while (++unit) { - if (unit->Count() - unit->DeadCount() > 0) - live.append(unit.value()); - } - - if (live.size() > 0) { - int ntries = 5; - while (!result && ntries-- > 0) { - int index = rand() % live.size(); - result = live[index]; - - int ship_class = result->GetShipClass(); - if (ship_class >= Ship::CRUISER && - ship_class <= Ship::FARCASTER) - result = 0; - } - } - - if (!result) { - ListIter comp = components; - while (++comp && !result) { - CombatUnit* u = comp->GetRandomUnit(); - if (u) - result = u; - } - } - - return result; -} - -CombatUnit* -CombatGroup::GetFirstUnit() -{ - int tmp_index = unit_index; - unit_index = 0; - CombatUnit* result = GetNextUnit(); - unit_index = tmp_index; - - return result; -} - -CombatUnit* -CombatGroup::GetNextUnit() -{ - if (units.size() > 0) { - List live; - - ListIter unit = units; - while (++unit) { - if (unit->Count() - unit->DeadCount() > 0) - live.append(unit.value()); - } - - if (live.size() > 0) { - return live[unit_index++ % live.size()]; - } - } - - if (components.size() > 0) { - return components[unit_index % components.size()]->GetNextUnit(); - } - - return 0; -} - -CombatUnit* -CombatGroup::FindUnit(const char* name) -{ - if (units.size() > 0) { - ListIter iter = units; - while (++iter) { - CombatUnit* unit = iter.value(); - if (unit->Name() == name) { - if (unit->Count() - unit->DeadCount() > 0) - return unit; - else - return 0; - } - } - } - - return 0; -} - -void -CombatGroup::AssignRegion(Text rgn) -{ - region = rgn; - - ListIter comp = components; - while (++comp) - comp->AssignRegion(rgn); - - ListIter unit = units; - while (++unit) - unit->SetRegion(rgn); -} - -// +--------------------------------------------------------------------+ - -static const char* group_name[] = { - "", - "force", - "wing", - "intercept_squadron", - "fighter_squadron", - "attack_squadron", - "lca_squadron", - "fleet", - "destroyer_squadron", - "battle_group", - "carrier_group", - "battalion", - "minefield", - "battery", - "missile", - "station", - "starbase", - "c3i", - "comm_relay", - "early_warning", - "fwd_control_ctr", - "ecm", - "support", - "courier", - "medical", - "supply", - "repair", - "civilian", - "war_production", - "factory", - "refinery", - "resource", - "infrastructure", - "transport", - "network", - "habitat", - "storage", - "non_com", - "freight", - "passenger", - "private" -}; - -// +--------------------------------------------------------------------+ - -int -CombatGroup::TypeFromName(const char* type_name) -{ - for (int i = FORCE; i < PRIVATE; i++) - if (!_stricmp(type_name, group_name[i])) - return i; - - return 0; -} - -const char* -CombatGroup::NameFromType(int type) -{ - return group_name[type]; -} - -// +--------------------------------------------------------------------+ - -int ShipClassFromName(const char* type_name) -{ - return Ship::ClassForName(type_name); -} - -// +--------------------------------------------------------------------+ - -#define GET_DEF_BOOL(n) if (pdef->name()->value()==(#n)) GetDefBool((n), pdef, filename) -#define GET_DEF_TEXT(n) if (pdef->name()->value()==(#n)) GetDefText((n), pdef, filename) -#define GET_DEF_NUM(n) if (pdef->name()->value()==(#n)) GetDefNumber((n), pdef, filename) -#define GET_DEF_VEC(n) if (pdef->name()->value()==(#n)) GetDefVec((n), pdef, filename) - -CombatGroup* -CombatGroup::LoadOrderOfBattle(const char* filename, int team, Combatant* combatant) -{ - CombatGroup* force = 0; - DataLoader* loader = DataLoader::GetLoader(); - BYTE* block; - loader->LoadBuffer(filename, block, true); - - Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block)); - Term* term = parser.ParseTerm(); - - if (!term) { - Print("ERROR: could not parse order of battle '%s'\n", filename); - return 0; - } - else { - TermText* file_type = term->isText(); - if (!file_type || file_type->value() != "ORDER_OF_BATTLE") { - Print("ERROR: invalid Order of Battle file '%s'\n", filename); - term->print(10); - return 0; - } - } - - - do { - delete term; term = 0; - term = parser.ParseTerm(); - - if (term) { - TermDef* def = term->isDef(); - if (def) { - if (def->name()->value() == "group") { - if (!def->term() || !def->term()->isStruct()) { - Print("WARNING: group struct missing in '%s'\n", filename); - } - else { - TermStruct* val = def->term()->isStruct(); - - char name[256]; - char type[64]; - char intel[64]; - char region[64]; - char system[64]; - char parent_type[64]; - int parent_id = 0; - int id = 0; - int iff = -1; - Vec3 loc = Vec3(1.0e9f,0.0f,0.0f); - - List unit_list; - char unit_name[64]; - char unit_regnum[16]; - char unit_design[64]; - char unit_skin[64]; - int unit_class = 0; - int unit_count = 1; - int unit_dead = 0; - int unit_damage = 0; - int unit_heading= 0; - int unit_index = 0; - - *name = 0; - *type = 0; - *intel = 0; - *region = 0; - *system = 0; - *parent_type = 0; - *unit_name = 0; - *unit_regnum = 0; - *unit_design = 0; - *unit_skin = 0; - - strcpy_s(intel, "KNOWN"); - - // all groups in this OOB default to the IFF of the main force - if (force) - iff = force->GetIFF(); - - for (int i = 0; i < val->elements()->size(); i++) { - TermDef* pdef = val->elements()->at(i)->isDef(); - if (pdef && (iff < 0 || team < 0 || iff == team)) { - GET_DEF_TEXT(name); - else GET_DEF_TEXT(type); - else GET_DEF_TEXT(intel); - else GET_DEF_TEXT(region); - else GET_DEF_TEXT(system); - else GET_DEF_VEC(loc); - else GET_DEF_TEXT(parent_type); - else GET_DEF_NUM(parent_id); - else GET_DEF_NUM(iff); - else GET_DEF_NUM(id); - else GET_DEF_NUM(unit_index); - - else if ((iff == team || team < 0) && pdef->name()->value() == "unit") { - if (!pdef->term() || !pdef->term()->isStruct()) { - Print("WARNING: unit struct missing for group '%s' in '%s'\n", name, filename); - } - else { - TermStruct* val = pdef->term()->isStruct(); - - char unit_region[64]; - char design[256]; - Vec3 unit_loc = Vec3(1.0e9f,0.0f,0.0f); - unit_count = 1; - - ZeroMemory(unit_region, sizeof(unit_region)); - ZeroMemory(design, sizeof(design)); - - for (int i = 0; i < val->elements()->size(); i++) { - TermDef* pdef = val->elements()->at(i)->isDef(); - if (pdef) { - if (pdef->name()->value() == "name") { - GetDefText(unit_name, pdef, filename); - } - else if (pdef->name()->value() == "regnum") { - GetDefText(unit_regnum, pdef, filename); - } - else if (pdef->name()->value() == "region") { - GetDefText(unit_region, pdef, filename); - } - else if (pdef->name()->value() == "loc") { - GetDefVec(unit_loc, pdef, filename); - } - else if (pdef->name()->value() == "type") { - char typestr[32]; - GetDefText(typestr, pdef, filename); - unit_class = ShipDesign::ClassForName(typestr); - } - else if (pdef->name()->value() == "design") { - GetDefText(unit_design, pdef, filename); - } - else if (pdef->name()->value() == "skin") { - GetDefText(unit_skin, pdef, filename); - } - else if (pdef->name()->value() == "count") { - GetDefNumber(unit_count, pdef, filename); - } - else if (pdef->name()->value() == "dead_count") { - GetDefNumber(unit_dead, pdef, filename); - } - else if (pdef->name()->value() == "damage") { - GetDefNumber(unit_damage, pdef, filename); - } - else if (pdef->name()->value() == "heading") { - GetDefNumber(unit_heading, pdef, filename); - } - } - } - - if (!ShipDesign::CheckName(unit_design)) { - Print("ERROR: invalid design '%s' for unit '%s' in '%s'\n", unit_design, unit_name, filename); - return 0; - } - - CombatUnit* cu = new(__FILE__,__LINE__) CombatUnit(unit_name, unit_regnum, unit_class, unit_design, unit_count, iff); - cu->SetRegion(unit_region); - cu->SetSkin(unit_skin); - cu->MoveTo(unit_loc); - cu->Kill(unit_dead); - cu->SetSustainedDamage(unit_damage); - cu->SetHeading(unit_heading * DEGREES); - unit_list.append(cu); - } - } - } - } // elements - - if (iff >= 0 && (iff == team || team < 0)) { - CombatGroup* parent_group = 0; - - if (force) { - parent_group = force->FindGroup(TypeFromName(parent_type), parent_id); - } - - CombatGroup* g = new(__FILE__,__LINE__) - CombatGroup(TypeFromName(type), id, name, iff, Intel::IntelFromName(intel), parent_group); - - g->region = region; - g->combatant = combatant; - g->unit_index = unit_index; - - if (loc.x >= 1e9) { - if (parent_group) - g->location = parent_group->location; - else - g->location = Vec3(0,0,0); - } - else { - g->location = loc; - } - - if (unit_list.size()) { - unit_list[0]->SetLeader(true); - - ListIter u = unit_list; - while (++u) { - u->SetCombatGroup(g); - - if (u->GetRegion().length() < 1) { - u->SetRegion(g->GetRegion()); - u->MoveTo(g->Location()); - } - - if (parent_group && - (u->Type() == Ship::FIGHTER || - u->Type() == Ship::ATTACK)) { - - CombatUnit* carrier = 0; - CombatGroup* p = parent_group; - - while (p && !carrier) { - if (p->units.size() && p->units[0]->Type() == Ship::CARRIER) { - carrier = p->units[0]; - u->SetCarrier(carrier); - u->SetRegion(carrier->GetRegion()); - } - - p = p->parent; - } - } - } - - g->units.append(unit_list); - } - - if (!force) - force = g; - } // iff == team? - } // group-struct - } // group - } // def - } // term - } - while (term); - - loader->ReleaseBuffer(block); - Print("Order of Battle Loaded (%s).\n", force ? force->Name().data() : "unknown force"); - - if (force) - force->CalcValue(); - - return force; -} - -// +--------------------------------------------------------------------+ - -void -CombatGroup::MergeOrderOfBattle(BYTE* block, const char* filename, int team, Combatant* combatant, Campaign* campaign) -{ - CombatGroup* force = 0; - - Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block)); - Term* term = parser.ParseTerm(); - - if (!term) { - Print("ERROR: could not parse order of battle '%s'\n", filename); - return; - } - else { - TermText* file_type = term->isText(); - if (!file_type || file_type->value() != "SAVEGAME") { - Print("ERROR: invalid Save Game file '%s'\n", filename); - term->print(10); - return; - } - } - - - do { - delete term; term = 0; - term = parser.ParseTerm(); - - if (term) { - TermDef* def = term->isDef(); - if (def) { - if (def->name()->value() == "group") { - if (!def->term() || !def->term()->isStruct()) { - Print("WARNING: group struct missing in '%s'\n", filename); - } - else { - TermStruct* val = def->term()->isStruct(); - - char name[256]; - char type[64]; - char intel[64]; - char region[64]; - char system[64]; - char zone[64]; - bool zone_locked = false; - int id = 0; - int iff = -1; - int sorties = -1; - int kills = -1; - int points = -1; - Vec3 loc = Vec3(1.0e9f,0.0f,0.0f); - - List unit_list; - char unit_name[64]; - char unit_regnum[16]; - char unit_design[64]; - int unit_class = 0; - int unit_count = 1; - int unit_dead = 0; - int unit_damage = 0; - int unit_heading= 0; - int unit_index = 0; - - *name = 0; - *type = 0; - *intel = 0; - *region = 0; - *system = 0; - *zone = 0; - *unit_name = 0; - *unit_regnum = 0; - *unit_design = 0; - - strcpy_s(intel, "KNOWN"); - - // all groups in this OOB default to the IFF of the main force - if (force) - iff = force->GetIFF(); - - for (int i = 0; i < val->elements()->size(); i++) { - TermDef* pdef = val->elements()->at(i)->isDef(); - if (pdef && (iff < 0 || team < 0 || iff == team)) { - GET_DEF_TEXT(name); - else GET_DEF_TEXT(type); - else GET_DEF_TEXT(intel); - else GET_DEF_TEXT(region); - else GET_DEF_TEXT(system); - else GET_DEF_TEXT(zone); - else GET_DEF_BOOL(zone_locked); - else GET_DEF_VEC(loc); - else GET_DEF_NUM(iff); - else GET_DEF_NUM(id); - else GET_DEF_NUM(sorties); - else GET_DEF_NUM(kills); - else GET_DEF_NUM(points); - else GET_DEF_NUM(unit_index); - - else if ((iff == team || team < 0) && pdef->name()->value() == "unit") { - if (!pdef->term() || !pdef->term()->isStruct()) { - Print("WARNING: unit struct missing for group '%s' in '%s'\n", name, filename); - } - else { - TermStruct* val = pdef->term()->isStruct(); - - char unit_region[64]; - char design[256]; - Vec3 unit_loc=Vec3(0.0f,0.0f,0.0f); - unit_count = 1; - - ZeroMemory(unit_region, sizeof(unit_region)); - ZeroMemory(design, sizeof(design)); - - for (int i = 0; i < val->elements()->size(); i++) { - TermDef* pdef = val->elements()->at(i)->isDef(); - if (pdef) { - if (pdef->name()->value() == "name") { - GetDefText(unit_name, pdef, filename); - } - else if (pdef->name()->value() == "regnum") { - GetDefText(unit_regnum, pdef, filename); - } - else if (pdef->name()->value() == "region") { - GetDefText(unit_region, pdef, filename); - } - else if (pdef->name()->value() == "loc") { - GetDefVec(unit_loc, pdef, filename); - } - else if (pdef->name()->value() == "type") { - char typestr[32]; - GetDefText(typestr, pdef, filename); - unit_class = ShipDesign::ClassForName(typestr); - } - else if (pdef->name()->value() == "design") { - GetDefText(unit_design, pdef, filename); - } - else if (pdef->name()->value() == "count") { - GetDefNumber(unit_count, pdef, filename); - } - else if (pdef->name()->value() == "dead_count") { - GetDefNumber(unit_dead, pdef, filename); - } - else if (pdef->name()->value() == "damage") { - GetDefNumber(unit_damage, pdef, filename); - } - else if (pdef->name()->value() == "heading") { - GetDefNumber(unit_heading, pdef, filename); - } - } - } - - if (!ShipDesign::CheckName(unit_design)) { - Print("ERROR: invalid design '%s' for unit '%s' in '%s'\n", unit_design, unit_name, filename); - return; - } - - if (force) { - CombatUnit* cu = new(__FILE__,__LINE__) CombatUnit(unit_name, unit_regnum, unit_class, unit_design, unit_count, iff); - cu->SetRegion(unit_region); - cu->MoveTo(unit_loc); - cu->Kill(unit_dead); - cu->SetSustainedDamage(unit_damage); - cu->SetHeading(unit_heading * DEGREES); - unit_list.append(cu); - } - } - } - } - } // elements - - if (iff >= 0 && (iff == team || team < 0)) { - // have we found the force group we are looking for yet? - if (!force && !_stricmp(name, combatant->Name())) { - force = combatant->GetForce(); - } - - else { - if (!force) - continue; - - // if we already have a force, and we find a second one, - // it must be the start of a different combatant. - // So don't process any further: - if (TypeFromName(type) == CombatGroup::FORCE) { - break; - } - } - - CombatGroup* g = force->FindGroup(TypeFromName(type), id); - - if (!g) { - ::Print("WARNING: unexpected combat group %s %d '%s' in '%s'\n", type, id, name, filename); - continue; - } - - g->region = region; - g->combatant = combatant; - g->location = loc; - g->enemy_intel = Intel::IntelFromName(intel); - g->unit_index = unit_index; - - if (*zone) { - CombatZone* combat_zone = campaign->GetZone(zone); - - if (combat_zone) { - g->SetAssignedZone(combat_zone); - g->SetZoneLock(zone_locked); - } - else { - ::Print("WARNING: could not find combat zone '%s' for group %s %d '%s' in '%s'\n", zone, type, id, name, filename); - } - } - else if (*system) { - g->SetAssignedSystem(system); - } - - if (sorties >= 0) g->SetSorties(sorties); - if (kills >= 0) g->SetKills(kills); - if (points >= 0) g->SetPoints(points); - - if (unit_list.size()) { - ListIter u_iter = unit_list; - while (++u_iter) { - CombatUnit* load_unit = u_iter.value(); - CombatUnit* u = g->FindUnit(load_unit->Name()); - - if (u) { - if (load_unit->GetRegion().length() > 0) { - u->SetRegion(load_unit->GetRegion()); - u->MoveTo(load_unit->Location()); - } - else { - u->SetRegion(g->GetRegion()); - u->MoveTo(g->Location()); - } - u->SetDeadCount(load_unit->DeadCount()); - u->SetSustainedDamage(load_unit->GetSustainedDamage()); - u->SetHeading(load_unit->GetHeading()); - } - } - - unit_list.destroy(); - } - - if (!force) - force = g; - } // iff == team? - } // group-struct - } // group - } // def - } // term - } - while (term); - - Print("Order of Battle Loaded (%s).\n", force ? force->Name().data() : "unknown force"); - - if (force) - force->CalcValue(); -} - -// +--------------------------------------------------------------------+ - -Text FormatNumber(double n) -{ - char buffer[64]; - - if (fabs(n) < 1000) - sprintf_s(buffer, "%d", (int) n); - - else if (fabs(n) < 1e6) { - int nn = (int) n / 1000; - sprintf_s(buffer, "%de3", nn); - } - - else - sprintf_s(buffer, "%g", n); - - return buffer; -} - -void -SaveCombatUnit(FILE* f, CombatUnit* u) -{ - int type = u->Type(); - - if (type == 0 && u->GetDesign()) - type = u->GetDesign()->type; - - fprintf(f, "\n unit: {"); - fprintf(f, " name: \"%s\",", u->Name().data()); - fprintf(f, " type: \"%s\",", Ship::ClassName(type)); - fprintf(f, " design: \"%s\",", u->DesignName().data()); - - if (u->Count() > 1) { - fprintf(f, " count: %d,", u->Count()); - } - else { - fprintf(f, " regnum:\"%s\",", u->Registry().data()); - } - - if (u->GetRegion().length() > 0) { - fprintf(f, " region:\"%s\",", u->GetRegion().data()); - - Text x = FormatNumber(u->Location().x); - Text y = FormatNumber(u->Location().y); - Text z = FormatNumber(u->Location().z); - - fprintf(f, " loc:(%s, %s, %s),", x.data(), y.data(), z.data()); - } - - fprintf(f, " dead_count: %d, damage: %d, heading: %d },", - (int) u->DeadCount(), - (int) u->GetSustainedDamage(), - (int) (u->GetHeading() / DEGREES)); -} - -void -SaveCombatGroup(FILE* f, CombatGroup* g) -{ - fprintf(f, "group: {"); - fprintf(f, " type: %s,", CombatGroup::NameFromType(g->Type())); - fprintf(f, " id: %d,", g->GetID()); - fprintf(f, " name: \"%s\",", g->Name().data()); - fprintf(f, " intel: %s,", Intel::NameFromIntel(g->IntelLevel())); - fprintf(f, " iff: %d,", g->GetIFF()); - fprintf(f, " unit_index: %d,", g->UnitIndex()); - - if (g->GetRegion().length()) { - fprintf(f, " region:\"%s\",", g->GetRegion().data()); - } - - if (g->GetAssignedSystem().length()) { - fprintf(f, " system: \"%s\",", g->GetAssignedSystem().data()); - } - - if (g->GetAssignedZone()) { - fprintf(f, " zone: \"%s\",", g->GetAssignedZone()->Name().data()); - if (g->IsZoneLocked()) { - fprintf(f, " zone_locked: true,"); - } - } - - Text x = FormatNumber(g->Location().x); - Text y = FormatNumber(g->Location().y); - Text z = FormatNumber(g->Location().z); - - fprintf(f, " loc: (%s, %s, %s),", x.data(), y.data(), z.data()); - - CombatGroup* parent = g->GetParent(); - if (parent) { - fprintf(f, " parent_type:%s,", CombatGroup::NameFromType(parent->Type())); - fprintf(f, " parent_id:%d,", parent->GetID()); - } - - fprintf(f, " sorties: %d,", g->Sorties()); - fprintf(f, " kills: %d,", g->Kills()); - fprintf(f, " points: %d,", g->Points()); - - ListIter u = g->GetUnits(); - while (++u) { - SaveCombatUnit(f, u.value()); - } - - fprintf(f, " }\n"); - - ListIter c = g->GetComponents(); - while (++c) { - SaveCombatGroup(f, c.value()); - } -} - -void -CombatGroup::SaveOrderOfBattle(const char* filename, CombatGroup* force) -{ - FILE* f; - ::fopen_s(&f, filename, "a+"); - - if (f) { - SaveCombatGroup(f, force); - fprintf(f, "\n"); - fclose(f); - } -} +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: Stars.exe + FILE: CombatGroup.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + An element in the dynamic campaign +*/ + +#include "MemDebug.h" +#include "CombatGroup.h" +#include "CombatUnit.h" +#include "CombatZone.h" +#include "Combatant.h" +#include "CombatAssignment.h" +#include "Campaign.h" +#include "ShipDesign.h" +#include "Ship.h" + +#include "Game.h" +#include "DataLoader.h" +#include "ParseUtil.h" + +// +----------------------------------------------------------------------+ + +CombatGroup::CombatGroup(int t, int n, const char* s, int iff_code, int e, CombatGroup* p) +: type(t), id(n), name(s), iff(iff_code), enemy_intel(e), +parent(p), value(0), plan_value(0), unit_index(0), combatant(0), +expanded(false), sorties(0), kills(0), points(0), +current_zone(0), assigned_zone(0), zone_lock(false) +{ + if (parent) + parent->AddComponent(this); +} + +CombatGroup::~CombatGroup() +{ + assignments.destroy(); + components.destroy(); + units.destroy(); +} + +// +--------------------------------------------------------------------+ + +void +CombatGroup::AddComponent(CombatGroup* g) +{ + if (g) { + g->parent = this; + components.append(g); + } +} + +// +--------------------------------------------------------------------+ + +bool +CombatGroup::IsAssignable() const +{ + switch (type) { + case CARRIER_GROUP: + case BATTLE_GROUP: + case DESTROYER_SQUADRON: + case ATTACK_SQUADRON: + case FIGHTER_SQUADRON: + case INTERCEPT_SQUADRON: + case LCA_SQUADRON: + return ((CombatGroup*) this)->CalcValue() > 0; + } + + return false; +} + +bool +CombatGroup::IsTargetable() const +{ + // neutral / non-combatants are not *strategic* targets + // for any combatant: + if (iff < 1 || iff >= 100) + return false; + + // civilian / non-combatant are not strategic targets: + if (type == PASSENGER || + type == PRIVATE || + type == MEDICAL || + type == HABITAT) + return false; + + // must have units of our own to be targetable: + if (units.size() < 1) + return false; + + return ((CombatGroup*) this)->CalcValue() > 0; +} + +bool +CombatGroup::IsDefensible() const +{ + if (type >= SUPPORT) + return ((CombatGroup*) this)->CalcValue() > 0; + + return false; +} + +bool +CombatGroup::IsStrikeTarget() const +{ + if (type < BATTALION || + type == MINEFIELD || // assault, not strike + type == PASSENGER || + type == PRIVATE || + type == MEDICAL || + type == HABITAT) + return false; + + return ((CombatGroup*) this)->CalcValue() > 0; +} + +// +--------------------------------------------------------------------+ + +bool +CombatGroup::IsMovable() const +{ + switch (type) { + case CARRIER_GROUP: + case BATTLE_GROUP: + case DESTROYER_SQUADRON: + case ATTACK_SQUADRON: + case FIGHTER_SQUADRON: + case INTERCEPT_SQUADRON: + case LCA_SQUADRON: + case COURIER: + case MEDICAL: + case SUPPLY: + case REPAIR: + case FREIGHT: + case PASSENGER: + case PRIVATE: + return true; + } + + return false; +} + +// +--------------------------------------------------------------------+ + +bool +CombatGroup::IsFighterGroup() const +{ + switch (type) { + case WING: + case INTERCEPT_SQUADRON: + case FIGHTER_SQUADRON: + case ATTACK_SQUADRON: + return true; + } + + return false; +} + +bool +CombatGroup::IsStarshipGroup() const +{ + switch (type) { + case DESTROYER_SQUADRON: + case BATTLE_GROUP: + case CARRIER_GROUP: + return true; + } + + return false; +} + +// +--------------------------------------------------------------------+ + +bool +CombatGroup::IsReserve() const +{ + if (enemy_intel <= Intel::RESERVE) + return true; + + if (parent) + return parent->IsReserve(); + + return false; +} + +// +--------------------------------------------------------------------+ + +const int* +CombatGroup::PreferredAttacker(int type) +{ + static int p[8]; + + ZeroMemory(p, sizeof(p)); + + switch (type) { + //case FLEET: + case DESTROYER_SQUADRON: p[0] = DESTROYER_SQUADRON; + p[1] = BATTLE_GROUP; + p[2] = CARRIER_GROUP; + p[3] = ATTACK_SQUADRON; + break; + + case BATTLE_GROUP: p[0] = BATTLE_GROUP; + p[1] = DESTROYER_SQUADRON; + p[2] = CARRIER_GROUP; + p[3] = ATTACK_SQUADRON; + break; + + case CARRIER_GROUP: p[0] = ATTACK_SQUADRON; + p[1] = BATTLE_GROUP; + p[2] = DESTROYER_SQUADRON; + p[3] = CARRIER_GROUP; + break; + + //case WING: + case LCA_SQUADRON: + case ATTACK_SQUADRON: + case INTERCEPT_SQUADRON: + case FIGHTER_SQUADRON: p[0] = INTERCEPT_SQUADRON; + p[1] = FIGHTER_SQUADRON; + break; + + //case BATTALION: + case STATION: p[0] = BATTLE_GROUP; + p[1] = CARRIER_GROUP; + break; + + case STARBASE: + case BATTERY: + case MISSILE: p[0] = ATTACK_SQUADRON; + p[1] = FIGHTER_SQUADRON; + break; + + //case C3I: + case MINEFIELD: + case COMM_RELAY: + case EARLY_WARNING: + case FWD_CONTROL_CTR: + case ECM: p[0] = ATTACK_SQUADRON; + p[1] = FIGHTER_SQUADRON; + p[2] = DESTROYER_SQUADRON; + break; + + //case SUPPORT: + case COURIER: + case MEDICAL: + case SUPPLY: + case REPAIR: p[0] = DESTROYER_SQUADRON; + p[1] = BATTLE_GROUP; + p[2] = ATTACK_SQUADRON; + break; + + //case CIVILIAN: + + //case WAR_PRODuCTION: + case FACTORY: + case REFINERY: + case RESOURCE: p[0] = ATTACK_SQUADRON; + p[1] = FIGHTER_SQUADRON; + break; + + //case INFRASTRUCTURE: + case TRANSPORT: + case NETWORK: + case HABITAT: + case STORAGE: p[0] = ATTACK_SQUADRON; + p[1] = FIGHTER_SQUADRON; + break; + + //case NON_COM: + case FREIGHT: + case PASSENGER: + case PRIVATE: p[0] = DESTROYER_SQUADRON; + p[1] = ATTACK_SQUADRON; + break; + } + + return p; +} + +// +--------------------------------------------------------------------+ + +const int* +CombatGroup::PreferredDefender(int type) +{ + static int p[8]; + + ZeroMemory(p, sizeof(p)); + + switch (type) { + //case FLEET: + case CARRIER_GROUP: + case BATTLE_GROUP: + case DESTROYER_SQUADRON: + + //case WING: + case LCA_SQUADRON: + case ATTACK_SQUADRON: + case INTERCEPT_SQUADRON: + case FIGHTER_SQUADRON: break; + + //case BATTALION: + case STATION: p[0] = BATTLE_GROUP; + p[1] = CARRIER_GROUP; + p[2] = DESTROYER_SQUADRON; + break; + case STARBASE: + case MINEFIELD: + case BATTERY: + case MISSILE: p[0] = FIGHTER_SQUADRON; + p[1] = INTERCEPT_SQUADRON; + break; + + //case C3I: + case COMM_RELAY: + case EARLY_WARNING: + case FWD_CONTROL_CTR: + case ECM: p[0] = FIGHTER_SQUADRON; + p[1] = INTERCEPT_SQUADRON; + break; + + //case SUPPORT: + case COURIER: + case MEDICAL: + case SUPPLY: + case REPAIR: p[0] = DESTROYER_SQUADRON; + p[1] = BATTLE_GROUP; + p[2] = ATTACK_SQUADRON; + break; + + //case CIVILIAN: + + //case WAR_PRODuCTION: + case FACTORY: + case REFINERY: + case RESOURCE: p[0] = FIGHTER_SQUADRON; + p[1] = INTERCEPT_SQUADRON; + break; + + //case INFRASTRUCTURE: + case TRANSPORT: + case NETWORK: + case HABITAT: + case STORAGE: p[0] = FIGHTER_SQUADRON; + p[1] = INTERCEPT_SQUADRON; + break; + + //case NON_COM: + case FREIGHT: + case PASSENGER: + case PRIVATE: p[0] = DESTROYER_SQUADRON; + p[1] = BATTLE_GROUP; + break; + } + + return p; +} + +// +--------------------------------------------------------------------+ + +CombatGroup* +CombatGroup::FindGroup(int t, int n) +{ + CombatGroup* result = 0; + + if (type == t && (n < 0 || id == n)) + result = this; + + ListIter group = components; + while (!result && ++group) { + result = group->FindGroup(t, n); + } + + return result; +} + +// +--------------------------------------------------------------------+ + +CombatGroup* +CombatGroup::Clone(bool deep) +{ + CombatGroup* clone = new(__FILE__,__LINE__) + CombatGroup(type, id, name, iff, enemy_intel); + + clone->combatant = combatant; + clone->region = region; + clone->location = location; + clone->value = value; + clone->expanded = expanded; + + for (int i = 0; i < units.size(); i++) { + CombatUnit* u = new(__FILE__,__LINE__) CombatUnit(*units[i]); + u->SetCombatGroup(clone); + clone->units.append(u); + } + + if (deep) { + for (int i = 0; i < components.size(); i++) { + CombatGroup* g = components[i]->Clone(deep); + clone->AddComponent(g); + + if (g->Type() == FIGHTER_SQUADRON || + g->Type() == INTERCEPT_SQUADRON || + g->Type() == ATTACK_SQUADRON || + g->Type() == LCA_SQUADRON) { + + if (units.size() > 0) { + CombatUnit* carrier = units[0]; + + for (int u = 0; u < g->GetUnits().size(); u++) { + CombatUnit* unit = g->GetUnits()[u]; + + if (unit->Type() >= Ship::FIGHTER || + unit->Type() <= Ship::LCA) { + unit->SetCarrier(carrier); + unit->SetRegion(carrier->GetRegion()); + } + } + } + } + } + } + + return clone; +} + +// +--------------------------------------------------------------------+ + +const char* +CombatGroup::GetOrdinal() const +{ + static char ordinal[16]; + + int last_two_digits = id % 100; + + if (last_two_digits > 10 && last_two_digits < 20) { + sprintf_s(ordinal, "ordinal.%d", last_two_digits); + Text suffix = Game::GetText(ordinal); + + if (suffix != ordinal) + sprintf_s(ordinal, "%d%s", id, suffix.data()); + else + sprintf_s(ordinal, "%dth", id); + } + else { + int last_digit = last_two_digits % 10; + sprintf_s(ordinal, "ordinal.%d", last_digit); + Text suffix = Game::GetText(ordinal); + if (suffix != ordinal) + sprintf_s(ordinal, "%d%s", id, suffix.data()); + else if (last_digit == 1) + sprintf_s(ordinal, "%dst", id); + else if (last_digit == 2) + sprintf_s(ordinal, "%dnd", id); + else if (last_digit == 3) + sprintf_s(ordinal, "%drd", id); + else + sprintf_s(ordinal, "%dth", id); + } + + return ordinal; +} + +const char* +CombatGroup::GetDescription() const +{ + static char desc[256]; + static char name_desc[256]; + + if (name.length()) + sprintf_s(name_desc, " \"%s\"", (const char*) name); + else + name_desc[0] = 0; + + switch (type) { + case FORCE: strcpy_s(desc, (const char*) name); break; + + case FLEET: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FLEET").data(), name_desc); break; + case CARRIER_GROUP: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.CARRIER_GROUP").data(), name_desc); break; + case BATTLE_GROUP: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTLE_GROUP").data(), name_desc); break; + case DESTROYER_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.DESTROYER_SQUADRON").data(), name_desc); break; + + case WING: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.WING").data(), name_desc); break; + case ATTACK_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.ATTACK_SQUADRON").data(), name_desc); break; + case FIGHTER_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FIGHTER_SQUADRON").data(), name_desc); break; + case INTERCEPT_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.INTERCEPT_SQUADRON").data(), name_desc); break; + case LCA_SQUADRON: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.LCA_SQUADRON").data(), name_desc); break; + + case BATTALION: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTALION").data(), name_desc); break; + case STATION: sprintf_s(desc, "%s %s", Game::GetText("CombatGroup.STATION").data(), name.data()); break; + case STARBASE: sprintf_s(desc, "%s %d%s", Game::GetText("CombatGroup.STARBASE").data(), id, name_desc); break; + case MINEFIELD: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MINEFIELD").data(), name_desc); break; + case BATTERY: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTERY").data(), name_desc); break; + case MISSILE: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MISSILE").data(), name_desc); break; + + case C3I: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.C3I").data(), name_desc); break; + case COMM_RELAY: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.COMM_RELAY").data(), name_desc); break; + case EARLY_WARNING: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.EARLY_WARNING").data(), name_desc); break; + case FWD_CONTROL_CTR: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FWD_CONTROL_CTR").data(), name_desc); break; + case ECM: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.ECM").data(), name_desc); break; + + case SUPPORT: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.SUPPORT").data(), name_desc); break; + case COURIER: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.COURIER").data(), name_desc); break; + case SUPPLY: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.SUPPLY").data(), name_desc); break; + case REPAIR: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.REPAIR").data(), name_desc); break; + case MEDICAL: sprintf_s(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MEDICAL").data(), name_desc); break; + + case CIVILIAN: + case WAR_PRODUCTION: + case FACTORY: + case REFINERY: + case RESOURCE: strcpy_s(desc, (const char*) name); break; + + case INFRASTRUCTURE: + case TRANSPORT: + case NETWORK: + case HABITAT: + case STORAGE: + case FREIGHT: + case PASSENGER: + case PRIVATE: strcpy_s(desc, (const char*) name); break; + + default: sprintf_s(desc, "%s%s", Game::GetText("CombatGroup.default").data(), name_desc); break; + } + + return desc; +} + +const char* +CombatGroup::GetShortDescription() const +{ + static char desc[256]; + + switch (type) { + case FORCE: strcpy_s(desc, (const char*) name); break; + + case FLEET: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FLEET").data()); break; + case CARRIER_GROUP: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.CARRIER_GROUP").data()); break; + case BATTLE_GROUP: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTLE_GROUP").data()); break; + case DESTROYER_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.DESTROYER_SQUADRON").data()); break; + + case WING: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.WING").data()); break; + case ATTACK_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.ATTACK_SQUADRON").data()); break; + case FIGHTER_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FIGHTER_SQUADRON").data()); break; + case INTERCEPT_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.INTERCEPT_SQUADRON").data()); break; + case LCA_SQUADRON: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.LCA_SQUADRON").data()); break; + + case BATTALION: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTALION").data()); break; + case STATION: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.STATION").data()); break; + case STARBASE: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.STARBASE").data()); break; + case MINEFIELD: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.MINEFIELD").data()); break; + case BATTERY: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTERY").data()); break; + + case C3I: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.C3I").data()); break; + case COMM_RELAY: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.COMM_RELAY").data()); break; + case EARLY_WARNING: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.EARLY_WARNING").data()); break; + case FWD_CONTROL_CTR: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FWD_CONTROL_CTR").data()); break; + case ECM: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.ECM").data()); break; + + case SUPPORT: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.SUPPORT").data()); break; + case COURIER: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.COURIER").data()); break; + case MEDICAL: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.MEDICAL").data()); break; + case SUPPLY: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.SUPPLY").data()); break; + case REPAIR: sprintf_s(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.REPAIR").data()); break; + + case CIVILIAN: + case WAR_PRODUCTION: + case FACTORY: + case REFINERY: + case RESOURCE: strcpy_s(desc, (const char*) name); break; + + case INFRASTRUCTURE: + case TRANSPORT: + case NETWORK: + case HABITAT: + case STORAGE: + case FREIGHT: + case PASSENGER: + case PRIVATE: strcpy_s(desc, (const char*) name); break; + + default: sprintf_s(desc, "%s", Game::GetText("CombatGroup.abrv.default").data()); break; + } + + return desc; +} + +// +--------------------------------------------------------------------+ + +double +CombatGroup::GetNextJumpTime() const +{ + double t = 0; + + ListIter unit = ((CombatGroup*) this)->units; + while (++unit) + if (unit->GetNextJumpTime() > t) + t = unit->GetNextJumpTime(); + + return t; +} + +// +--------------------------------------------------------------------+ + +void +CombatGroup::MoveTo(const Point& loc) +{ + location = loc; +} + +// +--------------------------------------------------------------------+ + +void +CombatGroup::SetAssignedSystem(const char* s) +{ + assigned_system = s; + assigned_zone = 0; + zone_lock = false; + + ListIter iter = components; + while (++iter) { + CombatGroup* g = iter.value(); + g->SetAssignedSystem(s); + } +} + +void +CombatGroup::SetAssignedZone(CombatZone* z) +{ + assigned_zone = z; + + if (!assigned_zone) + zone_lock = false; + + ListIter iter = components; + while (++iter) { + CombatGroup* g = iter.value(); + g->SetAssignedZone(z); + } +} + +void +CombatGroup::ClearUnlockedZones() +{ + if (!zone_lock) + assigned_zone = 0; + + ListIter iter = components; + while (++iter) { + CombatGroup* g = iter.value(); + g->ClearUnlockedZones(); + } +} + +void +CombatGroup::SetZoneLock(bool lock) +{ + if (!assigned_zone) + zone_lock = false; + else + zone_lock = lock; + + if (zone_lock) + assigned_system = Text(); + + ListIter iter = components; + while (++iter) { + CombatGroup* g = iter.value(); + g->SetZoneLock(lock); + } +} + +// +--------------------------------------------------------------------+ + +void +CombatGroup::SetIntelLevel(int n) +{ + if (n < Intel::RESERVE || n > Intel::TRACKED) return; + + enemy_intel = n; + + // if this group has been discovered, the entire + // branch of the OOB tree must be exposed. Otherwise, + // no missions would ever be planned against this + // combat group. + + if (n > Intel::SECRET) { + CombatGroup* p = parent; + while (p) { + if (p->enemy_intel < Intel::KNOWN) + p->enemy_intel = Intel::KNOWN; + + p = p->parent; + } + } +} + +// +--------------------------------------------------------------------+ + +int +CombatGroup::CalcValue() +{ + int val = 0; + + ListIter unit = units; + while (++unit) + val += unit->GetValue(); + + ListIter comp = components; + while (++comp) + val += comp->CalcValue(); + + value = val; + return value; +} + +int +CombatGroup::CountUnits() const +{ + int n = 0; + + CombatGroup* g = (CombatGroup*) this; + + ListIter unit = g->units; + while (++unit) + n += unit->Count() - unit->DeadCount(); + + CombatGroup* pThis = ((CombatGroup*) this); + pThis->live_comp.clear(); + ListIter iter = g->components; + while (++iter) { + CombatGroup* comp = iter.value(); + + if (!comp->IsReserve()) { + int unit_count = comp->CountUnits(); + if (unit_count > 0) + pThis->live_comp.append(comp); + + n += unit_count; + } + } + + return n; +} + +// +--------------------------------------------------------------------+ + +void +CombatGroup::ClearAssignments() +{ + assignments.destroy(); + + ListIter comp = components; + while (++comp) + comp->ClearAssignments(); +} + +// +--------------------------------------------------------------------+ + +CombatGroup* +CombatGroup::FindCarrier() +{ + CombatGroup* p = GetParent(); + + while (p != 0 && + p->Type() != CombatGroup::CARRIER_GROUP && + p->Type() != CombatGroup::STATION && + p->Type() != CombatGroup::STARBASE) + p = p->GetParent(); + + if (p && p->GetUnits().size()) + return p; + + return 0; +} + +CombatUnit* +CombatGroup::GetRandomUnit() +{ + CombatUnit* result = 0; + List live; + + ListIter unit = units; + while (++unit) { + if (unit->Count() - unit->DeadCount() > 0) + live.append(unit.value()); + } + + if (live.size() > 0) { + int ntries = 5; + while (!result && ntries-- > 0) { + int index = rand() % live.size(); + result = live[index]; + + int ship_class = result->GetShipClass(); + if (ship_class >= Ship::CRUISER && + ship_class <= Ship::FARCASTER) + result = 0; + } + } + + if (!result) { + ListIter comp = components; + while (++comp && !result) { + CombatUnit* u = comp->GetRandomUnit(); + if (u) + result = u; + } + } + + return result; +} + +CombatUnit* +CombatGroup::GetFirstUnit() +{ + int tmp_index = unit_index; + unit_index = 0; + CombatUnit* result = GetNextUnit(); + unit_index = tmp_index; + + return result; +} + +CombatUnit* +CombatGroup::GetNextUnit() +{ + if (units.size() > 0) { + List live; + + ListIter unit = units; + while (++unit) { + if (unit->Count() - unit->DeadCount() > 0) + live.append(unit.value()); + } + + if (live.size() > 0) { + return live[unit_index++ % live.size()]; + } + } + + if (components.size() > 0) { + return components[unit_index % components.size()]->GetNextUnit(); + } + + return 0; +} + +CombatUnit* +CombatGroup::FindUnit(const char* name) +{ + if (units.size() > 0) { + ListIter iter = units; + while (++iter) { + CombatUnit* unit = iter.value(); + if (unit->Name() == name) { + if (unit->Count() - unit->DeadCount() > 0) + return unit; + else + return 0; + } + } + } + + return 0; +} + +void +CombatGroup::AssignRegion(Text rgn) +{ + region = rgn; + + ListIter comp = components; + while (++comp) + comp->AssignRegion(rgn); + + ListIter unit = units; + while (++unit) + unit->SetRegion(rgn); +} + +// +--------------------------------------------------------------------+ + +static const char* group_name[] = { + "", + "force", + "wing", + "intercept_squadron", + "fighter_squadron", + "attack_squadron", + "lca_squadron", + "fleet", + "destroyer_squadron", + "battle_group", + "carrier_group", + "battalion", + "minefield", + "battery", + "missile", + "station", + "starbase", + "c3i", + "comm_relay", + "early_warning", + "fwd_control_ctr", + "ecm", + "support", + "courier", + "medical", + "supply", + "repair", + "civilian", + "war_production", + "factory", + "refinery", + "resource", + "infrastructure", + "transport", + "network", + "habitat", + "storage", + "non_com", + "freight", + "passenger", + "private" +}; + +// +--------------------------------------------------------------------+ + +int +CombatGroup::TypeFromName(const char* type_name) +{ + for (int i = FORCE; i < PRIVATE; i++) + if (!_stricmp(type_name, group_name[i])) + return i; + + return 0; +} + +const char* +CombatGroup::NameFromType(int type) +{ + return group_name[type]; +} + +// +--------------------------------------------------------------------+ + +int ShipClassFromName(const char* type_name) +{ + return Ship::ClassForName(type_name); +} + +// +--------------------------------------------------------------------+ + +#define GET_DEF_BOOL(n) if (pdef->name()->value()==(#n)) GetDefBool((n), pdef, filename) +#define GET_DEF_TEXT(n) if (pdef->name()->value()==(#n)) GetDefText((n), pdef, filename) +#define GET_DEF_NUM(n) if (pdef->name()->value()==(#n)) GetDefNumber((n), pdef, filename) +#define GET_DEF_VEC(n) if (pdef->name()->value()==(#n)) GetDefVec((n), pdef, filename) + +CombatGroup* +CombatGroup::LoadOrderOfBattle(const char* filename, int team, Combatant* combatant) +{ + CombatGroup* force = 0; + DataLoader* loader = DataLoader::GetLoader(); + BYTE* block; + loader->LoadBuffer(filename, block, true); + + Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block)); + Term* term = parser.ParseTerm(); + + if (!term) { + Print("ERROR: could not parse order of battle '%s'\n", filename); + return 0; + } + else { + TermText* file_type = term->isText(); + if (!file_type || file_type->value() != "ORDER_OF_BATTLE") { + Print("ERROR: invalid Order of Battle file '%s'\n", filename); + term->print(10); + return 0; + } + } + + + do { + delete term; term = 0; + term = parser.ParseTerm(); + + if (term) { + TermDef* def = term->isDef(); + if (def) { + if (def->name()->value() == "group") { + if (!def->term() || !def->term()->isStruct()) { + Print("WARNING: group struct missing in '%s'\n", filename); + } + else { + TermStruct* val = def->term()->isStruct(); + + char name[256]; + char type[64]; + char intel[64]; + char region[64]; + char system[64]; + char parent_type[64]; + int parent_id = 0; + int id = 0; + int iff = -1; + Vec3 loc = Vec3(1.0e9f,0.0f,0.0f); + + List unit_list; + char unit_name[64]; + char unit_regnum[16]; + char unit_design[64]; + char unit_skin[64]; + int unit_class = 0; + int unit_count = 1; + int unit_dead = 0; + int unit_damage = 0; + int unit_heading= 0; + int unit_index = 0; + + *name = 0; + *type = 0; + *intel = 0; + *region = 0; + *system = 0; + *parent_type = 0; + *unit_name = 0; + *unit_regnum = 0; + *unit_design = 0; + *unit_skin = 0; + + strcpy_s(intel, "KNOWN"); + + // all groups in this OOB default to the IFF of the main force + if (force) + iff = force->GetIFF(); + + for (int i = 0; i < val->elements()->size(); i++) { + TermDef* pdef = val->elements()->at(i)->isDef(); + if (pdef && (iff < 0 || team < 0 || iff == team)) { + GET_DEF_TEXT(name); + else GET_DEF_TEXT(type); + else GET_DEF_TEXT(intel); + else GET_DEF_TEXT(region); + else GET_DEF_TEXT(system); + else GET_DEF_VEC(loc); + else GET_DEF_TEXT(parent_type); + else GET_DEF_NUM(parent_id); + else GET_DEF_NUM(iff); + else GET_DEF_NUM(id); + else GET_DEF_NUM(unit_index); + + else if ((iff == team || team < 0) && pdef->name()->value() == "unit") { + if (!pdef->term() || !pdef->term()->isStruct()) { + Print("WARNING: unit struct missing for group '%s' in '%s'\n", name, filename); + } + else { + TermStruct* val = pdef->term()->isStruct(); + + char unit_region[64]; + char design[256]; + Vec3 unit_loc = Vec3(1.0e9f,0.0f,0.0f); + unit_count = 1; + + ZeroMemory(unit_region, sizeof(unit_region)); + ZeroMemory(design, sizeof(design)); + + for (int i = 0; i < val->elements()->size(); i++) { + TermDef* pdef = val->elements()->at(i)->isDef(); + if (pdef) { + if (pdef->name()->value() == "name") { + GetDefText(unit_name, pdef, filename); + } + else if (pdef->name()->value() == "regnum") { + GetDefText(unit_regnum, pdef, filename); + } + else if (pdef->name()->value() == "region") { + GetDefText(unit_region, pdef, filename); + } + else if (pdef->name()->value() == "loc") { + GetDefVec(unit_loc, pdef, filename); + } + else if (pdef->name()->value() == "type") { + char typestr[32]; + GetDefText(typestr, pdef, filename); + unit_class = ShipDesign::ClassForName(typestr); + } + else if (pdef->name()->value() == "design") { + GetDefText(unit_design, pdef, filename); + } + else if (pdef->name()->value() == "skin") { + GetDefText(unit_skin, pdef, filename); + } + else if (pdef->name()->value() == "count") { + GetDefNumber(unit_count, pdef, filename); + } + else if (pdef->name()->value() == "dead_count") { + GetDefNumber(unit_dead, pdef, filename); + } + else if (pdef->name()->value() == "damage") { + GetDefNumber(unit_damage, pdef, filename); + } + else if (pdef->name()->value() == "heading") { + GetDefNumber(unit_heading, pdef, filename); + } + } + } + + if (!ShipDesign::CheckName(unit_design)) { + Print("ERROR: invalid design '%s' for unit '%s' in '%s'\n", unit_design, unit_name, filename); + return 0; + } + + CombatUnit* cu = new(__FILE__,__LINE__) CombatUnit(unit_name, unit_regnum, unit_class, unit_design, unit_count, iff); + cu->SetRegion(unit_region); + cu->SetSkin(unit_skin); + cu->MoveTo(unit_loc); + cu->Kill(unit_dead); + cu->SetSustainedDamage(unit_damage); + cu->SetHeading(unit_heading * DEGREES); + unit_list.append(cu); + } + } + } + } // elements + + if (iff >= 0 && (iff == team || team < 0)) { + CombatGroup* parent_group = 0; + + if (force) { + parent_group = force->FindGroup(TypeFromName(parent_type), parent_id); + } + + CombatGroup* g = new(__FILE__,__LINE__) + CombatGroup(TypeFromName(type), id, name, iff, Intel::IntelFromName(intel), parent_group); + + g->region = region; + g->combatant = combatant; + g->unit_index = unit_index; + + if (loc.x >= 1e9) { + if (parent_group) + g->location = parent_group->location; + else + g->location = Vec3(0,0,0); + } + else { + g->location = loc; + } + + if (unit_list.size()) { + unit_list[0]->SetLeader(true); + + ListIter u = unit_list; + while (++u) { + u->SetCombatGroup(g); + + if (u->GetRegion().length() < 1) { + u->SetRegion(g->GetRegion()); + u->MoveTo(g->Location()); + } + + if (parent_group && + (u->Type() == Ship::FIGHTER || + u->Type() == Ship::ATTACK)) { + + CombatUnit* carrier = 0; + CombatGroup* p = parent_group; + + while (p && !carrier) { + if (p->units.size() && p->units[0]->Type() == Ship::CARRIER) { + carrier = p->units[0]; + u->SetCarrier(carrier); + u->SetRegion(carrier->GetRegion()); + } + + p = p->parent; + } + } + } + + g->units.append(unit_list); + } + + if (!force) + force = g; + } // iff == team? + } // group-struct + } // group + } // def + } // term + } + while (term); + + loader->ReleaseBuffer(block); + Print("Order of Battle Loaded (%s).\n", force ? force->Name().data() : "unknown force"); + + if (force) + force->CalcValue(); + + return force; +} + +// +--------------------------------------------------------------------+ + +void +CombatGroup::MergeOrderOfBattle(BYTE* block, const char* filename, int team, Combatant* combatant, Campaign* campaign) +{ + CombatGroup* force = 0; + + Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block)); + Term* term = parser.ParseTerm(); + + if (!term) { + Print("ERROR: could not parse order of battle '%s'\n", filename); + return; + } + else { + TermText* file_type = term->isText(); + if (!file_type || file_type->value() != "SAVEGAME") { + Print("ERROR: invalid Save Game file '%s'\n", filename); + term->print(10); + return; + } + } + + + do { + delete term; term = 0; + term = parser.ParseTerm(); + + if (term) { + TermDef* def = term->isDef(); + if (def) { + if (def->name()->value() == "group") { + if (!def->term() || !def->term()->isStruct()) { + Print("WARNING: group struct missing in '%s'\n", filename); + } + else { + TermStruct* val = def->term()->isStruct(); + + char name[256]; + char type[64]; + char intel[64]; + char region[64]; + char system[64]; + char zone[64]; + bool zone_locked = false; + int id = 0; + int iff = -1; + int sorties = -1; + int kills = -1; + int points = -1; + Vec3 loc = Vec3(1.0e9f,0.0f,0.0f); + + List unit_list; + char unit_name[64]; + char unit_regnum[16]; + char unit_design[64]; + int unit_class = 0; + int unit_count = 1; + int unit_dead = 0; + int unit_damage = 0; + int unit_heading= 0; + int unit_index = 0; + + *name = 0; + *type = 0; + *intel = 0; + *region = 0; + *system = 0; + *zone = 0; + *unit_name = 0; + *unit_regnum = 0; + *unit_design = 0; + + strcpy_s(intel, "KNOWN"); + + // all groups in this OOB default to the IFF of the main force + if (force) + iff = force->GetIFF(); + + for (int i = 0; i < val->elements()->size(); i++) { + TermDef* pdef = val->elements()->at(i)->isDef(); + if (pdef && (iff < 0 || team < 0 || iff == team)) { + GET_DEF_TEXT(name); + else GET_DEF_TEXT(type); + else GET_DEF_TEXT(intel); + else GET_DEF_TEXT(region); + else GET_DEF_TEXT(system); + else GET_DEF_TEXT(zone); + else GET_DEF_BOOL(zone_locked); + else GET_DEF_VEC(loc); + else GET_DEF_NUM(iff); + else GET_DEF_NUM(id); + else GET_DEF_NUM(sorties); + else GET_DEF_NUM(kills); + else GET_DEF_NUM(points); + else GET_DEF_NUM(unit_index); + + else if ((iff == team || team < 0) && pdef->name()->value() == "unit") { + if (!pdef->term() || !pdef->term()->isStruct()) { + Print("WARNING: unit struct missing for group '%s' in '%s'\n", name, filename); + } + else { + TermStruct* val = pdef->term()->isStruct(); + + char unit_region[64]; + char design[256]; + Vec3 unit_loc=Vec3(0.0f,0.0f,0.0f); + unit_count = 1; + + ZeroMemory(unit_region, sizeof(unit_region)); + ZeroMemory(design, sizeof(design)); + + for (int i = 0; i < val->elements()->size(); i++) { + TermDef* pdef = val->elements()->at(i)->isDef(); + if (pdef) { + if (pdef->name()->value() == "name") { + GetDefText(unit_name, pdef, filename); + } + else if (pdef->name()->value() == "regnum") { + GetDefText(unit_regnum, pdef, filename); + } + else if (pdef->name()->value() == "region") { + GetDefText(unit_region, pdef, filename); + } + else if (pdef->name()->value() == "loc") { + GetDefVec(unit_loc, pdef, filename); + } + else if (pdef->name()->value() == "type") { + char typestr[32]; + GetDefText(typestr, pdef, filename); + unit_class = ShipDesign::ClassForName(typestr); + } + else if (pdef->name()->value() == "design") { + GetDefText(unit_design, pdef, filename); + } + else if (pdef->name()->value() == "count") { + GetDefNumber(unit_count, pdef, filename); + } + else if (pdef->name()->value() == "dead_count") { + GetDefNumber(unit_dead, pdef, filename); + } + else if (pdef->name()->value() == "damage") { + GetDefNumber(unit_damage, pdef, filename); + } + else if (pdef->name()->value() == "heading") { + GetDefNumber(unit_heading, pdef, filename); + } + } + } + + if (!ShipDesign::CheckName(unit_design)) { + Print("ERROR: invalid design '%s' for unit '%s' in '%s'\n", unit_design, unit_name, filename); + return; + } + + if (force) { + CombatUnit* cu = new(__FILE__,__LINE__) CombatUnit(unit_name, unit_regnum, unit_class, unit_design, unit_count, iff); + cu->SetRegion(unit_region); + cu->MoveTo(unit_loc); + cu->Kill(unit_dead); + cu->SetSustainedDamage(unit_damage); + cu->SetHeading(unit_heading * DEGREES); + unit_list.append(cu); + } + } + } + } + } // elements + + if (iff >= 0 && (iff == team || team < 0)) { + // have we found the force group we are looking for yet? + if (!force && !_stricmp(name, combatant->Name())) { + force = combatant->GetForce(); + } + + else { + if (!force) + continue; + + // if we already have a force, and we find a second one, + // it must be the start of a different combatant. + // So don't process any further: + if (TypeFromName(type) == CombatGroup::FORCE) { + break; + } + } + + CombatGroup* g = force->FindGroup(TypeFromName(type), id); + + if (!g) { + ::Print("WARNING: unexpected combat group %s %d '%s' in '%s'\n", type, id, name, filename); + continue; + } + + g->region = region; + g->combatant = combatant; + g->location = loc; + g->enemy_intel = Intel::IntelFromName(intel); + g->unit_index = unit_index; + + if (*zone) { + CombatZone* combat_zone = campaign->GetZone(zone); + + if (combat_zone) { + g->SetAssignedZone(combat_zone); + g->SetZoneLock(zone_locked); + } + else { + ::Print("WARNING: could not find combat zone '%s' for group %s %d '%s' in '%s'\n", zone, type, id, name, filename); + } + } + else if (*system) { + g->SetAssignedSystem(system); + } + + if (sorties >= 0) g->SetSorties(sorties); + if (kills >= 0) g->SetKills(kills); + if (points >= 0) g->SetPoints(points); + + if (unit_list.size()) { + ListIter u_iter = unit_list; + while (++u_iter) { + CombatUnit* load_unit = u_iter.value(); + CombatUnit* u = g->FindUnit(load_unit->Name()); + + if (u) { + if (load_unit->GetRegion().length() > 0) { + u->SetRegion(load_unit->GetRegion()); + u->MoveTo(load_unit->Location()); + } + else { + u->SetRegion(g->GetRegion()); + u->MoveTo(g->Location()); + } + u->SetDeadCount(load_unit->DeadCount()); + u->SetSustainedDamage(load_unit->GetSustainedDamage()); + u->SetHeading(load_unit->GetHeading()); + } + } + + unit_list.destroy(); + } + + if (!force) + force = g; + } // iff == team? + } // group-struct + } // group + } // def + } // term + } + while (term); + + Print("Order of Battle Loaded (%s).\n", force ? force->Name().data() : "unknown force"); + + if (force) + force->CalcValue(); +} + +// +--------------------------------------------------------------------+ + +Text FormatNumber(double n) +{ + char buffer[64]; + + if (fabs(n) < 1000) + sprintf_s(buffer, "%d", (int) n); + + else if (fabs(n) < 1e6) { + int nn = (int) n / 1000; + sprintf_s(buffer, "%de3", nn); + } + + else + sprintf_s(buffer, "%g", n); + + return buffer; +} + +void +SaveCombatUnit(FILE* f, CombatUnit* u) +{ + int type = u->Type(); + + if (type == 0 && u->GetDesign()) + type = u->GetDesign()->type; + + fprintf(f, "\n unit: {"); + fprintf(f, " name: \"%s\",", u->Name().data()); + fprintf(f, " type: \"%s\",", Ship::ClassName(type)); + fprintf(f, " design: \"%s\",", u->DesignName().data()); + + if (u->Count() > 1) { + fprintf(f, " count: %d,", u->Count()); + } + else { + fprintf(f, " regnum:\"%s\",", u->Registry().data()); + } + + if (u->GetRegion().length() > 0) { + fprintf(f, " region:\"%s\",", u->GetRegion().data()); + + Text x = FormatNumber(u->Location().x); + Text y = FormatNumber(u->Location().y); + Text z = FormatNumber(u->Location().z); + + fprintf(f, " loc:(%s, %s, %s),", x.data(), y.data(), z.data()); + } + + fprintf(f, " dead_count: %d, damage: %d, heading: %d },", + (int) u->DeadCount(), + (int) u->GetSustainedDamage(), + (int) (u->GetHeading() / DEGREES)); +} + +void +SaveCombatGroup(FILE* f, CombatGroup* g) +{ + fprintf(f, "group: {"); + fprintf(f, " type: %s,", CombatGroup::NameFromType(g->Type())); + fprintf(f, " id: %d,", g->GetID()); + fprintf(f, " name: \"%s\",", g->Name().data()); + fprintf(f, " intel: %s,", Intel::NameFromIntel(g->IntelLevel())); + fprintf(f, " iff: %d,", g->GetIFF()); + fprintf(f, " unit_index: %d,", g->UnitIndex()); + + if (g->GetRegion().length()) { + fprintf(f, " region:\"%s\",", g->GetRegion().data()); + } + + if (g->GetAssignedSystem().length()) { + fprintf(f, " system: \"%s\",", g->GetAssignedSystem().data()); + } + + if (g->GetAssignedZone()) { + fprintf(f, " zone: \"%s\",", g->GetAssignedZone()->Name().data()); + if (g->IsZoneLocked()) { + fprintf(f, " zone_locked: true,"); + } + } + + Text x = FormatNumber(g->Location().x); + Text y = FormatNumber(g->Location().y); + Text z = FormatNumber(g->Location().z); + + fprintf(f, " loc: (%s, %s, %s),", x.data(), y.data(), z.data()); + + CombatGroup* parent = g->GetParent(); + if (parent) { + fprintf(f, " parent_type:%s,", CombatGroup::NameFromType(parent->Type())); + fprintf(f, " parent_id:%d,", parent->GetID()); + } + + fprintf(f, " sorties: %d,", g->Sorties()); + fprintf(f, " kills: %d,", g->Kills()); + fprintf(f, " points: %d,", g->Points()); + + ListIter u = g->GetUnits(); + while (++u) { + SaveCombatUnit(f, u.value()); + } + + fprintf(f, " }\n"); + + ListIter c = g->GetComponents(); + while (++c) { + SaveCombatGroup(f, c.value()); + } +} + +void +CombatGroup::SaveOrderOfBattle(const char* filename, CombatGroup* force) +{ + FILE* f; + ::fopen_s(&f, filename, "a+"); + + if (f) { + SaveCombatGroup(f, force); + fprintf(f, "\n"); + fclose(f); + } +} -- cgit v1.1