void
CampaignPlanStrategic::ExecFrame()
{
    if (campaign && campaign->IsActive()) {
        if (Campaign::Stardate() - exec_time < 300)
        return;

        ListIter<CombatZone> zone = campaign->GetZones();
        while (++zone)
        zone->Clear();

        ListIter<Combatant>  iter = campaign->GetCombatants();
        while (++iter) {
            Combatant*     c     = iter.value();
            CombatGroup*   force = c->GetForce();

            force->CalcValue();

            PlaceGroup(force);
            ScoreCombatant(c);
            ScoreNeeds(c);

            force->ClearUnlockedZones();
            AssignZones(c);
            ResolveZoneMovement(force);
        }

        exec_time = Campaign::Stardate();
    }
}
bool
CombatAction::IsAvailable() const
{
	CombatAction* pThis = (CombatAction*) this;

	if (rval < 0) {
		pThis->rval = (int) Random(0, 100);

		if (rval > probability)
		pThis->status = SKIPPED;
	}

	if (status != PENDING)
	return false;

	if (min_rank > 0 || max_rank < 100) {
		Player* player = Player::GetCurrentPlayer();

		if (player->Rank() < min_rank || player->Rank() > max_rank)
		return false;
	}

	Campaign* campaign = Campaign::GetCampaign();
	if (campaign) {
		if (campaign->GetTime() < start_after) {
			return false;
		}

		if (campaign->GetTime() > start_before) {
			pThis->status = FAILED; // too late!
			return false;
		}

		// check requirements against actions in current campaign:
		ListIter<CombatActionReq> iter = pThis->requirements;
		while (++iter) {
			CombatActionReq* r  = iter.value();
			bool             ok = false;

			if (r->action > 0) {
				ListIter<CombatAction> action = campaign->GetActions();
				while (++action) {
					CombatAction* a = action.value();

					if (a->Identity() == r->action) {
						if (r->not) {
							if (a->Status() == r->stat)
							return false;
						}
						else {
							if (a->Status() != r->stat)
							return false;
						}
					}
				}
			}

			// group-based requirement
			else if (r->group_type > 0) {
				if (r->c1) {
					CombatGroup* group = r->c1->FindGroup(r->group_type, r->group_id);

					if (group) {
						int test = 0;
						int comp = 0;

						if (r->intel) {
							test = group->IntelLevel();
							comp = r->intel;
						}

						else {
							test = group->CalcValue();
							comp = r->score;
						}

						switch (r->comp) {
						case CombatActionReq::LT:  ok = (test <  comp); break;
						case CombatActionReq::LE:  ok = (test <= comp); break;
						case CombatActionReq::GT:  ok = (test >  comp); break;
						case CombatActionReq::GE:  ok = (test >= comp); break;
						case CombatActionReq::EQ:  ok = (test == comp); break;
						}
					}

					if (!ok)
					return false;
				}
			}

			// score-based requirement
			else {
				int test = 0;

				if (r->comp <= CombatActionReq::EQ) {  // absolute
					if (r->c1) {
						int test = r->c1->Score();

						switch (r->comp) {
						case CombatActionReq::LT:  ok = (test <  r->score); break;
						case CombatActionReq::LE:  ok = (test <= r->score); break;
						case CombatActionReq::GT:  ok = (test >  r->score); break;
						case CombatActionReq::GE:  ok = (test >= r->score); break;
						case CombatActionReq::EQ:  ok = (test == r->score); break;
						}
					}
				}

				else {                                 // relative
					if (r->c1 && r->c2) {
						int test = r->c1->Score() - r->c2->Score();

						switch (r->comp) {
						case CombatActionReq::RLT: ok = (test <  r->score); break;
						case CombatActionReq::RLE: ok = (test <= r->score); break;
						case CombatActionReq::RGT: ok = (test >  r->score); break;
						case CombatActionReq::RGE: ok = (test >= r->score); break;
						case CombatActionReq::REQ: ok = (test == r->score); break;
						}
					}
				}

				if (!ok)
				return false;
			}

			if (delay > 0) {
				pThis->start_after = (int) campaign->GetTime() + delay;
				pThis->delay       = 0;
				return IsAvailable();
			}
		}
	}

	return true;
}
CampaignMissionRequest*
CampaignPlanMission::PlanRandomFighterMission()
{
    CampaignMissionRequest* request  = 0;
    int                     type     = fighter_mission_types[fighter_mission_index++];
    int                     ownside  = player_group->GetIFF();
    CombatGroup*            primary  = player_group;
    CombatGroup*            obj      = 0;

    if (fighter_mission_index > 15)
    fighter_mission_index = 0;

    if (type == Mission::ESCORT_FREIGHT) {
        CombatGroup*  freight  = campaign->FindGroup(ownside, CombatGroup::FREIGHT);
        if (!freight || freight->CalcValue() < 1)
        type = Mission::PATROL;
        else
        obj  = freight;
    }

    else if (type == Mission::ESCORT_SHUTTLE) {
        CombatGroup*  shuttle = campaign->FindGroup(ownside, CombatGroup::LCA_SQUADRON);
        if (!shuttle || shuttle->CalcValue() < 1)
        type = Mission::PATROL;
        else
        obj  = shuttle;
    }

    else if (primary->Type() == CombatGroup::WING) {
        if (RandomChance())
        primary = primary->FindGroup(CombatGroup::INTERCEPT_SQUADRON);
        else
        primary = primary->FindGroup(CombatGroup::FIGHTER_SQUADRON);
    }

    if (type >= Mission::AIR_PATROL && type <= Mission::AIR_INTERCEPT) {
        CombatZone* zone     = 0;
        bool        airborne = false;

        if (primary)
        zone = primary->GetAssignedZone();

        if (zone && zone->GetRegions().size() > 1) {
            Text        air_region = *zone->GetRegions().at(1);
            StarSystem* system     = campaign->GetSystem(zone->System());

            if (system) {
                OrbitalRegion* rgn = system->FindRegion(air_region);

                if (rgn && rgn->Type() == Orbital::TERRAIN)
                airborne = true;
            }
        }

        if (!airborne) {
            if (type == Mission::AIR_INTERCEPT)
            type = Mission::INTERCEPT;

            else if (type == Mission::AIR_SWEEP)
            type = Mission::SWEEP;

            else
            type = Mission::PATROL;
        }
    }

    request = new(__FILE__,__LINE__)
    CampaignMissionRequest(campaign, type, start, primary);

    if (request)
    request->SetObjective(obj);

    return request;
}