void
CameraDirector::SetViewObjectGroup(ListIter<Ship> group, bool quick)
{
    if (!ship) return;

    Starshatter* stars = Starshatter::GetInstance();

    if (!stars->InCutscene()) {
        // only view solid contacts:
        while (++group) {
            Ship* s = group.value();

            if (s->GetIFF() != ship->GetIFF()) {
                Contact* c = ship->FindContact(s);
                if (!c || !c->ActLock())
                return;
            }

            if (s->Life() == 0 || s->IsDying() || s->IsDead())
            return;
        }
    }

    group.reset();

    if (external_group.size() > 1 &&
            external_group.size() == group.size()) {

        bool same = true;

        for (int i = 0; same && i < external_group.size(); i++) {
            if (external_group[i] != group.container()[i])
            same = false;
        }

        if (same) {
            SetMode(MODE_ZOOM);
            return;
        }
    }

    ClearGroup();

    if (quick) {
        mode = MODE_ORBIT;
        transition = 0;
    }
    else {
        SetMode(MODE_TRANSLATE);
    }

    external_group.append(group.container());

    ListIter<Ship> iter = external_group;
    while (++iter) {
        Ship* s = iter.value();
        region = s->GetRegion();
        Observe(s);
    }
}
void
CameraDirector::CycleViewObject()
{
    if (!ship) return;

    Ship* current = external_ship;
    external_ship  = 0;

    ListIter<Contact> iter = ship->ContactList();
    while (++iter && !external_ship) {
        Contact* c = iter.value();
        Ship*    c_ship = c->GetShip();

        if (c_ship && !current) {
            external_ship = c_ship;
        }
        
        else if (current && c_ship == current) {
            while (++iter && !external_ship) {
                c = iter.value();
                if (c->ActLock())
                external_ship = c->GetShip();
            }
        }
    }

    if (external_ship != current) {
        if (external_ship) {
            if (external_ship->Life() == 0 || external_ship->IsDying() || external_ship->IsDead()) {
                external_point = external_ship->Location();
                external_ship = 0;
            }
            else {
                Observe(external_ship);
            }
        }

        if (mode == MODE_ORBIT) {
            SetMode(MODE_TRANSLATE);
            ExternalRange(1);
        }
    }
}
void
StarshipAI::FireControl()
{
	// identify unknown contacts:
	if (identify) {
		if (fabs(ship->GetHelmHeading() - ship->CompassHeading()) < 10*DEGREES) {
			Contact* contact = ship->FindContact(target);

			if (contact && !contact->ActLock()) {
				if (!ship->GetProbe()) {
					ship->LaunchProbe();
				}
			}
		}

		return;
	}

	// investigate last known location of enemy ship:
	if (rumor && !target && ship->GetProbeLauncher() && !ship->GetProbe()) {
		// is rumor in basket?
		Point rmr = Transform(rumor->Location());
		rmr.Normalize();

		double dx = fabs(rmr.x);
		double dy = fabs(rmr.y);

		if (dx < 10*DEGREES && dy < 10*DEGREES && rmr.z > 0) {
			ship->LaunchProbe();
		}
	}

	// Corvettes and Frigates are anti-air platforms.  They need to
	// target missile threats even when the threat is aimed at another
	// friendly ship.  Forward facing weapons must be on auto fire,
	// while lateral and aft facing weapons are set to point defense.

	if (ship->Class() == Ship::CORVETTE || ship->Class() == Ship::FRIGATE) {
		ListIter<WeaponGroup> iter = ship->Weapons();
		while (++iter) {
			WeaponGroup* group = iter.value();

			ListIter<Weapon> w_iter = group->GetWeapons();
			while (++w_iter) {
				Weapon* weapon = w_iter.value();

				double az = weapon->GetAzimuth();
				if (fabs(az) < 45*DEGREES) {
					weapon->SetFiringOrders(Weapon::AUTO);
					weapon->SetTarget(target, 0);
				}

				else {
					weapon->SetFiringOrders(Weapon::POINT_DEFENSE);
				}
			}
		}
	}

	// All other starships are free to engage ship targets.  Weapon
	// fire control is managed by the type of weapon.

	else {
		System* subtgt = SelectSubtarget();

		ListIter<WeaponGroup> iter = ship->Weapons();
		while (++iter) {
			WeaponGroup* weapon = iter.value();

			if (weapon->GetDesign()->target_type & Ship::DROPSHIPS) {   // anti-air weapon?
				weapon->SetFiringOrders(Weapon::POINT_DEFENSE);
			}
			else if (weapon->IsDrone()) {                               // torpedoes
				weapon->SetFiringOrders(Weapon::MANUAL);
				weapon->SetTarget(target, 0);

				if (target && target->GetRegion() == ship->GetRegion()) {
					Point  delta = target->Location() - ship->Location();
					double range = delta.length();

					if (range < weapon->GetDesign()->max_range * 0.9 && 
							!AssessTargetPointDefense())
					weapon->SetFiringOrders(Weapon::AUTO);

					else if (range < weapon->GetDesign()->max_range * 0.5)
					weapon->SetFiringOrders(Weapon::AUTO);
				}
			}
			else {                                                      // anti-ship weapon
				weapon->SetFiringOrders(Weapon::AUTO);
				weapon->SetTarget(target, subtgt);
				weapon->SetSweep(subtgt ? Weapon::SWEEP_NONE : Weapon::SWEEP_TIGHT);
			}
		}
	}
}
void
CameraDirector::ExecFrame(double seconds)
{
    if (!ship)
    return;

    hud = HUDView::GetInstance();

    int flight_phase = ship->GetFlightPhase();

    if (flight_phase < Ship::LOCKED)
    SetMode(MODE_DOCKING);

    if (ship->IsAirborne()) {
        if (flight_phase >= Ship::DOCKING)
        SetMode(MODE_DOCKING);
    }
    else {
        if (flight_phase >= Ship::RECOVERY)
        SetMode(MODE_DOCKING);
    }

    if (flight_phase >= Ship::LOCKED && flight_phase < Ship::ACTIVE) {
        int m = GetMode();
        if (m != MODE_COCKPIT && m != MODE_VIRTUAL)
        SetMode(MODE_COCKPIT);
    }

    if (ship->InTransition()) {
        SetMode(MODE_DROP);
    }
    // automatically restore mode after transition:
    else if (old_mode != MODE_NONE) {
        mode           = old_mode;
        requested_mode = old_mode;
        old_mode       = MODE_NONE;
    }

    int op_mode = mode;
    if (requested_mode > mode)
    op_mode = requested_mode;

    Starshatter* stars = Starshatter::GetInstance();

    // if we are in padlock, and have not locked a ship
    // try to padlock the current target:
    if (op_mode == MODE_TARGET && !external_ship) {
        SimObject* tgt = ship->GetTarget();
        if (tgt && tgt->Type() == SimObject::SIM_SHIP)
        SetViewObject((Ship*) tgt);
    }

    // if in an external mode, check the external ship:
    else if (op_mode >= MODE_TARGET && op_mode <= MODE_ZOOM) {
        if (external_ship && external_ship != ship && !stars->InCutscene()) {
            Contact* c = ship->FindContact(external_ship);
            if (!c || !c->ActLock()) {
                SetViewObject(ship);
            }
        }
    }

    if (ship->Rep()) {
        if (op_mode == MODE_COCKPIT) {
            ship->HideRep();
            ship->HideCockpit();
        }
        else if (op_mode == MODE_VIRTUAL || op_mode == MODE_TARGET) {
            if (ship->Cockpit()) {
                ship->HideRep();
                ship->ShowCockpit();
            }
            else {
                ship->ShowRep();
            }
        }
        else {
            ship->Rep()->SetForeground(op_mode == MODE_DOCKING);
            ship->ShowRep();
            ship->HideCockpit();
        }
    }

    if (hud && hud->Ambient() != Color::Black)
    sim->GetScene()->SetAmbient( hud->Ambient() );
    else
    sim->GetScene()->SetAmbient( sim->GetStarSystem()->Ambient() );

    switch (op_mode) {
    default:
    case MODE_COCKPIT:      Cockpit(seconds);    break;
    case MODE_CHASE:        Chase(seconds);      break;
    case MODE_TARGET:       Target(seconds);     break;
    case MODE_THREAT:       Threat(seconds);     break;
    case MODE_VIRTUAL:      Virtual(seconds);    break;
    case MODE_ORBIT:
    case MODE_TRANSLATE:
    case MODE_ZOOM:         Orbit(seconds);      break;
    case MODE_DOCKING:      Docking(seconds);    break;
    case MODE_DROP:         Drop(seconds);       break;
    }

    if (ship->Shake() > 0 && 
            (op_mode <  MODE_ORBIT ||
                (op_mode == MODE_VIRTUAL && ship->Cockpit()))) {

        Point vib = ship->Vibration() * 0.2;
        camera.MoveBy(vib);
        camera.Aim(0, vib.y, vib.z);
    }

    Transition(seconds);
    DetailSet::SetReference(region, camera.Pos());
}
void
CameraDirector::SetViewObject(Ship* obj, bool quick)
{
    if (!ship) return;
    if (!obj) {
        obj = ship;
        region = ship->GetRegion();
    }

    external_body  = 0;
    external_point = Point();

    Starshatter* stars = Starshatter::GetInstance();

    if (obj->GetIFF() != ship->GetIFF() && !stars->InCutscene()) {
        // only view solid contacts:
        Contact* c = ship->FindContact(obj);
        if (!c || !c->ActLock()) 
        return;
    }

    if (mode == MODE_TARGET) {
        ClearGroup();
        if (external_ship) {
            external_ship = 0;
        }
    }
    else if (mode >= MODE_ORBIT) {
        if (quick) {
            mode       = MODE_ORBIT;
            transition = 0;
        }
        else {
            SetMode(MODE_TRANSLATE);
        }

        if (external_group.size()) {
            ClearGroup();

            if (external_ship) {
                external_ship = 0;
            }
        }

        else {
            if ((obj == external_ship) || (obj==ship && external_ship==0)) {
                if (!quick)
                SetMode(MODE_ZOOM);
            }
            
            else if (external_ship) {
                external_ship = 0;
            }
        }
    }

    if (external_ship != obj) {
        external_ship = obj;

        if (external_ship) {
            region = external_ship->GetRegion();

            if (external_ship->Life() == 0 || external_ship->IsDying() || external_ship->IsDead()) {
                external_ship = 0;
                range_min = 100;
            }
            else {
                Observe(external_ship);

                if (sim)
                sim->ActivateRegion(external_ship->GetRegion());

                range_min = external_ship->Radius() * 1.5;
            }
        }

        Observe(external_ship);
        ExternalRange(1);
    }
}