void Attack(TimeSpan localTime)
        {
            FriendlyShipScratchpad.Clear();

            var intelItems = IntelProvider.GetFleetIntelligences(localTime);

            foreach (var kvp in intelItems)
            {
                if (kvp.Key.Item1 == IntelItemType.Friendly)
                {
                    var friendly = (FriendlyShipIntel)kvp.Value;
                    if (friendly.AgentClass == AgentClass.Fighter && (friendly.AgentStatus & AgentStatus.Docked) != 0 && (friendly.GetPositionFromCanonicalTime(localTime + IntelProvider.CanonicalTimeDiff) - Controller.GetPosition()).Length() < 100)
                    {
                        FriendlyShipScratchpad.Add(friendly);
                    }
                }
            }

            if (FriendlyShipScratchpad.Count == 0)
            {
                return;
            }

            for (int i = 0; i < FriendlyShipScratchpad.Count; i++)
            {
                var targetWaypoint = new Waypoint();

                var gravDir = Controller.GetNaturalGravity();
                targetWaypoint.Position = Controller.GetPosition();
                var angle = 2 * i * Math.PI / FriendlyShipScratchpad.Count;

                if (gravDir != Vector3D.Zero)
                {
                    gravDir.Normalize();
                    var flatForward = Controller.WorldMatrix.Forward - VectorHelpers.VectorProjection(Controller.WorldMatrix.Forward, gravDir);
                    flatForward.Normalize();
                    var flatLeftDir = Vector3D.Cross(flatForward, gravDir);
                    targetWaypoint.Position += (flatForward * TrigHelpers.FastCos(angle) + flatLeftDir * TrigHelpers.FastSin(angle)) * 500;
                    targetWaypoint.Position -= gravDir * 200;
                }
                else
                {
                    targetWaypoint.Position += (Controller.WorldMatrix.Forward * TrigHelpers.FastCos(angle) + Controller.WorldMatrix.Left * TrigHelpers.FastSin(angle)) * 500;
                }

                targetWaypoint.Position += Controller.GetShipVelocities().LinearVelocity * 3;
                IntelProvider.ReportFleetIntelligence(targetWaypoint, localTime);
                IntelProvider.ReportCommand(FriendlyShipScratchpad[i], TaskType.Attack, MyTuple.Create(IntelItemType.Waypoint, targetWaypoint.ID), localTime);
            }
        }
Example #2
0
        public void Main(string argument, UpdateType updateSource)
        {
            subsystemManager.UpdateTime();

            if (argument == "descend")
            {
                Mode       = 1;
                lockMatrix = Drive.Controller.WorldMatrix;
            }
            else if (argument == "ascend")
            {
                Mode       = 2;
                lockMatrix = Drive.Controller.WorldMatrix;
            }
            else if (argument == "stop")
            {
                Mode       = 0;
                lockMatrix = MatrixD.Zero;
                Drive.Clear();
            }
            else if (commandLine.TryParse(argument))
            {
                subsystemManager.Command(commandLine.Argument(0), commandLine.Argument(1), commandLine.ArgumentCount > 2 ? commandLine.Argument(2) : null);
            }
            else
            {
                try
                {
                    subsystemManager.Update(updateSource);

                    runs++;
                    if (runs % 5 == 0)
                    {
                        OutputBuilder.Clear();

                        OutputBuilder.Append($"STATUS: ").AppendLine(Mode == 0 ? "STOP" : (Mode == 1 ? "DESC" : "ASCE"));

                        foreach (var screen in Displays)
                        {
                            screen.ContentType = ContentType.TEXT_AND_IMAGE;
                            screen.WriteText(OutputBuilder.ToString());
                        }

                        var destination = new Waypoint();

                        if (Mode == 0)
                        {
                            Drive.Clear();
                        }
                        else if (Mode == 1)
                        {
                            destination.MaxSpeed = DescendSpeed;
                            var gravdir = Cockpit.GetNaturalGravity();
                            if (gravdir == Vector3D.Zero)
                            {
                                gravdir.Normalize();

                                destination.Position = gravdir * 10 + Cockpit.GetPosition();

                                var flatForward = Cockpit.WorldMatrix.Forward - VectorHelpers.VectorProjection(Cockpit.WorldMatrix.Forward, gravdir);
                                flatForward.Normalize();
                                var flatLeft = Cockpit.WorldMatrix.Left - VectorHelpers.VectorProjection(Cockpit.WorldMatrix.Forward, gravdir);
                                flatLeft.Normalize();

                                destination.Direction = flatForward * TrigHelpers.FastCos(0.02 * RotateSpeed) + flatLeft * TrigHelpers.FastSin(0.02 * RotateSpeed);
                            }
                            else
                            {
                                destination.Position    = lockMatrix.Down * 10 + Cockpit.GetPosition();
                                destination.DirectionUp = lockMatrix.Up;

                                var flatForward = Cockpit.WorldMatrix.Forward - VectorHelpers.VectorProjection(Cockpit.WorldMatrix.Forward, lockMatrix.Down);
                                flatForward.Normalize();
                                var flatLeft = Cockpit.WorldMatrix.Left - VectorHelpers.VectorProjection(Cockpit.WorldMatrix.Forward, lockMatrix.Down);
                                flatLeft.Normalize();

                                destination.Direction = flatForward * TrigHelpers.FastCos(0.02 * RotateSpeed) + flatLeft * TrigHelpers.FastSin(0.02 * RotateSpeed);
                            }
                        }
                        else if (Mode == 2)
                        {
                            destination.MaxSpeed = AscendSpeed;
                            if (Drive.Controller == Cockpit)
                            {
                                var gravdir = Cockpit.GetNaturalGravity();
                                gravdir.Normalize();

                                destination.Position = -gravdir * 10 + Cockpit.GetPosition();
                            }
                            else
                            {
                                destination.Position  = lockMatrix.Down * 10 + Cockpit.GetPosition();
                                destination.Direction = lockMatrix.Forward;
                            }
                        }

                        Drive.Move(destination.Position);
                        Drive.Turn(destination.Direction);
                        Drive.Spin(destination.DirectionUp);
                        Drive.SetMaxSpeed(destination.MaxSpeed);
                    }
                }
                catch (Exception e)
                {
                    Me.GetSurface(0).WriteText(e.Message);
                    Me.GetSurface(0).WriteText("\n", true);
                    Me.GetSurface(0).WriteText(e.StackTrace);
                    Me.GetSurface(0).WriteText("\n", true);
                    Me.GetSurface(0).WriteText(e.ToString());
                }
                var s = subsystemManager.GetStatus();
                if (!string.IsNullOrEmpty(s))
                {
                    Echo(s);
                }
                else
                {
                    Echo(((int)subsystemManager.OutputMode).ToString());
                }
            }
        }
