Пример #1
0
        /// <summary>
        /// Generates a new asteroid name appropriate for the chosen set.
        /// </summary>
        ///
        /// <param name="group">The set to which the asteroid belongs.</param>
        /// <returns>A randomly generated name. The name will be prefixed by a population-specific
        /// name if custom names are enabled, or by "Ast." if they are disabled.</returns>
        ///
        /// <exception cref="System.InvalidOperationException">Thrown if <c>group</c> cannot
        /// generate valid names. The program state will be unchanged in the event of an
        /// exception.</exception>
        /// <exception cref="System.NullReferenceException">Thrown if <c>group</c> is
        /// null.</exception>
        private static string makeName(AsteroidSet group)
        {
            string name = DiscoverableObjectsUtil.GenerateAsteroidName();

            if (AsteroidManager.getOptions().getRenameOption())
            {
                GroupCollection parsed = astName.Match(name).Groups;
                if (parsed [0].Success)
                {
                    string newBase = group.getAsteroidName();
                    if (!newBase.Contains("<<1>>"))
                    {
                        newBase += " <<1>>";
                    }
                    string id = parsed ["id"].ToString();
                    name = Localizer.Format(newBase, id);
                }
                // if asteroid name doesn't match expected format, leave it as-is
#if DEBUG
                Debug.Log("[CustomAsteroids]: "
                          + Localizer.Format("#autoLOC_CustomAsteroids_LogRename", name));
#endif
            }

            return(name);
        }
