void
FltDlg::OnMissionType(AWEvent* event)
{
	mission_type = -1;

	for (int i = 0; i < 6; i++) {
		if (mission_btn[i]) {
			if (mission_btn[i] == event->window) {
				mission_btn[i]->SetButtonState(1);
				mission_type = i;
			}
			else {
				mission_btn[i]->SetButtonState(0);
			}
		}
	}

	if (objective_list && mission_type > -1) {
		objective_list->ClearItems();

		char txt[32];
		Sim* sim = Sim::GetSim();
		ListIter<Element> iter = sim->GetElements();

		while (++iter) {
			Element* elem = iter.value();

			if (!elem->IsActive() || elem->IsFinished() || elem->IsSquadron())
			continue;

			CombatGroup*   group = elem->GetCombatGroup();
			int            iff   = elem->GetIFF();
			Ship*          s     = elem->GetShip(1);
			double         r     = 0;
			bool           con   = false;

			if (iff != ship->GetIFF()) {
				if (elem->IntelLevel() < Intel::LOCATED)
				continue;

				if (group && group->IntelLevel() < Intel::LOCATED)
				continue;
			}

			if (s) {
				Point s_loc = s->Location()    + s->GetRegion()->Location();
				Point h_loc = ship->Location() + ship->GetRegion()->Location();

				r = (s_loc - h_loc).length();

				con = ship->FindContact(s) != 0;

				if (con) {
					FormatNumber(txt, r);
				}
				else {
					strcpy_s(txt, Game::GetText("FltDlg.Unknown").data());
					r = 2e9;
				}
			}

			switch (mission_type) {
			case 1:  // INTERCEPT
				if (iff && iff != ship->GetIFF() && s && s->IsDropship()) {
					int item = objective_list->AddItem(elem->Name()) - 1;
					objective_list->SetItemText(item, 1, s->GetRegion()->Name());

					objective_list->SetItemText(item, 2, txt);
					objective_list->SetItemData(item, 2, (DWORD) r);
				}
				break;

			case 2:  // ASSAULT
				if (iff && iff != ship->GetIFF() && s && (s->IsStarship() || s->IsStatic())) {
					int item = objective_list->AddItem(elem->Name()) - 1;
					objective_list->SetItemText(item, 1, s->GetRegion()->Name());

					objective_list->SetItemText(item, 2, txt);
					objective_list->SetItemData(item, 2, (DWORD) r);
				}
				break;

			case 3:  // STRIKE
				if (iff && iff != ship->GetIFF() && s && s->IsGroundUnit()) {
					int item = objective_list->AddItem(elem->Name()) - 1;
					objective_list->SetItemText(item, 1, s->GetRegion()->Name());

					objective_list->SetItemText(item, 2, txt);
					objective_list->SetItemData(item, 2, (DWORD) r);
				}
				break;

			case 4:  // ESCORT
				if ((iff == 0 || iff == ship->GetIFF()) && (!s || !s->IsStatic())) {
					int item = objective_list->AddItem(elem->Name()) - 1;

					if (s) {
						objective_list->SetItemText(item, 1, s->GetRegion()->Name());
						objective_list->SetItemText(item, 2, txt);
						objective_list->SetItemData(item, 2, (DWORD) r);
					}

					else {
						objective_list->SetItemText(item, 1, "0");
						objective_list->SetItemData(item, 1, 0);
					}
				}
				break;

			case 5:  // SCOUT?
				break;

			default: break;
			}
		}
	}

	if (loadout_list && mission_type > -1) {
		loadout_list->ClearItems();

		if (design) {
			ListIter<ShipLoad> sl = (List<ShipLoad>&) design->loadouts;
			while (++sl) {
				int item = loadout_list->AddItem(sl->name) - 1;

				char weight[32];
				sprintf_s(weight, "%d kg", (int) ((design->mass + sl->mass) * 1000));
				loadout_list->SetItemText(item, 1, weight);
				loadout_list->SetItemData(item, 1, (DWORD) (sl->mass * 1000));
			}
		}
	}
}
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;
}
void
CampaignPlanMovement::MoveUnit(CombatUnit* u)
{
	if (u) {
		// starship repair:
		double damage  = u->GetSustainedDamage();

		if (damage > 0 && u->GetDesign()) {
			int    percent = (int) (100 * damage / u->GetDesign()->integrity);

			if (percent > 50) {
				u->SetSustainedDamage(0.90 * damage);
			}
		}

		Point  loc  = u->Location();
		Point  dir  = loc;
		double dist = dir.Normalize();

		const double MAX_RAD  = 320e3;
		const double MIN_DIST = 150e3;

		if (dist < MAX_RAD) {
			double scale = 1 - dist/MAX_RAD;

			loc += dir * (Random(30e3, 90e3) * scale) + RandomDirection() * 10e3;

			if (fabs(loc.z) > 20e3)
			loc.z *= 0.1;

			u->MoveTo(loc);

			CombatGroup* g = u->GetCombatGroup();
			if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) {
				g->MoveTo(loc);

				if (g->IntelLevel() > Intel::KNOWN)
				g->SetIntelLevel(Intel::KNOWN);
			}
		}

		else if (dist > 1.25 * MAX_RAD) {
			double scale = 1 - dist/MAX_RAD;

			loc += dir * (Random(80e3, 120e3) * scale) + RandomDirection() * 3e3;

			if (fabs(loc.z) > 20e3)
			loc.z *= 0.1;

			u->MoveTo(loc);

			CombatGroup* g = u->GetCombatGroup();
			if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) {
				g->MoveTo(loc);

				if (g->IntelLevel() > Intel::KNOWN)
				g->SetIntelLevel(Intel::KNOWN);
			}
		}

		else {
			loc += RandomDirection() * 30e3;

			if (fabs(loc.z) > 20e3)
			loc.z *= 0.1;

			u->MoveTo(loc);

			CombatGroup* g = u->GetCombatGroup();
			if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) {
				g->MoveTo(loc);

				if (g->IntelLevel() > Intel::KNOWN)
				g->SetIntelLevel(Intel::KNOWN);
			}
		}

		CombatUnit* closest_unit = 0;
		double      closest_dist = 1e6;

		ListIter<CombatUnit> iter = all_units;
		while (++iter) {
			CombatUnit* unit = iter.value();

			if (unit->GetCombatGroup() != u->GetCombatGroup() && unit->GetRegion() == u->GetRegion() && !unit->IsDropship()) {
				Point  delta = loc - unit->Location();
				dist  = delta.Normalize();

				if (dist < closest_dist) {
					closest_unit = unit;
					closest_dist = dist;
				}
			}
		}

		if (closest_unit && closest_dist < MIN_DIST) {
			Point  delta = loc - closest_unit->Location();
			dist  = delta.Normalize();

			loc += delta * 1.1 * (MIN_DIST - closest_dist);

			if (fabs(loc.z) > 20e3)
			loc.z *= 0.1;

			u->MoveTo(loc);

			CombatGroup* g = u->GetCombatGroup();
			if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) {
				g->MoveTo(loc);

				if (g->IntelLevel() > Intel::KNOWN)
				g->SetIntelLevel(Intel::KNOWN);
			}
		}
	}
}