Example #3
0
        public void Update(TimeSpan timestamp, UpdateFrequency updateFlags)
        {
            if (runs % 240 == 0)
            {
                statusBuilder.Clear();
                foreach (var tube in LandpedoTubes)
                {
                    tube.CheckLandpedo();
                    // statusBuilder.AppendLine(tube.GetStatus());
                }
            }

            if (runs % 10 == 0)
            {
                statusBuilder.Clear();
                var targets       = IntelProvider.GetFleetIntelligences(timestamp);
                var canonicalTime = IntelProvider.CanonicalTimeDiff + timestamp;
                DeadLandpedos.Clear();
                foreach (var landpedo in Landpedos)
                {
                    statusBuilder.AppendLine("LANDPEDO===");

                    if (landpedo.Fired)
                    {
                        landpedo.DeadCount--;
                    }

                    if (!landpedo.IsOK())
                    {
                        DeadLandpedos.Add(landpedo);
                        continue;
                    }
                    landpedo.runs++;
                    var landpedoPosition = landpedo.Controller.WorldMatrix.Translation;
                    var landpedoVelocity = landpedo.Controller.GetShipVelocities().LinearVelocity;
                    var gravDir          = landpedo.Controller.GetNaturalGravity();
                    var gravStr          = gravDir.Normalize();
                    var planarVelocity   = landpedoVelocity - VectorHelpers.VectorProjection(landpedoVelocity, gravDir);

                    if (landpedo.TargetID != -1)
                    {
                        var key = MyTuple.Create(IntelItemType.Enemy, landpedo.TargetID);
                        if (targets.ContainsKey(key))
                        {
                            var target = targets[key];
                            landpedo.TargetPosition = target.GetPositionFromCanonicalTime(canonicalTime);
                            landpedo.TargetVelocity = target.GetVelocity();
                        }
                    }

                    statusBuilder.AppendLine(landpedo.TargetPosition.ToString());

                    if (landpedo.TargetPosition != Vector3D.Zero)
                    {
                        var relativeVector = landpedo.TargetPosition - landpedoPosition;
                        var planarVector   = relativeVector - VectorHelpers.VectorProjection(relativeVector, gravDir);

                        var targetPoint = AttackHelpers.GetAttackPoint(landpedo.TargetVelocity, landpedo.TargetPosition - landpedoPosition, landpedo.lastSpeed);
                        if (targetPoint == Vector3D.Zero)
                        {
                            targetPoint = landpedo.TargetPosition - landpedoPosition;
                        }
                        var targetPointDist = targetPoint.Length();

                        var planarDist = planarVector.Length();
                        var velocity   = landpedo.Controller.GetShipVelocities().LinearVelocity;

                        var verticalVelocity = VectorHelpers.VectorProjection(velocity, gravDir);

                        var planarLeft = landpedo.Controller.WorldMatrix.Left - VectorHelpers.VectorProjection(landpedo.Controller.WorldMatrix.Left, gravDir);
                        planarLeft.Normalize();

                        var planarForward = landpedo.Controller.WorldMatrix.Forward - VectorHelpers.VectorProjection(landpedo.Controller.WorldMatrix.Forward, gravDir);
                        planarForward.Normalize();

                        double altitude;
                        landpedo.Controller.TryGetPlanetElevation(MyPlanetElevation.Surface, out altitude);

                        if (targetPointDist > 350 || landpedo.Launchers.Count == 0)
                        {
                            landpedo.desiredAltitude = planarVector.Length() > 200 ? 10 : 50;
                            planarVector.Normalize();

                            MatrixD orientationMatrix = MatrixD.Identity;
                            orientationMatrix.Translation = landpedo.Controller.WorldMatrix.Translation;

                            orientationMatrix.Up      = -gravDir;
                            orientationMatrix.Left    = planarLeft;
                            orientationMatrix.Forward = planarForward;

                            var spinAngle = VectorHelpers.VectorAngleBetween(planarForward, planarVector) * Math.Sign(-planarLeft.Dot(planarVector));

                            // var planarVelocity = velocity - verticalVelocity;
                            // var velocityAdjust = planarVelocity - VectorHelpers.VectorProjection(velocity, planarVector);
                            // velocityAdjust.Normalize();
                            // planarVector -= velocityAdjust;
                            // var MoveIndicator = Vector3D.TransformNormal(planarVector, MatrixD.Transpose(orientationMatrix));

                            var rangeVector    = planarVector;
                            var waypointVector = rangeVector;
                            var distTargetSq   = rangeVector.LengthSquared();

                            var targetPlanarVelocity = landpedo.TargetVelocity - VectorHelpers.VectorProjection(landpedo.TargetVelocity, gravDir);


                            Vector3D velocityVector = targetPlanarVelocity - planarVelocity;
                            var      speed          = planarVelocity.Length();

                            Vector3D AccelerationVector;

                            double alignment = planarVelocity.Dot(ref waypointVector);
                            if (alignment > 0)
                            {
                                Vector3D rangeDivSqVector = waypointVector / waypointVector.LengthSquared();
                                Vector3D compensateVector = velocityVector - (velocityVector.Dot(ref waypointVector) * rangeDivSqVector);

                                Vector3D targetANVector;
                                var      targetAccel = (landpedo.lastTargetVelocity - targetPlanarVelocity) * 0.16667;

                                targetANVector = targetAccel - (targetAccel.Dot(ref waypointVector) * rangeDivSqVector);

                                bool accelerating = speed > landpedo.lastSpeed + 1;
                                if (accelerating)
                                {
                                    AccelerationVector = planarVelocity + (10 * 1.5 * (compensateVector + (0.5 * targetANVector)));
                                }
                                else
                                {
                                    AccelerationVector = planarVelocity + (10 * (compensateVector + (0.5 * targetANVector)));
                                }
                            }
                            // going backwards or perpendicular
                            else
                            {
                                AccelerationVector = (waypointVector * 0.1) + velocityVector;
                            }

                            landpedo.lastTargetVelocity = landpedo.TargetVelocity;
                            landpedo.lastSpeed          = speed;

                            var MoveIndicator = Vector3D.TransformNormal(AccelerationVector, MatrixD.Transpose(orientationMatrix));

                            MoveIndicator.Y = 0;
                            MoveIndicator.Normalize();

                            statusBuilder.AppendLine(MoveIndicator.ToString());

                            landpedo.Drive.MoveIndicators     = MoveIndicator;
                            landpedo.Drive.RotationIndicators = new Vector3(0, spinAngle, 0);

                            landpedo.Drive.Drive();

                            if (verticalVelocity.Length() > 10 && verticalVelocity.Dot(gravDir) > 0)
                            {
                                landpedo.Drive.maxFlightPitch = 20;
                                landpedo.Drive.maxFlightRoll  = 20;
                            }
                            else
                            {
                                landpedo.Drive.maxFlightPitch = 60;
                                landpedo.Drive.maxFlightRoll  = 60;
                            }

                            if (targetPointDist < 1000)
                            {
                                landpedo.Drive.maxFlightPitch = 20;
                            }
                        }
                        else if (landpedo.TargetID != -1)
                        {
                            var key = MyTuple.Create(IntelItemType.Enemy, landpedo.TargetID);
                            if (!targets.ContainsKey(key))
                            {
                                return;
                            }
                            var target = targets[key];

                            var posDiff = target.GetPositionFromCanonicalTime(canonicalTime) - landpedoPosition;

                            var avgVel = 400 * Math.Sqrt(posDiff.Length() / 400);

                            var relativeAttackPoint = AttackHelpers.GetAttackPoint(landpedo.TargetVelocity - velocity, posDiff, avgVel);
                            targetPointDist = relativeAttackPoint.Length();

                            double yawAngle, pitchAngle;

                            TrigHelpers.GetRotationAngles(relativeAttackPoint, landpedo.Controller.WorldMatrix.Forward, landpedo.Controller.WorldMatrix.Left, landpedo.Controller.WorldMatrix.Up, out yawAngle, out pitchAngle);
                            TrigHelpers.ApplyGyroOverride(landpedo.PitchPID.Control(pitchAngle), landpedo.YawPID.Control(yawAngle), 0, landpedo.Gyros, landpedo.Controller.WorldMatrix);

                            if ((targetPointDist < 125 || landpedo.minDist < planarDist))
                            {
                                foreach (var weapon in landpedo.Launchers)
                                {
                                    weapon.Enabled = true;
                                }
                                landpedo.Fired = true;
                            }

                            landpedo.minDist = Math.Min(landpedo.minDist, planarDist);
                        }

                        var verticalThrustRatio = TrigHelpers.FastCos(VectorHelpers.VectorAngleBetween(landpedo.Controller.WorldMatrix.Down, gravDir));
                        var desiredThrust       = ((landpedo.desiredAltitude - altitude) + verticalVelocity.LengthSquared() * 0.1 + gravStr) * landpedo.Controller.CalculateShipMass().PhysicalMass / verticalThrustRatio;
                        var individualThrust    = desiredThrust / landpedo.Thrusters.Count();
                        if (individualThrust <= 0)
                        {
                            individualThrust = 0.001;
                        }

                        foreach (var thruster in landpedo.Thrusters)
                        {
                            thruster.Enabled        = ((verticalVelocity.Length() > 20 && verticalVelocity.Dot(gravDir) < 0) || ((verticalVelocity.Length() > 2 && verticalVelocity.Dot(gravDir) < 0) && targetPointDist < 1000)) ? false : true;
                            thruster.ThrustOverride = (float)individualThrust;
                        }

                        statusBuilder.AppendLine($"{landpedo.Drive.thrustController.CalculateThrustToHover()}");
                        statusBuilder.AppendLine($"{landpedo.Drive.thrustController.upThrusters.Count} : {landpedo.Drive.thrustController.downThrusters.Count}");
                    }
                }

                foreach (var landpedo in DeadLandpedos)
                {
                    Landpedos.Remove(landpedo);
                }
            }
            runs++;
        }
        double GetMaxAccelFromAngleDeviation(float angle)
        {
            var DownAccel    = MaxDownThrust / shipMass;
            var LateralAccel = MaxLateralThrust / shipMass;
            var LiftAccel    = MaxLiftThrust / shipMass;

            return(Math.Min(LateralAccel - gravStr * TrigHelpers.FastSin(angle), Math.Min(LiftAccel - gravStr * TrigHelpers.FastCos(angle), DownAccel + gravStr * TrigHelpers.FastCos(angle))));
        }
        public void Update(TimeSpan timestamp, UpdateFrequency updateFlags)
        {
            runs++;

            if (runs % 30 == 0)
            {
                MaxLiftThrust    = 0;
                MaxLateralThrust = 0;
                MaxDownThrust    = 0;
                foreach (var kvp in Thrusters)
                {
                    if (kvp.Key == Base6Directions.Direction.Down)
                    {
                        foreach (var thruster in kvp.Value)
                        {
                            MaxLiftThrust += thruster.MaxEffectiveThrust;
                        }
                    }
                    else if (kvp.Key == Base6Directions.Direction.Forward)
                    {
                        foreach (var thruster in kvp.Value)
                        {
                            MaxLateralThrust += thruster.MaxEffectiveThrust;
                        }
                    }
                    else if (kvp.Key == Base6Directions.Direction.Up)
                    {
                        foreach (var thruster in kvp.Value)
                        {
                            MaxDownThrust += thruster.MaxEffectiveThrust;
                        }
                    }

                    ThrusterManagers[kvp.Key].RecalculateThrust();
                }
            }

            if (runs % 5 == 0)
            {
                StatusBuilder.Clear();

                shipMass = Controller.CalculateShipMass().PhysicalMass;
                gravDir  = Controller.GetNaturalGravity();

                double yawAngle, pitchAngle, spinAngle;
                yawAngle = pitchAngle = spinAngle = 0;
                MatrixD orientationMatrix = new MatrixD();

                var flatCurrentDir = Controller.WorldMatrix.Forward - VectorHelpers.VectorProjection(Controller.WorldMatrix.Forward, gravDir);
                flatCurrentDir.Normalize();

                var flatLeftDir = Vector3D.Cross(flatCurrentDir, gravDir);

                if (gravDir != Vector3D.Zero)
                {
                    gravStr = gravDir.Length();
                    gravDir.Normalize();
                    // Rotational Control
                    var targetDir = Vector3D.Zero;
                    if (ForwardDir != Vector3D.Zero)
                    {
                        targetDir = ForwardDir;
                    }
                    else if (FullAuto)
                    {
                        targetDir = Controller.WorldMatrix.Forward - VectorHelpers.VectorProjection(Controller.WorldMatrix.Forward, gravDir);
                    }

                    if (targetDir != Vector3D.Zero)
                    {
                        if (UpDir == Vector3D.Zero)
                        {
                            var angleFromVertical    = VectorHelpers.VectorAngleBetween(targetDir, gravDir) - Math.PI * 0.5;
                            var maxAngleFromVertical = GetMaxAngleConstraint();
                            angleFromVertical = Math.Max(Math.Min(angleFromVertical, maxAngleFromVertical), -maxAngleFromVertical);
                            var flatAimDir = targetDir - VectorHelpers.VectorProjection(targetDir, gravDir);
                            flatAimDir.Normalize();

                            var downDir = TrigHelpers.FastCos(angleFromVertical) * gravDir + TrigHelpers.FastSin(angleFromVertical) * flatAimDir;

                            orientationMatrix.Forward = Controller.WorldMatrix.Down;
                            orientationMatrix.Left    = Controller.WorldMatrix.Left;
                            orientationMatrix.Up      = Controller.WorldMatrix.Forward;

                            spinAngle = -VectorHelpers.VectorAngleBetween(flatAimDir, flatCurrentDir) * Math.Sign(Controller.WorldMatrix.Left.Dot(flatAimDir));
                            TrigHelpers.GetRotationAngles(downDir, orientationMatrix.Forward, orientationMatrix.Left, orientationMatrix.Up, out yawAngle, out pitchAngle);
                        }
                        else
                        {
                            orientationMatrix = reference.WorldMatrix;
                            TrigHelpers.GetRotationAngles(ForwardDir, reference.WorldMatrix.Forward, reference.WorldMatrix.Left, reference.WorldMatrix.Up, out yawAngle, out pitchAngle);
                            var projectedTargetUp = UpDir - reference.WorldMatrix.Forward.Dot(UpDir) * reference.WorldMatrix.Forward;
                            spinAngle = -1 * VectorHelpers.VectorAngleBetween(reference.WorldMatrix.Up, projectedTargetUp) * Math.Sign(reference.WorldMatrix.Left.Dot(UpDir));
                        }
                    }
                }
                else if (ForwardDir != Vector3D.Zero)
                {
                    orientationMatrix = reference.WorldMatrix;
                    TrigHelpers.GetRotationAngles(ForwardDir, reference.WorldMatrix.Forward, reference.WorldMatrix.Left, reference.WorldMatrix.Up, out yawAngle, out pitchAngle);

                    if (UpDir != Vector3D.Zero)
                    {
                        var projectedTargetUp = UpDir - reference.WorldMatrix.Forward.Dot(UpDir) * reference.WorldMatrix.Forward;
                        spinAngle = -1 * VectorHelpers.VectorAngleBetween(reference.WorldMatrix.Up, projectedTargetUp) * Math.Sign(reference.WorldMatrix.Left.Dot(UpDir));
                    }
                }

                if (yawAngle != 0 || pitchAngle != 0 || spinAngle != 0)
                {
                    TrigHelpers.ApplyGyroOverride(PitchPID.Control(pitchAngle), YawPID.Control(yawAngle), gravDir == Vector3D.Zero ? spinAngle : SpinPID.Control(spinAngle), Gyros, orientationMatrix);
                }
                else
                {
                    foreach (var gyro in Gyros)
                    {
                        if (gyro.GyroOverride)
                        {
                            gyro.GyroOverride = false;
                        }
                    }
                }

                // Translational Control

                if (Destination == Vector3D.Zero && TargetDrift == Vector3D.Zero)
                {
                    foreach (var kvp in ThrusterManagers)
                    {
                        kvp.Value.SetThrust(0);
                    }

                    if (FullAuto)
                    {
                        Controller.DampenersOverride = true;
                    }
                }
                else
                {
                    Controller.DampenersOverride = false;

                    // Compute direction of motion
                    var destinationDir  = Destination - Reference.GetPosition();
                    var destinationDist = destinationDir.Length();
                    destinationDir.Normalize();

                    // Compute current motion to find desired acceleration
                    var currentVel = Controller.GetShipVelocities().LinearVelocity;

                    if (destinationDist < 0.25 && currentVel.Length() < 0.25 && TargetDrift == Vector3D.Zero)
                    {
                        foreach (var kvp in ThrusterManagers)
                        {
                            kvp.Value.SetThrust(0);
                        }
                        Controller.DampenersOverride = true;
                        Destination = Vector3D.Zero;
                    }
                    else
                    {
                        Vector3D desiredVel = Vector3D.Zero;
                        if (Destination != Vector3D.Zero)
                        {
                            var maxSpeed = Math.Min(MaxSpeed, GetMaxSpeedFromBrakingDistance(destinationDist, GetMaxAccelFromAngleDeviation((float)GetMaxAngleConstraint() * MaxAngleTolerance)) * 0.9);
                            maxSpeed   = Math.Min(maxSpeed, destinationDist * destinationDist + 0.5);
                            desiredVel = destinationDir * maxSpeed;
                        }
                        desiredVel += TargetDrift;
                        var adjustVector = currentVel - VectorHelpers.VectorProjection(currentVel, desiredVel);
                        var desiredAccel = desiredVel - currentVel - adjustVector * 2;

                        // Transform desired acceleration into remote control frame
                        var gridDesiredAccel = Vector3D.TransformNormal(desiredAccel, MatrixD.Transpose(Controller.WorldMatrix));

                        double accelMagnitude = gridDesiredAccel.Length();
                        if (accelMagnitude < PrecisionThreshold)
                        {
                            gridDesiredAccel *= Math.Max(accelMagnitude / PrecisionThreshold, .1);
                        }

                        gridDesiredAccel.X = XPID.Control(gridDesiredAccel.X);
                        gridDesiredAccel.Y = YPID.Control(gridDesiredAccel.Y);
                        gridDesiredAccel.Z = ZPID.Control(gridDesiredAccel.Z);

                        double MinScale    = 10;
                        var    gridGravDir = Vector3D.TransformNormal(gravDir, MatrixD.Transpose(Controller.WorldMatrix));
                        foreach (var kvp in ThrusterManagers)
                        {
                            var desiredDirectionalThrust = -1 * Base6Directions.GetVector(kvp.Key).Dot(gridDesiredAccel) * shipMass;
                            var gravAssist = Base6Directions.GetVector(kvp.Key).Dot(gridGravDir) * shipMass;
                            if (desiredDirectionalThrust > 0)
                            {
                                MinScale = Math.Min((kvp.Value.MaxThrust - gravAssist) / desiredDirectionalThrust, MinScale);
                            }
                        }

                        gridDesiredAccel *= MinScale;
                        gridDesiredAccel -= gridGravDir * gravStr;

                        foreach (var kvp in ThrusterManagers)
                        {
                            kvp.Value.SetThrust(-1 * Base6Directions.GetVector(kvp.Key).Dot(gridDesiredAccel + gridGravDir) * shipMass);
                        }
                    }
                }
            }
        }
        public void Update(TimeSpan timestamp, UpdateFrequency updateFlags)
        {
            if (timestamp == TimeSpan.Zero)
            {
                return;
            }
            foreach (var bird in Hummingbirds)
            {
                if (bird.IsAlive())
                {
                    bird.Update();
                }
                else
                {
                    DeadBirds.Add(bird);
                }
            }

            foreach (var bird in DeadBirds)
            {
                DeregisterBird(bird);
            }

            runs++;
            if (runs % 20 == 0)
            {
                var intelItems = IntelProvider.GetFleetIntelligences(timestamp);

                // Get the top targets
                TopEnemies.Clear();
                EnemyToScore.Clear();

                foreach (var intelItem in intelItems)
                {
                    if (intelItem.Key.Item1 == IntelItemType.Enemy)
                    {
                        var esi  = (EnemyShipIntel)intelItem.Value;
                        var dist = (esi.GetPositionFromCanonicalTime(timestamp + IntelProvider.CanonicalTimeDiff) - Context.Reference.WorldMatrix.Translation).Length();
                        if (dist > MaxEngagementDist)
                        {
                            continue;
                        }

                        var priority = IntelProvider.GetPriority(esi.ID);
                        var size     = esi.Radius;

                        if (size < MinEngagementSize && priority < 3)
                        {
                            continue;
                        }

                        if (priority < 2)
                        {
                            continue;
                        }

                        int score = (int)(priority * 10000 + size);

                        EnemyToScore[esi] = score;

                        for (int i = 0; i <= TopEnemies.Count; i++)
                        {
                            if (i == TopEnemies.Count || score > EnemyToScore[TopEnemies[i]])
                            {
                                TopEnemies.Insert(i, esi);
                                break;
                            }
                        }
                    }
                }

                // Determine how many birds should be assigned to each enemy
                EnemyToNumBirds.Clear();

                int totalNeededBirds = 0;

                for (int i = 0; i < TopEnemies.Count && i < 4; i++)
                {
                    EnemyToNumBirds[TopEnemies[i]]      = EnemyCountToNumBirdsPerEnemy[TopEnemies.Count][i];
                    EnemyToAssignedBirds[TopEnemies[i]] = 0;
                    totalNeededBirds += EnemyToNumBirds[TopEnemies[i]];
                }

                // Remove excess birds from enemies
                foreach (var bird in Hummingbirds)
                {
                    var birdTargetID = BirdToEnemy[bird];
                    if (birdTargetID == 0)
                    {
                        continue;
                    }

                    if (!bird.IsCombatCapable())
                    {
                        BirdToEnemy[bird] = 0;
                        continue;
                    }

                    var birdTargetKey = MyTuple.Create(IntelItemType.Enemy, birdTargetID);
                    if (!intelItems.ContainsKey(birdTargetKey))
                    {
                        BirdToEnemy[bird] = 0;
                        continue;
                    }

                    var birdTarget = (EnemyShipIntel)intelItems[birdTargetKey];
                    if (!EnemyToNumBirds.ContainsKey(birdTarget) || EnemyToNumBirds[birdTarget] == 0)
                    {
                        BirdToEnemy[bird] = 0;
                        continue;
                    }

                    EnemyToNumBirds[birdTarget]--;
                    totalNeededBirds--;
                }

                // Assign birds to enemies
                foreach (var bird in Hummingbirds)
                {
                    if (totalNeededBirds == 0)
                    {
                        break;
                    }

                    // Bird can't fight, keep looking
                    if (!bird.IsCombatCapable())
                    {
                        continue;
                    }

                    // Bird already has target, keep looking
                    if (BirdToEnemy[bird] != 0)
                    {
                        continue;
                    }

                    EnemyShipIntel targetEnemy = null;
                    foreach (var enemy in EnemyToNumBirds.Keys)
                    {
                        if (EnemyToNumBirds[enemy] > 0)
                        {
                            targetEnemy = enemy;
                            break;
                        }
                    }

                    BirdToEnemy[bird] = targetEnemy.ID;
                    EnemyToNumBirds[targetEnemy]--;
                    totalNeededBirds--;
                }

                NeedsMoreBirds = totalNeededBirds > 0;
                int birdIndex;

                // ASSUME birds are not far enough from main controller that gravity direction matters too much
                var gravDir = Controller.GetTotalGravity();
                gravDir.Normalize();

                // For each enemy, assign bird target and destination
                foreach (var enemy in TopEnemies)
                {
                    birdIndex = 0;
                    foreach (var bird in Hummingbirds)
                    {
                        if (BirdToEnemy[bird] != enemy.ID)
                        {
                            continue;
                        }

                        if (bird.Gats.Count == 0)
                        {
                            continue;
                        }
                        var birdAltitudeTheta = Math.PI * ((runs / (BirdSineConstantSeconds * 30) % 2) - 1);
                        var birdSwayTheta     = Math.PI * ((runs / (BirdPendulumConstantSeconds * 30) % 2) - 1);
                        var targetPos         = enemy.GetPositionFromCanonicalTime(timestamp + IntelProvider.CanonicalTimeDiff);

                        bird.SetTarget(targetPos, enemy.GetVelocity() - gravDir * (float)TrigHelpers.FastCos(birdAltitudeTheta) * 2);

                        var targetToBase = bird.Base.WorldMatrix.Translation - targetPos;
                        targetToBase -= VectorHelpers.VectorProjection(targetToBase, gravDir);
                        var targetToBaseDist = targetToBase.Length();
                        targetToBase.Normalize();

                        var engageLocationLocus    = targetToBase * Math.Min(600, targetToBaseDist + 400) + targetPos;
                        var engageLocationSwayDir  = targetToBase.Cross(gravDir);
                        var engageLocationSwayDist = (TrigHelpers.FastCos(birdSwayTheta) - EnemyToAssignedBirds[enemy] * 0.5 + birdIndex + 0.5) * 100;

                        bird.SetDest(engageLocationLocus + engageLocationSwayDist * engageLocationSwayDir);

                        var birdDir = bird.Controller.WorldMatrix.Translation - Controller.WorldMatrix.Translation;
                        birdDir -= VectorHelpers.VectorProjection(birdDir, gravDir);;
                        var birdDist = birdDir.Length();

                        bird.Drive.DesiredAltitude = birdDist < 100 ? Hummingbird.RecommendedServiceCeiling :
                                                     (float)TrigHelpers.FastSin(birdAltitudeTheta + Math.PI * 0.5) *
                                                     (Hummingbird.RecommendedServiceCeiling - Hummingbird.RecommendedServiceFloor) + Hummingbird.RecommendedServiceFloor;

                        birdIndex++;
                    }
                }

                // Assign orbit task for unassigned birds
                int numReserveBirds = 0;
                foreach (var bird in Hummingbirds)
                {
                    if (BirdToEnemy[bird] == 0 && bird.IsCombatCapable())
                    {
                        numReserveBirds++;
                    }
                }
                birdIndex = 0;
                var randomPoint = new Vector3D(190, 2862, 809);
                randomPoint -= VectorHelpers.VectorProjection(randomPoint, gravDir);
                randomPoint.Normalize();

                var randomPointCross = randomPoint.Cross(gravDir);

                foreach (var bird in Hummingbirds)
                {
                    if (BirdToEnemy[bird] != 0)
                    {
                        continue;
                    }
                    bird.SetTarget(Vector3D.Zero, Vector3D.Zero);
                    bird.Drive.DesiredAltitude = 30;

                    if (bird.IsCombatCapable() && !bird.IsRetiring)
                    {
                        var birdOrbitTheta = Math.PI * (((2 * birdIndex / (float)numReserveBirds) + runs / (BirdOrbitSeconds * 30)) % 2 - 1);

                        var birdOrbitDest = Controller.WorldMatrix.Translation +
                                            TrigHelpers.FastCos(birdOrbitTheta) * randomPoint * BirdOrbitDist +
                                            TrigHelpers.FastSin(birdOrbitTheta) * randomPointCross * BirdOrbitDist;

                        bird.SetDest(birdOrbitDest);

                        birdIndex++;
                    }
                    else if (!bird.IsRetiring)
                    {
                        RetireBird(bird);
                    }
                    else if (bird.IsRetiring)
                    {
                        if (!bird.IsLanding)
                        {
                            var birdDir = bird.Controller.WorldMatrix.Translation - bird.Destination;
                            birdDir -= VectorHelpers.VectorProjection(birdDir, gravDir);
                            var birdDist = birdDir.Length();
                            birdDir.Normalize();

                            if (birdDist < 50)
                            {
                                bird.SetDest(Vector3D.Zero);
                                bird.Drive.Flush();
                                foreach (var engine in bird.Drive.HoverEngines)
                                {
                                    engine.AltitudeMin = 0;
                                }
                                bird.IsLanding = true;
                            }
                        }
                        else
                        {
                            double altitude;
                            bird.Controller.TryGetPlanetElevation(MyPlanetElevation.Surface, out altitude);
                            if (altitude < 6)
                            {
                                foreach (var engine in bird.Drive.HoverEngines)
                                {
                                    engine.Block.Enabled = false;
                                    DeadBirds.Add(bird);
                                }
                            }
                        }
                    }
                }
            }

            foreach (var cradle in Cradles)
            {
                if (cradle == null)
                {
                    continue;
                }
                cradle.Update();
            }

            if (runs % 60 == 0)
            {
                BirdReleaseTimeout--;
                for (int i = 0; i < Cradles.Count(); i++)
                {
                    if (Cradles[i] == null)
                    {
                        continue;
                    }
                    if (Cradles[i].Hummingbird == null)
                    {
                        Cradles[i].CheckHummingbird();
                    }
                    else if (NeedsMoreBirds && BirdReleaseTimeout <= 0)
                    {
                        Hummingbird bird = Cradles[i].Release();
                        bird.Base = Context.Reference;
                        RegisterBird(bird);
                        BirdReleaseTimeout = 5;
                    }
                }
            }

            DeadBirds.Clear();
        }