Пример #2
0
        /// <summary>
        /// Transforms the orbit from the population's reference frame to the KSP frame.
        /// </summary>
        ///
        /// <param name="orbit">The orbit relative to the population's reference frame.</param>
        ///
        /// <exception cref="InvalidOperationException">Thrown if the desired transform could not be
        /// carried out.</exception>
        void frameTransform(Orbit orbit)
        {
            ReferencePlane plane = refPlane != null
                ? AsteroidManager.getReferencePlane(refPlane)
                : AsteroidManager.getDefaultPlane();

            if (plane != null)
            {
                double   ut = Planetarium.GetUniversalTime();
                Vector3d x  = orbit.getRelativePositionAtUT(ut);
                Vector3d v  = orbit.getOrbitalVelocityAtUT(ut);

#if DEBUG
                Debug.Log("[CustomAsteroids]: "
                          + Localizer.Format("#autoLOC_CustomAsteroids_LogRotate", plane));
#endif

                Vector3d xNew = plane.toDefaultFrame(x);
                Vector3d vNew = plane.toDefaultFrame(v);

                orbit.UpdateFromStateVectors(xNew, vNew, orbit.referenceBody, ut);
            }
            else if (refPlane != null)
            {
                throw new InvalidOperationException(
                          Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitRotate", refPlane));
            }
        }
Пример #3
0
        public override Orbit drawOrbit ()
        {
            CelestialBody body = AsteroidManager.getPlanetByName (targetBody);

            Debug.Log ("[CustomAsteroids]: "
                       + Localizer.Format ("#autoLOC_CustomAsteroids_LogOrbitDraw", getName ()));

            double deltaV = wrappedDraw (vSoi, getName (), "vSoi");
            double deltaT = wrappedDraw (warnTime, getName (), "warnTime");

            if (deltaV <= 0.0) {
                throw new InvalidOperationException (
                    Localizer.Format ("#autoLOC_CustomAsteroids_ErrorFlybyBadVInf",
                                      getName (), deltaV));
            }
            // Negative deltaT is allowed

            double peri;
            switch (approach.getParam ()) {
            case ApproachRange.Type.ImpactParameter:
                double b = wrappedDraw (approach, getName (), "approach");
                if (b < 0.0) {
                    throw new InvalidOperationException (
                        Localizer.Format ("#autoLOC_CustomAsteroids_ErrorFlybyBadB", getName (), b));
                }
                double a = -body.gravParameter / (deltaV * deltaV);
                double x = b / a;
                peri = a * (1.0 - Math.Sqrt (x * x + 1.0));
                break;
            case ApproachRange.Type.Periapsis:
                peri = wrappedDraw (approach, getName (), "approach");
                if (peri < 0.0) {
                    throw new InvalidOperationException (
                        Localizer.Format ("#autoLOC_CustomAsteroids_ErrorFlybyBadPeri",
                                          getName (), peri));
                }
                break;
            default:
                throw new InvalidOperationException (
                    Localizer.Format ("#autoLOC_CustomAsteroids_ErrorFlybyBadApproach",
                                      getName (), approach.getParam ()));
            }
#if DEBUG
            Debug.Log ($"[CustomAsteroids]: "
                       + Localizer.Format ("#autoLOC_CustomAsteroids_LogFlyby",
                                          peri, targetBody, deltaT / (6.0 * 3600.0), deltaV)
                      );
#endif

            Orbit newOrbit = createHyperbolicOrbit (body, peri, deltaV,
                                                    Planetarium.GetUniversalTime () + deltaT);
            // Sun has sphereOfInfluence = +Infinity, so condition will always fail for Sun-centric
            // orbit. No special treatment needed for the case where the orbit lies entirely outside
            // the SoI
            while (needsSoITransition (newOrbit)) {
                newOrbit = patchToParent (newOrbit);
            }
            newOrbit.UpdateFromUT (Planetarium.GetUniversalTime ());
            return newOrbit;
        }
Пример #4
0
 /// <summary>
 /// Prints an error message visible to the player. Does not throw exceptions.
 /// </summary>
 /// <param name="format">The error message, a localization tag, or a Lingoona format string
 /// for the message.</param>
 /// <param name="param">Any parameters to place in the format string (optional).</param>
 public static void errorToPlayerLoc(string format, params object [] param)
 {
     if (AsteroidManager.getOptions().getErrorReporting())
     {
         ScreenMessages.PostScreenMessage(
             "[CustomAsteroids]: " + Localizer.Format(format, param),
             5.0f, ScreenMessageStyle.UPPER_RIGHT);
     }
 }
Пример #5
0
        public Tuple <double, double> drawTrackingTime()
        {
            Tuple <float, float> trackTimes = AsteroidManager.getOptions().getUntrackedTimes();
            double lifetime = RandomDist.drawUniform(trackTimes.Item1, trackTimes.Item2)
                              * SECONDS_PER_EARTH_DAY;
            double maxLifetime = trackTimes.Item2 * SECONDS_PER_EARTH_DAY;

            return(Tuple.Create(lifetime, maxLifetime));
        }
Пример #6
0
 /// <summary>
 /// <para>Creates a new asteroid from a randomly chosen asteroid set. The asteroid will be
 /// given the properties specified by that set, and added to the game as an untracked
 /// object.</para>
 /// <para>If asteroid creation failed for any reason, this method will log the error rather
 /// than propagating the exception into client code.</para>
 /// </summary>
 /// <returns>the newly created asteroid, or null if no asteroid was created. May be used as
 /// a hook by spawners that need more control over asteroid properties. Clients should
 /// assume the returned vessel is already registered in the game.</returns>
 protected ProtoVessel spawnAsteroid()
 {
     try {
         return(spawnAsteroid(AsteroidManager.drawAsteroidSet()));
     } catch (Exception e) {
         Util.errorToPlayer(e, Localizer.Format("#autoLOC_CustomAsteroids_ErrorSpawnFail"));
         Debug.LogException(e);
         return(null);
     }
 }
 public double getSpawnRate()
 {
     if (detectable != null && !detectable.check())
     {
         return(0.0);
     }
     if (AsteroidManager.countAsteroidsInSet(this) >= spawnMax)
     {
         return(0.0);
     }
     return(spawnRate);
 }
 public string drawAsteroidType()
 {
     try {
         return(AsteroidManager.drawAsteroidType(classRatios));
     } catch (InvalidOperationException e) {
         Util.errorToPlayer(e, Localizer.Format(
                                "#autoLOC_CustomAsteroids_ErrorNoClass",
                                name));
         Debug.LogException(e);
         return("PotatoRoid");
     }
 }
Пример #9
0
        /// <summary>
        /// Generates tracking station info appropriate for the chosen set.
        /// </summary>
        ///
        /// <param name="group">The set to which the asteroid belongs.</param>
        /// <returns>A ConfigNode storing the asteroid's DiscoveryInfo object.</returns>
        ///
        /// <exception cref="System.InvalidOperationException">Thrown if <c>group</c> cannot
        /// generate valid data. The program state will be unchanged in the event of an
        /// exception.</exception>
        /// <exception cref="System.NullReferenceException">Thrown if <c>group</c> is
        /// null.</exception>
        private static ConfigNode makeDiscoveryInfo(AsteroidSet group)
        {
            Pair <float, float> trackTimes = AsteroidManager.getOptions().getUntrackedTimes();
            double lifetime = UnityEngine.Random.Range(trackTimes.first, trackTimes.second)
                              * SECONDS_PER_EARTH_DAY;
            double maxLifetime        = trackTimes.second * SECONDS_PER_EARTH_DAY;
            UntrackedObjectClass size = (UntrackedObjectClass)(int)
                                        (stockSizeCurve.Evaluate(UnityEngine.Random.Range(0.0f, 1.0f))
                                         * Enum.GetNames(typeof(UntrackedObjectClass)).Length);
            ConfigNode trackingInfo = ProtoVessel.CreateDiscoveryNode(
                DiscoveryLevels.Presence, size, lifetime, maxLifetime);

            return(trackingInfo);
        }
Пример #10
0
        /// <summary>
        /// Returns the time until the next asteroid should be detected. Does not throw exceptions.
        /// </summary>
        /// <returns>The number of seconds before an asteroid detection, or infinity if asteroids
        /// should not spawn.</returns>
        private static double asteroidWaitTime()
        {
            double rate = AsteroidManager.spawnRate();  // asteroids per day

            if (rate > 0.0)
            {
                // Waiting time in a Poisson process follows an exponential distribution
                rate /= SECONDS_PER_EARTH_DAY;          // asteroids per second
                return(RandomDist.drawExponential(1.0 / rate));
            }
            else
            {
                return(double.PositiveInfinity);
            }
        }
 /// <summary>
 /// Uses criteria similar to ScenarioDiscoverableObjects to decide whether to create an
 /// asteroid.
 /// </summary>
 protected override void checkSpawn()
 {
     if (areAsteroidsTrackable() &&
         AsteroidManager.spawnRate() > 0.0 &&
         countUntrackedAsteroids() < rng.Next(SPAWN_GROUP_MIN_LIMIT, SPAWN_GROUP_MAX_LIMIT))
     {
         if (rng.NextDouble() < 1.0 / (1.0 + SPAWN_ODDS_AGAINST))
         {
             spawnAsteroid();
         }
         else
         {
             Debug.Log("[StockalikeSpawner]: "
                       + Localizer.Format("#autoLOC_CustomAsteroids_LogSpawnStock",
                                          SPAWN_ODDS_AGAINST));
         }
     }
 }
Пример #12
0
        /// <summary>
        /// Returns the probability of detecting an asteroid in one tick. Does not throw exceptions.
        /// </summary>
        /// <param name="tickLength">The number of seconds in the current tick.</param>
        /// <returns>The asteroid discovery probability, scaled for the current time warp.</returns>
        private static float asteroidChance(double tickLength)
        {
            if (tickLength <= 0.0)
            {
                return(0.0f);
            }
            double rate = AsteroidManager.spawnRate() / SECONDS_PER_EARTH_DAY;  // asteroids per second

            if (rate > 0.0)
            {
                float expected = (float)(rate * tickLength);
                return(Mathf.Max(0.0f, Mathf.Min(1.0f - Mathf.Exp(-expected), 1.0f)));
            }
            else
            {
                return(0.0f);     // Avoid small nonzero probability if rate == 0.0
            }
        }
Пример #13
0
 /// <summary>
 /// <para>Creates a new asteroid from a randomly chosen asteroid set. The asteroid will be
 /// given the properties specified by that set, and added to the game as an untracked
 /// object.</para>
 /// <para>If asteroid creation failed for any reason, this method will log the error rather
 /// than propagating the exception into client code.</para>
 /// </summary>
 /// <returns>the newly created asteroid, or null if no asteroid was created. May be used as
 /// a hook by spawners that need more control over asteroid properties. Clients should
 /// assume the returned vessel is already registered in the game.</returns>
 /// <remarks>At least one population must have a positive spawn rate.</remarks>
 protected ProtoVessel spawnAsteroid()
 {
     try {
         AsteroidSet group    = AsteroidManager.drawAsteroidSet();
         ProtoVessel asteroid = spawnAsteroid(group);
         try {
             registerAsteroid(asteroid, group);
         } catch (ArgumentException e) {
             Debug.LogWarning("[CustomAsteroids]: Duplicate entry in CustomAsteroidRegistry.");
             Debug.LogException(e);
         }
         return(asteroid);
     } catch (Exception e) {
         Util.errorToPlayer(e, Localizer.Format("#autoLOC_CustomAsteroids_ErrorSpawnFail"));
         Debug.LogException(e);
         return(null);
     }
 }
Пример #14
0
        /// <summary>Initializes the scenario prior to loading persistent data. Custom Asteroids
        /// options must have already been loaded.</summary>
        internal CustomAsteroidSpawner()
        {
            driverRoutine = null;

            var spawnerType = AsteroidManager.getOptions().getSpawner();

            switch (spawnerType)
            {
            case SpawnerType.FixedRate:
                spawner = new FixedRateSpawner();
                break;

            case SpawnerType.Stock:
                spawner = new StockalikeSpawner();
                break;

            default:
                throw new System.InvalidOperationException(
                          Localizer.Format("#autoLOC_CustomAsteroids_ErrorNoSpawner", spawnerType));
            }
        }
Пример #15
0
        /// <summary>
        /// Returns the desired property of a known celestial body.
        /// </summary>
        ///
        /// <param name="planet">The exact, case-sensitive name of the celestial body. Assumes all
        /// loaded celestial bodies have unique names.</param>
        /// <param name="property">The short name of the property to recover. Must be one
        /// of ("rad", "soi", "sma", "per", "apo", "ecc", "inc", "ape", "lan", "mna0", "mnl0",
        /// "prot", "psun", "porb", "vesc", "vorb", "vmin", or "vmax").
        /// The only properties supported for Sun are "rad", "soi", "prot", and "vesc".</param>
        /// <returns>The value of <c>property</c> appropriate for <c>planet</c>. Distances are given
        /// in meters, angles are given in degrees.</returns>
        ///
        /// <exception cref="ArgumentException">Thrown if no planet named <c>name</c> exists, or if
        /// <c>property</c> does not have one of the allowed values. The program state shapp remain
        /// unchanged in the event of an exception.</exception>
        protected static double getPlanetProperty(string planet, string property)
        {
            CelestialBody body = AsteroidManager.getPlanetByName(planet);

            switch (property.ToLower())
            {
            case "rad":
                return(body.Radius);

            case "soi":
                return(body.sphereOfInfluence);

            case "prot":
                return(body.rotates ? body.rotationPeriod : double.PositiveInfinity);

            case "psol":
                if (body.solarRotationPeriod)
                {
                    return(body.solarDayLength);
                }
                else
                {
                    throw new ArgumentException(
                              Localizer.Format("#autoLOC_CustomAsteroids_ErrorPlanetNoDay", planet),
                              nameof(property));
                }

            case "vesc":
                return(Math.Sqrt(2 * body.gravParameter / body.Radius));

            default:
                if (body.GetOrbitDriver() == null)
                {
                    throw new ArgumentException(
                              Localizer.Format("#autoLOC_CustomAsteroids_ErrorPlanetNoOrbit", planet),
                              nameof(planet));
                }
                Orbit orbit = body.GetOrbit();

                switch (property.ToLower())
                {
                case "sma":
                    return(orbit.semiMajorAxis);

                case "per":
                    return(orbit.PeR);

                case "apo":
                    return(orbit.ApR);

                case "ecc":
                    return(orbit.eccentricity);

                case "inc":
                    return(orbit.inclination);

                case "ape":
                    return(orbit.argumentOfPeriapsis);

                case "lpe":
                    // Ignore inclination: http://en.wikipedia.org/wiki/Longitude_of_periapsis
                    return(orbit.LAN + orbit.argumentOfPeriapsis);

                case "lan":
                    return(orbit.LAN);

                case "mna0":
                    return(meanAnomalyAtUT(orbit, 0.0) * 180.0 / Math.PI);

                case "mnl0":
                    return(anomalyToLong(meanAnomalyAtUT(orbit, 0.0) * 180.0 / Math.PI,
                                         orbit.inclination, orbit.argumentOfPeriapsis, orbit.LAN));

                case "porb":
                    return(orbit.period);

                case "vorb":
                    // Need circumference of an ellipse; closed form does not exist
                    double sum        = orbit.semiMajorAxis + orbit.semiMinorAxis;
                    double diff       = orbit.semiMajorAxis - orbit.semiMinorAxis;
                    double h          = diff * diff / (sum * sum);
                    double correction = 1.0;
                    for (int n = 1; n < 10; n++)
                    {
                        double coeff = Util.DoubleFactorial(2 * n - 1)
                                       / (Math.Pow(2, n) * Util.Factorial(n))
                                       / (2 * n - 1);
                        correction += coeff * coeff * Math.Pow(h, n);
                    }
                    return(Math.PI * sum * correction / orbit.period);

                case "vmin":
                    return(orbit.getOrbitalSpeedAtDistance(orbit.ApR));

                case "vmax":
                    return(orbit.getOrbitalSpeedAtDistance(orbit.PeR));

                default:
                    throw new ArgumentException(
                              Localizer.Format("#autoLOC_CustomAsteroids_ErrorPlanetBadProperty", property),
                              nameof(property));
                }
            }
        }
Пример #16
0
        public override Orbit drawOrbit()
        {
            // Would like to only calculate this once, but I don't know for sure that this object
            // will be initialized after FlightGlobals
            CelestialBody orbitee = AsteroidManager.getPlanetByName(centralBody);

            Debug.Log($"[CustomAsteroids]: "
                      + Localizer.Format("#autoLOC_CustomAsteroids_LogOrbitDraw", getName()));

            // Properties with only one reasonable parametrization
            double e = wrappedDraw(eccentricity, getName(), "eccentricity");

            if (e < 0.0)
            {
                throw new InvalidOperationException(
                          Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitBadEcc", getName(), e));
            }
            // Sign of inclination is redundant with 180-degree shift in longitude of ascending node
            // So it's ok to just have positive inclinations
            double i   = wrappedDraw(inclination, getName(), "inclination");
            double lAn = wrappedDraw(ascNode, getName(), "ascNode");

            // Position of periapsis
            double aPe;
            double peri = wrappedDraw(periapsis, getName(), "periapsis");

            switch (periapsis.getParam())
            {
            case PeriRange.Type.Argument:
                aPe = peri;
                break;

            case PeriRange.Type.Longitude:
                aPe = peri - lAn;
                break;

            default:
                throw new InvalidOperationException(
                          Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitBadPeri", getName(),
                                           periapsis.getParam()));
            }

            // Semimajor axis
            double a;
            double size = wrappedDraw(orbitSize, getName(), "orbitSize");

            switch (orbitSize.getParam())
            {
            case SizeRange.Type.SemimajorAxis:
                a = size;
                break;

            case SizeRange.Type.Periapsis:
                a = size / (1.0 - e);
                break;

            case SizeRange.Type.Apoapsis:
                if (e > 1.0)
                {
                    throw new InvalidOperationException(
                              Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitHyperbolicApo",
                                               getName(), e));
                }
                a = size / (1.0 + e);
                break;

            default:
                throw new InvalidOperationException(
                          Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitBadSize",
                                           getName(), orbitSize.getParam()));
            }

            // Mean anomaly at given epoch
            double mEp, epoch;
            double phase = wrappedDraw(orbitPhase, getName(), "orbitPhase");

            switch (orbitPhase.getParam())
            {
            case PhaseRange.PhaseType.MeanAnomaly:
                // Mean anomaly is the ONLY orbital angle that needs to be given in radians
                mEp = Math.PI / 180.0 * phase;
                break;

            case PhaseRange.PhaseType.MeanLongitude:
                mEp = Math.PI / 180.0 * longToAnomaly(phase, i, aPe, lAn);
                break;

            default:
                throw new InvalidOperationException(
                          Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitBadPhase",
                                           getName(), orbitPhase.getParam()));
            }
            switch (orbitPhase.getEpoch())
            {
            case PhaseRange.EpochType.GameStart:
                epoch = 0.0;
                break;

            case PhaseRange.EpochType.Now:
                epoch = Planetarium.GetUniversalTime();
                break;

            default:
                throw new InvalidOperationException(
                          Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitBadEpoch",
                                           getName(), orbitPhase.getEpoch()));
            }

            // Fix accidentally hyperbolic orbits
            if (a * (1.0 - e) < 0.0)
            {
                a = -a;
            }

            Debug.Log($"[CustomAsteroids]: "
                      + Localizer.Format("#autoLOC_CustomAsteroids_LogOrbit",
                                         a, e, i, aPe, lAn, mEp, epoch));

            // Does Orbit(...) throw exceptions?
            Orbit newOrbit = new Orbit(i, e, a, lAn, aPe, mEp, epoch, orbitee);

            frameTransform(newOrbit);
            newOrbit.UpdateFromUT(Planetarium.GetUniversalTime());

            return(newOrbit);
        }