int
CombatGroup::CountUnits() const
{
	int n = 0;

	CombatGroup* g = (CombatGroup*) this;

	ListIter<CombatUnit> unit = g->units;
	while (++unit)
	n += unit->Count() - unit->DeadCount();

	CombatGroup* pThis = ((CombatGroup*) this);
	pThis->live_comp.clear();
	ListIter<CombatGroup> 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;
}
CampaignMissionRequest*
CampaignPlanMission::PlanRandomStarshipMission()
{
    int type    = Mission::PATROL;
    int r       = RandomIndex();
    int ownside = player_group->GetIFF();

    if (mission_type_index < 0)
    mission_type_index = r;

    else if (mission_type_index >= 16)
    mission_type_index = 0;

    type = mission_types[mission_type_index++];

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

    CampaignMissionRequest* request = 0;
    request = new(__FILE__,__LINE__)
    CampaignMissionRequest(campaign, type, start, player_group);

    return request;
}
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();
    }
}
void
CombatGroup::ClearUnlockedZones()
{
	if (!zone_lock)
	assigned_zone = 0;

	ListIter<CombatGroup> iter = components;
	while (++iter) {
		CombatGroup* g = iter.value();
		g->ClearUnlockedZones();
	}
}
void
CombatGroup::SetAssignedSystem(const char* s)
{
	assigned_system = s;
	assigned_zone   = 0;
	zone_lock       = false;

	ListIter<CombatGroup> 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<CombatGroup> iter = components;
	while (++iter) {
		CombatGroup* g = iter.value();
		g->SetAssignedZone(z);
	}
}
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;
}
void
CombatGroup::SetZoneLock(bool lock)
{
	if (!assigned_zone)
	zone_lock = false;
	else
	zone_lock = lock;

	if (zone_lock)
	assigned_system = Text();

	ListIter<CombatGroup> iter = components;
	while (++iter) {
		CombatGroup* g = iter.value();
		g->SetZoneLock(lock);
	}
}
CampaignMissionRequest*
CampaignPlanMission::PlanCampaignMission()
{
    CampaignMissionRequest* request = 0;

    ListIter<CombatAction> iter = campaign->GetActions();
    while (++iter && !request) {
        CombatAction* action = iter.value();

        if (action->Type() != CombatAction::MISSION_TEMPLATE)
        continue;

        if (action->IsAvailable()) {

            // only fire each action once every two hours:
            if (action->ExecTime() > 0 && campaign->GetTime() - action->ExecTime() < 7200)
            continue;

            CombatGroup* g = campaign->FindGroup(action->GetIFF(),
            action->AssetType(),
            action->AssetId());

            if (g && (g == player_group || 
                        (player_group->Type() == CombatGroup::WING &&
                            player_group->FindGroup(g->Type(), g->GetID())))) {

                request = new(__FILE__,__LINE__)
                CampaignMissionRequest(campaign, 
                action->Subtype(),
                start, 
                g);

                if (request) {
                    request->SetOpposingType(action->OpposingType());
                    request->SetScript(action->GetText());
                }

                action->FireAction();
            }
        }
    }

    return request;
}
CampaignMissionRequest*
CampaignPlanMission::PlanStrategicMission()
{
    CampaignMissionRequest* request = 0;

    if (slot > 1)
    return request;

    // build list of assignments:
    List<CombatAssignment> assignments;
    assignments.append(player_group->GetAssignments());

    if (player_group->Type() == CombatGroup::WING) {
        ListIter<CombatGroup> iter = player_group->GetComponents();
        while (++iter) {
            CombatGroup* g = iter.value();
            assignments.append(g->GetAssignments());
        }
    }

    // pick next assignment as basis for mission:
    static int assignment_index = 0;

    if (assignments.size()) {
        if (assignment_index >= assignments.size())
        assignment_index = 0;

        CombatAssignment* a = assignments[assignment_index++];

        request = new(__FILE__,__LINE__)
        CampaignMissionRequest(campaign, 
        a->Type(),
        start, 
        a->GetResource());

        if (request)
        request->SetObjective(a->GetObjective());
    }

    return request;
}
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;
}
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));
			}
		}
	}
}
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);
                }
            }
        }
    }
}
void
MissionEvent::Execute(bool silent)
{
	Starshatter*   stars    = Starshatter::GetInstance();
	HUDView*       hud      = HUDView::GetInstance();
	Sim*           sim      = Sim::GetSim();
	Ship*          player   = sim->GetPlayerShip();
	Ship*          ship     = 0;
	Ship*          src      = 0;
	Ship*          tgt      = 0;
	Element*       elem     = 0;
	int            pan      = 0;
	bool           end_mission = false;

	if (event_ship.length())
	ship = sim->FindShip(event_ship);
	else
	ship = player;

	if (event_source.length())
	src = sim->FindShip(event_source);

	if (event_target.length())
	tgt = sim->FindShip(event_target);

	if (ship)
	elem = ship->GetElement();

	else if (event_ship.length()) {
		elem = sim->FindElement(event_ship);

		if (elem)
		ship = elem->GetShip(1);
	}

	// expire the delay, if any remains
	delay = 0;

	// fire the event action
	switch (event) {
	case MESSAGE:
		if (event_message.length() > 0) {
			if (ship) {
				RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(ship, src, event_param[0]);
				msg->SetInfo(event_message);
				msg->SetChannel(ship->GetIFF());
				if (tgt)
				msg->AddTarget(tgt);
				RadioTraffic::Transmit(msg);
			}

			else if (elem) {
				RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, src, event_param[0]);
				msg->SetInfo(event_message);
				msg->SetChannel(elem->GetIFF());
				if (tgt)
				msg->AddTarget(tgt);
				RadioTraffic::Transmit(msg);
			}
		}

		if (event_sound.length() > 0) {
			pan = event_param[0];
		}
		break;

	case OBJECTIVE: 
		if (elem) {
			if (event_param[0]) {
				elem->ClearInstructions();
				elem->ClearObjectives();
			}

			Instruction* obj = new(__FILE__,__LINE__) Instruction(event_param[0], 0);
			obj->SetTarget(event_target);
			elem->AddObjective(obj);

			if (elem->Contains(player)) {
				HUDView* hud = HUDView::GetInstance();

				if (hud)
				hud->ShowHUDInst();
			}
		}
		break;

	case INSTRUCTION:
		if (elem) {
			if (event_param[0])
			elem->ClearInstructions();

			elem->AddInstruction(event_message);

			if (elem->Contains(player) && event_message.length() > 0) {
				HUDView* hud = HUDView::GetInstance();

				if (hud)
				hud->ShowHUDInst();
			}
		}
		break;

	case IFF:
		if (elem) {
			elem->SetIFF(event_param[0]);
		}

		else if (ship) {
			ship->SetIFF(event_param[0]);
		}
		break;

	case DAMAGE:
		if (ship) {
			ship->InflictDamage(event_param[0]);

			if (ship->Integrity() < 1) {
				NetUtil::SendObjKill(ship, 0, NetObjKill::KILL_MISC);
				ship->DeathSpiral();
				Print("    %s Killed By Scripted Event %d (%s)\n", (const char*) ship->Name(), id, FormatGameTime());
			}
		}
		else {
			Print("   EVENT %d: Could not apply damage to ship '%s' (not found).\n", id, (const char*) event_ship);
		}
		break;

	case JUMP:
		if (ship) {
			SimRegion* rgn = sim->FindRegion(event_target);

			if (rgn && ship->GetRegion() != rgn) {
				if (rgn->IsOrbital()) {
					QuantumDrive* quantum_drive = ship->GetQuantumDrive();
					if (quantum_drive) {
						quantum_drive->SetDestination(rgn, Point(0,0,0));
						quantum_drive->Engage(true); // request immediate jump
					}

					else if (ship->IsAirborne()) {
						ship->MakeOrbit();
					}
				}

				else {
					ship->DropOrbit();
				}
			}

		}
		break;

	case HOLD:
		if (elem)
		elem->SetHoldTime(event_param[0]);
		break;

	case SKIP: {
			for (int i = 0; i < event_nparams; i++) {
				int skip_id = event_param[i];

				ListIter<MissionEvent> iter = sim->GetEvents();
				while (++iter) {
					MissionEvent* e = iter.value();
					if (e->EventID() == skip_id) {
						if (e->status != COMPLETE)
						e->status = SKIPPED;
					}
				}
			}
		}
		break;

	case END_MISSION:
		Print("    END MISSION By Scripted Event %d (%s)\n", id, FormatGameTime());
		end_mission = true;
		break;

		//
		// NOTE: CUTSCENE EVENTS DO NOT APPLY IN MULTIPLAYER
		//
	case BEGIN_SCENE:
		Print("    ------------------------------------\n");
		Print("    Begin Cutscene '%s'\n", event_message.data());
		stars->BeginCutscene();
		break;

	case END_SCENE:
		Print("    End Cutscene '%s'\n", event_message.data());
		Print("    ------------------------------------\n");
		stars->EndCutscene();
		break;

	case CAMERA:
		if (stars->InCutscene()) {
			CameraDirector* cam_dir = CameraDirector::GetInstance();

			if (!cam_dir->GetShip())
			cam_dir->SetShip(player);

			switch (event_param[0]) {
			case 1:  
				if (cam_dir->GetMode() != CameraDirector::MODE_COCKPIT)
				cam_dir->SetMode(CameraDirector::MODE_COCKPIT, event_rect.x);
				break;

			case 2:
				if (cam_dir->GetMode() != CameraDirector::MODE_CHASE)
				cam_dir->SetMode(CameraDirector::MODE_CHASE, event_rect.x);
				break;

			case 3:
				if (cam_dir->GetMode() != CameraDirector::MODE_ORBIT)
				cam_dir->SetMode(CameraDirector::MODE_ORBIT, event_rect.x);
				break;

			case 4:
				if (cam_dir->GetMode() != CameraDirector::MODE_TARGET)
				cam_dir->SetMode(CameraDirector::MODE_TARGET, event_rect.x);
				break;
			}

			if (event_target.length()) {
				::Print("Mission Event %d: setting camera target to %s\n", id, (const char*) event_target);
				Ship* s_tgt = 0;
				
				if (event_target.indexOf("body:") < 0)
				s_tgt = sim->FindShip(event_target);

				if (s_tgt) {
					::Print("   found ship %s\n", s_tgt->Name());
					cam_dir->SetViewOrbital(0);

					if (cam_dir->GetViewObject() != s_tgt) {

						if (event_param[0] == 6) {
							s_tgt->DropCam(event_param[1], event_param[2]);
							cam_dir->SetShip(s_tgt);
							cam_dir->SetMode(CameraDirector::MODE_DROP, 0);
						}
						else {
							Ship* cam_ship = cam_dir->GetShip();

							if (cam_ship && cam_ship->IsDropCam()) {
								cam_ship->CompleteTransition();
							}

							if (cam_dir->GetShip() != sim->GetPlayerShip())
							cam_dir->SetShip(sim->GetPlayerShip());
							cam_dir->SetViewObject(s_tgt, true); // immediate, no transition
						}
					}
				}

				else {
					const char* body_name = event_target.data();

					if (!strncmp(body_name, "body:", 5))
					body_name += 5;

					Orbital* orb = sim->FindOrbitalBody(body_name);

					if (orb) {
						::Print("   found body %s\n", orb->Name());
						cam_dir->SetViewOrbital(orb);
					}
				}
			}

			if (event_param[0] == 3) {
				cam_dir->SetOrbitPoint(event_point.x, event_point.y, event_point.z);
			}

			else if (event_param[0] == 5) {
				cam_dir->SetOrbitRates(event_point.x, event_point.y, event_point.z);
			}
		}
		break;

	case VOLUME:
		if (stars->InCutscene()) {
			AudioConfig* audio_cfg = AudioConfig::GetInstance();

			audio_cfg->SetEfxVolume(event_param[0]);
			audio_cfg->SetWrnVolume(event_param[0]);
		}
		break;

	case DISPLAY:
		if (stars->InCutscene()) {
			DisplayView* disp_view = DisplayView::GetInstance();

			if (disp_view) {
				Color color;
				color.Set(event_param[0]);

				if (event_message.length() && event_source.length()) {

					if (event_message.contains('$')) {
						Campaign*      campaign = Campaign::GetCampaign();
						Player*        user     = Player::GetCurrentPlayer();
						CombatGroup*   group    = campaign->GetPlayerGroup();

						if (user) {
							event_message = FormatTextReplace(event_message, "$NAME",  user->Name().data());
							event_message = FormatTextReplace(event_message, "$RANK",  Player::RankName(user->Rank()));
						}

						if (group) {
							event_message = FormatTextReplace(event_message, "$GROUP", group->GetDescription());
						}

						if (event_message.contains("$TIME")) {
							char timestr[32];
							FormatDayTime(timestr, campaign->GetTime(), true);
							event_message = FormatTextReplace(event_message, "$TIME", timestr);
						}
					}

					disp_view->AddText(  event_message,
					FontMgr::Find(event_source),
					color,
					event_rect,
					event_point.y,
					event_point.x,
					event_point.z);

				}

				else if (event_target.length()) {
					DataLoader* loader = DataLoader::GetLoader();

					if (loader) {
						loader->SetDataPath(0);
						loader->LoadBitmap(event_target, image, 0, true);
					}

					if (image.Width() && image.Height())
					disp_view->AddImage( &image,
					color,
					Video::BLEND_ALPHA,
					event_rect,
					event_point.y,
					event_point.x,
					event_point.z);
				}
			}
		}
		break;

	case FIRE_WEAPON:
		if (ship) {
			// fire single weapon:
			if (event_param[0] >= 0) {
				ship->FireWeapon(event_param[0]);
			}

			// fire all weapons:
			else {
				ListIter<WeaponGroup> g_iter = ship->Weapons();
				while (++g_iter) {
					ListIter<Weapon> w_iter = g_iter->GetWeapons();
					while (++w_iter) {
						Weapon* w = w_iter.value();
						w->Fire();
					}
				}
			}
		}
		break;

	default:
		break;
	}

	sim->ProcessEventTrigger(TRIGGER_EVENT, id);

	if (!silent && !sound && event_sound.length()) {
		DataLoader* loader = DataLoader::GetLoader();
		bool        use_fs = loader->IsFileSystemEnabled();
		DWORD       flags  = pan ? Sound::LOCKED|Sound::LOCALIZED : 
		Sound::LOCKED|Sound::AMBIENT;

		loader->UseFileSystem(true);
		loader->SetDataPath("Sounds/");
		loader->LoadSound(event_sound, sound, flags);
		loader->SetDataPath(0);

		if (!sound) {
			loader->SetDataPath("Mods/Sounds/");
			loader->LoadSound(event_sound, sound, flags);
			loader->SetDataPath(0);
		}

		if (!sound) {
			loader->LoadSound(event_sound, sound, flags);
		}

		loader->UseFileSystem(use_fs);

		// fire and forget:
		if (sound) {
			if (sound->GetFlags() & Sound::STREAMED) {
				sound->SetFlags(flags | sound->GetFlags());
				sound->SetVolume(AudioConfig::VoxVolume());
				sound->Play();
			}
			else {
				sound->SetFlags(flags);
				sound->SetVolume(AudioConfig::VoxVolume());
				sound->SetPan(pan);
				sound->SetFilename(event_sound);
				sound->AddToSoundCard();
				sound->Play();
			}
		}
	}

	status = COMPLETE;

	if (end_mission) {
		StarServer*  server = StarServer::GetInstance();

		if (stars) {
			stars->EndMission();
		}

		else if (server) {
			// end mission event uses event_target member
			// to forward server to next mission in the chain:
			if (event_target.length())
			server->SetNextMission(event_target);

			server->SetGameMode(StarServer::MENU_MODE);
		}
	}
}
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;
}
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;
}
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<CombatUnit>  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);
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);
			}
		}
	}
}