void
CampaignPlanStrategic::AssignZones(Combatant* c)
{
    // find the list of assignable groups, in priority order:
    List<CombatGroup> groups;
    BuildGroupList(c->GetForce(), groups);
    groups.sort();

    // for each group, assign a zone:
    ListIter<CombatGroup> g_iter = groups;

    // first pass: fighter and attack squadrons assigned to star bases
    while (++g_iter) {
        CombatGroup* g     = g_iter.value();
        int          gtype = g->Type();

        if (gtype == CombatGroup::ATTACK_SQUADRON    ||
                gtype == CombatGroup::FIGHTER_SQUADRON   ||
                gtype == CombatGroup::INTERCEPT_SQUADRON) {
            CombatGroup* parent = g->GetParent();

            if (parent && parent->Type() == CombatGroup::WING)
            parent = parent->GetParent();

            if (!parent || parent->Type() == CombatGroup::CARRIER_GROUP)
            continue;

            // these groups are attached to fixed resources,
            // so they must be assigned to the parent's zone:
            CombatZone* parent_zone = campaign->GetZone(parent->GetRegion());

            if (parent_zone) {
                ZoneForce*  parent_force = parent_zone->FindForce(g->GetIFF());

                if (parent_force) {
                    g->SetAssignedZone(parent_zone);
                    parent_force->AddNeed(g->Type(), -(g->Value()));
                }
            }
        }
    }

    // second pass: carrier groups
    g_iter.reset();
    while (++g_iter) {
        CombatGroup* g     = g_iter.value();
        int          gtype = g->Type();

        if (gtype == CombatGroup::CARRIER_GROUP) {
            int         current_zone_need = 0;
            int         highest_zone_need = 0;
            CombatZone* highest_zone      = 0;
            ZoneForce*  highest_force     = 0;
            CombatZone* current_zone      = 0;
            ZoneForce*  current_force     = 0;

            List<CombatZone> possible_zones;

            if (g->IsZoneLocked()) {
                current_zone  = g->GetAssignedZone();
                current_force = current_zone->FindForce(g->GetIFF());
            }

            else {
                ListIter<CombatZone> z_iter = campaign->GetZones();
                while (++z_iter) {
                    CombatZone* zone  = z_iter.value();
                    ZoneForce*  force = zone->FindForce(g->GetIFF());
                    int         need  = force->GetNeed(CombatGroup::CARRIER_GROUP)     +
                    force->GetNeed(CombatGroup::ATTACK_SQUADRON)   +
                    force->GetNeed(CombatGroup::FIGHTER_SQUADRON)  +
                    force->GetNeed(CombatGroup::INTERCEPT_SQUADRON);

                    if (g->IsSystemLocked() && zone->System() != g->GetAssignedSystem())
                    continue;

                    possible_zones.append(zone);

                    if (zone->HasRegion(g->GetRegion())) {
                        current_zone_need = need;
                        current_zone      = zone;
                        current_force     = force;
                    }

                    if (need > highest_zone_need) {
                        highest_zone_need = need;
                        highest_zone      = zone;
                        highest_force     = force;
                    }
                }
            }

            CombatZone* assigned_zone  = current_zone;
            ZoneForce*  assigned_force = current_force;

            if (highest_zone_need > current_zone_need) {
                assigned_zone  = highest_zone;
                assigned_force = highest_force;
            }

            // if we couldn't find anything suitable,
            // just pick a zone at random:
            if (!assigned_zone) {
                if (possible_zones.isEmpty())
                possible_zones.append(campaign->GetZones());

                int nzones = possible_zones.size();
                int n      = RandomIndex() % nzones;

                assigned_zone  = possible_zones.at(n);
                assigned_force = assigned_zone->FindForce(g->GetIFF());
            }

            if (assigned_force && assigned_zone) {
                Text assigned_rgn;
                if (!campaign->GetZone(g->GetRegion())) {
                    assigned_rgn = *assigned_zone->GetRegions().at(0);
                    g->AssignRegion(assigned_rgn);
                }

                g->SetAssignedZone(assigned_zone);
                assigned_force->AddNeed(g->Type(), -(g->Value()));

                // also assign the carrier's wing and squadrons to the same zone:
                ListIter<CombatGroup> squadron = g->GetComponents();
                while (++squadron) {
                    squadron->SetAssignedZone(assigned_zone);
                    assigned_force->AddNeed(squadron->Type(), -(squadron->Value()));

                    if (squadron->Type() == CombatGroup::WING) {
                        ListIter<CombatGroup> s = squadron->GetComponents();
                        while (++s) {
                            s->SetAssignedZone(assigned_zone);
                            assigned_force->AddNeed(s->Type(), -(s->Value()));
                        }
                    }
                }
            }
        }
    }

    // third pass: everything else
    g_iter.reset();
    while (++g_iter) {
        CombatGroup* g     = g_iter.value();
        int          gtype = g->Type();

        if (gtype == CombatGroup::BATTLE_GROUP || gtype == CombatGroup::DESTROYER_SQUADRON) {
            int         current_zone_need = 0;
            int         highest_zone_need = 0;
            CombatZone* highest_zone      = 0;
            ZoneForce*  highest_force     = 0;
            CombatZone* current_zone      = 0;
            ZoneForce*  current_force     = 0;

            List<CombatZone> possible_zones;

            if (g->IsZoneLocked()) {
                current_zone  = g->GetAssignedZone();
                current_force = current_zone->FindForce(g->GetIFF());
            }

            else {
                ListIter<CombatZone> z_iter = campaign->GetZones();
                while (++z_iter) {
                    CombatZone* zone  = z_iter.value();
                    ZoneForce*  force = zone->FindForce(g->GetIFF());
                    int         need  = force->GetNeed(g->Type());

                    if (g->IsSystemLocked() && zone->System() != g->GetAssignedSystem())
                    continue;

                    possible_zones.append(zone);

                    // battle groups can do double-duty:
                    if (gtype == CombatGroup::BATTLE_GROUP)
                    need += force->GetNeed(CombatGroup::DESTROYER_SQUADRON);

                    if (zone->HasRegion(g->GetRegion())) {
                        current_zone_need = need;
                        current_zone      = zone;
                        current_force     = force;
                    }

                    if (need > highest_zone_need) {
                        highest_zone_need = need;
                        highest_zone      = zone;
                        highest_force     = force;
                    }
                }
            }

            if (highest_zone_need > current_zone_need) {
                g->SetAssignedZone(highest_zone);

                if (highest_force)
                highest_force->AddNeed(g->Type(), -(g->Value()));
            }
            else {
                if (!current_zone) {
                    if (possible_zones.isEmpty())
                    possible_zones.append(campaign->GetZones());

                    int nzones = possible_zones.size();
                    int n      = RandomIndex() % nzones;

                    current_zone  = possible_zones.at(n);
                    current_force = current_zone->FindForce(g->GetIFF());
                }

                g->SetAssignedZone(current_zone);

                if (current_force)
                current_force->AddNeed(g->Type(), -(g->Value()));

                Text assigned_rgn;
                if (!campaign->GetZone(g->GetRegion())) {
                    assigned_rgn = *current_zone->GetRegions().at(0);
                    g->AssignRegion(assigned_rgn);
                }
            }
        }
    }
}