Back to Index Page generated: Nov 12, 2024, 11:02:04 PM

Expansion Planetary Systems

Content

Warnings

  1. No version in dependency reference to oolite.oxp.spara.additional_planets_sr_base:null
  2. Conflict Expansions mismatch between OXP Manifest and Expansion Manager at character position 0077 (DIGIT ZERO vs LATIN SMALL LETTER N)

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description This OXP simulates (pseudo)dynamic solar systems with terrestrial planets and gas giants. This OXP simulates (pseudo)dynamic solar systems with terrestrial planets and gas giants.
Identifier oolite.oxp.stranger.PlanetarySystems oolite.oxp.stranger.PlanetarySystems
Title Planetary Systems Planetary Systems
Category Ambience Ambience
Author stranger stranger
Version 1.8.0 1.8.0
Tags planets, system planets, system
Required Oolite Version
Maximum Oolite Version
Required Expansions
  • oolite.oxp.stranger.SunGear:4.4.0
  • oolite.oxp.stranger.SunGear:4.4.0
  • Optional Expansions
    Conflict Expansions
  • oolite.oxp.spara.additional_planets_sr_base:0
  • oolite.oxp.spara.additional_planets_sr_base:
  • Information URL http://aegidian.org/bb/viewtopic.php?f=4&t=20016 n/a
    Download URL https://wiki.alioth.net/img_auth.php/a/ac/PlanetarySystems.oxz n/a
    License CC-BY-NC-SA 3.0 CC-BY-NC-SA 3.0
    File Size n/a
    Upload date 1626308174

    Documentation

    Also read http://wiki.alioth.net/index.php/Planetary%20Systems

    Planetary Systems Readme.txt

    ---------------------
    Planetary Systems OXP
    ---------------------
    
    This package based of Orbits 1.2.1 (authors - Ebi, Kaks) with textured planets and some tweaks in main script.
    Tweaked by Stranger.
    
    This OXP is released under the Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license.
    You are free to use and distribute this OXP on non-commercial basis. 
    Rebundling of this OXP within another distribution is permitted as long as it is unchanged.
    Any mods/derivatives of this OXP must be distributed under another names.
    The license information (either as this file or merged into a larger one) must be included in the OXP.
    
    Features:
    
    This OXP simulates a bit more realistic (pseudo)dynamic solar systems with terrestrial planets and gas giants.
    Smart algorithm of planet texture selection is implemented (texture selected based on planet radius, temperature and atmosphere pressure).
    Original dynamic orbital mechanism supplemented with variation of main planet orbital period (function of calculated sun mass and orbit radius).
    Orbital parameters and physical properties of planets and moons are calculated as function of their radii and insolation levels. Besides using these parameters in texture selection this is planet/moon properties page displayed only if you are docked in surface port. Select F4 screen and open �Planet Data Sheet� interface to view this info page.
    
    There are up to eight additional planets in system, selected randomly:
    
    Miniterra (Mercury analog)
    Subterra (Mars analog)
    Terra (Earth or Venus analog)
    Superterra
    Two gas giants (Jupiter and Saturn analogs)
    Ice giant (Neptune analog)
    Transneptunian (Pluto analog)
    
    Non-linear scale is implemented to large planets (super-earths, ice and gas giants). Being only 172500 m radius in game really, largest gas giant is displayed in Planet Data Sheet page as gas giant of 72500 km radius instead 17250 km of default planet scale. Planet mass, escape velocity and surface gravity is displayed scaled too.
    There are no �surface� in case of gas giants. Planet Data Sheet page takes 1 bar atmosphere pressure level as reference point in this case. 
    
    Dependencies:
    
    This OXP uses AstroLibrary functions in Sun Gear OXP to calculate properties of system planets and moons.
    Habitable Main Planets OXP is highly recommended to provide more reliable data for main planet. This is no way to match calculated atmosphere density and surface temperature for planet of 3000 km radius, for example, with vanilla game classification of such world as agricultural/tropical paradise. :-)
    
    Known issues:
    
    Interaction of Planetary Systems OXP with planets added by other OXPs can cause inflation of system far beyond planned size. Diso.oxp with 4 additional planets is a good example of such system.
    Planetary Systems OXP is incompatible with Additional Planets SR due to same reason.
    Like original Orbits, Planetary Systems needs some time to generate solar system, so problems with positioning of moons, stations and ships added by third party OXPs or system populator can appear.
    Specific case of incompatibility is Market Cooldown OXZ (author spara). It uses sum of station distances to witchpoint and to sun as unique key for port identification. This method works in static deterministic system dynamic system, but fails in dynamic systems with changing psw triangle.
    
    INSTALLATION:
    
    To provide easy updates all planet textures repacked as separate texture packs.
    
    Planetary Systems.oxp - core package.
    Planetary Systems Texture Pack A.oxp - Ice Deserts	
    Planetary Systems Texture Pack B.oxp - Cold Deserts
    Planetary Systems Texture Pack C.oxp - Sand Deserts
    Planetary Systems Texture Pack D.oxp - Volcanic Deserts
    Planetary Systems Texture Pack E.oxp - Cold Mars Analogs
    Planetary Systems Texture Pack F.oxp - Hot Mars Analogs
    Planetary Systems Texture Pack G.oxp - Gas and Ice Giants
    Planetary Systems Texture Pack H.oxp - Misc (Mercury, Pluto, Venus analogs and super-earths).
    
    Download and unzip core package Planetary Systems and all Planetary Systems Texture Pack volumes.
    Place all unzipped packages inside the Oolite/AddOns folder.
    
    Read notifications on my personal page to update packages individually.
    
    ---------------
    Version history
    ---------------
    
    15.07.2021 - Version 1.8.0	Fixed issue with declaration of function mkPlanetIndex()
    07.07.2020 - Version 1.7.0	Fixed error in log file generated in case of misjump.
    16.06.2020 - Version 1.6.0	Minimal planet radius, used to discriminate planets from moons, decreased from 2700 to 2500 km.
    				Planet radii recalibrated.
    				Minor improvement in algorithm of planet texture selection.
    08.02.2019 - Version 1.5.2	Conversion to OXZ.
    30.12.2018 - Version 1.5.1	manifest.plist and Readme edited.
    15.12.2018 - Version 1.5	Code modified to select three additional planet textures.
    				manifest.plist added.
    				Texture packs rearranged onto 8 volumes.
    				More detailed credit info added onto all 8 texture packs.
    10.12.2018 - Version 1.4	Code modified to select two additional planet textures.
    				Some textures replaced.
    12.08.2018 - Version 1.3	Code preventing displaying physical properties of main planet on Planet Data Sheet is removed.
    19.06.2018 - Version 1.2	Calculation of planet/moon properties added.
    				Planet Engine calculates orbital and physical parameters of planets and moons.
    				New �Planet Data Sheet� interface on F4 screen allows to view planet/moon info page.
    				Planet textures selected as function of planet radius, surface temperature and atmosphere pressure.
    				Non-linear scale for large planets is implemented.
    				Original procedure of orbit scaling is readjusted to decrease too wide gap between main planet and nearest outer planet.
    				Superterrae are placed between terrae ang ice/gas giants.
    				Texture pack divided onto 2 volumes (Texture Pack A and Texture Pack B).
    09.05.2018 - Version 1.1	Maximal planet number increased to 8 planets again.
    				New class of superterrestrial planets introduced.
    				Planet radii covers range from dwarf planets to gas giants without gaps.
    				Code changes again to adopt rewritten AstroLibrary functions.
    17.07.2016 - Version 1.0	Every planet class divided onto 10 subclasses to provide better variability
    				(40 variants of terrestrial planets and 30 variants of gas giants now).
    				All texture files repacked as separate texture pack.
    06.11.2015 - Version 0.9	Code changes to adopt rewritten AstroLibrary functions.
    27.06.2014 - Version 0.8	OXP renamed from Orbits ST to Planetary Systems for clarity reasons.
    				Local year is a function of sun spectral class.
    24.06.2014 - Version 0.7	Texture selection for Pluto analog improved (it takes Mercury texture in case of proximity to sun).
    19.05.2014 - Version 0.6	Planet list rearranged.
    				Planet number reduced to 7 again.
    				Texture selection improved from 8 generic textures to 35 system-dependent textures.
    				Script for texture selection implemented instead of static planetinfo.plist textures.
    08.02.2014 - Version 0.5	Refined system of texture naming.
    20.01.2014 - Version 0.4	Maximal planet number increased to 8.
    				Maximal planet radius increased to 15000 km.
    				planetinfo.plist readjusted to rearranged planet set.
    				Planet rotational velocities set to 0.001 rad/s for terrestrial planets and to 0.002 rad/s for gas giants.
    				Planet textures renamed to improve compatibility with System Redux.
    12.01.2014 - Version 0.3	Maximal moon radius filter parameter increased from 1000 to 2700 km to allow simulation of large moons.
    09.01.2014 - Version 0.2	Cube mapped textures replaced by equirectangular projections.
    23.06.2013 - Version 0.1	Initial release.
    
    Credits:
    Orbits main script - Ebi, Kaks.
    Textures - Celestia Motherlode and freebitmaps.blogspot.com.
    See more detailed texture credits in Texture Pack Readme documents.
    Thanks Milo for reporting issue with function declaration.
    
    *****
    
    Original Ebi's readme is below.
    
    *****
    
    ======================================================
    Orbits is an Add-on for Oolite by Giles Williams
    ======================================================
    
    Version:1.2.1   -   01 Jul 2010
    
    Update of v1.2 for Oolite v1.74.x
    
    
    Readme notes included with v1.2:
    
    ======================================================
    
    Author: Ebi
    
    Created on: 11 Jul 08
    
    version: 1.1
    ======================================================
    
    "Orbits" places planets on concentric and coplanar orbits around the
    sun. It works as stand-alone OXP or in combination with other OXPs
    which add planets to a system. I use it together with Farsun and
    System Redux. Note, the installation order the OXPs may be
    relevant. If you have trouble to get it right you may want to let
    Orbits emulate farsun as well.
    
    The OXP derives the layout of a planet system from the system
    name. Therefore when you return to a system again the same number of
    planets and the same orbits will be there. However, since "Orbits"
    checks the game clock the planets will have move along their orbits a
    little bit. (Hmm, I use a coordinate system which depends on the
    positions of the sun, the main planet, and the witchpoint buoy and in
    the previous sentence I assumed that those positions are
    static. Nothing to worry about, we will not notice the difference
    anyway, just don't ask the guys in the astro labs:))
    
    In the config folder you will find the file "script.js". It defines
    some variables which determine the layout. You may want to play with
    them:
    
    this.MaxPlanets = 7;
    
    "Orbits" can add planets to a system. I've defined seven planets in
    the planetinfo.plist (see the Config directory). So the maximum value
    is currently 7. If you want even more just extend
    planetinfo.plist. Those planets are not textured, in combination with
    System Redux you can reduce the maximum.
    
    this.Yr = 300;
    
    "Orbits" calculates the distances between the sun and the planets by
    scaling the vector [sun, planet]. It first calulates the orbital
    periods and then by applying Kepler's law that scalar. The orbital
    period is expressed as multiple of the days per year of the main
    planet. This value is also use to add some pseudo randomness.
    Increasing its value will give larger orbits, I guess.
    
    this.X0
    this.X1
    this.Y0
    this.Y1
    
    The radius of a planet determines the distances to its neighbours. It
    works like a charge which pushes bigger planets away. The idea is that
    a big planet has attracted the bodies in its vicinity and no bodies
    are left there. At startup "Orbits" assigns the "mass" Y1 to the
    biggest and Y0 to the smallest planet and interpolates the other
    values. You can overwrite the min/max radii by defining X0 and/or
    X1. The resulting numbers are factors in the product used to calculate
    the orbital periods. Bigger numbers produce biggers gaps.
    
    this.SpreadFactor = 3;
    
    Adds space between the orbits in sparse system. "Orbits" multiplies
    the orbital periods by SpreadFactor ^ (1 / (#planets - 1))
    
    this.RandomOffsetFactor = 0.1
    
    Let t[i] be the orbital period of planet i. Then "Orbits" gets t[i+1]'
    by multiplying Yr with the weights explained above and adding the
    result to t[i]. After that it adds a pseudo random number in the range
    [0, t[i+1]' * RandomOffsetFactor] to get the final t[i+1].
    
    this.FarsunMultiplier
    
    The initialization order of OXPs seems unpredictable to me. If you are
    using Farsun.OXP and you have the feeling the planets are too close to
    the main planet or they are not orbiting around the sun then the
    installation order is probably wrong. "Orbits" can emulate Farsun. If
    nothing works set FarsunMultiplier to an appropriate value. Set it to
    1 if you don't want a far sun. I'm using 6.
    
    

    Equipment

    This expansion declares no equipment. This may be related to warnings.

    Ships

    This expansion declares no ships. This may be related to warnings.

    Models

    This expansion declares no models. This may be related to warnings.

    Scripts

    Path
    Scripts/orbits.js
    "use strict";
    this.name = "PlanetOrbits";
    this.author = "Stranger";
    this.description = "Adds planets on concentric orbits around the sun."
    this.version = "1.8";
    this.copyright = "This work is hereby placed in the public domain.";
    
    /*
    This script tightly based on Ebi, Kaks Orbits 1.2.1 script.
    Original planet seeding procedure is overrided.
    Moon filter radius increased to 2500 km.
    Local year duration is a function of sun spectral class (reference to Sun Gear AstroLibrary included).
    Periods of outer planets readjusted (0.5x multiplier added).
    Original code reformatted to ease readings (yet too complicated to me!).
    */
    
    function pow(x, y)
        {
        return Math.exp(y * Math.log(x));
        }
    
    // Maximum number of planets to be added (See planetinfo.plist as well).
    this.MaxPlanets = 8;
    // Outer planet orbits scale factor
    this.$scaleOrbits = 0.5;    // No works yet. Edit number in line 360 directly to have effect.
                                // Default scale value was 1.0. 
    
    // Days per year of the Main planet
    this.Yr = 360;
    
    // Get weights by linearily interpolating the planet radii as defined
    // by [X0,Y0] and [X1,Y1]. Those weights are used as multiplier when
    // calculating orbital periods. If X0 or X1 are 0 or undefined "orbits"
    // will select the minimum or maximum from the planets in the
    // system.planets list.
    this.X0 = 2500 * 10;		// planetinfo.plist minimum in game units
    this.X1 = 18000 * 10;		// planetinfo.plist maximum in game units
    this.Y0 = pow(2, 3.0 / 4.0);	// Compensate Keplers law and take square root
    this.Y1 = pow(3.5, 3.0 / 4.0);  // Thus, scale radius by values in [2,3.5]
    // System spread factor
    this.SpreadFactor = 3.0;
    // Add a random offset in the range [0, t*RandomOffsetFactor] to the
    // orbital period t of a planet.
    this.RandomOffsetFactor = 0.1;
    // Log to logfile (1) and/or console (2)
    this.Verbose = 0; //1|4;
    // Oh man, it's too tricky to get the OXP initialization order right
    // for Oolite. It may be random, therefore I offer the farsun
    // functionality here as well. Shifts the sun and increases (or
    // decreases if less than 1) its distance to the main planet by this
    // factor. Thus, the value 1 will not show any effect.
    this.FarsunMultiplier = 1; // 6;
    
    this.Log = function(dest, msg)
        {
        if (dest & 1)
    	log (msg);
        if (dest & 2)
    	player.commsMessage(msg);
        }
    
    this.startUp = this.reset = function()
        {
        this.systemDone = false;
        this.Log(this.Verbose & 1, "Initialising OXP " + this.name);
        }
    
    this.shipWillLaunchFromStation  =  function()
        {
        if (!this.systemDone)
            {
            this.shipWillExitWitchspace();
            delete this.shipWillLaunchFromStation;
            }
        }
    
    this.shipLaunchedFromStation = function()
        {
        if (!this.systemDone)
    	this.shipExitedWitchspace();
        }
    
    this.shipWillExitWitchspace = function()
        {
        if (system.isInterstellarSpace) return;
        var w = worldScripts.AstroLibrary;
        var sunRadius = system.sun.radius;
        var sunMass = w.$astroLib_sunMass(sunRadius);
        var mainYr = w.$astroLib_solPeriod(sunMass);
        this.Yr = mainYr;
        this.createOrbits();
        }
    
    this.shipExitedWitchspace = function()
        {
        if (system.isInterstellarSpace) return;
        this.systemDone = true;
        if ((this.Verbose & (2|4)) == 0 || this.innerPlanets == 0)
    	return;
        var msg =
    	system.name + ": " +
    	this.innerPlanets + "+" +
    	this.outerPlanets + " planets";
        if (this.info != undefined)
    	msg = msg + "[" + this.info + "]";
        this.Log(2, msg);
        }
    
    this.isMoon = function(p)
        {
        // 10 * Radius from planetinfo.plist
        return !p.isMainPlanet && p.radius < 2500 * 10;
        }
    
    this.mkPlanetIndex = function mkPlanetIndex()
        {
        function aPlanet(entity)
            {
            return entity.isPlanet
            }
      
        return system.filteredEntities(this, aPlanet, system.sun);
        }
    
    this.planetsMinMax = function(x)
        {
        var needX0 = this.X0 == undefined || this.X0 == 0;
        var needX1 = this.X1 == undefined || this.X1 == 0;
        var np = system.planets.length;
        var x0 = +Infinity, x1 = 0;
    
        if (needX0 || needX1)
            {
            for (var i = 0; i < system.planets.length; ++i)
                {
                if (this.isMoon(system.planets[i]))
                    {
                    np--;
                    continue;
                    }
                if (system.planets[i].radius > x1)
                x1 = system.planets[i].radius;
                if (system.planets[i].radius < x0)
                x0 = system.planets[i].radius;
                }
            }
        x[0] = needX0 ? x0 : this.X0;
        x[1] = needX1 ? x1 : this.X1;
        return np;
        }
    
    this.addPlanets = function(id)
        {
        // Add planets in system dependent order. Id is used here in two
        // ways:
        // - Its lower bits determine the planets in planetinfo.plist
        // - It represents a permutation and hence the order how the selected
        //   planets are added (see wikipedia for factoradic) and Entity-IDs
        //   are assigned.
    
        var selectPlanet1 = system.scrambledPseudoRandomNumber(163);
        var selectPlanet2 = system.scrambledPseudoRandomNumber(171);
        var selectPlanet3 = system.scrambledPseudoRandomNumber(179);
        var selectPlanet4 = system.scrambledPseudoRandomNumber(187);
        var selectPlanet5 = system.scrambledPseudoRandomNumber(195);
        var selectPlanet6 = system.scrambledPseudoRandomNumber(203);
        var selectPlanet7 = system.scrambledPseudoRandomNumber(211);
        var selectPlanet8 = system.scrambledPseudoRandomNumber(219);
    
        var selectPlanetData = [
        selectPlanet1,
        selectPlanet2,
        selectPlanet3,
        selectPlanet4,
        selectPlanet5,
        selectPlanet6,
        selectPlanet7,
        selectPlanet8
        ];
    
        var v = new Array(this.MaxPlanets), w = new Array(this.MaxPlanets);
        // Avoid huge factorials by grouping up to 12 planets.
        var mask = id;
        for (var i = 0; i < this.MaxPlanets && mask != 0 && id != 0; )
            {
            var m = 0, f = 1;
            // Type conversion prohibits use of the shift operator. Kind
            // of weird to apply "&" on a floating point number!
            for (; m < 12 && i < this.MaxPlanets; ++i, mask = Math.floor(mask / 2))
                if ((mask & 1) != 0)
                v[m] = i, w[m] = m, f *= ++m;
            var fn = id % f;	// fn = sum(j in [0..m-1]:j!*c[j]), c[j] <= j
            id = Math.floor(id / f);
            for (; m > 0; --m)
                {
                f /= m;
                var c = Math.floor(fn / f);
                fn -= f * c;
                this.Log(this.Verbose,
                    "" + m + ":selecting oplanet" +
                    (v[w[c]] + 1) + "," + w[c] + "," +
                    c + "," + f + "," + fn);
    
                var addPlanetTagIn = v[w[c]];
                var addPlanetTagOut = 10*(addPlanetTagIn + 1) + Math.floor(selectPlanetData[addPlanetTagIn]*10);
    
                system.addPlanet("oplanet" + addPlanetTagOut);
    //          system.addPlanet("oplanet" + (v[w[c]] + 1));
                for (; c + 1 < m; ++c)
                w[c] = w[c + 1];
                }
            }
        }
    
    this.createOrbits = function()
        {
        this.outerPlanets = 0;
        this.innerPlanets = 0;
    
        var s = system.sun;
        // Is there system.witchpoint?
        var w = system.shipsWithPrimaryRole("buoy-witchpoint")[0];
        var pm = system.mainPlanet;
        if (!s || !w || !pm)
    	return;
    
        s.position=(pm.position.add(
    	s.position.subtract(pm.position).multiply(this.FarsunMultiplier)));
    
        function PlanetVecs(s, w, p)
            {
            // "this" for sizzies:(
            this.s = s;
            this.sp = p.subtract(s);
            this.dist = this.sp.magnitude();
            // Rotation axis, perpendicular to the plane of triangle [s,p,w]
            this.rx = this.sp.cross(w.subtract(s)).direction();
            this.q = new Quaternion (1,0,0,0);
            this.rotateQ = function(q, axis, angle)
                {
                // Ahruman's replacement for buggy Quaternion.rotate()
                angle *= 0.5;
                axis = axis.multiply(Math.sin(angle));
                return q.multiply([Math.cos(angle), axis.x, axis.y, axis.z]);
                }
            this.position = function(a, d)
                {
                // Move sun to origin, rotate planet, and translate back again
                return this.s.add(this.sp.rotateBy(
                this.rotateQ(this.q, this.rx, a)).multiply(d));
                }
            }
    
        var pv = new PlanetVecs(s.position, w.position, pm.position);
    
        function SystemId(name)
            {
            this.prime = 29;
            this.name = name;
            this.code = 0;
    
            this.charToInt = function(c)
                {
                var l = "abcdefghijklmnopqrstuvwxyz";
                var i = l.indexOf(c);
                if (i >= 0)
                return i;
                var u = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
                i = u.indexOf(c);
                return i >= 0 ? i : 0; 
                }
    
            this.nextPrime = function()
                {
                var p = this.prime;
                if ((p & 1) == 0)
                ++p;
                for (;;)
                    {
                    p += 2;
                    for (var i = 3; ; i += 2)
                        {
                        if (i * i > p)
                        return this.prime = p;
                        if (p % i == 0)
                        break;
                        }
                    }
                }
    
            this.calcCode = function()
                {
                // Use letters of n as digits of number with base prime.
                var l = this.name.length;
                var x = 0;
                var pp = 1;
                for (var i = 0; i < l; ++i)
                    {
                    x += this.charToInt (this.name.charAt(i)) * pp;
                    pp *= this.prime;
                    }
                return this.code = x;
                }
            }
        // Derive numbers from system name
        var id = new SystemId(system.name);
    
        id.calcCode();
        this.Log (this.Verbose, system.name + ":id is " + id.code);
        this.addPlanets(id.code);
        this.innerPlanets = 1;
    
        var x = [+Infinity, 0];
        var np = this.planetsMinMax(x);
        if (np <= 1)
        return;
    
        function Map(x0, y0, x1, y1)
            {
            var m, X0 = x0, Y0 = y0;
            if (x0 == x1 || y0 == y1)
                {
                m = (y0 + y1) / 2;
                this.interpolate = function(x) { return m; }
                }
            else
                {
                m = (y1 - y0) / (x1 - x0);
                this.interpolate = function(x) { return Y0 + m * (x - X0); }
                }
            }
        var map = new Map(x[0], this.Y0, x[1], this.Y1);
    
        function PlanetWeights(p, s)
            {
            this.c = 0, this.p = p, this.s = s;
            this.product = function() { return this.s * this.c * this.p; }
            this.next = function() { this.p = this.c; }
            }
        var ow = new PlanetWeights(
    	map.interpolate(pm.radius),    
    	pow(this.SpreadFactor, 1.0 / (np - 1)));
        var iw = new PlanetWeights(ow.p, ow.s);
    
        function InnerOrbitalPeriod(orbits, s)
            {
            this.t = orbits.Yr;
            var r = orbits.RandomOffsetFactor;
            r /= 1 + r;
            var f = orbits.Yr / (orbits.Y1 * orbits.Y1 * s);
            this.calc = function(id, iw)
                {
                var mod = this.t * r;
                return this.t - id.code % mod - f * iw.product();
                }
            }
        var iop = new InnerOrbitalPeriod(this, iw.s);
    
        function OuterOrbitalPeriod(orbits)
            {
            this.t = orbits.Yr;
            var r = orbits.RandomOffsetFactor;
            var f = orbits.Yr;
            this.calc = function(id, ow)
                {
                var t = this.t + 0.5 * f * ow.product();
                var mod = t * r;
                if (mod != 0)
                t += id.code % mod;
                return t;
                }
            }
        var oop = new OuterOrbitalPeriod(this);
    
        // Grrr, need an index sorted by Entity.ID as planets[] order may
        // change on different system entries.
        var planetsarray = this.mkPlanetIndex();
        var today = clock.days + id.code;
        var a = 2.0 * Math.PI * (today % this.Yr) / this.Yr;
        var info = (180 * a / Math.PI).toFixed();
    
        this.Log(this.Verbose, system.name + ":" +
    	     today + "," +
    	     ow.p.toFixed(4) + "," +
    	     ow.s.toFixed(4) + "," +
    	     (pm.radius  / 10).toFixed()+ "," +
    	     (180 * a / Math.PI).toFixed());
    
        // Pretend to rotate the main planet.
        var pmOffset = pm.position.subtract(pv.position(a, 1));
    
        for (var i = 0; i < planetsarray.length; ++i)
            {
            var pi = planetsarray[i];
            if (pi.isMainPlanet || this.isMoon(pi))
                continue;
            iw.c = ow.c = map.interpolate(pi.radius);
    
            id.nextPrime();
            id.calcCode();
    
            // Current planet's days per year. Try to make it an inner
            // planet. If that fails it will be an outer planet.
            var d;
            var t = iop.calc(id, iw);
            var inner = t > this.Yr / 3;
            if (inner)
                {
                // Orbit Radius in planet-sun units (3. Kepler's law)
                d = pow(t / this.Yr, 2.0 / 3.0);
                inner =
                // idea: 0.3 * pv.dist / pm.r <= d * pv.dist / pi.r 
                pm.radius * d >= pi.radius * 0.3  &&
                d * pv.dist > 15 * s.radius;
                }
    
            if (!inner)
                {
                t = oop.calc(id, ow);
                d = pow(t / this.Yr, 2.0 / 3.0);
                }
    
            // Current angle in radians
            var a = 2.0 * Math.PI * (today % t) / t;
            var aa = (180.0 * a / Math.PI).toFixed();
            this.Log(this.Verbose,
                system.name + "[" + i + "]" + ":" +
                id.prime + "," +
                id.code + "," +
                (pi.radius / 10).toFixed() + "," +
                ow.c.toFixed(4) + "," +
                t.toFixed() + "," +
                d.toFixed(4) + "," +
                aa);
            pi.position=pv.position(a, d).add(pmOffset);
            if (inner)
                {
                iw.next(), iop.t = t, ++this.innerPlanets;
                info = aa + "," + info;
                }
            else
                {
                ow.next(), oop.t = t, ++this.outerPlanets;
                info = info + "," + aa;
                }
            }
        s.position=(s.position.add(pmOffset));
        this.info = info;
        }
    
    Scripts/planet_engine.js
    "use strict";
    this.name           = "PlanetEngine"; 
    this.author         = "Stranger"; 
    this.copyright      = "This script is hereby placed in the public domain."; 
    this.version        = "1.7"; 
    this.description    = "Gives info and define texture to additional planets genereted by Orbits";
    
    this.$planetRadius = 6400;
    this.$planetDensity = 5.525;
    this.$planetEscapeVelocity = 11180;
    this.$surfaceGravity = 1;
    this.$atmosphereMass = 1;
    
    this.$planetFlag = -1;
    
    this.$orbitRadiusList = new Array();
    this.$orbitPeriodList = new Array();
    this.$insolationList = new Array();
    this.$planetDensityList = new Array();
    this.$planetMassList = new Array();
    this.$escapeVelocityList = new Array();
    this.$planetGravityList = new Array();
    this.$atmosphereMassList = new Array();
    this.$atmospherePressureList = new Array();
    this.$atmosphereTemperatureList = new Array();
    
    this.shipWillExitWitchspace = function()
        {
        if (system.isInterstellarSpace) return;
        this.$scriptDelay = new Timer(this, this.$scanSystem, 0.25);    //delay to set planet orbits!
        }
    
    this.shipWillLaunchFromStation = function()
        {
        this.shipWillExitWitchspace();
        delete this.shipWillLaunchFromStation;
        }
    
    this.shipApproachingPlanetSurface = function(planet) 
        {
        this.$planetFlag = planet.radius;
        }
    
    this.shipDockedWithStation = function(station)
        {
        if(station.hasRole("planetFall_surface"))
            {
            this.$initInterface(station);    
            }
        }
    
    this.$initInterface = function(station)
        {
    	station.setInterface(this.name,{
    		title:"Planet Data Sheet",
    		category:"Ambience",
    		summary:"Provide extended set of planetary data",
    		callback:this.$setupMissionPage.bind(this)
            });
        }
    
    this.$setupMissionPage = function()
        {
        if(this.$planetFlag > 25000)
            {
            this.$planetInfo();
            }
        else
            {
            this.$moonInfo();
            }
        }
    
    this.$scanSystem = function()
        {
        var w = worldScripts.AstroLibrary;
    	var info = system.info;
        if (system.info.sun_radius)
    		{
            var sunRadius = system.sun.radius;
    		}
        var sunMass = w.$astroLib_sunMass(sunRadius);
        var sunTemperature = w.$astroLib_sunTemperature(sunRadius);
        var sunLuminosity = w.$astroLib_sunLuminosity(sunTemperature,sunRadius);
        var localYear = w.$astroLib_solPeriod(sunMass);
        var mainOrbitVector = new Vector3D(system.sun.position.subtract(system.mainPlanet.position));
        var ouScale = mainOrbitVector.magnitude();
        var planetRadius = this.$planetRadius;
        var planetDensity = this.$planetDensity;
        var escapeVelocity = this.$planetEscapeVelocity;
        var surfaceGravity = this.$surfaceGravity;
        var atmosphereMass = this.$atmosphereMass;
        var mainInsolation = sunLuminosity * Math.pow((50000000/ouScale),2);
        var insolation = mainInsolation;
        var meanTemperature = w.$astroLib_equilibriumTemperature(insolation);
        var equilibriumTemperature = meanTemperature;
        this.$orbitRadiusList = w.$astroLib_orbitRadius();
        for (let i=0; i<system.planets.length; i++)
            {
            planetRadius = system.planets[i].radius/10;        
            this.$orbitPeriodList[i] = localYear * Math.pow(this.$orbitRadiusList[i],1.5);
            this.$insolationList[i] = mainInsolation * Math.pow(this.$orbitRadiusList[i],-2);
            insolation = this.$insolationList[i];
            meanTemperature = w.$astroLib_equilibriumTemperature(insolation);
            equilibriumTemperature = meanTemperature;
            if(planetRadius < 10000)
                {
                this.$planetDensityList[i] = w.$astroLib_planetDensity(planetRadius, equilibriumTemperature);
                }
            else
                {
                if(planetRadius < 15000)
                    {
                    this.$planetDensityList[i] = 0.5 + 1.5 * (15000 - planetRadius)/5000 + 0.25 * system.scrambledPseudoRandomNumber(i);
                    }
                else
                    {
                    this.$planetDensityList[i] = 0.5 + 0.75 * (planetRadius - 15000)/2500 + 0.25 * system.scrambledPseudoRandomNumber(i);
                    }
                }
            planetDensity =  this.$planetDensityList[i];
            this.$planetMassList[i] = planetDensity/this.$planetDensity * Math.pow((planetRadius/this.$planetRadius),3);
            this.$escapeVelocityList[i] = this.$planetEscapeVelocity * planetRadius/this.$planetRadius * Math.sqrt(planetDensity/this.$planetDensity);
            escapeVelocity = this.$escapeVelocityList[i];
            this.$planetGravityList[i] = escapeVelocity * escapeVelocity / (planetRadius * 1000) / (9.81*2);
            surfaceGravity =  this.$planetGravityList[i];
            this.$atmosphereMassList[i] = w.$astroLib_atmosphereMass(planetRadius, escapeVelocity, equilibriumTemperature);
            if(planetRadius < 10000)
                {
                atmosphereMass = this.$atmosphereMassList[i];
                this.$atmospherePressureList[i] = atmosphereMass * surfaceGravity;
                }
            else
                {
                this.$atmospherePressureList[i] = 1;
                atmosphereMass = 1 / surfaceGravity;
                }
            this.$atmosphereTemperatureList[i] = w.$astroLib_atmosphereTemperature(equilibriumTemperature, atmosphereMass);
            }
        this.$paintPlanet();
        }
    
    this.$planetInfo = function()
        {
        var planetRadius_scaled;
        var planetMass_scaled;
        var escapeVelocity_scaled;
        var surfaceGravity_scaled;
        var atmospherePressure_scaled;
        for (let i=0; i<system.planets.length; i++)
            {
            if(this.$planetFlag == system.planets[i].radius)
                {
                this.$reportFlag = i;
                }
            }
        var r = this.$reportFlag;
        if(system.planets[r].radius < 75000)
            {
            planetRadius_scaled = system.planets[r].radius / 10;          // default planet scale
            }
        else
            {
            if(system.planets[r].radius >= 75000 && system.planets[r].radius < 100000)
                {
                planetRadius_scaled = (75000 + (system.planets[r].radius - 75000) * 2) / 10;    // super-earth - 2x increment
                }
            else
                {
                if(system.planets[r].radius >= 100000 && system.planets[r].radius < 125000)
                    {
                    planetRadius_scaled = (150000 + (system.planets[r].radius - 100000) * 4) / 10;    // ice giant - 4x increment
                    }
                else
                    {
                    if(system.planets[r].radius >= 125000)
                        {
                        planetRadius_scaled = (250000 + (system.planets[r].radius - 125000) * 10) / 10;     // gas giant - 10x increment
                        }
                    }
                }
            }
        var scale_factor = planetRadius_scaled / system.planets[r].radius * 10;
        planetMass_scaled = this.$planetMassList[r] * Math.pow(scale_factor,3);
        escapeVelocity_scaled = this.$escapeVelocityList[r]/1000 * scale_factor;
        surfaceGravity_scaled = this.$planetGravityList[r] * scale_factor;
        atmospherePressure_scaled = this.$atmospherePressureList[r] * scale_factor;
        if(system.planets[r].radius >= 100000)
            {
            atmospherePressure_scaled = 1;
            }
        if (this.$atmospherePressureList[r] < 0.001)
            {
            atmospherePressure_scaled = 0;
            }
        var planetInfo = "ORBITAL DATA" + "\n"
        + "Orbit radius: " +  this.$orbitRadiusList[r].toFixed(3) + " OU" + "\n"
        + "Orbital period: " + this.$orbitPeriodList[r].toFixed(3) + " days" + "\n"
        + "PHYSICAL DATA" + "\n"
        + "Radius: " + planetRadius_scaled + " km" + "\n"
        + "Mean density: " + this.$planetDensityList[r].toFixed(3) + " g/cm3" + "\n"
        + "Mass: " + planetMass_scaled.toFixed(3) + " TU" + "\n"
        + "Escape velocity: " + escapeVelocity_scaled.toFixed(3) + " km/s" + "\n"
        + "Surface gravity: " + surfaceGravity_scaled.toFixed(3) + " G" + "\n"
        + "Atmosphere pressure: " + atmospherePressure_scaled.toPrecision(4) + " bar" + "\n"
        + "Surface temperature: " + this.$atmosphereTemperatureList[r].toFixed(1) + " K" + "\n";
        mission.runScreen({
            title: "Planet Data Sheet",
    //        background: {name: "specialCargoLoad.png", height: 480 },
            message: planetInfo,}
            );
        }
    
    this.$moonInfo = function()
        {
        var w = worldScripts.SpawnMoons;
        for (let i=0; i<system.planets.length; i++)
            {
            if (this.$planetFlag == system.planets[i].radius)
                {
                this.$reportFlag = i;
                }
            }
        var r = this.$reportFlag;
        var moonRadius = system.planets[r].radius;
        var moonOrbitRadius;
        var moonOrbitPeriod;
        var p = w.$targetPlanet_Jovian;
        var moonOrbitVelocity;
    
        if (moonRadius >= 12500 && moonRadius < 15000)
            {
            moonOrbitRadius = w.$orbitRadius_Luna;
            moonOrbitPeriod = w.$orbitPeriod_Luna / 1440;
            }
    
        if (moonRadius >= 15000 && moonRadius < 17500)
            {
            moonOrbitRadius = w.$orbitRadius_Selene;
            moonOrbitPeriod = w.$orbitPeriod_Selene / 1440;
            }
        if(moonRadius >= 17500 && moonRadius < 20000)
            {
            moonOrbitRadius = w.$orbitRadius_ZeusInner;
            moonOrbitVelocity = Math.sqrt(this.$escapeVelocityList[p]*this.$escapeVelocityList[p]/moonOrbitRadius/2);
            moonOrbitPeriod = 2.0 * Math.PI * (moonOrbitRadius * system.planets[p].radius * 100 / moonOrbitVelocity) / 86400;
            }
        if(moonRadius >= 20000 && moonRadius < 25000)
            {
            moonOrbitRadius = w.$orbitRadius_ZeusOuter;
            moonOrbitVelocity = Math.sqrt(this.$escapeVelocityList[p]*this.$escapeVelocityList[p]/moonOrbitRadius/2);
            moonOrbitPeriod = 2.0 * Math.PI * (moonOrbitRadius * system.planets[p].radius * 100 / moonOrbitVelocity) / 86400;
            }
        //
    
        var moonInfo = "ORBITAL DATA" + "\n"
        + "Orbit radius: " +  moonOrbitRadius.toFixed(3) + " R" + "\n"
        + "Orbital period: " + moonOrbitPeriod.toFixed(3) + " days" + "\n"
        + "PHYSICAL DATA" + "\n"
        + "Radius: " + system.planets[r].radius/10 + " km" + "\n"
        + "Mean density: " + this.$planetDensityList[r].toFixed(3) + " g/cm3" + "\n"
        + "Mass: " + this.$planetMassList[r].toFixed(2) + " TU" + "\n"
        + "Escape velocity: " + (this.$escapeVelocityList[r]/1000).toFixed(3) + " km/s" + "\n"
        + "Surface gravity: " + this.$planetGravityList[r].toFixed(3) + " G" + "\n"
        + "Atmosphere pressure: " + this.$atmospherePressureList[r].toPrecision(4) + " bar" + "\n"
        + "Surface temperature: " + this.$atmosphereTemperatureList[r].toFixed(1) + " K" + "\n";
        mission.runScreen({
            title: "Moon Data Sheet",
    //        background: {name: "specialCargoLoad.png", height: 480 },
            message: moonInfo,}
            );
        }
    
    this.$paintPlanet = function()
        {
        if (system.isInterstellarSpace) return;
    
        var paintPlanet1 = system.scrambledPseudoRandomNumber(161);
        var paintPlanet2 = system.scrambledPseudoRandomNumber(162);
        var paintPlanet3 = system.scrambledPseudoRandomNumber(163);
        var paintPlanet4 = system.scrambledPseudoRandomNumber(164);
        var paintPlanet5 = system.scrambledPseudoRandomNumber(165);
        var paintPlanet6 = system.scrambledPseudoRandomNumber(166);
        var paintPlanet7 = system.scrambledPseudoRandomNumber(167);
        var paintPlanet8 = system.scrambledPseudoRandomNumber(168);
        var paintPlanet9 = system.scrambledPseudoRandomNumber(169);
        var paintPlanet10 = system.scrambledPseudoRandomNumber(170);
        var paintPlanet11 = system.scrambledPseudoRandomNumber(171);
        var paintPlanet12 = system.scrambledPseudoRandomNumber(172);
    
        var aeolisShift = Math.floor(5 * paintPlanet1) + 1;
        var aeolisTexture = ('oplanet_Aeolis' + aeolisShift + '.png');          // Set 1 - Cold wind-eroded deserts
        var aphroditeShift = Math.floor(5 * paintPlanet2) + 1;
        var aphroditeTexture = ('oplanet_Aphrodite' + aphroditeShift + '.png'); // Set 2 - Venus class superdense atmosphere with opaque clouds
        var aresShift = Math.floor(5 * paintPlanet3) + 1;
        var aresTexture = ('oplanet_Ares' + aresShift + '.png');                // Set 3 - Mars class cold desert with impact craters
        var boreusShift = Math.floor(5 * paintPlanet4) + 1;
        var boreusTexture = ('oplanet_Boreus' + boreusShift + '.png');          // Set 4 - Snowball Earth
        var cronusShift = Math.floor(5 * paintPlanet5) + 1;
        var cronusTexture = ('oplanet_Cronus' + cronusShift + '.png');          // Set 5 - Saturn class gas giants
        var hadesShift = Math.floor(5 * paintPlanet6) + 1;
        var hadesTexture = ('oplanet_Hades' + hadesShift + '.png');             // Set 6 - Pluto class ice worlds
        var hermesShift = Math.floor(5 * paintPlanet7) + 1;
        var hermesTexture = ('oplanet_Hermes' + hermesShift + '.png');          // Set 7 - Mercury class heavy cratered dry airless deserts
        var phaethonShift = Math.floor(5 * paintPlanet8) + 1;
        var phaethonTexture = ('oplanet_Phaethon' + phaethonShift + '.png');    // Set 8 - Warm Mars without permafrost and polar caps
        var poseidonShift = Math.floor(5 * paintPlanet9) + 1;
        var poseidonTexture = ('oplanet_Poseidon' + poseidonShift + '.png');    // Set 9 - Neptune class ice giants
        var setShift = Math.floor(5 * paintPlanet10) + 1;
        var setTexture = ('oplanet_Set' + setShift + '.png');                   // Set 10 - Warm sandy deserts
        var typhonShift = Math.floor(5 * paintPlanet11) + 1;
        var typhonTexture = ('oplanet_Typhon' + typhonShift + '.png');          // Set 11 - Hot volcanic worlds
        var zeusShift = Math.floor(5 * paintPlanet12) + 1;
        var zeusTexture = ('oplanet_Zeus' + zeusShift + '.png');                // Set 12 - Jupiter class gas giant
    
        var w = worldScripts.AstroLibrary;
        var planetOrbitScan = w.$astroLib_orbitRadius();
        var surfaceTemp;
        var surfaceAirPressure;
        var textureIndex;
        var planetTexture;
        for (let i=0; i<system.planets.length; i++) 
            {
            surfaceTemp = this.$atmosphereTemperatureList[i];
            surfaceAirPressure = this.$atmospherePressureList[i];
            // Mercury analog
            if(system.planets[i].radius >= 27500 && system.planets[i].radius < 30000 && system.planets[i] != system.mainPlanet)
                {
                system.planets[i].texture = hermesTexture;
                }  
            // Snowball Earth OR Venus analog
            if(system.planets[i].radius >= 50000 && system.planets[i].radius < 75000 && system.planets[i] != system.mainPlanet)
                {
                if(surfaceAirPressure < 0.01)
                    {
                    system.planets[i].texture = hermesTexture;
                    }
                else
                    {
                    if(surfaceAirPressure >= 2.5)
                        {
                        system.planets[i].texture = aphroditeTexture;
                        }
                    else
                        {
                        textureIndex = this.$climateIndex(surfaceTemp, surfaceAirPressure);
                        switch (textureIndex)
                            {
                            case 2: planetTexture = aeolisTexture; break;
                            case 3: planetTexture = aeolisTexture; break;
                            case 4: planetTexture = setTexture; break;
                            case 20: planetTexture = boreusTexture; break;
                            case 30: planetTexture = aeolisTexture; break;
                            case 40: planetTexture = typhonTexture; break;
                            default: planetTexture = aeolisTexture;
                            }
                        system.planets[i].texture = planetTexture;
                        }
                    }
                }
            // Mars analog
            if(system.planets[i].radius >= 30000 && system.planets[i].radius < 50000 && system.planets[i] != system.mainPlanet)
                {            
                if(surfaceAirPressure < 0.01)
                    {
                    system.planets[i].texture = hermesTexture;
                    }
                else
                    {
                    if(surfaceAirPressure >= 2.5)
                        {
                        system.planets[i].texture = aphroditeTexture;
                        }
                    else
                        {
                        textureIndex = this.$climateIndex(surfaceTemp, surfaceAirPressure);
                        switch (textureIndex)
                            {
                            case 2: planetTexture = aresTexture; break;
                            case 3: planetTexture = phaethonTexture; break;
                            case 4: planetTexture = setTexture; break;
                            case 20: planetTexture = aeolisTexture; break;
                            case 30: planetTexture = aeolisTexture; break;
                            case 40: planetTexture = setTexture; break;
                            default: planetTexture = aresTexture;
                            }
                        system.planets[i].texture = planetTexture;
                        }
                    }
                }
            // Super Earth
            if(system.planets[i].radius >= 75000 && system.planets[i].radius < 100000 && system.planets[i] != system.mainPlanet)
                {
                system.planets[i].texture = aphroditeTexture;
                }
            // Jupiter analog
            if(system.planets[i].radius >= 150000 && system.planets[i].radius < 175000 && system.planets[i] != system.mainPlanet)
                {
                system.planets[i].texture = zeusTexture;
                } 
            // Saturn analog
            if(system.planets[i].radius >= 125000 && system.planets[i].radius < 150000 && system.planets[i] != system.mainPlanet)
                {
                system.planets[i].texture = cronusTexture;
                } 
            // Neptune analog
            if(system.planets[i].radius >= 100000 && system.planets[i].radius < 125000 && system.planets[i] != system.mainPlanet)
                {
                system.planets[i].texture = poseidonTexture;
                }
            // Pluto analog
            if(system.planets[i].radius >= 25000 && system.planets[i].radius < 27500 && system.planets[i] != system.mainPlanet)
                {
                var orbitHadejan = planetOrbitScan[i];
                if(orbitHadejan > 1)
                    {
                    system.planets[i].texture = hadesTexture;
                    }
                else
                    {
                    system.planets[i].texture = hermesTexture;
                    }
                }
            }
        }
    
    this.$climateIndex = function(surfaceTemp, surfaceAirPressure)
        {
        var tempIndex;
        var pressureIndex;
        var climateIndex;
        if(surfaceTemp < 273)
            {
            tempIndex = 2;
            }
        else
            {
            if(surfaceTemp >= 373)
                {
                tempIndex = 4;
                }
            else
                {
                tempIndex = 3;
                }
            }
        if(surfaceAirPressure < 0.25)
            {
            pressureIndex = 1;
            }
        else
            {
            pressureIndex = 10;
            }
        climateIndex = tempIndex * pressureIndex;
        return climateIndex;
        }