diff --git a/data/systems/00_sol.lua b/data/systems/00_sol.lua index 7ac206ecfdf..24a4112381d 100644 --- a/data/systems/00_sol.lua +++ b/data/systems/00_sol.lua @@ -159,12 +159,15 @@ local mars = CustomSystemBody:new('Mars', 'PLANET_TERRESTRIAL') local mars_starports = { CustomSystemBody:new('Bradbury Landing', 'STARPORT_SURFACE') + :seed(201299135) :latitude(math.deg2rad(-4.5895)) :longitude(math.deg2rad(-137.4417)), CustomSystemBody:new('Cydonia', 'STARPORT_SURFACE') + :seed(2874781459) :latitude(math.deg2rad(-29)) :longitude(math.deg2rad(124)), CustomSystemBody:new('Olympus Mons', 'STARPORT_SURFACE') + :seed(3046926584) :latitude(math.deg2rad(25.60955)) :longitude(math.deg2rad(-41.35269)), CustomSystemBody:new('Mars High', 'STARPORT_ORBITAL') diff --git a/data/systems/01_epsilon_eridani.lua b/data/systems/01_epsilon_eridani.lua index c59e0081487..f2da08c8583 100644 --- a/data/systems/01_epsilon_eridani.lua +++ b/data/systems/01_epsilon_eridani.lua @@ -74,6 +74,7 @@ local newhope = CustomSystemBody:new('New Hope', 'PLANET_TERRESTRIAL') local newhope_starports = { CustomSystemBody:new('Itzalean', 'STARPORT_SURFACE') + :seed(795500669) :latitude(math.deg2rad(0.0)) :longitude(math.deg2rad(45.864)), CustomSystemBody:new('New Hope', 'STARPORT_SURFACE') diff --git a/data/systems/02_local_stars.lua b/data/systems/02_local_stars.lua index 35a1ea336f9..a983534ba95 100644 --- a/data/systems/02_local_stars.lua +++ b/data/systems/02_local_stars.lua @@ -1,7 +1,7 @@ -- Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details -- Licensed under the terms of the GPL v3. See licenses/GPL-3.txt -CustomSystem:new('GJ 1075',{'STAR_K'}):add_to_sector(2,-1,-4,v(0.451,0.409,0.034)) +-- CustomSystem:new('GJ 1075',{'STAR_K'}):add_to_sector(2,-1,-4,v(0.451,0.409,0.034)) CustomSystem:new('NN 3707',{'STAR_M'}):add_to_sector(-1,3,-1,v(0.845,0.512,0.054)) CustomSystem:new('NN 3253',{'STAR_M'}):add_to_sector(3,-2,1,v(0.185,0.023,0.148)) CustomSystem:new('Gliese 766',{'STAR_M','STAR_M'}):add_to_sector(-4,-2,1,v(0.511,0.265,0.998)) diff --git a/data/systems/custom/00_sol.json b/data/systems/custom/00_sol.json new file mode 100644 index 00000000000..51362402ea5 --- /dev/null +++ b/data/systems/custom/00_sol.json @@ -0,0 +1,2762 @@ +{ + "bodies": [ + { + "agricultural": "f0/0", + "argOfPeriapsis": "f2/2338331550", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 255 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 5700, + "axialTilt": "f0/0", + "children": [ + 1, + 2, + 3, + 16, + 25, + 26, + 27, + 28, + 29, + 49, + 59, + 64, + 69 + ], + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f1/0", + "metallicity": "f0/0", + "name": "Sol", + "orbitalOffset": "f1/3165313515", + "orbitalPhase": "f4/4197316763", + "population": "f0/0", + "radius": "f1/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 1107383957, + "semiMajorAxis": "f0/0", + "type": "STAR_G", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f6/965421491", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 340, + "axialTilt": "f0/749614", + "eccentricity": "f0/880468295", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/524729244", + "life": "f0/0", + "mass": "f0/236223201", + "metallicity": "f0/3865470566", + "name": "Mercury", + "orbitalOffset": "f0/3452269205", + "orbitalPhase": "f4/4259118386", + "parent": 0, + "population": "f0/0", + "radius": "f0/1632087572", + "rotationPeriod": "f59/0", + "rotationPhase": "f0/0", + "seed": 6, + "semiMajorAxis": "f0/1662152343", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/85899345", + "volatileLiquid": "f0/0", + "volcanicity": "f0/2147483648" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f2/1297065845", + "aspectRatio": "f1/0", + "atmosColor": [ + 224, + 255, + 178, + 255 + ], + "atmosDensity": "f64/3972844717", + "atmosOxidizing": "f0/515396075", + "averageTemp": 735, + "axialTilt": "f0/194899886", + "eccentricity": "f0/30064771", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/254118876", + "life": "f0/0", + "mass": "f0/3500398346", + "metallicity": "f0/2147483648", + "name": "Venus", + "orbitalOffset": "f4/3508469582", + "orbitalPhase": "f4/1410581576", + "parent": 0, + "population": "f0/0", + "radius": "f0/4080218931", + "rotationPeriod": "f243/0", + "rotationPhase": "f0/0", + "seed": 613440353, + "semiMajorAxis": "f0/3105261355", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/3435973836" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f6/1019853762", + "aspectRatio": "f1/0", + "atmosColor": [ + 101, + 140, + 242, + 255 + ], + "atmosDensity": "f1/966367641", + "atmosOxidizing": "f0/4252017623", + "averageTemp": 288, + "axialTilt": "f0/1757097442", + "children": [ + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13 + ], + "eccentricity": "f0/71725953", + "hasRings": false, + "heightMapFilename": "heightmaps/earth.hmap", + "heightMapFractal": 0, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/3865470566", + "mass": "f1/0", + "metallicity": "f0/2147483648", + "name": "Earth", + "orbitalOffset": "f3/1578867387", + "orbitalPhase": "f5/3712225840", + "parent": 0, + "population": "f0/0", + "radius": "f1/0", + "rotationPeriod": "f1/0", + "rotationPhase": "f2/4153519558", + "seed": 1857401659, + "semiMajorAxis": "f1/0", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/2147483648", + "volatileLiquid": "f0/3006477107", + "volcanicity": "f0/429496729" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/1318626458", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/2323800938", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Shanghai", + "orbitalOffset": "f4294967293/3814582098", + "orbitalPhase": "f3/2540216228", + "parent": 3, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 484033947, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/3336998098", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/1424265091", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Mexico City", + "orbitalOffset": "f1/3126203441", + "orbitalPhase": "f4/2757481195", + "parent": 3, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 1270709154, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/3767666229", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/3823027349", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "London", + "orbitalOffset": "f0/0", + "orbitalPhase": "f5/3311845977", + "parent": 3, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 2469086855, + "semiMajorAxis": "f0/0", + "spaceStationType": "ground_station", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/1204238362", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/4122872631", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Moscow", + "orbitalOffset": "f4294967295/1483917775", + "orbitalPhase": "f0/713206236", + "parent": 3, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 2393333709, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/702654488", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f4294967295/3140562960", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Brasilia", + "orbitalOffset": "f0/3598143387", + "orbitalPhase": "f2/762404405", + "parent": 3, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 1842681150, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/2688750662", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/2548684899", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Los Angeles", + "orbitalOffset": "f2/255501236", + "orbitalPhase": "f1/300244814", + "parent": 3, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 186112527, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/1260910071", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/500742786", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Torvalds Platform", + "orbitalOffset": "f4/1654751467", + "orbitalPhase": "f0/0", + "parent": 3, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/994205", + "rotationPhase": "f0/0", + "seed": 0, + "semiMajorAxis": "f0/214748", + "type": "STARPORT_ORBITAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/849351994", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Gates Spaceport", + "orbitalOffset": "f5/919208327", + "orbitalPhase": "f5/597160473", + "parent": 3, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/994205", + "rotationPhase": "f0/0", + "seed": 1, + "semiMajorAxis": "f0/4294967", + "type": "STARPORT_ORBITAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/1814061917", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Jobs Pad", + "orbitalOffset": "f4/3333947896", + "orbitalPhase": "f0/3642647339", + "parent": 3, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/994205", + "rotationPhase": "f0/0", + "seed": 13, + "semiMajorAxis": "f0/4294967", + "type": "STARPORT_ORBITAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/1622258002", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 220, + "axialTilt": "f0/500742786", + "children": [ + 14, + 15 + ], + "eccentricity": "f0/235793704", + "hasRings": false, + "heightMapFilename": "heightmaps/moon.hmap", + "heightMapFractal": 1, + "humanActivity": "f0/0", + "inclination": "f0/385675994", + "life": "f0/0", + "mass": "f0/51539607", + "metallicity": "f0/0", + "name": "Moon", + "orbitalOffset": "f1/1244416494", + "orbitalPhase": "f0/0", + "parent": 3, + "population": "f0/0", + "radius": "f0/1172526071", + "rotationPeriod": "f27/1288490188", + "rotationPhase": "f0/0", + "seed": 4294967291, + "semiMajorAxis": "f0/11038065", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f2/301955263", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/51535907", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Tranquility Base", + "orbitalOffset": "f0/1756598609", + "orbitalPhase": "f1/4229414232", + "parent": 13, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 1032626772, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/2206786918", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/1049458488", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Mariasurīru", + "orbitalOffset": "f4294967290/3041531376", + "orbitalPhase": "f4/2222041476", + "parent": 13, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 2341189786, + "semiMajorAxis": "f0/0", + "spaceStationType": "ground_station", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/1170984768", + "aspectRatio": "f1/0", + "atmosColor": [ + 254, + 242, + 197, + 255 + ], + "atmosDensity": "f0/2572792783", + "atmosOxidizing": "f0/4080223226", + "averageTemp": 278, + "axialTilt": "f0/1888280059", + "children": [ + 17, + 18, + 19, + 20, + 21, + 23 + ], + "eccentricity": "f0/400720448", + "hasRings": false, + "heightMapFilename": "heightmaps/mars.hmap", + "heightMapFractal": 0, + "humanActivity": "f0/0", + "inclination": "f0/138678443", + "life": "f0/429496729", + "mass": "f0/459561500", + "metallicity": "f2/3435973836", + "name": "Mars", + "orbitalOffset": "f4/2960333524", + "orbitalPhase": "f0/899537940", + "parent": 0, + "population": "f0/0", + "radius": "f0/2289217568", + "rotationPeriod": "f1/115964116", + "rotationPhase": "f0/0", + "seed": 2977652237, + "semiMajorAxis": "f1/2233382993", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/1889785610", + "volatileLiquid": "f0/429496729", + "volcanicity": "f0/858993459" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f6/210231978", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f4294967295/3950932316", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Bradbury Landing", + "orbitalOffset": "f4294967293/2582090554", + "orbitalPhase": "f0/526463491", + "parent": 16, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 4126130448, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/2594801908", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f4294967295/2121089000", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Cydonia", + "orbitalOffset": "f2/705269160", + "orbitalPhase": "f2/2505461848", + "parent": 16, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 1148906070, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/2090424034", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/1919725687", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Olympus Mons", + "orbitalOffset": "f4294967295/1195115045", + "orbitalPhase": "f2/980258550", + "parent": 16, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 1064378724, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/372148392", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Mars High", + "orbitalOffset": "f4/3909988521", + "orbitalPhase": "f4/3845019832", + "parent": 16, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/1968526677", + "rotationPhase": "f0/0", + "seed": 3278817225, + "semiMajorAxis": "f0/217668", + "type": "STARPORT_ORBITAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/1074416820", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 233, + "axialTilt": "f0/0", + "children": [ + 22 + ], + "eccentricity": "f0/64854006", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/81932723", + "life": "f0/0", + "mass": "f0/7", + "metallicity": "f0/3435973836", + "name": "Phobos", + "orbitalOffset": "f5/736397179", + "orbitalPhase": "f3/2058261697", + "parent": 16, + "population": "f0/0", + "radius": "f0/9019431", + "rotationPeriod": "f0/1370094567", + "rotationPhase": "f0/0", + "seed": 439771126, + "semiMajorAxis": "f0/269294", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/3221225472" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/1681710547", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/374806602", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Phobos Base", + "orbitalOffset": "f4294967295/3920160694", + "orbitalPhase": "f3/1187163110", + "parent": 21, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 1030333527, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/1074416820", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 233, + "axialTilt": "f0/0", + "children": [ + 24 + ], + "eccentricity": "f0/858993", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/69714028", + "life": "f0/0", + "mass": "f0/1", + "metallicity": "f0/3006477107", + "name": "Deimos", + "orbitalOffset": "f5/736397179", + "orbitalPhase": "f3/2058261697", + "parent": 16, + "population": "f0/0", + "radius": "f0/5153960", + "rotationPeriod": "f1/1129576398", + "rotationPhase": "f0/0", + "seed": 439771126, + "semiMajorAxis": "f0/673450", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f1/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/1069888146", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Tomm's Sanctuary", + "orbitalOffset": "f3/1187163110", + "orbitalPhase": "f1/1681710547", + "parent": 23, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 1030333527, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/3571308588", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 164, + "axialTilt": "f0/0", + "eccentricity": "f0/957777707", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/811756140", + "life": "f0/0", + "mass": "f0/4", + "metallicity": "f0/0", + "name": "Eros", + "orbitalOffset": "f4/667326742", + "orbitalPhase": "f4/61274666", + "parent": 0, + "population": "f0/0", + "radius": "f0/6442450", + "rotationPeriod": "f0/942315824", + "rotationPhase": "f0/0", + "seed": 842785371, + "semiMajorAxis": "f1/1967095021", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f2/3802856908", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 164, + "axialTilt": "f1/2001798284", + "eccentricity": "f0/993297086", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/2611652409", + "life": "f0/0", + "mass": "f0/151612", + "metallicity": "f0/0", + "name": "Pallas", + "orbitalOffset": "f1/1533268421", + "orbitalPhase": "f3/2733569613", + "parent": 0, + "population": "f0/0", + "radius": "f0/171798691", + "rotationPeriod": "f7/3435973836", + "rotationPhase": "f0/0", + "seed": 910770044, + "semiMajorAxis": "f2/3313996765", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/2406215657", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 177, + "axialTilt": "f0/2173883355", + "eccentricity": "f0/381135397", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/535256062", + "life": "f0/0", + "mass": "f0/186315", + "metallicity": "f0/0", + "name": "Vesta", + "orbitalOffset": "f2/3792181104", + "orbitalPhase": "f4/4123987008", + "parent": 0, + "population": "f0/0", + "radius": "f0/176093659", + "rotationPeriod": "f0/956059720", + "rotationPhase": "f0/0", + "seed": 1385920280, + "semiMajorAxis": "f2/1553876218", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/2885488135", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 168, + "axialTilt": "f0/299845980", + "eccentricity": "f0/325558521", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/794065268", + "life": "f0/0", + "mass": "f0/644245", + "metallicity": "f0/0", + "name": "Ceres", + "orbitalOffset": "f1/3186359528", + "orbitalPhase": "f1/589656504", + "parent": 0, + "population": "f0/0", + "radius": "f0/317827579", + "rotationPeriod": "f0/1623927134", + "rotationPhase": "f0/0", + "seed": 2762125245, + "semiMajorAxis": "f2/3296387399", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f2/3642825793", + "aspectRatio": "f1/0", + "atmosColor": [ + 255, + 255, + 255, + 3 + ], + "atmosDensity": "f1/4281008650", + "atmosOxidizing": "f0/3435973836", + "averageTemp": 165, + "axialTilt": "f0/234629479", + "children": [ + 30, + 31, + 32, + 33, + 35, + 37, + 39, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48 + ], + "eccentricity": "f0/209594404", + "hasRings": true, + "humanActivity": "f0/0", + "inclination": "f0/97824523", + "life": "f0/0", + "mass": "f317/3435973836", + "metallicity": "f0/0", + "name": "Jupiter", + "orbitalOffset": "f2/1064615691", + "orbitalPhase": "f1/1327144829", + "parent": 0, + "population": "f0/0", + "radius": "f11/0", + "ringsBaseColor": [ + 155, + 122, + 97, + 204 + ], + "ringsMaxRadius": "f1/759779714", + "ringsMinRadius": "f1/505088154", + "rotationPeriod": "f0/1717986918", + "rotationPhase": "f0/0", + "seed": 786424632, + "semiMajorAxis": "f5/876173328", + "type": "PLANET_GAS_GIANT", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f2/3644263769", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 123, + "axialTilt": "f0/0", + "eccentricity": "f0/858993", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/117689273", + "life": "f0/0", + "mass": "f0/27", + "metallicity": "f0/8589934", + "name": "Metis", + "orbitalOffset": "f3/3197834728", + "orbitalPhase": "f5/275591020", + "parent": 29, + "population": "f0/0", + "radius": "f0/14474039", + "rotationPeriod": "f0/1266156358", + "rotationPhase": "f0/0", + "seed": 4294967198, + "semiMajorAxis": "f0/3676492", + "type": "PLANET_ASTEROID", + "volatileIces": "f100/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/2224876505", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 122, + "axialTilt": "f0/0", + "eccentricity": "f0/6442450", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/2248839", + "life": "f0/0", + "mass": "f0/1", + "metallicity": "f0/3006477107", + "name": "Adrastea", + "orbitalOffset": "f3/921639142", + "orbitalPhase": "f5/1155684482", + "parent": 29, + "population": "f0/0", + "radius": "f0/14516989", + "rotationPeriod": "f0/1281188744", + "rotationPhase": "f0/0", + "seed": 4294963315, + "semiMajorAxis": "f0/3702261", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f1/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/3589659611", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 112, + "axialTilt": "f0/0", + "eccentricity": "f0/12884901", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/28035533", + "life": "f0/0", + "mass": "f0/1494", + "metallicity": "f0/3006477107", + "name": "Amalthea", + "orbitalOffset": "f4/420256030", + "orbitalPhase": "f4/3646064787", + "parent": 29, + "population": "f0/0", + "radius": "f0/55834574", + "rotationPeriod": "f0/2139662512", + "rotationPhase": "f0/0", + "seed": 4294957314, + "semiMajorAxis": "f0/5196910", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f1/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/2715663979", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 124, + "axialTilt": "f0/0", + "children": [ + 34 + ], + "eccentricity": "f0/75161927", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/80658380", + "life": "f0/0", + "mass": "f0/309", + "metallicity": "f0/3006477107", + "name": "Thebe", + "orbitalOffset": "f4/3169905413", + "orbitalPhase": "f5/835479586", + "parent": 29, + "population": "f0/0", + "radius": "f0/33200097", + "rotationPeriod": "f0/2897110059", + "rotationPhase": "f0/0", + "seed": 4293977314, + "semiMajorAxis": "f0/6356551", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f1/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/1836829153", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f4294967295/4287471164", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Thebe Gas Refinery", + "orbitalOffset": "f0/1589179996", + "orbitalPhase": "f1/68568831", + "parent": 33, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 128933881, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/2295449881", + "aspectRatio": "f1/0", + "atmosColor": [ + 224, + 255, + 178, + 255 + ], + "atmosDensity": "f0/429496729", + "atmosOxidizing": "f0/515396075", + "averageTemp": 130, + "axialTilt": "f0/0", + "children": [ + 36 + ], + "eccentricity": "f0/17609365", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/165664518", + "life": "f0/429496729", + "mass": "f0/64424509", + "metallicity": "f0/3865470566", + "name": "Io", + "orbitalOffset": "f0/848713004", + "orbitalPhase": "f0/1278760766", + "parent": 29, + "population": "f0/0", + "radius": "f0/1228360646", + "rotationPeriod": "f1/3307124817", + "rotationPhase": "f0/0", + "seed": 4290011317, + "semiMajorAxis": "f0/12111807", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/3006477107" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/700509251", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f4294967295/68648042", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Dante's Base", + "orbitalOffset": "f0/712882158", + "orbitalPhase": "f4/3423042200", + "parent": 35, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 3040668453, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/3273623078", + "aspectRatio": "f1/0", + "atmosColor": [ + 63, + 114, + 255, + 255 + ], + "atmosDensity": "f0/368293444", + "atmosOxidizing": "f1/0", + "averageTemp": 102, + "axialTilt": "f0/0", + "children": [ + 38 + ], + "eccentricity": "f0/38654705", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f0/34359738", + "metallicity": "f0/3221225472", + "name": "Europa", + "orbitalOffset": "f6/622279243", + "orbitalPhase": "f0/697519623", + "parent": 29, + "population": "f0/0", + "radius": "f0/1052266987", + "rotationPeriod": "f3/2362232012", + "rotationPhase": "f0/0", + "seed": 2102431459, + "semiMajorAxis": "f0/18940805", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f1/0", + "volatileLiquid": "f0/3865470566", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/2888117462", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Clarke's Station", + "orbitalOffset": "f4/3532933415", + "orbitalPhase": "f2/1942438958", + "parent": 37, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/994205", + "rotationPhase": "f0/0", + "seed": 1063147543, + "semiMajorAxis": "f0/103079", + "type": "STARPORT_ORBITAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/1866447288", + "aspectRatio": "f1/0", + "atmosColor": [ + 63, + 114, + 255, + 255 + ], + "atmosDensity": "f0/436690799", + "atmosOxidizing": "f1/0", + "averageTemp": 180, + "axialTilt": "f0/0", + "children": [ + 40 + ], + "eccentricity": "f0/5583457", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/14992264", + "life": "f0/0", + "mass": "f0/107374182", + "metallicity": "f0/2576980377", + "name": "Ganymede", + "orbitalOffset": "f5/1830982006", + "orbitalPhase": "f2/1534672710", + "parent": 29, + "population": "f0/0", + "radius": "f0/1773821493", + "rotationPeriod": "f7/858993459", + "rotationPhase": "f0/0", + "seed": 3062955636, + "semiMajorAxis": "f0/30923764", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/3006477107", + "volatileLiquid": "f0/1288490188", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/1417216437", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f1/2001783632", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Enki Catena", + "orbitalOffset": "f1/2901319479", + "orbitalPhase": "f3/1915834389", + "parent": 39, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 2089909340, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/448734599", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 134, + "axialTilt": "f0/0", + "eccentricity": "f0/31782757", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/14392573", + "life": "f0/0", + "mass": "f0/77309411", + "metallicity": "f0/1073741824", + "name": "Callisto", + "orbitalOffset": "f2/2491657099", + "orbitalPhase": "f5/901417451", + "parent": 29, + "population": "f0/0", + "radius": "f0/1623497637", + "rotationPeriod": "f16/3006477107", + "rotationPhase": "f0/0", + "seed": 1272712740, + "semiMajorAxis": "f0/54116587", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f6/231723569", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Discovery Base", + "orbitalOffset": "f0/2074856203", + "orbitalPhase": "f0/1078926878", + "parent": 29, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f20/1717986918", + "rotationPhase": "f0/0", + "seed": 518598116, + "semiMajorAxis": "f0/60129542", + "type": "STARPORT_ORBITAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/705982959", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 124, + "axialTilt": "f0/0", + "eccentricity": "f0/861570439", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/3559163501", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/8589934", + "name": "Themisto", + "orbitalOffset": "f2/761054520", + "orbitalPhase": "f4/1444052897", + "parent": 29, + "population": "f0/0", + "radius": "f0/2692944", + "rotationPeriod": "f129/3521873182", + "rotationPhase": "f0/0", + "seed": 134102334, + "semiMajorAxis": "f0/212171384", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/4035724044", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 124, + "axialTilt": "f0/0", + "eccentricity": "f0/687194767", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/2181374428", + "life": "f0/0", + "mass": "f0/3", + "metallicity": "f0/8589934", + "name": "Leda", + "orbitalOffset": "f4/888640528", + "orbitalPhase": "f5/2138034445", + "parent": 29, + "population": "f0/0", + "radius": "f0/6700148", + "rotationPeriod": "f129/3521873182", + "rotationPhase": "f0/0", + "seed": 4211482628, + "semiMajorAxis": "f0/319975063", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/807371287", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 124, + "axialTilt": "f0/0", + "eccentricity": "f0/687194767", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/2218105475", + "life": "f0/0", + "mass": "f0/4818", + "metallicity": "f0/94489280", + "name": "Himalia", + "orbitalOffset": "f0/2360243525", + "orbitalPhase": "f1/3575989679", + "parent": 29, + "population": "f0/0", + "radius": "f0/57294863", + "rotationPeriod": "f129/3521873182", + "rotationPhase": "f0/0", + "seed": 1344978, + "semiMajorAxis": "f0/328994494", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f1/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f2/2176361871", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 124, + "axialTilt": "f0/0", + "eccentricity": "f0/472446402", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/1931753231", + "life": "f0/0", + "mass": "f0/45", + "metallicity": "f0/8589934", + "name": "Lysithea", + "orbitalOffset": "f1/3423848357", + "orbitalPhase": "f0/899537940", + "parent": 29, + "population": "f0/0", + "radius": "f0/12111807", + "rotationPeriod": "f12/3951369912", + "rotationPhase": "f0/0", + "seed": 3934, + "semiMajorAxis": "f0/336295939", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/172152048", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 124, + "axialTilt": "f0/0", + "eccentricity": "f0/944892805", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/2298314089", + "life": "f0/0", + "mass": "f0/625", + "metallicity": "f0/34359738", + "name": "Elara", + "orbitalOffset": "f3/599699565", + "orbitalPhase": "f1/2601490244", + "parent": 29, + "population": "f0/0", + "radius": "f0/28991029", + "rotationPeriod": "f0/2147483648", + "rotationPhase": "f0/0", + "seed": 128860219, + "semiMajorAxis": "f0/335436945", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/3175964892", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 113, + "axialTilt": "f0/0", + "eccentricity": "f0/901943132", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/2113909240", + "life": "f0/0", + "mass": "f0/45", + "metallicity": "f0/34359738", + "name": "Aega", + "orbitalOffset": "f0/58748266", + "orbitalPhase": "f3/1582666647", + "parent": 29, + "population": "f0/0", + "radius": "f0/1344324", + "rotationPeriod": "f0/3435973836", + "rotationPhase": "f0/0", + "seed": 6953, + "semiMajorAxis": "f0/347033357", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/585632248", + "aspectRatio": "f1/0", + "atmosColor": [ + 255, + 255, + 255, + 3 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 134, + "axialTilt": "f0/2003720761", + "children": [ + 50, + 51, + 52, + 53, + 57, + 58 + ], + "eccentricity": "f0/239229678", + "hasRings": true, + "humanActivity": "f0/0", + "inclination": "f0/186278881", + "life": "f0/0", + "mass": "f95/652835028", + "metallicity": "f0/0", + "name": "Saturn", + "orbitalOffset": "f6/696052547", + "orbitalPhase": "f3/3381742527", + "parent": 0, + "population": "f0/0", + "radius": "f9/0", + "ringsBaseColor": [ + 110, + 105, + 85, + 229 + ], + "ringsMaxRadius": "f2/1644972474", + "ringsMinRadius": "f1/1279900254", + "rotationPeriod": "f0/1717986918", + "rotationPhase": "f0/0", + "seed": 174249538, + "semiMajorAxis": "f9/2499670966", + "type": "PLANET_GAS_GIANT", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/2874293141", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 86, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/83956679", + "life": "f0/0", + "mass": "f0/442381", + "metallicity": "f0/0", + "name": "Tethys", + "orbitalOffset": "f0/1798287011", + "orbitalPhase": "f0/1626376617", + "parent": 49, + "population": "f0/0", + "radius": "f0/356482285", + "rotationPeriod": "f1/3809635991", + "rotationPhase": "f0/0", + "seed": 1431129037, + "semiMajorAxis": "f0/8589934", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/1220752563", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 87, + "axialTilt": "f0/0", + "eccentricity": "f0/9448928", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/1424265", + "life": "f0/0", + "mass": "f0/1408749", + "metallicity": "f0/2147483648", + "name": "Dione", + "orbitalOffset": "f0/3195711946", + "orbitalPhase": "f3/352706784", + "parent": 49, + "population": "f0/0", + "radius": "f0/378386618", + "rotationPeriod": "f2/3165390897", + "rotationPhase": "f0/0", + "seed": 3732948941, + "semiMajorAxis": "f0/10823317", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/3815187252", + "aspectRatio": "f1/0", + "atmosColor": [ + 255, + 255, + 255, + 255 + ], + "atmosDensity": "f0/431429464", + "atmosOxidizing": "f0/0", + "averageTemp": 81, + "axialTilt": "f0/0", + "eccentricity": "f0/5411658", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/25861655", + "life": "f0/0", + "mass": "f0/17437", + "metallicity": "f0/0", + "name": "Rhea", + "orbitalOffset": "f0/1248944029", + "orbitalPhase": "f4/1113653212", + "parent": 49, + "population": "f0/0", + "radius": "f0/515396075", + "rotationPeriod": "f4/2233382993", + "rotationPhase": "f0/0", + "seed": 2273638334, + "semiMajorAxis": "f0/15118284", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/1148469283", + "aspectRatio": "f1/0", + "atmosColor": [ + 191, + 152, + 102, + 255 + ], + "atmosDensity": "f14/4058744087", + "atmosOxidizing": "f0/2576980377", + "averageTemp": 94, + "axialTilt": "f0/0", + "children": [ + 54, + 55, + 56 + ], + "eccentricity": "f0/123695058", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/26127018", + "life": "f0/0", + "mass": "f0/96636764", + "metallicity": "f0/0", + "name": "Titan", + "orbitalOffset": "f4/1654751467", + "orbitalPhase": "f4/1260910071", + "parent": 49, + "population": "f0/0", + "radius": "f0/1717986918", + "rotationPeriod": "f15/4058744094", + "rotationPhase": "f0/0", + "seed": 0, + "semiMajorAxis": "f0/35218731", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f1/514064758", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f4294967294/3687464227", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Oasis City", + "orbitalOffset": "f1/1926822312", + "orbitalPhase": "f3/1193415960", + "parent": 53, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 1860480932, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/2196020786", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/2323800938", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Port Makenzie", + "orbitalOffset": "f2/480385198", + "orbitalPhase": "f3/4111993793", + "parent": 53, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 3618009704, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/2235236607", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/214748364", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Daniel's Haven", + "orbitalOffset": "f4/846508107", + "orbitalPhase": "f6/743510356", + "parent": 53, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f1/954437176", + "rotationPhase": "f0/0", + "seed": 3794892876, + "semiMajorAxis": "f0/103079", + "type": "STARPORT_ORBITAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f6/311316435", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 115, + "axialTilt": "f0/0", + "eccentricity": "f0/124554051", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/1159651629", + "life": "f0/0", + "mass": "f0/1288490", + "metallicity": "f0/3435973836", + "name": "Iapetus", + "orbitalOffset": "f5/874381017", + "orbitalPhase": "f0/3269749314", + "parent": 49, + "population": "f0/0", + "radius": "f0/496068722", + "rotationPeriod": "f79/1374389534", + "rotationPhase": "f0/0", + "seed": 3650944653, + "semiMajorAxis": "f0/102220221", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/3918636298", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 115, + "axialTilt": "f0/0", + "eccentricity": "f0/670014898", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f2/2787694645", + "life": "f0/0", + "mass": "f0/5970", + "metallicity": "f0/0", + "name": "Phoebe", + "orbitalOffset": "f3/2379193243", + "orbitalPhase": "f1/929867307", + "parent": 49, + "population": "f0/0", + "radius": "f0/73014444", + "rotationPeriod": "f0/1657857376", + "rotationPhase": "f0/0", + "seed": 1740171277, + "semiMajorAxis": "f0/373662154", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f5/2692424230", + "aspectRatio": "f1/0", + "atmosColor": [ + 255, + 255, + 255, + 3 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 76, + "axialTilt": "f1/3034018070", + "children": [ + 60, + 61, + 62, + 63 + ], + "eccentricity": "f0/190696547", + "hasRings": true, + "humanActivity": "f0/0", + "inclination": "f0/57870139", + "life": "f0/0", + "mass": "f14/2147483648", + "metallicity": "f0/0", + "name": "Uranus", + "orbitalOffset": "f2/1004643831", + "orbitalPhase": "f4/1185697091", + "parent": 0, + "population": "f0/0", + "radius": "f4/0", + "ringsBaseColor": [ + 130, + 122, + 97, + 204 + ], + "ringsMaxRadius": "f2/0", + "ringsMinRadius": "f1/3233251380", + "rotationPeriod": "f0/3006477107", + "rotationPhase": "f0/0", + "seed": 1365118445, + "semiMajorAxis": "f19/983547510", + "type": "PLANET_GAS_GIANT", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/1853570593", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 60, + "axialTilt": "f0/0", + "eccentricity": "f0/5153960", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/19489943", + "life": "f0/0", + "mass": "f0/970662", + "metallicity": "f0/0", + "name": "Ariel", + "orbitalOffset": "f4/2509972533", + "orbitalPhase": "f2/667675", + "parent": 59, + "population": "f0/0", + "radius": "f0/389983030", + "rotationPeriod": "f2/2233382993", + "rotationPhase": "f0/0", + "seed": 1238441829, + "semiMajorAxis": "f0/5484673", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f6/533447274", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 75, + "axialTilt": "f0/0", + "eccentricity": "f0/16750372", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/9595049", + "life": "f0/0", + "mass": "f0/858993", + "metallicity": "f0/0", + "name": "Umbriel", + "orbitalOffset": "f3/1102562897", + "orbitalPhase": "f0/1954076253", + "parent": 59, + "population": "f0/0", + "radius": "f0/395136991", + "rotationPeriod": "f4/618475290", + "rotationPhase": "f0/0", + "seed": 320162060, + "semiMajorAxis": "f0/7645041", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/3222080717", + "aspectRatio": "f1/0", + "atmosColor": [ + 255, + 255, + 255, + 255 + ], + "atmosDensity": "f0/431429464", + "atmosOxidizing": "f0/0", + "averageTemp": 70, + "axialTilt": "f0/0", + "eccentricity": "f0/4724464", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/25486848", + "life": "f0/0", + "mass": "f0/2537466", + "metallicity": "f0/0", + "name": "Titania", + "orbitalOffset": "f5/2492231836", + "orbitalPhase": "f2/3411216235", + "parent": 59, + "population": "f0/0", + "radius": "f0/530428461", + "rotationPeriod": "f8/3006477107", + "rotationPhase": "f0/0", + "seed": 3375230666, + "semiMajorAxis": "f0/12511239", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/121005768", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 75, + "axialTilt": "f0/0", + "eccentricity": "f0/6012954", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/4347756", + "life": "f0/0", + "mass": "f0/2167240", + "metallicity": "f0/0", + "name": "Oberon", + "orbitalOffset": "f5/3223101659", + "orbitalPhase": "f2/1419483193", + "parent": 59, + "population": "f0/0", + "radius": "f0/512819095", + "rotationPeriod": "f13/2147483648", + "rotationPhase": "f0/0", + "seed": 2123607039, + "semiMajorAxis": "f0/16750372", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/3132048710", + "aspectRatio": "f1/0", + "atmosColor": [ + 255, + 255, + 255, + 3 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 72, + "axialTilt": "f0/2122909538", + "children": [ + 65, + 66, + 68 + ], + "eccentricity": "f0/48103633", + "hasRings": true, + "humanActivity": "f0/0", + "inclination": "f0/132531614", + "life": "f0/0", + "mass": "f17/631360192", + "metallicity": "f0/0", + "name": "Neptune", + "orbitalOffset": "f6/211086076", + "orbitalPhase": "f6/691603959", + "parent": 0, + "population": "f0/0", + "radius": "f3/3435973836", + "ringsBaseColor": [ + 181, + 173, + 174, + 191 + ], + "ringsMaxRadius": "f2/1546188226", + "ringsMinRadius": "f2/837518622", + "rotationPeriod": "f0/3221225472", + "rotationPhase": "f0/0", + "seed": 1365118457, + "semiMajorAxis": "f30/446676598", + "type": "PLANET_GAS_GIANT", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/1072645309", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 51, + "axialTilt": "f0/0", + "eccentricity": "f0/2276332", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/39279731", + "life": "f0/0", + "mass": "f0/36206", + "metallicity": "f0/3006477107", + "name": "Proteus", + "orbitalOffset": "f4/3540830078", + "orbitalPhase": "f0/2530512846", + "parent": 64, + "population": "f0/0", + "radius": "f0/133143986", + "rotationPeriod": "f1/523986010", + "rotationPhase": "f0/0", + "seed": 1251043226, + "semiMajorAxis": "f0/3375844", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/3435973836", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/4190422647", + "aspectRatio": "f1/0", + "atmosColor": [ + 255, + 255, + 255, + 255 + ], + "atmosDensity": "f0/429496729", + "atmosOxidizing": "f0/214748364", + "averageTemp": 38, + "axialTilt": "f0/0", + "children": [ + 67 + ], + "eccentricity": "f0/68719", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f2/3170372187", + "life": "f0/0", + "mass": "f0/15418932", + "metallicity": "f0/0", + "name": "Triton", + "orbitalOffset": "f2/2495501786", + "orbitalPhase": "f5/4195201268", + "parent": 64, + "population": "f0/0", + "radius": "f0/911392060", + "rotationPeriod": "f5/3758096384", + "rotationPhase": "f0/0", + "seed": 1809800440, + "semiMajorAxis": "f0/101833674", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f1/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/1288490188" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f0/1344523725", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/0", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Poseidon Station", + "orbitalOffset": "f3/2271096915", + "orbitalPhase": "f1/2035982421", + "parent": 66, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f1/2454267026", + "rotationPhase": "f0/0", + "seed": 1733499399, + "semiMajorAxis": "f0/103079", + "type": "STARPORT_ORBITAL", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f2/2774453342", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 50, + "axialTilt": "f0/0", + "eccentricity": "f0/3221225472", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/2439990984", + "life": "f0/0", + "mass": "f0/19413", + "metallicity": "f0/0", + "name": "Nereid", + "orbitalOffset": "f4/826947653", + "orbitalPhase": "f1/404996741", + "parent": 64, + "population": "f0/0", + "radius": "f0/114589727", + "rotationPeriod": "f0/2058005162", + "rotationPhase": "f0/0", + "seed": 2821626605, + "semiMajorAxis": "f0/158269544", + "type": "PLANET_ASTEROID", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/3258678000", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 44, + "axialTilt": "f0/2218860251", + "children": [ + 70, + 71 + ], + "eccentricity": "f0/1069446856", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f0/890540488", + "life": "f0/0", + "mass": "f0/9019431", + "metallicity": "f0/0", + "name": "Pluto", + "orbitalOffset": "f3/3980898494", + "orbitalPhase": "f5/2138034445", + "parent": 0, + "population": "f0/0", + "radius": "f0/773094113", + "rotationPeriod": "f6/1610612736", + "rotationPhase": "f0/0", + "seed": 368872266, + "semiMajorAxis": "f39/1717986918", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f1/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f3/632636060", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 0, + "axialTilt": "f0/0", + "eccentricity": "f0/0", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f1/2001783632", + "life": "f0/0", + "mass": "f0/0", + "metallicity": "f0/0", + "name": "Pluto Research Base", + "orbitalOffset": "f1/2901319479", + "orbitalPhase": "f6/940265785", + "parent": 69, + "population": "f0/0", + "radius": "f0/0", + "rotationPeriod": "f0/0", + "rotationPhase": "f0/0", + "seed": 2016552058, + "semiMajorAxis": "f0/0", + "type": "STARPORT_SURFACE", + "volatileIces": "f0/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + }, + { + "agricultural": "f0/0", + "argOfPeriapsis": "f4/2841149246", + "aspectRatio": "f1/0", + "atmosColor": [ + 0, + 0, + 0, + 0 + ], + "atmosDensity": "f0/0", + "atmosOxidizing": "f0/0", + "averageTemp": 44, + "axialTilt": "f0/500742786", + "eccentricity": "f0/94489280", + "hasRings": false, + "humanActivity": "f0/0", + "inclination": "f2/367943217", + "life": "f0/0", + "mass": "f0/858993", + "metallicity": "f0/0", + "name": "Charon", + "orbitalOffset": "f6/632757679", + "orbitalPhase": "f4/1535590412", + "parent": 69, + "population": "f0/0", + "radius": "f0/386547056", + "rotationPeriod": "f0/2576980377", + "rotationPhase": "f0/0", + "seed": 3353189046, + "semiMajorAxis": "f0/503370", + "type": "PLANET_TERRESTRIAL", + "volatileIces": "f1/0", + "volatileLiquid": "f0/0", + "volcanicity": "f0/0" + } + ], + "comment": "Sol needs no introduction.", + "explored": true, + "faction": "Solar Federation", + "govType": "EARTHDEMOC", + "lawlessness": "f0/42949672", + "longDesc": "Sol is a fine joint", + "name": "Sol", + "pos": [ + 0.00800000037997961, + 0.00800000037997961, + 0.00800000037997961 + ], + "sector": [ + 0, + 0, + 0 + ], + "seed": 0, + "shortDesc": "The historical birthplace of humankind", + "stars": [ + "STAR_G" + ] +} \ No newline at end of file diff --git a/data/systems/partial/02_local_stars.json b/data/systems/partial/02_local_stars.json new file mode 100644 index 00000000000..42876ea24bd --- /dev/null +++ b/data/systems/partial/02_local_stars.json @@ -0,0 +1,3 @@ +[ + {"name":"GJ 1075","stars":["STAR_K"],"sector":[2,-1,-4],"pos":[3.608,3.272,0.272]} +] diff --git a/src/CityOnPlanet.cpp b/src/CityOnPlanet.cpp index 0f2d5446911..fc173179c76 100644 --- a/src/CityOnPlanet.cpp +++ b/src/CityOnPlanet.cpp @@ -273,6 +273,7 @@ void CityOnPlanet::Uninit() void CityOnPlanet::SetCityModelPatterns(const SystemPath &path) { PROFILE_SCOPED() + // FIXME: should use system seed + body seed instead of path index Uint32 _init[5] = { path.systemIndex, Uint32(path.sectorX), Uint32(path.sectorY), Uint32(path.sectorZ), UNIVERSE_SEED }; Random rand(_init, 5); diff --git a/src/FileSystem.h b/src/FileSystem.h index 5ff6fb50731..989206c25dd 100644 --- a/src/FileSystem.h +++ b/src/FileSystem.h @@ -269,6 +269,11 @@ namespace FileSystem { return FileEnumerator(*this, path, enumeratorFlags); } + virtual FileEnumerator Recurse(const std::string &path, int enumeratorFlags = 0) + { + return FileEnumerator(*this, path, FileEnumerator::Recurse | enumeratorFlags); + } + bool IsTrusted() const { return m_trusted; } protected: diff --git a/src/JsonUtils.cpp b/src/JsonUtils.cpp index a0f195f7abb..68e6e0dff5b 100644 --- a/src/JsonUtils.cpp +++ b/src/JsonUtils.cpp @@ -70,8 +70,7 @@ namespace JsonUtils { if (out.is_null() || !with_merge) return out; for (auto info : FileSystem::gameDataFiles.LookupAll(filename + ".patch")) { - Json patch = LoadJson(info.Read()); - if (!patch.is_null()) out.merge_patch(patch); + ApplyJsonPatch(out, LoadJson(info.Read()), info.GetPath()); } return out; @@ -106,6 +105,25 @@ namespace JsonUtils { return nullptr; } } + + bool ApplyJsonPatch(Json &inObject, const Json &patchObject, const std::string &filename) + { + if (!inObject.is_object() || !patchObject.is_object()) + return false; + + for (auto &field : patchObject.items()) { + if (starts_with(field.key(), "$")) { + // JSON pointer patch object + Json::json_pointer ptr(field.key().substr(1)); + inObject[ptr].merge_patch(field.value()); + } else { + // "Regular" JSON merge-patch object + inObject[field.key()].merge_patch(field.value()); + } + } + + return true; + } } // namespace JsonUtils #define USE_STRING_VERSIONS @@ -538,22 +556,22 @@ void from_json(const Json &obj, fixed &f) else { std::string str = obj; // must have at least f1/1, though can be f1234567/135758548 etc. - if (str.size() < 4 || obj[0] != 'f') + if (str.size() < 4 || str[0] != 'f') throw Json::type_error::create(320, "cannot pickle string to fixed point number"); char *next_str = const_cast(str.c_str()) + 1; - int64_t numerator = std::strtol(next_str, &next_str, 10); + int64_t integer = std::strtol(next_str, &next_str, 10); // handle cases: f/34, f1356, f14+4 - if (numerator == 0 || next_str == nullptr || size_t(next_str - str.c_str()) >= str.size() || *next_str++ != '/') + if (next_str == nullptr || size_t(next_str - str.c_str()) >= str.size() || *next_str++ != '/') throw Json::type_error::create(320, "cannot pickle string to fixed point number"); - int64_t denominator = std::strtol(next_str, &next_str, 10); + int64_t fractional = std::strtol(next_str, &next_str, 10); // handle cases f1345/7684gfrty; fixed numbers should not have any garbage data involved if (next_str != str.c_str() + str.size()) throw Json::type_error::create(320, "cannot pickle string to fixed point number"); - f = fixed(numerator, denominator); + f = fixed(integer << f.FRAC | fractional); } } diff --git a/src/JsonUtils.h b/src/JsonUtils.h index 7b350f67264..ae54f82f498 100644 --- a/src/JsonUtils.h +++ b/src/JsonUtils.h @@ -33,6 +33,8 @@ namespace JsonUtils { Json LoadJsonDataFile(const std::string &filename, bool with_merge = true); // Loads an optionally-gzipped, optionally-CBOR encoded JSON file from the specified source. Json LoadJsonSaveFile(const std::string &filename, FileSystem::FileSource &source); + // Patches a Json object with an extended Merge-Patch object + bool ApplyJsonPatch(Json &inObject, const Json &patch, const std::string &filename); } // namespace JsonUtils // To-JSON functions. These are called explicitly, and are passed a reference to the object to fill. diff --git a/src/Planet.cpp b/src/Planet.cpp index 4d4886dcc5c..9d190d33b58 100644 --- a/src/Planet.cpp +++ b/src/Planet.cpp @@ -41,43 +41,8 @@ Planet::Planet(const Json &jsonObj, Space *space) : void Planet::InitParams(const SystemBody *sbody) { - double specificHeatCp; - double gasMolarMass; - if (sbody->GetSuperType() == SystemBody::SUPERTYPE_GAS_GIANT) { - specificHeatCp = 12950.0; // constant pressure specific heat, for a combination of hydrogen and helium - gasMolarMass = 0.0023139903; - } else { - specificHeatCp = 1000.5; // constant pressure specific heat, for the combination of gasses that make up air - // XXX using earth's molar mass of air... - gasMolarMass = 0.02897; - } - const double GAS_CONSTANT = 8.3144621; - const double PA_2_ATMOS = 1.0 / 101325.0; - - // surface gravity = G*M/planet radius^2 - m_surfaceGravity_g = G * sbody->GetMass() / (sbody->GetRadius() * sbody->GetRadius()); - const double lapseRate_L = m_surfaceGravity_g / specificHeatCp; // deg/m - const double surfaceTemperature_T0 = sbody->GetAverageTemp(); //K - - double surfaceDensity, h; - Color c; - sbody->GetAtmosphereFlavor(&c, &surfaceDensity); // kg / m^3 - surfaceDensity /= gasMolarMass; // convert to moles/m^3 - - //P = density*R*T=(n/V)*R*T - const double surfaceP_p0 = PA_2_ATMOS * ((surfaceDensity)*GAS_CONSTANT * surfaceTemperature_T0); // in atmospheres - if (surfaceP_p0 < 0.002) - h = 0; - else { - //*outPressure = p0*(1-l*h/T0)^(g*M/(R*L); - // want height for pressure 0.001 atm: - // h = (1 - exp(RL/gM * log(P/p0))) * T0 / l - double RLdivgM = (GAS_CONSTANT * lapseRate_L) / (m_surfaceGravity_g * gasMolarMass); - h = (1.0 - exp(RLdivgM * log(0.001 / surfaceP_p0))) * surfaceTemperature_T0 / lapseRate_L; - // double h2 = (1.0 - pow(0.001/surfaceP_p0, RLdivgM)) * surfaceTemperature_T0 / lapseRate_L; - // double P = surfaceP_p0*pow((1.0-lapseRate_L*h/surfaceTemperature_T0),1/RLdivgM); - } - m_atmosphereRadius = h + sbody->GetRadius(); + m_surfaceGravity_g = sbody->CalcSurfaceGravity(); + m_atmosphereRadius = sbody->GetAtmRadius() + sbody->GetRadius(); SetPhysRadius(std::max(m_atmosphereRadius, GetMaxFeatureRadius() + 1000)); // NB: Below abandoned due to docking problems with low altitude orbiting space stations @@ -110,59 +75,25 @@ void Planet::GetAtmosphericState(double dist, double *outPressure, double *outDe } #endif - // This model has no atmosphere beyond the adiabetic limit - // Note: some code duplicated in InitParams(). Check if changing. + // This model has no atmosphere beyond the adiabatic limit if (dist >= m_atmosphereRadius) { *outDensity = 0.0; *outPressure = 0.0; return; } - double surfaceDensity; - double specificHeatCp; - double gasMolarMass; - const SystemBody *sbody = this->GetSystemBody(); - if (sbody->GetSuperType() == SystemBody::SUPERTYPE_GAS_GIANT) { - specificHeatCp = 12950.0; // constant pressure specific heat, for a combination of hydrogen and helium - gasMolarMass = 0.0023139903; - } else { - specificHeatCp = 1000.5; // constant pressure specific heat, for the combination of gasses that make up air - // XXX using earth's molar mass of air... - gasMolarMass = 0.02897; - } - const double GAS_CONSTANT = 8.3144621; - const double PA_2_ATMOS = 1.0 / 101325.0; - - // lapse rate http://en.wikipedia.org/wiki/Adiabatic_lapse_rate#Dry_adiabatic_lapse_rate - // the wet adiabatic rate can be used when cloud layers are incorporated - // fairly accurate in the troposphere - const double lapseRate_L = m_surfaceGravity_g / specificHeatCp; // deg/m - - const double height_h = (dist - sbody->GetRadius()); // height in m - const double surfaceTemperature_T0 = sbody->GetAverageTemp(); //K - - Color c; - sbody->GetAtmosphereFlavor(&c, &surfaceDensity); // kg / m^3 - // convert to moles/m^3 - surfaceDensity /= gasMolarMass; - - //P = density*R*T=(n/V)*R*T - const double surfaceP_p0 = PA_2_ATMOS * ((surfaceDensity)*GAS_CONSTANT * surfaceTemperature_T0); // in atmospheres + const SystemBody *sbody = GetSystemBody(); + const double height_h = (dist - sbody->GetRadius()); // height in m // height below zero should not occur if (height_h < 0.0) { - *outPressure = surfaceP_p0; - *outDensity = surfaceDensity * gasMolarMass; + *outPressure = sbody->GetAtmSurfacePressure(); + *outDensity = sbody->GetAtmSurfaceDensity(); return; } - //*outPressure = p0*(1-l*h/T0)^(g*M/(R*L); - *outPressure = surfaceP_p0 * pow((1 - lapseRate_L * height_h / surfaceTemperature_T0), (m_surfaceGravity_g * gasMolarMass / (GAS_CONSTANT * lapseRate_L))); // in ATM since p0 was in ATM - // ^^g used is abs(g) - // temperature at height - double temp = surfaceTemperature_T0 - lapseRate_L * height_h; - - *outDensity = (*outPressure / (PA_2_ATMOS * GAS_CONSTANT * temp)) * gasMolarMass; + *outPressure = sbody->GetAtmPressure(height_h); + *outDensity = sbody->GetAtmDensity(height_h, *outPressure); } void Planet::GenerateRings(Graphics::Renderer *renderer) diff --git a/src/Random.h b/src/Random.h index 163fda19819..ea94dc7d82a 100644 --- a/src/Random.h +++ b/src/Random.h @@ -12,6 +12,8 @@ #include #include +#include +#include #include "RefCounted.h" #include "fixed.h" @@ -55,6 +57,12 @@ class Random : public RefCounted { seed(reinterpret_cast(seeds), length * 2); } + // Construct a new generator given an array of 32-bit seeds. + Random(std::initializer_list seeds) + { + seed(seeds); + } + // // Seed functions // @@ -73,6 +81,11 @@ class Random : public RefCounted { seed(reinterpret_cast(seeds), length * 2); } + // Seed using an initializer_list of 32-bit integers + void seed(std::initializer_list list) { + seed(&*list.begin(), list.size()); + } + // Seed using a single 32-bit integer void seed(const Uint32 value) { @@ -233,6 +246,23 @@ class Random : public RefCounted { return o; } + // Returns an approximation of a normal distribution in the bounded interval + // (-1, 1) + inline fixed NormFixed() + { + // Because addition is fully commutative, the compiler can order these + // Fixed() calls in any order it wants without changing the result + fixed o = Fixed() + Fixed() + Fixed(); + return o * fixed(10, 15) - fixed(1, 1); + } + + // interval (mean - maxdev, mean + maxdev) + // this is an approximation of a gaussian distribution with cross-platform determinism + inline fixed NormFixed(fixed mean, fixed maxdev) + { + return mean + maxdev * NormFixed(); + } + const pcg32 &GetPCG() const { return mPCG; } private: diff --git a/src/core/Log.h b/src/core/Log.h index c025ea9587c..55336e3f483 100644 --- a/src/core/Log.h +++ b/src/core/Log.h @@ -29,6 +29,7 @@ namespace Log { void LogLevel(Severity sv, const char *message); bool SetLogFile(std::string filename); + FILE *GetLogFileHandle() { return file; } // Return the severity cutoff at which log messages will be printed to stderr. Severity GetSeverity() { return m_maxSeverity; } diff --git a/src/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index 93d183cb204..939f0ae8f0b 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -5,18 +5,26 @@ #include "Galaxy.h" #include "SystemPath.h" +#include "core/FNV1a.h" #include "core/LZ4Format.h" +#include "core/Log.h" +#include "core/StringUtils.h" #include "../gameconsts.h" +#include "EnumStrings.h" +#include "JsonUtils.h" #include "Factions.h" #include "FileSystem.h" #include "Polit.h" -#include "core/Log.h" +#include "galaxy/StarSystemGenerator.h" +#include "galaxy/SystemBody.h" #include "lua/LuaConstants.h" #include "lua/LuaFixed.h" #include "lua/LuaUtils.h" #include "lua/LuaVector.h" +#include "profiler/Profiler.h" + #include const CustomSystemsDatabase::SystemList CustomSystemsDatabase::s_emptySystemList; // see: Null Object pattern @@ -57,8 +65,8 @@ static int l_csb_new(lua_State *L) *csbptr = new CustomSystemBody; luaL_setmetatable(L, LuaCustomSystemBody_TypeName); - (*csbptr)->name = name; - (*csbptr)->type = static_cast(type); + (*csbptr)->bodyData.m_name = name; + (*csbptr)->bodyData.m_type = static_cast(type); return 1; } @@ -80,18 +88,19 @@ static double *getDoubleOrFixed(lua_State *L, int which) } // Used when the value MUST not be NEGATIVE but can be Zero, for life, etc -#define CSB_FIELD_SETTER_FIXED(luaname, fieldname) \ - static int l_csb_##luaname(lua_State *L) \ - { \ - CustomSystemBody *csb = l_csb_check(L, 1); \ - double *value = getDoubleOrFixed(L, 2); \ - if (value == nullptr) \ - return luaL_error(L, "Bad datatype. Expected fixed or float, got %s", luaL_typename(L, 2)); \ - if (*value < 0.0) \ - Output("Error: Custom system definition: Value cannot be negative (%lf) for %s : %s\n", *value, csb->name.c_str(), #luaname); \ - csb->fieldname = fixed::FromDouble(*value); \ - lua_settop(L, 1); \ - return 1; \ +#define CSB_FIELD_SETTER_FIXED(luaname, fieldname) \ + static int l_csb_##luaname(lua_State *L) \ + { \ + CustomSystemBody *csb = l_csb_check(L, 1); \ + double *value = getDoubleOrFixed(L, 2); \ + if (value == nullptr) \ + return luaL_error(L, "Bad datatype. Expected fixed or float, got %s", luaL_typename(L, 2)); \ + if (*value < 0.0) \ + Output("Error: Custom system definition: Value cannot be negative (%lf) for %s : %s\n", \ + *value, csb->bodyData.m_name.c_str(), #luaname); \ + csb->fieldname = fixed::FromDouble(*value); \ + lua_settop(L, 1); \ + return 1; \ } #define CSB_FIELD_SETTER_REAL(luaname, fieldname) \ @@ -124,23 +133,22 @@ static double *getDoubleOrFixed(lua_State *L, int which) return 1; \ } -CSB_FIELD_SETTER_FIXED(radius, radius) -CSB_FIELD_SETTER_FIXED(mass, mass) -CSB_FIELD_SETTER_INT(temp, averageTemp) -CSB_FIELD_SETTER_FIXED(semi_major_axis, semiMajorAxis) -CSB_FIELD_SETTER_FIXED(eccentricity, eccentricity) -CSB_FIELD_SETTER_REAL(latitude, latitude) -CSB_FIELD_SETTER_REAL(longitude, longitude) -CSB_FIELD_SETTER_FIXED(rotation_period, rotationPeriod) -CSB_FIELD_SETTER_FIXED(axial_tilt, axialTilt) -CSB_FIELD_SETTER_FIXED(metallicity, metallicity) -CSB_FIELD_SETTER_FIXED(volcanicity, volcanicity) -CSB_FIELD_SETTER_FIXED(atmos_density, volatileGas) -CSB_FIELD_SETTER_FIXED(atmos_oxidizing, atmosOxidizing) -CSB_FIELD_SETTER_FIXED(ocean_cover, volatileLiquid) -CSB_FIELD_SETTER_FIXED(ice_cover, volatileIces) -CSB_FIELD_SETTER_FIXED(life, life) -CSB_FIELD_SETTER_STRING(space_station_type, spaceStationType) +CSB_FIELD_SETTER_FIXED(radius, bodyData.m_radius) +CSB_FIELD_SETTER_FIXED(mass, bodyData.m_mass) +CSB_FIELD_SETTER_INT(temp, bodyData.m_averageTemp) +CSB_FIELD_SETTER_FIXED(semi_major_axis, bodyData.m_semiMajorAxis) +CSB_FIELD_SETTER_FIXED(eccentricity, bodyData.m_eccentricity) +CSB_FIELD_SETTER_FIXED(inclination, bodyData.m_inclination) +CSB_FIELD_SETTER_FIXED(rotation_period, bodyData.m_rotationPeriod) +CSB_FIELD_SETTER_FIXED(axial_tilt, bodyData.m_axialTilt) +CSB_FIELD_SETTER_FIXED(metallicity, bodyData.m_metallicity) +CSB_FIELD_SETTER_FIXED(volcanicity, bodyData.m_volcanicity) +CSB_FIELD_SETTER_FIXED(atmos_density, bodyData.m_volatileGas) +CSB_FIELD_SETTER_FIXED(atmos_oxidizing, bodyData.m_atmosOxidizing) +CSB_FIELD_SETTER_FIXED(ocean_cover, bodyData.m_volatileLiquid) +CSB_FIELD_SETTER_FIXED(ice_cover, bodyData.m_volatileIces) +CSB_FIELD_SETTER_FIXED(life, bodyData.m_life) +CSB_FIELD_SETTER_STRING(space_station_type, bodyData.m_spaceStationType) #undef CSB_FIELD_SETTER_FIXED #undef CSB_FIELD_SETTER_FLOAT @@ -151,7 +159,7 @@ static int l_csb_radius_km(lua_State *L) CustomSystemBody *csb = l_csb_check(L, 1); double value = luaL_checknumber(L, 2); // earth mean radiusMean radius = 6371.0 km (source: wikipedia) - csb->radius = (value / 6371.0); + csb->bodyData.m_radius = (value / 6371.0); lua_settop(L, 1); return 1; } @@ -159,7 +167,7 @@ static int l_csb_radius_km(lua_State *L) static int l_csb_seed(lua_State *L) { CustomSystemBody *csb = l_csb_check(L, 1); - csb->seed = luaL_checkunsigned(L, 2); + csb->bodyData.m_seed = luaL_checkunsigned(L, 2); csb->want_rand_seed = false; lua_settop(L, 1); return 1; @@ -171,7 +179,7 @@ static int l_csb_orbital_offset(lua_State *L) double *value = getDoubleOrFixed(L, 2); if (value == nullptr) return luaL_error(L, "Bad datatype. Expected fixed or float, got %s", luaL_typename(L, 2)); - csb->orbitalOffset = fixed::FromDouble(*value); + csb->bodyData.m_orbitalOffset = fixed::FromDouble(*value); csb->want_rand_offset = false; lua_settop(L, 1); return 1; @@ -185,7 +193,8 @@ static int l_csb_orbital_phase_at_start(lua_State *L) return luaL_error(L, "Bad datatype. Expected fixed or float, got %s", luaL_typename(L, 2)); if ((*value < 0.0) || (*value > double(2.0 * M_PI))) return luaL_error(L, "Error: Custom system definition: Orbital phase at game start must be between 0 and 2 PI radians (including 0 but not 2 PI)."); - csb->orbitalPhaseAtStart = fixed::FromDouble(*value); + csb->bodyData.m_orbitalPhaseAtStart = fixed::FromDouble(*value); + csb->want_rand_phase = false; lua_settop(L, 1); return 1; } @@ -198,7 +207,7 @@ static int l_csb_rotational_phase_at_start(lua_State *L) return luaL_error(L, "Bad datatype. Expected fixed or float, got %s", luaL_typename(L, 2)); if ((*value < 0.0) || (*value > double(2.0 * M_PI))) return luaL_error(L, "Error: Custom system definition: Rotational phase at start must be between 0 and 2 PI radians (including 0 but not 2 PI).\n The rotational phase is the phase of the body's spin about it's axis at game start."); - csb->rotationalPhaseAtStart = fixed::FromDouble(*value); + csb->bodyData.m_rotationalPhaseAtStart = fixed::FromDouble(*value); lua_settop(L, 1); return 1; } @@ -212,8 +221,8 @@ static int l_csb_height_map(lua_State *L) return luaL_error(L, "invalid terrain fractal type"); } - csb->heightMapFilename = FileSystem::JoinPathBelow("heightmaps", fname); - csb->heightMapFractal = fractal; + csb->bodyData.m_heightMapFilename = FileSystem::JoinPathBelow("heightmaps", fname); + csb->bodyData.m_heightMapFractal = fractal; lua_settop(L, 1); return 1; } @@ -232,11 +241,11 @@ static int l_csb_rings(lua_State *L) double *value = getDoubleOrFixed(L, 2); if (value == nullptr) return luaL_error(L, "Bad datatype. Expected fixed or float, got %s", luaL_typename(L, 2)); - csb->ringInnerRadius = fixed::FromDouble(*value); + csb->bodyData.m_rings.minRadius = fixed::FromDouble(*value); value = getDoubleOrFixed(L, 3); if (value == nullptr) return luaL_error(L, "Bad datatype. Expected fixed or float, got %s", luaL_typename(L, 3)); - csb->ringOuterRadius = fixed::FromDouble(*value); + csb->bodyData.m_rings.maxRadius = fixed::FromDouble(*value); luaL_checktype(L, 4, LUA_TTABLE); Color4f col; lua_rawgeti(L, 4, 1); @@ -247,7 +256,7 @@ static int l_csb_rings(lua_State *L) col.b = luaL_checknumber(L, -1); lua_rawgeti(L, 4, 4); col.a = luaL_optnumber(L, -1, 0.85); // default alpha value - csb->ringColor = col; + csb->bodyData.m_rings.baseColor = col; } lua_settop(L, 1); return 1; @@ -268,12 +277,12 @@ static int l_csb_aspect_ratio(lua_State *L) double *value = getDoubleOrFixed(L, 2); if (value == nullptr) return luaL_error(L, "Bad datatype. Expected fixed or float, got %s", luaL_typename(L, 2)); - csb->aspectRatio = fixed::FromDouble(*value); - if (csb->aspectRatio < fixed(1, 1)) { + csb->bodyData.m_aspectRatio = fixed::FromDouble(*value); + if (csb->bodyData.m_aspectRatio < fixed(1, 1)) { return luaL_error( L, "Error: Custom system definition: Equatorial to Polar radius ratio cannot be less than 1."); } - if (csb->aspectRatio > fixed(10000, 1)) { + if (csb->bodyData.m_aspectRatio > fixed(10000, 1)) { return luaL_error( L, "Error: Custom system definition: Equatorial to Polar radius ratio cannot be greater than 10000.0."); } @@ -293,10 +302,10 @@ static luaL_Reg LuaCustomSystemBody_meta[] = { { "eccentricity", &l_csb_eccentricity }, { "orbital_offset", &l_csb_orbital_offset }, { "orbital_phase_at_start", &l_csb_orbital_phase_at_start }, - { "latitude", &l_csb_latitude }, + { "latitude", &l_csb_inclination }, // latitude is for surface bodies, inclination is for orbiting bodies (but they're the same field) - { "inclination", &l_csb_latitude }, - { "longitude", &l_csb_longitude }, + { "inclination", &l_csb_inclination }, + { "longitude", &l_csb_orbital_offset }, { "rotation_period", &l_csb_rotation_period }, { "rotational_phase_at_start", &l_csb_rotational_phase_at_start }, // 0 to 2 pi { "axial_tilt", &l_csb_axial_tilt }, @@ -375,7 +384,9 @@ static int l_csys_new(lua_State *L) luaL_setmetatable(L, LuaCustomSystem_TypeName); (*csptr)->name = name; + (*csptr)->nameHash = hash_64_fnv1a((*csptr)->name.data(), (*csptr)->name.size()); (*csptr)->numStars = numStars; + assert(numStars <= 4); for (unsigned i = 0; i < numStars; ++i) (*csptr)->primaryType[i] = static_cast(starTypes[i]); @@ -386,6 +397,7 @@ static int l_csys_seed(lua_State *L) { CustomSystem *cs = l_csys_check(L, 1); cs->seed = luaL_checkunsigned(L, 2); + cs->want_rand_seed = cs->seed == 0; lua_settop(L, 1); return 1; } @@ -475,7 +487,7 @@ static int l_csys_lawlessness(lua_State *L) return 1; } -static void _add_children_to_sbody(lua_State *L, CustomSystemBody *sbody) +static void _add_children_to_sbody(lua_State *L, CustomSystem *cs, CustomSystemBody *sbody) { lua_checkstack(L, 5); // grow the stack if necessary LUA_DEBUG_START(L); @@ -494,12 +506,14 @@ static void _add_children_to_sbody(lua_State *L, CustomSystemBody *sbody) lua_pop(L, 1); LUA_DEBUG_CHECK(L, 0); + cs->bodies.push_back(kid); + // then there are any number of sub-tables containing direct children while (true) { lua_rawgeti(L, -1, i + 1); LUA_DEBUG_CHECK(L, 1); if (!lua_istable(L, -1)) break; - _add_children_to_sbody(L, kid); + _add_children_to_sbody(L, cs, kid); lua_pop(L, 1); LUA_DEBUG_CHECK(L, 0); ++i; @@ -520,7 +534,7 @@ static unsigned count_stars(CustomSystemBody *csb) if (!csb) return 0; unsigned count = 0; - if (csb->type >= SystemBody::TYPE_STAR_MIN && csb->type <= SystemBody::TYPE_STAR_MAX) + if (csb->bodyData.m_type >= SystemBody::TYPE_STAR_MIN && csb->bodyData.m_type <= SystemBody::TYPE_STAR_MAX) ++count; for (CustomSystemBody *child : csb->children) count += count_stars(child); @@ -531,7 +545,7 @@ static int l_csys_bodies(lua_State *L) { CustomSystem *cs = l_csys_check(L, 1); CustomSystemBody **primary_ptr = l_csb_check_ptr(L, 2); - int primary_type = (*primary_ptr)->type; + int primary_type = (*primary_ptr)->bodyData.m_type; luaL_checktype(L, 3, LUA_TTABLE); if ((primary_type < SystemBody::TYPE_STAR_MIN || primary_type > SystemBody::TYPE_STAR_MAX) && primary_type != SystemBody::TYPE_GRAVPOINT) @@ -539,8 +553,10 @@ static int l_csys_bodies(lua_State *L) if (primary_type != cs->primaryType[0] && primary_type != SystemBody::TYPE_GRAVPOINT) return luaL_error(L, "first body type does not match the system's primary star type"); + cs->bodies.push_back(*primary_ptr); + lua_pushvalue(L, 3); - _add_children_to_sbody(L, *primary_ptr); + _add_children_to_sbody(L, cs, *primary_ptr); lua_pop(L, 1); cs->sBody = *primary_ptr; @@ -571,10 +587,12 @@ static int l_csys_add_to_sector(lua_State *L) (*csptr)->sectorX = x; (*csptr)->sectorY = y; (*csptr)->sectorZ = z; - (*csptr)->pos = vector3f(*v); + (*csptr)->pos = vector3f(*v) * Sector::SIZE; // NOTE: lua uses 0..1 interval inside a sector cell //Output("l_csys_add_to_sector: %s added to %d, %d, %d\n", (*csptr)->name.c_str(), x, y, z); + s_activeCustomSystemsDatabase->RunLuaSystemSanityChecks(*csptr); + s_activeCustomSystemsDatabase->AddCustomSystem(SystemPath(x, y, z), *csptr); *csptr = 0; return 0; @@ -605,6 +623,82 @@ static luaL_Reg LuaCustomSystem_meta[] = { { 0, 0 } }; +void CustomSystem::LoadFromJson(const Json &systemdef) +{ + name = systemdef["name"].get(); + nameHash = hash_64_fnv1a(name.data(), name.size()); + + if (systemdef.count("otherNames") && systemdef["otherNames"].is_array()) { + for (const Json &name : systemdef["otherNames"]) + other_names.push_back(name.get()); + } + + numStars = systemdef["stars"].size(); + + size_t starIdx = 0; + for (const Json &type : systemdef["stars"]) { + if (starIdx >= COUNTOF(primaryType)) + break; + primaryType[starIdx++] = SystemBody::BodyType(EnumStrings::GetValue("BodyType", type.get().c_str())); + } + + const Json §or = systemdef["sector"]; + sectorX = sector[0].get(); + sectorZ = sector[1].get(); + sectorY = sector[2].get(); + + const Json &position = systemdef["pos"]; + pos.x = sector[0].get(); + pos.y = sector[1].get(); + pos.z = sector[2].get(); + + seed = systemdef.value("seed", 0); + explored = systemdef.value("explored", true); + lawlessness = systemdef.value("lawlessness", 0); + + want_rand_seed = !systemdef.count("seed"); + want_rand_explored = !systemdef.count("explored"); + want_rand_lawlessness = !systemdef.count("lawlessness"); + + govType = Polit::GovType(EnumStrings::GetValue("PolitGovType", systemdef.value("govType", "NONE").c_str())); + + shortDesc = systemdef.value("shortDesc", ""); + longDesc = systemdef.value("longDesc", ""); +} + +// NOTE: not currently used, custom systems are initially generated using StarSystem::DumpToJson instead. +void CustomSystem::SaveToJson(Json &obj) +{ + obj["name"] = name; + + if (!other_names.empty()) { + Json &out_names = obj["otherNames"] = Json::array(); + for (auto &name : other_names) + out_names.push_back(name); + } + + Json &out_types = obj["stars"] = Json::array(); + for (size_t idx = 0; idx < numStars; idx++) + out_types.push_back(EnumStrings::GetString("BodyType", primaryType[idx])); + + obj["numStars"] = numStars; + + obj["sector"] = Json::array({ sectorX, sectorY, sectorZ }); + obj["pos"] = Json::array({ pos.x, pos.y, pos.z }); + obj["seed"] = seed; + + if (!want_rand_explored) + obj["explored"] = explored; + if(!want_rand_lawlessness) + obj["lawlessness"] = lawlessness; + + obj["govType"] = EnumStrings::GetString("PolitGovType", govType); + + obj["shortDesc"] = shortDesc; + obj["longDesc"] = longDesc; + +} + // ------ CustomSystem initialisation ------ static void register_class(lua_State *L, const char *tname, luaL_Reg *meta) @@ -629,10 +723,8 @@ static void RegisterCustomSystemsAPI(lua_State *L) register_class(L, LuaCustomSystemBody_TypeName, LuaCustomSystemBody_meta); } -void CustomSystemsDatabase::Load() +lua_State *CustomSystemsDatabase::CreateLoaderState() { - assert(!s_activeCustomSystemsDatabase); - s_activeCustomSystemsDatabase = this; lua_State *L = luaL_newstate(); LUA_DEBUG_START(L); @@ -660,7 +752,59 @@ void CustomSystemsDatabase::Load() RegisterCustomSystemsAPI(L); - LUA_DEBUG_CHECK(L, 0); + LUA_DEBUG_END(L, 0); + return L; +} + +void CustomSystemsDatabase::Load() +{ + PROFILE_SCOPED() + + LoadAllLuaSystems(); + + // Load Json array files containing random-fill system definitions + std::string partialPath = FileSystem::JoinPathBelow(m_customSysDirectory, "partial"); + for (auto &file : FileSystem::gameDataFiles.Recurse(partialPath)) { + if (!ends_with_ci(file.GetPath(), ".json")) + continue; + + PROFILE_SCOPED_DESC("Load Partial System List") + const Json fileData = JsonUtils::LoadJsonDataFile(file.GetPath()); + for (const Json &sysdef : fileData) { + if (!sysdef.is_object()) + continue; + + LoadSystemFromJSON(file.GetPath(), sysdef); + } + } + + // Load top-level custom system defines + for (auto &file : FileSystem::gameDataFiles.Enumerate(m_customSysDirectory, 0)) { + if (!ends_with_ci(file.GetPath(), ".json")) + continue; + + LoadSystemFromJSON(file.GetPath(), JsonUtils::LoadJsonDataFile(file.GetPath())); + } + + // Load all complete custom system definitions + std::string customPath = FileSystem::JoinPathBelow(m_customSysDirectory, "custom"); + for (auto &file : FileSystem::gameDataFiles.Recurse(customPath)) { + if (!ends_with_ci(file.GetPath(), ".json")) + continue; + + LoadSystemFromJSON(file.GetPath(), JsonUtils::LoadJsonDataFile(file.GetPath())); + } +} + +void CustomSystemsDatabase::LoadAllLuaSystems() +{ + PROFILE_SCOPED() + + assert(!s_activeCustomSystemsDatabase); + s_activeCustomSystemsDatabase = this; + lua_State *L = CreateLoaderState(); + LUA_DEBUG_START(L); + pi_lua_dofile_recursive(L, m_customSysDirectory); LUA_DEBUG_END(L, 0); @@ -668,6 +812,129 @@ void CustomSystemsDatabase::Load() s_activeCustomSystemsDatabase = nullptr; } +const CustomSystem *CustomSystemsDatabase::LoadSystem(std::string_view filepath) +{ + assert(!s_activeCustomSystemsDatabase); + s_activeCustomSystemsDatabase = this; + + m_lastAddedSystem.second = SIZE_MAX; + + lua_State *L = CreateLoaderState(); + LUA_DEBUG_START(L); + + pi_lua_dofile(L, std::string(filepath)); + + LUA_DEBUG_END(L, 0); + lua_close(L); + s_activeCustomSystemsDatabase = nullptr; + + if (m_lastAddedSystem.second == SIZE_MAX) + return nullptr; + + return m_sectorMap[m_lastAddedSystem.first][m_lastAddedSystem.second]; +} + +CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view filename, const Json &systemdef, bool mergeWithGalaxy) +{ + PROFILE_SCOPED() + + CustomSystem *sys = new CustomSystem(); + + try { + + sys->LoadFromJson(systemdef); + + // Validate number of stars + constexpr int MAX_STARS = COUNTOF(sys->primaryType); + if (sys->numStars > MAX_STARS) { + Log::Warning("Custom system {} ({}) defines {} stars of {} max! Extra stars will not be used in Sector generation.", + sys->name, filename, sys->numStars, MAX_STARS); + sys->numStars = MAX_STARS; + } + + // Set system faction pointer + auto factionName = systemdef.value("faction", ""); + if (!factionName.empty()) { + if (!GetGalaxy()->GetFactions()->IsInitialized()) { + GetGalaxy()->GetFactions()->RegisterCustomSystem(sys, factionName); + } else { + sys->faction = GetGalaxy()->GetFactions()->GetFaction(factionName); + if (sys->faction->idx == Faction::BAD_FACTION_IDX) { + Log::Warning("Unknown faction {} for custom system {} ({}).", factionName, sys->name, filename); + sys->faction = nullptr; + } + } + } + + if (sys->want_rand_seed) { + Random rand = { + hash_32_fnv1a(sys->name.data(), sys->name.size()), + uint32_t(sys->sectorX), uint32_t(sys->sectorY), uint32_t(sys->sectorZ), UNIVERSE_SEED + }; + + sys->seed = rand.Int32(); + sys->want_rand_seed = false; + } + + // Partially-defined system, return as-is + if (!systemdef.count("bodies")) { + if (mergeWithGalaxy) + AddCustomSystem(SystemPath(sys->sectorX, sys->sectorY, sys->sectorZ), sys); + + return sys; + } + + size_t numBodies = systemdef["bodies"].size(); + sys->bodies.reserve(numBodies); + + // Load all bodies in order + for (const Json &bodynode : systemdef["bodies"]) { + + sys->bodies.emplace_back(new CustomSystemBody()); + + CustomSystemBody *body = sys->bodies.back(); + body->bodyData.LoadFromJson(bodynode); + + if (bodynode.count("children")) { + + for (const Json &childIndex : bodynode["children"]) { + if (childIndex >= numBodies) { + Log::Warning("Body {} in system {} ({}) has out-of-range child index {}", + body->bodyData.m_name, sys->name, filename, childIndex.get()); + continue; + } + + body->childIndicies.push_back(childIndex.get()); + } + + } + + } + + sys->sBody = sys->bodies[0]; + + // Resolve body children pointers + for (CustomSystemBody *body : sys->bodies) { + + for (uint32_t childIdx : body->childIndicies) { + body->children.push_back(sys->bodies[childIdx]); + } + + } + + if (mergeWithGalaxy) + AddCustomSystem(SystemPath(sys->sectorX, sys->sectorY, sys->sectorZ), sys); + + return sys; + + } catch (Json::out_of_range &e) { + Log::Warning("Could not load JSON system definition {}!", filename); + + delete sys; + return nullptr; + } +} + CustomSystemsDatabase::~CustomSystemsDatabase() { for (SectorMap::iterator secIt = m_sectorMap.begin(); secIt != m_sectorMap.end(); ++secIt) { @@ -688,13 +955,107 @@ const CustomSystemsDatabase::SystemList &CustomSystemsDatabase::GetCustomSystems void CustomSystemsDatabase::AddCustomSystem(const SystemPath &path, CustomSystem *csys) { - m_sectorMap[path].push_back(csys); + SystemList §orSystems = m_sectorMap[path]; + + for (const CustomSystem *&system : sectorSystems) { + if (system->nameHash != csys->nameHash) + continue; + + // Ensure no hash collisions occur + if (system->name != csys->name) + continue; + + // Partially-defined systems are ignored if there is an existing + // system already loaded with that name in that sector + if (csys->IsRandom()) { + delete csys; + return; + } + + // Fully-defined custom systems override existing systems + csys->systemIndex = system->systemIndex; + m_lastAddedSystem = SystemIndex(path, csys->systemIndex); + + delete system; + system = csys; + return; + } + + csys->systemIndex = sectorSystems.size(); + m_lastAddedSystem = SystemIndex(path, csys->systemIndex); + sectorSystems.push_back(csys); +} + +void CustomSystemsDatabase::RunLuaSystemSanityChecks(CustomSystem *csys) +{ + SystemPath path(csys->sectorX, csys->sectorY, csys->sectorZ); + Random rand; + uint32_t _init[5] = { 0, uint32_t(csys->sectorX), uint32_t(csys->sectorY), uint32_t(csys->sectorZ), UNIVERSE_SEED }; + + // We need a unique source of randomness that does not depend on the + // order in which systems are loaded or generated. + // Use the hash of the system or body name to generate a unique seed if + // it was not specified in the custom system file. + + if (csys->want_rand_seed) { + _init[0] = hash_32_fnv1a(csys->name.data(), csys->name.size()); + rand.seed(_init, 5); + + csys->seed = rand.Int32(); + csys->want_rand_seed = false; + } + + for (CustomSystemBody *body : csys->bodies) { + + // Generate the body's seed if missing + if (body->want_rand_seed) { + _init[0] = hash_32_fnv1a(body->bodyData.m_name.data(), body->bodyData.m_name.size()); + body->bodyData.m_seed = rand.Int32(); + } + + if (body->bodyData.m_volatileGas != 0) + body->bodyData.m_volatileGas *= fixed(1225, 1000); // lua volatile gas treated as 1.0 = 1.225kg/m^3 + + bool wantRings = body->ringStatus == CustomSystemBody::WANT_RANDOM_RINGS || body->ringStatus == CustomSystemBody::WANT_RINGS; + + if (!(body->want_rand_offset || body->want_rand_phase || body->want_rand_arg_periapsis || wantRings)) + continue; + + // Generate body orbit parameters from its seed + _init[0] = body->bodyData.m_seed; + rand.seed(_init, 5); + + if (body->want_rand_offset) + body->bodyData.m_orbitalOffset = fixed::FromDouble(rand.Double(2 * M_PI)); + + if (body->want_rand_phase) + body->bodyData.m_orbitalPhaseAtStart = fixed::FromDouble(rand.Double(2 * M_PI)); + + if (body->want_rand_arg_periapsis) + body->bodyData.m_argOfPeriapsis = fixed::FromDouble(rand.Double(2 * M_PI)); + + if (wantRings) { + // pick or specify rings + switch (body->ringStatus) { + case CustomSystemBody::WANT_RINGS: + StarSystemLegacyGeneratorBase::PickRings(&body->bodyData, true); + break; + case CustomSystemBody::WANT_RANDOM_RINGS: + StarSystemLegacyGeneratorBase::PickRings(&body->bodyData, false); + break; + default: break; + } + } + + } } CustomSystem::CustomSystem() : sBody(nullptr), numStars(0), seed(0), + nameHash(0), + want_rand_seed(true), want_rand_explored(true), faction(nullptr), govType(Polit::GOV_INVALID), @@ -718,15 +1079,11 @@ void CustomSystem::SanityChecks() } CustomSystemBody::CustomSystemBody() : - aspectRatio(fixed(1, 1)), - averageTemp(1), want_rand_offset(true), - latitude(0.0), - longitude(0.0), - volatileGas(0), - ringStatus(WANT_RANDOM_RINGS), - seed(0), - want_rand_seed(true) + want_rand_phase(true), + want_rand_arg_periapsis(true), + want_rand_seed(true), + ringStatus(WANT_RANDOM_RINGS) { } @@ -741,38 +1098,38 @@ CustomSystemBody::~CustomSystemBody() static void checks(CustomSystemBody &csb) { - if (csb.name.empty()) { - Error("custom system with name not set!\n"); + if (csb.bodyData.m_name.empty()) { + Error("custom system body with name not set!\n"); // throw an exception? Then it can be "catch" *per file*... } - if (csb.radius <= 0 && csb.mass <= 0) { - if (csb.type != SystemBody::TYPE_STARPORT_ORBITAL && - csb.type != SystemBody::TYPE_STARPORT_SURFACE && - csb.type != SystemBody::TYPE_GRAVPOINT) Error("custom system body '%s' with both radius ans mass left undefined!", csb.name.c_str()); - } - if (csb.radius <= 0 && csb.type != SystemBody::TYPE_STARPORT_ORBITAL && - csb.type != SystemBody::TYPE_STARPORT_SURFACE && - csb.type != SystemBody::TYPE_GRAVPOINT) { - Output("Warning: 'radius' is %f for body '%s'\n", csb.radius.ToFloat(), csb.name.c_str()); - } - if (csb.mass <= 0 && csb.type != SystemBody::TYPE_STARPORT_ORBITAL && - csb.type != SystemBody::TYPE_STARPORT_SURFACE && - csb.type != SystemBody::TYPE_GRAVPOINT) { - Output("Warning: 'mass' is %f for body '%s'\n", csb.mass.ToFloat(), csb.name.c_str()); - } - if (csb.averageTemp <= 0 && csb.type != SystemBody::TYPE_STARPORT_ORBITAL && - csb.type != SystemBody::TYPE_STARPORT_SURFACE && - csb.type != SystemBody::TYPE_GRAVPOINT) { - Output("Warning: 'averageTemp' is %i for body '%s'\n", csb.averageTemp, csb.name.c_str()); - } - if (csb.type == SystemBody::TYPE_STAR_S_BH || - csb.type == SystemBody::TYPE_STAR_IM_BH || - csb.type == SystemBody::TYPE_STAR_SM_BH) { - double schwarzschild = 2 * csb.mass.ToDouble() * ((G * SOL_MASS) / (LIGHT_SPEED * LIGHT_SPEED)); + if (csb.bodyData.m_radius <= 0 && csb.bodyData.m_mass <= 0) { + if (csb.bodyData.m_type != SystemBody::TYPE_STARPORT_ORBITAL && + csb.bodyData.m_type != SystemBody::TYPE_STARPORT_SURFACE && + csb.bodyData.m_type != SystemBody::TYPE_GRAVPOINT) Error("custom system body '%s' with both radius ans mass left undefined!", csb.bodyData.m_name.c_str()); + } + if (csb.bodyData.m_radius <= 0 && csb.bodyData.m_type != SystemBody::TYPE_STARPORT_ORBITAL && + csb.bodyData.m_type != SystemBody::TYPE_STARPORT_SURFACE && + csb.bodyData.m_type != SystemBody::TYPE_GRAVPOINT) { + Output("Warning: 'radius' is %f for body '%s'\n", csb.bodyData.m_radius.ToFloat(), csb.bodyData.m_name.c_str()); + } + if (csb.bodyData.m_mass <= 0 && csb.bodyData.m_type != SystemBody::TYPE_STARPORT_ORBITAL && + csb.bodyData.m_type != SystemBody::TYPE_STARPORT_SURFACE && + csb.bodyData.m_type != SystemBody::TYPE_GRAVPOINT) { + Output("Warning: 'mass' is %f for body '%s'\n", csb.bodyData.m_mass.ToFloat(), csb.bodyData.m_name.c_str()); + } + if (csb.bodyData.m_averageTemp <= 0 && csb.bodyData.m_type != SystemBody::TYPE_STARPORT_ORBITAL && + csb.bodyData.m_type != SystemBody::TYPE_STARPORT_SURFACE && + csb.bodyData.m_type != SystemBody::TYPE_GRAVPOINT) { + Output("Warning: 'averageTemp' is %i for body '%s'\n", csb.bodyData.m_averageTemp, csb.bodyData.m_name.c_str()); + } + if (csb.bodyData.m_type == SystemBody::TYPE_STAR_S_BH || + csb.bodyData.m_type == SystemBody::TYPE_STAR_IM_BH || + csb.bodyData.m_type == SystemBody::TYPE_STAR_SM_BH) { + double schwarzschild = 2 * csb.bodyData.m_mass.ToDouble() * ((G * SOL_MASS) / (LIGHT_SPEED * LIGHT_SPEED)); schwarzschild /= SOL_RADIUS; - if (csb.radius < schwarzschild) { + if (csb.bodyData.m_radius < schwarzschild) { Output("Warning: Blackhole radius defaulted to Schwarzschild radius (%f Sol radii)\n", schwarzschild); - csb.radius = schwarzschild; + csb.bodyData.m_radius = schwarzschild; } } } diff --git a/src/galaxy/CustomSystem.h b/src/galaxy/CustomSystem.h index b4e976ba030..ef9a7fa9d5e 100644 --- a/src/galaxy/CustomSystem.h +++ b/src/galaxy/CustomSystem.h @@ -6,6 +6,7 @@ #include "Color.h" #include "Polit.h" +#include "JsonFwd.h" #include "galaxy/SystemBody.h" #include "fixed.h" @@ -16,72 +17,60 @@ class Galaxy; class CustomSystemBody { public: - CustomSystemBody(); - ~CustomSystemBody(); - - std::string name; - SystemBody::BodyType type; - fixed radius; // in earth radii for planets, sol radii for stars (equatorial radius) - fixed aspectRatio; // the ratio between equatorial radius and polar radius for bodies flattened due to equatorial bulge (1.0 to infinity) - fixed mass; // earth masses or sol masses - int averageTemp; // kelvin - fixed semiMajorAxis; // in AUs - fixed eccentricity; - fixed orbitalOffset; - fixed orbitalPhaseAtStart; // mean anomaly at start 0 to 2 pi - bool want_rand_offset; - // for orbiting things, latitude = inclination - float latitude, longitude; // radians - fixed rotationPeriod; // in days - fixed rotationalPhaseAtStart; // 0 to 2 pi - fixed axialTilt; // in radians - std::string heightMapFilename; - int heightMapFractal; - std::vector children; - - /* composition */ - fixed metallicity; // (crust) 0.0 = light (Al, SiO2, etc), 1.0 = heavy (Fe, heavy metals) - fixed volatileGas; // 1.0 = earth atmosphere density - fixed volatileLiquid; // 1.0 = 100% ocean cover (earth = 70%) - fixed volatileIces; // 1.0 = 100% ice cover (earth = 3%) - fixed volcanicity; // 0 = none, 1.0 = fucking volcanic - fixed atmosOxidizing; // 0.0 = reducing (H2, NH3, etc), 1.0 = oxidising (CO2, O2, etc) - fixed life; // 0.0 = dead, 1.0 = teeming - - /* rings */ enum RingStatus { WANT_RANDOM_RINGS, WANT_RINGS, WANT_NO_RINGS, WANT_CUSTOM_RINGS }; - RingStatus ringStatus; - fixed ringInnerRadius; - fixed ringOuterRadius; - Color ringColor; - Uint32 seed; + CustomSystemBody(); + ~CustomSystemBody(); + + SystemBodyData bodyData; + // TODO: these two are separate implementations to handle Lua/Json based systems + std::vector children; + std::vector childIndicies; + + // TODO: these are only to be used for Lua system generation + bool want_rand_offset; + bool want_rand_phase; + bool want_rand_arg_periapsis; bool want_rand_seed; - std::string spaceStationType; + RingStatus ringStatus; void SanityChecks(); - }; class CustomSystem { public: + static const int CUSTOM_ONLY_RADIUS = 4; CustomSystem(); ~CustomSystem(); std::string name; std::vector other_names; + uint64_t nameHash; + CustomSystemBody *sBody; + // TODO: this holds system body objects when loaded from Json + // This depends on serialized body order being exactly the same as + // depth-first hierarchy traversal order. + // Otherwise, subtle inconsistencies and outright wrong random generation + // will creep in. + // The fix is to fully deprecate the depth-first traversal order and + // "flatten" StarSystemCustomGenerator::CustomGetKidsOf + // TODO: this should act as storage for all bodies instead of holding ptrs + std::vector bodies; SystemBody::BodyType primaryType[4]; unsigned numStars; int sectorX, sectorY, sectorZ; + Uint32 systemIndex; vector3f pos; Uint32 seed; + // NOTE: these are only intended to be used for Lua system generation + bool want_rand_seed; bool want_rand_explored; bool explored; const Faction *faction; @@ -94,6 +83,9 @@ class CustomSystem { void SanityChecks(); bool IsRandom() const { return !sBody; } + + void LoadFromJson(const Json &systemdef); + void SaveToJson(Json &obj); }; class CustomSystemsDatabase { @@ -105,18 +97,30 @@ class CustomSystemsDatabase { void Load(); + + void LoadAllLuaSystems(); + const CustomSystem *LoadSystem(std::string_view filepath); + + CustomSystem *LoadSystemFromJSON(std::string_view filename, const Json &systemdef, bool mergeWithGalaxy = true); + typedef std::vector SystemList; // XXX this is not as const-safe as it should be const SystemList &GetCustomSystemsForSector(int sectorX, int sectorY, int sectorZ) const; void AddCustomSystem(const SystemPath &path, CustomSystem *csys); Galaxy *GetGalaxy() const { return m_galaxy; } + void RunLuaSystemSanityChecks(CustomSystem *csys); + private: typedef std::map SectorMap; + typedef std::pair SystemIndex; + + lua_State *CreateLoaderState(); Galaxy *const m_galaxy; const std::string m_customSysDirectory; SectorMap m_sectorMap; + SystemIndex m_lastAddedSystem; static const SystemList s_emptySystemList; // see: Null Object pattern }; diff --git a/src/galaxy/Factions.cpp b/src/galaxy/Factions.cpp index 21a977462e5..03314ed3525 100644 --- a/src/galaxy/Factions.cpp +++ b/src/galaxy/Factions.cpp @@ -451,6 +451,8 @@ static void RegisterFactionsAPI(lua_State *L) void FactionsDatabase::Init() { + PROFILE_SCOPED() + assert(!s_activeFactionsDatabase); s_activeFactionsDatabase = this; @@ -491,6 +493,8 @@ void FactionsDatabase::Init() void FactionsDatabase::PostInit() { + PROFILE_SCOPED() + assert(m_initialized); assert(m_galaxy->IsInitialized()); SetHomeSectors(); @@ -792,7 +796,6 @@ Faction::Faction(Galaxy *galaxy) : void FactionsDatabase::Octsapling::Add(const Faction *faction) { - PROFILE_SCOPED() /* The general principle here is to put the faction in every octbox cell that a system that is a member of that faction could be in. This should let us cut the number of factions that have to be checked by GetNearestFaction, by eliminating right off @@ -874,7 +877,6 @@ void FactionsDatabase::Octsapling::PruneDuplicates(const int bx, const int by, c const std::vector &FactionsDatabase::Octsapling::CandidateFactions(const Sector::System *sys) const { - PROFILE_SCOPED() /* answer the factions that we've put in the same octobox cell as the one the system would go in. This part happens every time we do GetNearest faction so *is* performance criticale.e diff --git a/src/galaxy/GalaxyGenerator.cpp b/src/galaxy/GalaxyGenerator.cpp index f0bfe4c633e..70341b7404b 100644 --- a/src/galaxy/GalaxyGenerator.cpp +++ b/src/galaxy/GalaxyGenerator.cpp @@ -187,8 +187,8 @@ RefCountedPtr GalaxyGenerator::GenerateStarSystem(RefCountedPtrm_systems.size()); Uint32 seed = sec->m_systems[path.systemIndex].GetSeed(); std::string name = sec->m_systems[path.systemIndex].GetName(); - Uint32 _init[6] = { path.systemIndex, Uint32(path.sectorX), Uint32(path.sectorY), Uint32(path.sectorZ), UNIVERSE_SEED, Uint32(seed) }; - Random rng(_init, 6); + Uint32 _init[5] = { Uint32(seed), Uint32(path.sectorX), Uint32(path.sectorY), Uint32(path.sectorZ), UNIVERSE_SEED }; + Random rng(_init, 5); StarSystemConfig config; RefCountedPtr system(new StarSystem::GeneratorAPI(path, galaxy, cache, rng)); for (StarSystemGeneratorStage *sysgen : m_starSystemStage) diff --git a/src/galaxy/NameGenerator.cpp b/src/galaxy/NameGenerator.cpp new file mode 100644 index 00000000000..c35ef79e236 --- /dev/null +++ b/src/galaxy/NameGenerator.cpp @@ -0,0 +1,180 @@ +// Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details +// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt + +#include "NameGenerator.h" +#include "utils.h" + +void NameGenerator::GetSystemName(std::string &name, Random &rng) +{ + int nameGen = rng.Int32(0, 3); + switch (nameGen) { + case 0: FrontierNames::GetName(name, rng); break; + case 1: HybridNames::GetName(name, rng); break; + case 2: Doomdark::GetName(name, rng); break; + case 3: Katakana::GetName(name, rng); break; + default: + FrontierNames::GetName(name, rng); + break; + } +} + +namespace FrontierNames { + static const char *sys_names[] = { + "en", "la", "can", "be", + "and", "phi", "eth", "ol", + "ve", "ho", "a", "lia", + "an", "ar", "ur", "mi", + "in", "ti", "qu", "so", + "ed", "ess", "ex", "io", + "ce", "ze", "fa", "ay", + "wa", "de", "ack", "gre", + "le", "du", "do", "ne" + }; + static const unsigned int SYS_NAME_FRAGS = ((unsigned int)(sizeof(sys_names) / sizeof(char *))); + + void GetName(std::string &name, Random &rng) + { + // add fragments to build a name + int len = rng.Int32(2, 3); + for (int i = 0; i < len; i++) { + name += sys_names[rng.Int32(0, SYS_NAME_FRAGS - 1)]; + } + + name[0] = toupper(name[0]); + } +} // namespace FrontierNames + +namespace HybridNames { + static const char *sys_names[] = { + "en", "la", "can", "be", + "and", "phi", "eth", "ol", + "ve", "ho", "a", "lia", + "an", "ar", "ur", "mi", + "in", "ti", "qu", "so", + "ed", "ess", "ex", "io", + "ce", "ze", "fa", "ay", + "wa", "de", "ack", "gre", + "le", "du", "do", "ne", + + //Doomdark-esque additions + "img", "or", "ir", "dol", + "orth", "angr", "igr", "ash", + "el", "mor", "ul", "atr", + "orm", "udr", "is", "ildr", + "orn", "il", "iel", "im", + "uk", "ium", "ia", "eon", + "ob", "ak", "arg", "ber", + "ane", "esh", "ad", "un", + + //WKFO + "ank", "bur", "ist", "iz", + "erz", "tra", "shir", "gu", + "ant", "kon", "ya", "us", + "esk", "ig", "kah", "zon", + "tay", "ash", "mar", "van", + "sus", "tar", "run", "isk", + "hir", "gaz", "sun", "gat", + "pi", "cis", "ele", "ova" + }; + static const unsigned int SYS_NAME_FRAGS = ((unsigned int)(sizeof(sys_names) / sizeof(char *))); + + void GetName(std::string &name, Random &rng) + { + // add fragments to build a name + int len = rng.Int32(2, 3); + for (int i = 0; i < len; i++) { + name += sys_names[rng.Int32(0, SYS_NAME_FRAGS - 1)]; + } + + name[0] = toupper(name[0]); + } +} // namespace HybridNames + +namespace Doomdark { + static const char *Prefixes[] = { + "img", "dol", "lor", "ush", "mor", "tal", "car", "ulf", "as", "tor", "ob", "f", "gl", + "s", "th", "gan", "mal", "im", "var", "hag", "zar", "anv", "ber", "kah", "ash", "du" + }; + static const unsigned int PREFIX_FRAGS = ((unsigned int)(sizeof(Prefixes) / sizeof(char *))); + + static const char *Midwords[] = { + "ar", "or", "ir", "en", "orth", "angr", "igr", "ash", "el", "in", "ul", "atr", "orm", "udr", "is", "ildr" + }; + static const unsigned int MIDWORD_FRAGS = ((unsigned int)(sizeof(Midwords) / sizeof(char *))); + + static const char *Suffixes[] = { + "orn", "il", "iel", "im", "uk", "ium", "ia", "eon", "ay", "ak", "arg", "and", "ane", "esh", "ad", "un", "ne" + }; + static const unsigned int SUFFIX_FRAGS = ((unsigned int)(sizeof(Suffixes) / sizeof(char *))); + + void GetName(std::string &name, Random &rng) + { + // Doodarken a name + name += Prefixes[rng.Int32(0, PREFIX_FRAGS - 1)]; + name += Midwords[rng.Int32(0, MIDWORD_FRAGS - 1)]; + name += Suffixes[rng.Int32(0, SUFFIX_FRAGS - 1)]; + + name[0] = toupper(name[0]); + } +} // namespace Doomdark + +namespace Katakana { + // clang-format off + static const char *StartFragments[] = { + "kyo","gyo","shu","sho","chu","cho","hyu","myo", + "ryu","chi","tsu","shi","ka","ki","ku","ke", + "ko","ga","gi","gu","ge","go","sa","su", + "se","so","za","zu","ze","ta","te","to", + "da","na","ni","nu","ne","no","ha","hi", + "fu","he","ho","ba","bi","bu","be","ma", + "mi","mu","me","mo","ya","yu","yo","ri", + "ru","wa","jo","a","i","u","e","o", + + }; + static const char *MiddleFragments[] = { + "sshi","ppo","tto","mbo","kka","kyu","sho","chu", + "chi","tsu","shi","ka","ki","ku","ke","ko", + "ga","gi","gu","ge","go","sa","su","se", + "so","za","ji","zu","ze","ta","te","to", + "da","de","do","na","ni","nu","ne","no", + "ha","hi","fu","ho","ba","bi","bu","be", + "bo","ma","mi","mu","me","mo","ya","yo", + "ra","ri","ru","re","ro","wa","ju","jo", + "a","i","u","e","o","n", + }; + static const char *EndFragments[] = { + "ttsu","ppu","ssa","tto","tte","noh","mba","kko", + "kyo","shu","chu","nyu","nyo","ryu","chi","tsu", + "shi","ka","ki","ku","ke","ko","ga","gi", + "gu","go","sa","su","se","so","za","ji", + "zu","ze","zo","ta","te","to","da","de", + "do","na","ni","ne","no","ha","hi","fu", + "he","ho","ba","bi","bu","be","bo","ma", + "mi","mu","me","mo","ya","yo","ra","ri", + "ru","re","ro","wa","ja","jo","i","e", + "o","n", + }; + // clang-format on + + static const unsigned int NUM_START_FRAGS = COUNTOF(StartFragments); + static const unsigned int NUM_MIDDLE_FRAGS = COUNTOF(MiddleFragments); + static const unsigned int NUM_END_FRAGS = COUNTOF(EndFragments); + + void GetName(std::string &name, Random &rng) + { + // beginning + name += StartFragments[rng.Int32(0, NUM_START_FRAGS - 1)]; + + // middle + size_t count = rng.Int32(0, 2); + for (size_t i = 0; i < count; i++) { + name += MiddleFragments[rng.Int32(0, NUM_MIDDLE_FRAGS - 1)]; + } + + // end + name += EndFragments[rng.Int32(0, NUM_END_FRAGS - 1)]; + + // Capitalisation + name[0] = toupper(name[0]); + } +} // namespace Katakana diff --git a/src/galaxy/NameGenerator.h b/src/galaxy/NameGenerator.h new file mode 100644 index 00000000000..e8d4abc9166 --- /dev/null +++ b/src/galaxy/NameGenerator.h @@ -0,0 +1,27 @@ +// Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details +// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt + +#pragma once + +#include "Random.h" +#include + +namespace NameGenerator { + void GetSystemName(std::string &name, Random &rng); +} + +namespace FrontierNames { + void GetName(std::string &name, Random &rng); +} + +namespace HybridNames { + void GetName(std::string &name, Random &rng); +} + +namespace Doomdark { + void GetName(std::string &name, Random &rng); +} + +namespace Katakana { + void GetName(std::string &name, Random &rng); +} diff --git a/src/galaxy/SectorGenerator.cpp b/src/galaxy/SectorGenerator.cpp index c75fea57ea3..e6555040044 100644 --- a/src/galaxy/SectorGenerator.cpp +++ b/src/galaxy/SectorGenerator.cpp @@ -9,170 +9,11 @@ #include "Galaxy.h" #include "GameSaveError.h" #include "Json.h" -#include "core/StringUtils.h" -#include "core/macros.h" +#include "NameGenerator.h" +#include "utils.h" #define Square(x) ((x) * (x)) -namespace FrontierNames { - static const char *sys_names[] = { - "en", "la", "can", "be", - "and", "phi", "eth", "ol", - "ve", "ho", "a", "lia", - "an", "ar", "ur", "mi", - "in", "ti", "qu", "so", - "ed", "ess", "ex", "io", - "ce", "ze", "fa", "ay", - "wa", "de", "ack", "gre" - }; - static const unsigned int SYS_NAME_FRAGS = ((unsigned int)(sizeof(sys_names) / sizeof(char *))); - - void GetName(std::string &name, Random &rng) - { - // add fragments to build a name - int len = rng.Int32(2, 3); - for (int i = 0; i < len; i++) { - name += sys_names[rng.Int32(0, SYS_NAME_FRAGS - 1)]; - } - - name[0] = toupper(name[0]); - } -} // namespace FrontierNames - -namespace HybridNames { - static const char *sys_names[] = { - "en", "la", "can", "be", - "and", "phi", "eth", "ol", - "ve", "ho", "a", "lia", - "an", "ar", "ur", "mi", - "in", "ti", "qu", "so", - "ed", "ess", "ex", "io", - "ce", "ze", "fa", "ay", - "wa", "de", "ack", "gre", - - //Doomdark-esque additions - "img", "or", "ir", "dol", - "orth", "angr", "igr", "ash", - "el", "mor", "ul", "atr", - "orm", "udr", "is", "ildr", - "orn", "il", "iel", "im", - "uk", "ium", "ia", "eon", - "ob", "ak", "arg", "ber", - "ane", "esh", "ad", "un", - - //WKFO - "ank", "bur", "ist", "iz", - "erz", "tra", "shir", "gu", - "ant", "kon", "ya", "us", - "esk", "ig", "kah", "zon", - "tay", "ash", "mar", "van", - "sus", "tar", "run", "isk", - "hir", "gaz", "sun", "gat", - "pi", "cis", "ele", "ova" - }; - static const unsigned int SYS_NAME_FRAGS = ((unsigned int)(sizeof(sys_names) / sizeof(char *))); - - void GetName(std::string &name, Random &rng) - { - // add fragments to build a name - int len = rng.Int32(2, 3); - for (int i = 0; i < len; i++) { - name += sys_names[rng.Int32(0, SYS_NAME_FRAGS - 1)]; - } - - name[0] = toupper(name[0]); - } -} // namespace HybridNames - -namespace Doomdark { - static const char *Prefixes[] = { - "img", "dol", "lor", "ush", "mor", "tal", "car", "ulf", "as", "tor", "ob", "f", "gl", - "s", "th", "gan", "mal", "im", "var", "hag", "zar", "anv", "ber", "kah", "ash" - }; - static const unsigned int PREFIX_FRAGS = ((unsigned int)(sizeof(Prefixes) / sizeof(char *))); - - static const char *Midwords[] = { - "ar", "or", "ir", "en", "orth", "angr", "igr", "ash", "el", "in", "ul", "atr", "orm", "udr", "is", "ildr" - }; - static const unsigned int MIDWORD_FRAGS = ((unsigned int)(sizeof(Midwords) / sizeof(char *))); - - static const char *Suffixes[] = { - "orn", "il", "iel", "im", "uk", "ium", "ia", "eon", "ay", "ak", "arg", "and", "ane", "esh", "ad", "un" - }; - static const unsigned int SUFFIX_FRAGS = ((unsigned int)(sizeof(Suffixes) / sizeof(char *))); - - void GetName(std::string &name, Random &rng) - { - // Doodarken a name - name += Prefixes[rng.Int32(0, PREFIX_FRAGS - 1)]; - name += Midwords[rng.Int32(0, MIDWORD_FRAGS - 1)]; - name += Suffixes[rng.Int32(0, SUFFIX_FRAGS - 1)]; - - name[0] = toupper(name[0]); - } -} // namespace Doomdark - -namespace Katakana { - // clang-format off - static const char *StartFragments[] = { - "kyo","gyo","shu","sho","chu","cho","hyu","myo", - "ryu","chi","tsu","shi","ka","ki","ku","ke", - "ko","ga","gi","gu","ge","go","sa","su", - "se","so","za","zu","ze","ta","te","to", - "da","na","ni","nu","ne","no","ha","hi", - "fu","he","ho","ba","bi","bu","be","ma", - "mi","mu","me","mo","ya","yu","yo","ri", - "ru","wa","jo","a","i","u","e","o", - - }; - static const char *MiddleFragments[] = { - "sshi","ppo","tto","mbo","kka","kyu","sho","chu", - "chi","tsu","shi","ka","ki","ku","ke","ko", - "ga","gi","gu","ge","go","sa","su","se", - "so","za","ji","zu","ze","ta","te","to", - "da","de","do","na","ni","nu","ne","no", - "ha","hi","fu","ho","ba","bi","bu","be", - "bo","ma","mi","mu","me","mo","ya","yo", - "ra","ri","ru","re","ro","wa","ju","jo", - "a","i","u","e","o","n", - }; - static const char *EndFragments[] = { - "ttsu","ppu","ssa","tto","tte","noh","mba","kko", - "kyo","shu","chu","nyu","nyo","ryu","chi","tsu", - "shi","ka","ki","ku","ke","ko","ga","gi", - "gu","go","sa","su","se","so","za","ji", - "zu","ze","zo","ta","te","to","da","de", - "do","na","ni","ne","no","ha","hi","fu", - "he","ho","ba","bi","bu","be","bo","ma", - "mi","mu","me","mo","ya","yo","ra","ri", - "ru","re","ro","wa","ja","jo","i","e", - "o","n", - }; - // clang-format on - - static const unsigned int NUM_START_FRAGS = COUNTOF(StartFragments); - static const unsigned int NUM_MIDDLE_FRAGS = COUNTOF(MiddleFragments); - static const unsigned int NUM_END_FRAGS = COUNTOF(EndFragments); - - void GetName(std::string &name, Random &rng) - { - // beginning - name += StartFragments[rng.Int32(0, NUM_START_FRAGS - 1)]; - - // middle - size_t count = rng.Int32(0, 2); - for (size_t i = 0; i < count; i++) { - name += MiddleFragments[rng.Int32(0, NUM_MIDDLE_FRAGS - 1)]; - } - - // end - name += EndFragments[rng.Int32(0, NUM_END_FRAGS - 1)]; - - // Capitalisation - name[0] = toupper(name[0]); - } -} // namespace Katakana - bool SectorCustomSystemsGenerator::Apply(Random &rng, RefCountedPtr galaxy, RefCountedPtr sector, GalaxyGenerator::SectorConfig *config) { const int sx = sector->sx; @@ -192,7 +33,7 @@ bool SectorCustomSystemsGenerator::Apply(Random &rng, RefCountedPtr gala for (std::vector::const_iterator it = systems.begin(); it != systems.end(); ++it, ++sysIdx) { const CustomSystem *cs = *it; Sector::System s(sector.Get(), sx, sy, sz, sysIdx); - s.m_pos = Sector::SIZE * cs->pos; + s.m_pos = cs->pos; s.m_name = cs->name; s.m_other_names = cs->other_names; for (s.m_numStars = 0; s.m_numStars < cs->numStars; s.m_numStars++) { @@ -201,6 +42,7 @@ bool SectorCustomSystemsGenerator::Apply(Random &rng, RefCountedPtr gala } s.m_customSys = cs; s.m_seed = cs->seed; + if (cs->want_rand_explored) { /* * 0 - ~500ly from sol: explored @@ -265,16 +107,7 @@ const std::string SectorRandomSystemsGenerator::GenName(RefCountedPtr ga Uint32 weight = rng.Int32(chance); if (weight < 500 || galaxy->GetFactions()->IsHomeSystem(SystemPath(sx, sy, sz, si))) { // well done. you get a "real" name - int nameGen = rng.Int32(0, 3); - switch (nameGen) { - case 0: FrontierNames::GetName(name, rng); break; - case 1: HybridNames::GetName(name, rng); break; - case 2: Doomdark::GetName(name, rng); break; - case 3: Katakana::GetName(name, rng); break; - default: - FrontierNames::GetName(name, rng); - break; - } + NameGenerator::GetSystemName(name, rng); return name; } else if (weight < 800) { char buf[128]; @@ -489,6 +322,8 @@ bool SectorRandomSystemsGenerator::Apply(Random &rng, RefCountedPtr gala s.m_name = GenName(galaxy, *sector, s, customCount + i, rng); //Output("%s: \n", s.m_name.c_str()); + s.m_seed = rng.Int32(); + sector->m_systems.push_back(s); } return true; diff --git a/src/galaxy/StarSystem.cpp b/src/galaxy/StarSystem.cpp index b7205c0c2a9..8d53b466fb6 100644 --- a/src/galaxy/StarSystem.cpp +++ b/src/galaxy/StarSystem.cpp @@ -4,7 +4,7 @@ #include "StarSystem.h" #include "Galaxy.h" -#include "Json.h" +#include "JsonUtils.h" #include "Sector.h" #include "EnumStrings.h" @@ -558,3 +558,58 @@ void StarSystem::Dump(FILE *file, const char *indent, bool suppressSectorData) c } fprintf(file, "%s}\n", indent); } + +void StarSystem::DumpToJson(Json &obj) +{ + obj["name"] = m_name; + + if (!m_other_names.empty()) { + Json &out_names = obj["otherNames"] = Json::array(); + for (auto &name : m_other_names) + out_names.push_back(name); + } + + Json &out_types = obj["stars"] = Json::array(); + for (size_t idx = 0; idx < m_numStars; idx++) + out_types.push_back(EnumStrings::GetString("BodyType", m_stars[idx]->GetType())); + + obj["sector"] = Json::array({ m_path.sectorX, m_path.sectorY, m_path.sectorZ }); + obj["pos"] = Json::array({ m_pos.x, m_pos.y, m_pos.z }); + + obj["seed"] = m_seed; + obj["explored"] = m_explored == ExplorationState::eEXPLORED_AT_START; + + obj["lawlessness"] = m_polit.lawlessness; + + if (m_polit.govType != Polit::GOV_INVALID) + obj["govType"] = EnumStrings::GetString("PolitGovType", m_polit.govType); + + obj["shortDesc"] = m_shortDesc; + obj["longDesc"] = m_longDesc; + + if (m_faction) + obj["faction"] = m_faction->name; + + Json &bodies = obj["bodies"] = Json::array(); + + for (RefCountedPtr &body : m_bodies) { + + Json bodyObj = Json::object(); + + body->SaveToJson(bodyObj); + + // bodyIndex is (at least informally) guaranteed to be the index in m_bodies + if (body->GetParent()) + bodyObj["parent"] = body->GetParent()->GetPath().bodyIndex; + + if (body->HasChildren()) { + Json &children = bodyObj["children"] = Json::array(); + + for (SystemBody *child : body->GetChildren()) + children.push_back(child->GetPath().bodyIndex); + } + + bodies.emplace_back(std::move(bodyObj)); + + } +} diff --git a/src/galaxy/StarSystem.h b/src/galaxy/StarSystem.h index 4f28787fb3d..5f2d255677e 100644 --- a/src/galaxy/StarSystem.h +++ b/src/galaxy/StarSystem.h @@ -46,6 +46,8 @@ class StarSystem : public RefCounted { static void ToJson(Json &jsonObj, StarSystem *); static RefCountedPtr FromJson(RefCountedPtr galaxy, const Json &jsonObj); const SystemPath &GetPath() const { return m_path; } + const vector3f &GetPosition() const { return m_pos; } + const std::string &GetShortDescription() const { return m_shortDesc; } const std::string &GetLongDescription() const { return m_longDesc; } unsigned GetNumStars() const { return m_numStars; } @@ -94,6 +96,10 @@ class StarSystem : public RefCounted { void Dump(FILE *file, const char *indent = "", bool suppressSectorData = false) const; + // Dump all information about this system to JSON format suitable for + // loading as a custom system + void DumpToJson(Json &obj); + const RefCountedPtr m_galaxy; protected: @@ -121,6 +127,7 @@ class StarSystem : public RefCounted { std::string GetStarTypes(SystemBody *body); SystemPath m_path; + vector3f m_pos; unsigned m_numStars; std::string m_name; std::vector m_other_names; @@ -168,6 +175,7 @@ class StarSystem::GeneratorAPI : public StarSystem { m_isCustom = isCustom; m_hasCustomBodies = hasCustomBodies; } + void SetPosition(const vector3f &pos) { m_pos = pos; } void SetNumStars(int numStars) { m_numStars = numStars; } void SetRootBody(RefCountedPtr rootBody) { m_rootBody = rootBody; } void SetRootBody(SystemBody *rootBody) { m_rootBody.Reset(rootBody); } diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 71931cb8d2c..cf749a39333 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -10,6 +10,7 @@ #include "Lang.h" #include "Pi.h" #include "Sector.h" +#include "gameconsts.h" #include "core/Log.h" #include "core/macros.h" #include "galaxy/Economy.h" @@ -212,11 +213,11 @@ void StarSystemLegacyGeneratorBase::PickAtmosphere(SystemBody *sbody) case SystemBody::TYPE_PLANET_GAS_GIANT: sbody->m_atmosColor = Color(255, 255, 255, 3); - sbody->m_atmosDensity = 14.0; + // NOTE: realistic generation for gas giant atmospheres needed elsewhere + // sbody->m_atmosDensity = 14.0; break; case SystemBody::TYPE_PLANET_ASTEROID: sbody->m_atmosColor = Color::BLANK; - sbody->m_atmosDensity = 0.0; break; default: case SystemBody::TYPE_PLANET_TERRESTRIAL: @@ -273,7 +274,6 @@ void StarSystemLegacyGeneratorBase::PickAtmosphere(SystemBody *sbody) } else { sbody->m_atmosColor = Color::BLANK; } - sbody->m_atmosDensity = sbody->GetVolatileGas(); //Output("| Atmosphere :\n| red : [%f] \n| green : [%f] \n| blue : [%f] \n", r, g, b); //Output("-------------------------------\n"); break; @@ -292,7 +292,7 @@ static const unsigned char RANDOM_RING_COLORS[][4] = { { 207, 122, 98, 217 } // brown dwarf-like }; -void StarSystemLegacyGeneratorBase::PickRings(SystemBody *sbody, bool forceRings) +void StarSystemLegacyGeneratorBase::PickRings(SystemBodyData *sbody, bool forceRings) { sbody->m_rings.minRadius = fixed(); sbody->m_rings.maxRadius = fixed(); @@ -300,16 +300,16 @@ void StarSystemLegacyGeneratorBase::PickRings(SystemBody *sbody, bool forceRings bool bHasRings = forceRings; if (!bHasRings) { - Random ringRng(sbody->GetSeed() + 965467); + Random ringRng(sbody->m_seed + 965467); // today's forecast: - if (sbody->GetType() == SystemBody::TYPE_PLANET_GAS_GIANT) { + if (sbody->m_type == SystemBody::TYPE_PLANET_GAS_GIANT) { // 50% chance of rings bHasRings = ringRng.Double() < 0.5; - } else if (sbody->GetType() == SystemBody::TYPE_PLANET_TERRESTRIAL) { + } else if (sbody->m_type == SystemBody::TYPE_PLANET_TERRESTRIAL) { // 10% chance of rings bHasRings = ringRng.Double() < 0.1; } - /*else if (sbody->GetType() == SystemBody::TYPE_PLANET_ASTEROID) + /*else if (sbody->m_type == SystemBody::TYPE_PLANET_ASTEROID) { // 1:10 (10%) chance of rings bHasRings = ringRng.Double() < 0.1; @@ -317,7 +317,7 @@ void StarSystemLegacyGeneratorBase::PickRings(SystemBody *sbody, bool forceRings } if (bHasRings) { - Random ringRng(sbody->GetSeed() + 965467); + Random ringRng(sbody->m_seed + 965467); // today's forecast: 50% chance of rings double rings_die = ringRng.Double(); @@ -357,22 +357,25 @@ void StarSystemLegacyGeneratorBase::PickRings(SystemBody *sbody, bool forceRings /* * http://en.wikipedia.org/wiki/Hill_sphere */ -fixed StarSystemLegacyGeneratorBase::CalcHillRadius(SystemBody *sbody) const +fixedf<48> StarSystemLegacyGeneratorBase::CalcHillRadius(SystemBody *sbody) const { PROFILE_SCOPED() + // high-precision for working with very small numbers + // system distances are not expected to be larger than 32k AU + using fixedp = fixedf<48>; + if (sbody->GetSuperType() <= SystemBody::SUPERTYPE_STAR) { - return fixed(); + return fixedp(); } else { // playing with precision since these numbers get small // masses in earth masses - fixedf<32> mprimary = sbody->GetParent()->GetMassInEarths(); + fixed mprimary = sbody->GetParent()->GetMassInEarths(); - fixedf<48> a = sbody->GetSemiMajorAxisAsFixed(); - fixedf<48> e = sbody->GetEccentricityAsFixed(); + fixedp a = sbody->GetSemiMajorAxisAsFixed(); + fixedp e = sbody->GetEccentricityAsFixed(); + fixedp pe = a * (fixedp(1, 1) - e); // periapsis in higher precision - return fixed(a * (fixedf<48>(1, 1) - e) * - fixedf<48>::CubeRootOf(fixedf<48>( - sbody->GetMassAsFixed() / (fixedf<32>(3, 1) * mprimary)))); + return pe * fixedp::CubeRootOf(sbody->GetMassAsFixed() / (fixed(3, 1) * mprimary)); //fixed hr = semiMajorAxis*(fixed(1,1) - eccentricity) * // fixedcuberoot(mass / (3*mprimary)); @@ -380,197 +383,117 @@ fixed StarSystemLegacyGeneratorBase::CalcHillRadius(SystemBody *sbody) const } void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtr system, SystemBody *parent, - const std::vector &children, int *outHumanInfestedness, Random &rand) + const std::vector &children, int *outHumanInfestedness) { PROFILE_SCOPED() - // replaces gravpoint mass by sum of masses of its children - // the code goes here to cover also planetary gravpoints (gravpoints that are not rootBody) - if (parent->GetType() == SystemBody::TYPE_GRAVPOINT) { - fixed mass(0); - - for (std::vector::const_iterator i = children.begin(); i != children.end(); ++i) { - const CustomSystemBody *csbody = *i; - - if (csbody->type >= SystemBody::TYPE_STAR_MIN && csbody->type <= SystemBody::TYPE_STAR_MAX) - mass += csbody->mass; - else - mass += csbody->mass / SUN_MASS_TO_EARTH_MASS; - } - parent->m_mass = mass; - } + // gravpoints have no mass, but we sum the masses of its children instead + if (parent->GetType() == SystemBody::TYPE_GRAVPOINT) + parent->m_mass = fixed(0); for (std::vector::const_iterator i = children.begin(); i != children.end(); ++i) { const CustomSystemBody *csbody = *i; SystemBody *kid = system->NewBody(); - kid->m_type = csbody->type; kid->m_parent = parent; - kid->m_seed = csbody->want_rand_seed ? rand.Int32() : csbody->seed; - kid->m_radius = csbody->radius; - kid->m_aspectRatio = csbody->aspectRatio; - kid->m_averageTemp = csbody->averageTemp; - kid->m_name = csbody->name; kid->m_isCustomBody = true; - kid->m_mass = csbody->mass; - // obsolete adjustment, probably existed because of denominator precision problems, see LuaFixed.cpp - // if (kid->GetType() == SystemBody::TYPE_PLANET_ASTEROID) kid->m_mass /= 100000; - - kid->m_metallicity = csbody->metallicity; - //multiple of Earth's surface density - kid->m_volatileGas = csbody->volatileGas * fixed(1225, 1000); - kid->m_volatileLiquid = csbody->volatileLiquid; - kid->m_volatileIces = csbody->volatileIces; - kid->m_volcanicity = csbody->volcanicity; - kid->m_atmosOxidizing = csbody->atmosOxidizing; - kid->m_life = csbody->life; - kid->m_space_station_type = csbody->spaceStationType; - kid->m_rotationPeriod = csbody->rotationPeriod; - kid->m_rotationalPhaseAtStart = csbody->rotationalPhaseAtStart; - kid->m_eccentricity = csbody->eccentricity; - kid->m_orbitalOffset = csbody->orbitalOffset; - kid->m_orbitalPhaseAtStart = csbody->orbitalPhaseAtStart; - kid->m_axialTilt = csbody->axialTilt; - kid->m_inclination = fixed(csbody->latitude * 10000, 10000); - if (kid->GetType() == SystemBody::TYPE_STARPORT_SURFACE) - kid->m_orbitalOffset = fixed(csbody->longitude * 10000, 10000); - kid->m_semiMajorAxis = csbody->semiMajorAxis; - - if (csbody->heightMapFilename.length() > 0) { - kid->m_heightMapFilename = csbody->heightMapFilename; - kid->m_heightMapFractal = csbody->heightMapFractal; - } + *kid = csbody->bodyData; - if (parent->GetType() == SystemBody::TYPE_GRAVPOINT) // generalize Kepler's law to multiple stars - kid->m_orbit.SetShapeAroundBarycentre(csbody->semiMajorAxis.ToDouble() * AU, parent->GetMass(), kid->GetMass(), csbody->eccentricity.ToDouble()); - else - kid->m_orbit.SetShapeAroundPrimary(csbody->semiMajorAxis.ToDouble() * AU, parent->GetMass(), csbody->eccentricity.ToDouble()); + // parent gravpoint mass = sum of masses of its children + if (parent->GetType() == SystemBody::TYPE_GRAVPOINT) { + if (kid->GetSuperType() == SystemBody::SUPERTYPE_STAR) + parent->m_mass += kid->m_mass; + else + parent->m_mass += kid->m_mass / SUN_MASS_TO_EARTH_MASS; + } - kid->m_orbit.SetPhase(csbody->orbitalPhaseAtStart.ToDouble()); + kid->SetOrbitFromParameters(); + kid->SetAtmFromParameters(); - if (kid->GetType() == SystemBody::TYPE_STARPORT_SURFACE) { - kid->m_orbit.SetPlane(matrix3x3d::RotateY(csbody->longitude) * matrix3x3d::RotateX(-0.5 * M_PI + csbody->latitude)); - } else { + if (kid->GetType() != SystemBody::TYPE_STARPORT_SURFACE) { if (kid->GetSuperType() == SystemBody::SUPERTYPE_STARPORT) { fixed lowestOrbit = fixed().FromDouble(parent->CalcAtmosphereParams().atmosRadius + 500000.0 / EARTH_RADIUS); - if (kid->m_orbit.GetSemiMajorAxis() < lowestOrbit.ToDouble()) { - Error("%s's orbit is too close to its parent (%.2f/%.2f)", csbody->name.c_str(), kid->m_orbit.GetSemiMajorAxis(), lowestOrbit.ToFloat()); + if (kid->GetOrbit().GetSemiMajorAxis() < lowestOrbit.ToDouble()) { + Error("%s's orbit is too close to its parent (%.2f/%.2f)", kid->m_name.c_str(), kid->GetOrbit().GetSemiMajorAxis(), lowestOrbit.ToFloat()); } } else { - if (kid->m_orbit.GetSemiMajorAxis() < 1.2 * parent->GetRadius()) { - Error("%s's orbit is too close to its parent", csbody->name.c_str()); + if (kid->GetOrbit().GetSemiMajorAxis() < 1.2 * parent->GetRadius()) { + Error("%s's orbit is too close to its parent", kid->m_name.c_str()); } } - double offset = csbody->want_rand_offset ? rand.Double(2 * M_PI) : (csbody->orbitalOffset.ToDouble()); - kid->m_orbit.SetPlane(matrix3x3d::RotateY(offset) * matrix3x3d::RotateX(-0.5 * M_PI + csbody->latitude)); } + if (kid->GetSuperType() == SystemBody::SUPERTYPE_STARPORT) { (*outHumanInfestedness)++; system->AddSpaceStation(kid); } parent->m_children.push_back(kid); - // perihelion and aphelion (in AUs) - kid->m_orbMin = csbody->semiMajorAxis - csbody->eccentricity * csbody->semiMajorAxis; - kid->m_orbMax = 2 * csbody->semiMajorAxis - kid->m_orbMin; - PickAtmosphere(kid); - // pick or specify rings - switch (csbody->ringStatus) { - case CustomSystemBody::WANT_NO_RINGS: - kid->m_rings.minRadius = fixed(); - kid->m_rings.maxRadius = fixed(); - break; - case CustomSystemBody::WANT_RINGS: - PickRings(kid, true); - break; - case CustomSystemBody::WANT_RANDOM_RINGS: - PickRings(kid, false); - break; - case CustomSystemBody::WANT_CUSTOM_RINGS: - kid->m_rings.minRadius = csbody->ringInnerRadius; - kid->m_rings.maxRadius = csbody->ringOuterRadius; - kid->m_rings.baseColor = csbody->ringColor; - break; - } - - CustomGetKidsOf(system, kid, csbody->children, outHumanInfestedness, rand); + CustomGetKidsOf(system, kid, csbody->children, outHumanInfestedness); } } -bool StarSystemCustomGenerator::Apply(Random &rng, RefCountedPtr galaxy, RefCountedPtr system, GalaxyGenerator::StarSystemConfig *config) +bool StarSystemCustomGenerator::ApplyToSystem(Random &rng, RefCountedPtr system, const CustomSystem *customSys) { - PROFILE_SCOPED() - RefCountedPtr sec = galaxy->GetSector(system->GetPath()); - system->SetCustom(false, false); - if (const CustomSystem *customSys = sec->m_systems[system->GetPath().systemIndex].GetCustomSystem()) { - system->SetCustom(true, false); - system->SetNumStars(customSys->numStars); - if (customSys->shortDesc.length() > 0) system->SetShortDesc(customSys->shortDesc); - if (customSys->longDesc.length() > 0) system->SetLongDesc(customSys->longDesc); - if (!customSys->IsRandom()) { - system->SetCustom(true, true); - config->isCustomOnly = true; - const CustomSystemBody *csbody = customSys->sBody; - SystemBody *rootBody = system->NewBody(); - rootBody->m_type = csbody->type; - rootBody->m_parent = 0; - rootBody->m_seed = csbody->want_rand_seed ? rng.Int32() : csbody->seed; - rootBody->m_seed = rng.Int32(); - rootBody->m_radius = csbody->radius; - rootBody->m_aspectRatio = csbody->aspectRatio; - rootBody->m_mass = csbody->mass; - rootBody->m_averageTemp = csbody->averageTemp; - rootBody->m_name = csbody->name; - rootBody->m_isCustomBody = true; - - rootBody->m_rotationalPhaseAtStart = csbody->rotationalPhaseAtStart; - rootBody->m_orbitalPhaseAtStart = csbody->orbitalPhaseAtStart; - system->SetRootBody(rootBody); - - int humanInfestedness = 0; - CustomGetKidsOf(system, rootBody, csbody->children, &humanInfestedness, rng); - unsigned countedStars = 0; - for (RefCountedPtr b : system->GetBodies()) { - if (b->GetSuperType() == SystemBody::SUPERTYPE_STAR) { - ++countedStars; - system->AddStar(b.Get()); - } - } - assert(countedStars == system->GetNumStars()); + system->SetCustom(true, false); + system->SetNumStars(customSys->numStars); + system->SetPosition(customSys->pos); + system->SetOtherNames(customSys->other_names); + + if (customSys->name.length() > 0) system->SetName(customSys->name); + if (customSys->shortDesc.length() > 0) system->SetShortDesc(customSys->shortDesc); + if (customSys->longDesc.length() > 0) system->SetLongDesc(customSys->longDesc); + + SysPolit sysPolit; + sysPolit.govType = customSys->govType; + sysPolit.lawlessness = customSys->lawlessness; + + system->SetSysPolit(sysPolit); + + if (customSys->IsRandom()) + return false; - return true; + system->SetCustom(true, true); + + const CustomSystemBody *csbody = customSys->sBody; + + SystemBody *rootBody = system->NewBody(); + *rootBody = csbody->bodyData; + rootBody->m_parent = 0; + rootBody->m_isCustomBody = true; + + system->SetRootBody(rootBody); + + int humanInfestedness = 0; + CustomGetKidsOf(system, rootBody, csbody->children, &humanInfestedness); + unsigned countedStars = 0; + for (RefCountedPtr b : system->GetBodies()) { + if (b->GetSuperType() == SystemBody::SUPERTYPE_STAR) { + ++countedStars; + system->AddStar(b.Get()); } } + + (void) countedStars; + assert(countedStars == system->GetNumStars()); return true; } -/* - * These are the nice floating point surface temp calculating turds. - * -static const double boltzman_const = 5.6704e-8; -static double calcEnergyPerUnitAreaAtDist(double star_radius, double star_temp, double object_dist) +bool StarSystemCustomGenerator::Apply(Random &rng, RefCountedPtr galaxy, RefCountedPtr system, GalaxyGenerator::StarSystemConfig *config) { - const double total_solar_emission = boltzman_const * - star_temp*star_temp*star_temp*star_temp* - 4*M_PI*star_radius*star_radius; + PROFILE_SCOPED() + RefCountedPtr sec = galaxy->GetSector(system->GetPath()); + system->SetCustom(false, false); - return total_solar_emission / (4*M_PI*object_dist*object_dist); -} + if (const CustomSystem *customSys = sec->m_systems[system->GetPath().systemIndex].GetCustomSystem()) + config->isCustomOnly = ApplyToSystem(rng, system, customSys); -// bond albedo, not geometric -static double CalcSurfaceTemp(double star_radius, double star_temp, double object_dist, double albedo, double greenhouse) -{ - const double energy_per_meter2 = calcEnergyPerUnitAreaAtDist(star_radius, star_temp, object_dist); - const double surface_temp = pow(energy_per_meter2*(1-albedo)/(4*(1-greenhouse)*boltzman_const), 0.25); - return surface_temp; + return true; } -*/ -/* - * Instead we use these butt-ugly overflow-prone spat of ejaculate: - */ + /* * star_radius in sol radii * star_temp in kelvin, @@ -579,6 +502,10 @@ static double CalcSurfaceTemp(double star_radius, double star_temp, double objec */ static fixed calcEnergyPerUnitAreaAtDist(fixed star_radius, int star_temp, fixed object_dist) { + // energy = boltzmann * T^4 * 4 * PI * r^2 + // energy_per_m2 = energy / ( 4 * PI * dist^2 ) + // drop 4*PI because it directly cancels + // energy_per_m2 is later divided by the boltzmann constant so drop that too fixed temp = star_temp * fixed(1, 5778); //normalize to Sun's temperature const fixed total_solar_emission = temp * temp * temp * temp * star_radius * star_radius; @@ -649,6 +576,19 @@ int StarSystemRandomGenerator::CalcSurfaceTemp(const SystemBody *primary, fixed } energy_per_meter2 += calcEnergyPerUnitAreaAtDist(s->m_radius, s->m_averageTemp, dist); } + + /* + // Can't use this version as pow() is nowhere near deterministic across multiple platforms and compilers + // Luckily pow(x, 0.25) can be expressed as two successive sqrt operations + // bond albedo, not geometric + static double CalcSurfaceTemp(double star_radius, double star_temp, double object_dist, double albedo, double greenhouse) + { + const double energy_per_meter2 = calcEnergyPerUnitAreaAtDist(star_radius, star_temp, object_dist); + const double surface_temp = pow(energy_per_meter2*(1-albedo)/(4*(1-greenhouse)*boltzman_const), 0.25); + return surface_temp; + } + */ + const fixed surface_temp_pow4 = energy_per_meter2 * (1 - albedo) / (1 - greenhouse); return (279 * int(isqrt(isqrt((surface_temp_pow4.v))))) >> (fixed::FRAC / 4); //multiplied by 279 to convert from Earth's temps to Kelvin } @@ -673,6 +613,28 @@ const SystemBody *StarSystemRandomGenerator::FindStarAndTrueOrbitalRange(const S return star; } +/** + * In this and following functions, we attempt to capture even a fraction of the + * true majesty of the infinite cosmos. + * + * System generation is based primarily around a mass-based metric, where the + * total mass of a primary body determines the maximum mass of its individual + * satellites. + * + * The area of a body's "orbital slice" (between itself and any neighboring + * bodies) informs the mass of a body to avoid obviously-unnatural + * configurations with high-mass bodies orbiting so closely as to perturb each + * others' orbits beyond what Pioneer can simulate. + * + * A series of post-processing factors are applied to this initial maximum mass + * value to curve the mass factor over the entire Hill Sphere of the parent + * body. This reduces the incidence of "gas giant spam" and produces more + * perceptually-realistic arrangements of body types and masses. + * + * Body radius and type is inferred from the mass of the body and applied based + * on a set of density heuristics (in PickPlanetType), and remaining body + * parameters are set to complete body generation. + */ void StarSystemRandomGenerator::PickPlanetType(SystemBody *sbody, Random &rand) { PROFILE_SCOPED() @@ -880,13 +842,25 @@ static fixed mass_from_disk_area(fixed a, fixed b, fixed max) assert(b <= max); assert(a >= 0); fixed one_over_3max = fixed(2, 1) / (3 * max); - return (b * b - one_over_3max * b * b * b) - - (a * a - one_over_3max * a * a * a); + + // We have to avoid overflow of fixed-point numbers + // Find a representation that doesn't calculate x*x*x + // m = 2/(3 * discMax) + // a' = a^2 - m * a^3 + // a' a^-2 = a^-2 ( a^2 - m a^3 ) -> a^2 a^-2 - m a^3 a^-2 + // a' a^-2 = 1 - m a + + // return (b * b - one_over_3max * b * b * b) - + // (a * a - one_over_3max * a * a * a); + + fixed one_max_a = fixed(1, 1) - one_over_3max * a; + fixed one_max_b = fixed(1, 1) - one_over_3max * b; + return (b * b * one_max_b) - (a * a * one_max_a); } static fixed get_disc_density(SystemBody *primary, fixed discMin, fixed discMax, fixed percentOfPrimaryMass) { - discMax = std::max(discMax, discMin); + discMax = std::max(discMax, discMin + fixed(1, 100)); // avoid divide-by-zero fixed total = mass_from_disk_area(discMin, discMax, discMax); return primary->GetMassInEarths() * percentOfPrimaryMass / total; } @@ -899,24 +873,29 @@ static inline bool test_overlap(const fixed &x1, const fixed &x2, const fixed &y (y2 >= x1 && y2 <= x2); } -void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtr system, SystemBody *primary, Random &rand) +fixed StarSystemRandomGenerator::CalcBodySatelliteShellDensity(Random &rand, SystemBody *primary, fixed &discMin, fixed &discMax) { - PROFILE_SCOPED() - fixed discMin = fixed(); - fixed discMax = fixed(5000, 1); - fixed discDensity; - SystemBody::BodySuperType parentSuperType = primary->GetSuperType(); if (parentSuperType <= SystemBody::SUPERTYPE_STAR) { if (primary->GetType() == SystemBody::TYPE_GRAVPOINT) { /* around a binary */ - discMin = primary->m_children[0]->m_orbMax * SAFE_DIST_FROM_BINARY; + if (primary->HasChildren()) + discMin = primary->m_children[0]->m_orbMax * SAFE_DIST_FROM_BINARY; + /* empty gravpoint, should only be encountered while creating custom system */ + else + discMin = fixed(1, 1); } else { /* correct thing is roche limit, but lets ignore that because * it depends on body densities and gives some strange results */ discMin = 4 * primary->GetRadiusAsFixed() * AU_SOL_RADIUS; } + + if (primary->GetType() == SystemBody::TYPE_BROWN_DWARF) { + // Increase the minimum radius around brown dwarf stars + discMin = 100 * primary->GetRadiusAsFixed() * AU_SOL_RADIUS; + } + if (primary->GetType() == SystemBody::TYPE_WHITE_DWARF) { // white dwarfs will have started as stars < 8 solar // masses or so, so pick discMax according to that @@ -925,12 +904,19 @@ void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtrGetMassAsFixed()); + discMax = 100 * rand.NormFixed().Abs() * fixed::SqrtOf(primary->GetMassAsFixed()); } + // having limited discMin by bin-separation/fake roche, and // discMax by some relation to star mass, we can now compute // disc density - discDensity = rand.Fixed() * get_disc_density(primary, discMin, discMax, fixed(2, 100)); + fixed discDensity = get_disc_density(primary, discMin, discMax, fixed(1, 100)); + + // Avoid very small, dense stars creating unnatural amounts of gas giants surrounding + discDensity *= std::min(primary->GetRadiusAsFixed() / primary->GetMassAsFixed(), fixed(1,1)); + + // NOTE: limits applied here scale the density distribution function so + // that bodies are naturally of low mass at the binary/trinary limit if ((parentSuperType == SystemBody::SUPERTYPE_STAR) && (primary->m_parent)) { // limit planets out to 10% distance to star's binary companion @@ -938,78 +924,73 @@ void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtrGetStarSystem(); if (system->GetNumStars() >= 3) { discMax = std::min(discMax, fixed(5, 100) * system->GetRootBody()->GetChildren()[0]->m_orbMin); } + + return discDensity; + } else { fixed primary_rad = primary->GetRadiusAsFixed() * AU_EARTH_RADIUS; discMin = 4 * primary_rad; - /* use hill radius to find max size of moon system. for stars botch it. - And use planets orbit around its primary as a scaler to a moon's orbit*/ - discMax = std::min(discMax, fixed(1, 20) * CalcHillRadius(primary) * primary->m_orbMin * fixed(1, 10)); - - discDensity = rand.Fixed() * get_disc_density(primary, discMin, discMax, fixed(1, 500)); + discMax = fixed(5000, 1); + // use hill radius to find max size of moon system. for stars botch it. + // And use planets orbit around its primary as a scaler to a moon's orbit + + // assume satellites only exist max 1/10th of L1 distance + // generated value should be well within precision limits + // NOTE: this is opinionated and serves to limit "useless moons" for + // gameplay purposes instead of fully representing reality + fixedf<48> hillSphereRad = CalcHillRadius(primary) * fixedf<48>(1, 4); + discMax = std::min(discMax, fixed(hillSphereRad)); + + return get_disc_density(primary, discMin, discMax, fixed(1, 500)); } +} + +void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtr system, SystemBody *primary, Random &rand) +{ + PROFILE_SCOPED() + SystemBody::BodySuperType parentSuperType = primary->GetSuperType(); - //fixed discDensity = 20*rand.NFixed(4); + // NOTE: using a consistent seed value here as body shell density should be immutable across multiple invocations + const SystemPath &path = system->GetPath(); + Random rng { BODY_SATELLITE_SALT, primary->GetSeed(), uint32_t(path.sectorX), uint32_t(path.sectorY), uint32_t(path.sectorZ), UNIVERSE_SEED }; - //Output("Around %s: Range %f -> %f AU\n", primary->GetName().c_str(), discMin.ToDouble(), discMax.ToDouble()); + fixed discMin; + fixed discMax; + fixed discDensity = CalcBodySatelliteShellDensity(rng, primary, discMin, discMax); - fixed initialJump = rand.NFixed(5); - fixed pos = (fixed(1, 1) - initialJump) * discMin + (initialJump * discMax); + if (discMin > discMax || discDensity <= 0) + return; // can't make planets here, outside of Hill radius + + // Output("Around %s: Range %f -> %f AU, Density %g\n", primary->GetName().c_str(), discMin.ToDouble(), discMax.ToDouble(), discDensity.ToDouble()); + + fixed flatJump = parentSuperType == SystemBody::SUPERTYPE_STAR ? + (primary->GetRadiusAsFixed() * 10) * AU_SOL_RADIUS : + (primary->GetRadiusAsFixed() * 16) * AU_EARTH_RADIUS; + fixed initialJump = rand.NFixed(5) * discMax; + fixed pos = discMin + rand.NormFixed(fixed(3, 1), fixed(25, 10)) * flatJump + initialJump; const RingStyle &ring = primary->GetRings(); const bool hasRings = primary->HasRings(); - while (pos < discMax) { - // periapsis, apoapsis = closest, farthest distance in orbit - fixed periapsis = pos + pos * fixed(1, 2) * rand.NFixed(2); /* + jump */ - ; - fixed ecc = rand.NFixed(3); - fixed semiMajorAxis = periapsis / (fixed(1, 1) - ecc); - fixed apoapsis = 2 * semiMajorAxis - periapsis; - if (apoapsis > discMax) break; + assert(pos >= 0); - fixed mass; - { - const fixed a = pos; - const fixed b = fixed(135, 100) * apoapsis; - mass = mass_from_disk_area(a, b, discMax); - mass *= rand.Fixed() * discDensity; - } - if (mass < 0) { // hack around overflow - Output("WARNING: planetary mass has overflowed! (child of %s)\n", primary->GetName().c_str()); - mass = fixed(Sint64(0x7fFFffFFffFFffFFull)); - } - assert(mass >= 0); - - SystemBody *planet = system->NewBody(); - planet->m_eccentricity = ecc; - planet->m_axialTilt = fixed(100, 157) * rand.NFixed(2); - planet->m_semiMajorAxis = semiMajorAxis; - planet->m_type = SystemBody::TYPE_PLANET_TERRESTRIAL; - planet->m_seed = rand.Int32(); - planet->m_parent = primary; - planet->m_mass = mass; - planet->m_rotationPeriod = fixed(rand.Int32(1, 200), 24); - - const double e = ecc.ToDouble(); - - if (primary->m_type == SystemBody::TYPE_GRAVPOINT) - planet->m_orbit.SetShapeAroundBarycentre(semiMajorAxis.ToDouble() * AU, primary->GetMass(), planet->GetMass(), e); - else - planet->m_orbit.SetShapeAroundPrimary(semiMajorAxis.ToDouble() * AU, primary->GetMass(), e); + // Generating a body can fail if there is a small distance between pos and discMax + uint32_t numTries = 0; - double r1 = rand.Double(2 * M_PI); // function parameter evaluation order is implementation-dependent - double r2 = rand.NDouble(5); // can't put two rands in the same expression - planet->m_orbit.SetPlane(matrix3x3d::RotateY(r1) * matrix3x3d::RotateX(-0.5 * M_PI + r2 * M_PI / 2.0)); - planet->m_orbit.SetPhase(rand.Double(2 * M_PI)); + while (pos < discMax && numTries++ < 30) { + SystemBody *planet = MakeBodyInOrbitSlice(rand, system.Get(), primary, pos, fixed(0), discMax, discDensity); + + if (!planet) + continue; - planet->m_inclination = FIXED_PI; - planet->m_inclination *= r2 / 2.0; - planet->m_orbMin = periapsis; - planet->m_orbMax = apoapsis; primary->m_children.push_back(planet); + fixed periapsis = planet->m_orbMin; + fixed apoapsis = planet->m_orbMax; + if (hasRings && parentSuperType == SystemBody::SUPERTYPE_ROCKY_PLANET && test_overlap(ring.minRadius, ring.maxRadius, periapsis, apoapsis)) { @@ -1020,8 +1001,8 @@ void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtrm_rings.baseColor = Color(255, 255, 255, 255); } - /* minimum separation between planets of 1.35 */ - pos = apoapsis * fixed(135, 100); + /* minimum separation between planets of 1.2x */ + pos = apoapsis * (rand.NFixed(3) + fixed(12, 10)); } int idx = 0; @@ -1046,6 +1027,112 @@ void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtr e = rMax / a - 1 + eccentricity = apoapsis / semiMajorAxis - fixed(1, 1); + + } else { + // Calculate a random orbit greater than pos and smaller than discMax + + fixed slice_bump = min_slice * fixed(1, 2); + + // Increment the initial periapsis range by a value that falls off the + // further towards the disc edge we are if orbiting a star + if (primary->GetSuperType() == SystemBody::SUPERTYPE_STAR) { + fixed bump_factor = (fixed(1, 1) - min_slice / discMax); + slice_bump += bump_factor * bump_factor * min_slice; + } + + // periapsis, apoapsis = closest, farthest distance in orbit + fixed periapsis = min_slice + slice_bump * rand.NormFixed().Abs(); + + if (periapsis > discMax) + return nullptr; + + // the closer the orbit is to the primary, the higher chance of a regular, concentric orbit + fixed ecc_factor = fixed(1, 1) - periapsis / discMax; + ecc_factor *= ecc_factor; + + eccentricity = rand.NFixed(3) * (fixed(1, 1) - ecc_factor); + semiMajorAxis = periapsis / (fixed(1, 1) - eccentricity); + + fixed apoapsis = 2 * semiMajorAxis - periapsis; + if (apoapsis > discMax) + return nullptr; + + max_slice = fixed(135, 100) * apoapsis; + } + + // reduce disc area for bodies with highly elliptical orbits + fixed inv_eccentricity = fixed(1,1) - eccentricity; + fixed max_slice_eff = min_slice + (max_slice - min_slice) * (inv_eccentricity * inv_eccentricity); + + // random mass averaging ~1/2 the density distribution for this slice + fixed mass = mass_from_disk_area(min_slice, max_slice_eff, discMax); + + // effective planetary mass grows between the primary and this point, and falls from this point to the disc edge + fixed inner_point = discMax * fixed(1, 3); + + // reduce mass of bodies between 0 .. 0.3(discMax) + fixed inner_factor = std::min(semiMajorAxis / inner_point, fixed(1, 1)); + + // squared falloff to disc edge + fixed outer_factor = std::max(semiMajorAxis - inner_point, fixed(0)) / (discMax - inner_point); + outer_factor = fixed(1, 1) - outer_factor * outer_factor; + + mass *= rand.NormFixed(fixed(1, 2), fixed(1, 2)) * inner_factor * outer_factor; + + mass *= discDensity; + + if (mass.v < 0) { // hack around overflow + Output("WARNING: planetary mass has overflowed! (child %d of %s)\n", primary->GetNumChildren(), primary->GetName().c_str()); + mass = fixed(Sint64(0x7fFFffFFffFFffFFull)); + } + + SystemBody *planet = system->NewBody(); + planet->m_semiMajorAxis = semiMajorAxis; + planet->m_eccentricity = eccentricity; + planet->m_axialTilt = fixed(100, 157) * rand.NFixed(2); + planet->m_type = SystemBody::TYPE_PLANET_TERRESTRIAL; + planet->m_seed = rand.Int32(); + planet->m_parent = primary; + planet->m_mass = mass; + planet->m_rotationPeriod = fixed(rand.Int32(1, 200), 24); + + // longitude of ascending node + planet->m_orbitalOffset = rand.Fixed() * 2 * FIXED_PI; + // inclination in the hemisphere above the equator, low probability of high-inclination orbits + fixed incl_scale = rand.Fixed() * fixed(666, 1000); + planet->m_inclination = rand.NormFixed().Abs() * incl_scale * FIXED_PI * fixed(1, 2); + // argument of periapsis, interval -PI .. PI + planet->m_argOfPeriapsis = rand.NormFixed() * FIXED_PI; + + // rare chance of reversed orbit + if (rand.Fixed() < fixed(1, 20)) + planet->m_inclination = FIXED_PI - planet->m_inclination; + + // true anomaly as rotation beyond periapsis + planet->m_orbitalPhaseAtStart = rand.Fixed() * 2 * FIXED_PI; + + planet->SetOrbitFromParameters(); + planet->SetAtmFromParameters(); + + return planet; +} + void StarSystemRandomGenerator::MakeStarOfType(SystemBody *sbody, SystemBody::BodyType type, Random &rand) { PROFILE_SCOPED() @@ -1348,7 +1435,7 @@ void PopulateStarSystemGenerator::PopulateStage1(SystemBody *sbody, StarSystem:: return; } - Uint32 _init[6] = { system->GetPath().systemIndex, Uint32(system->GetPath().sectorX), + Uint32 _init[6] = { Uint32(system->GetSeed()), Uint32(system->GetPath().sectorX), Uint32(system->GetPath().sectorY), Uint32(system->GetPath().sectorZ), UNIVERSE_SEED, Uint32(sbody->GetSeed()) }; Random rand; @@ -1511,7 +1598,7 @@ void PopulateStarSystemGenerator::PopulateAddStations(SystemBody *sbody, StarSys for (auto *child : sbody->GetChildren()) PopulateAddStations(child, system); - Uint32 _init[6] = { system->GetPath().systemIndex, Uint32(system->GetPath().sectorX), + Uint32 _init[6] = { Uint32(system->GetSeed()), Uint32(system->GetPath().sectorX), Uint32(system->GetPath().sectorY), Uint32(system->GetPath().sectorZ), sbody->GetSeed(), UNIVERSE_SEED }; Random rand; @@ -1521,7 +1608,7 @@ void PopulateStarSystemGenerator::PopulateAddStations(SystemBody *sbody, StarSys namerand->seed(_init, 6); if (sbody->GetPopulationAsFixed() < fixed(1, 1000)) return; - fixed orbMaxS = fixed(1, 4) * CalcHillRadius(sbody); + fixed orbMaxS = fixed(1, 4) * fixed(CalcHillRadius(sbody)); fixed orbMinS = fixed().FromDouble((sbody->CalcAtmosphereParams().atmosRadius + +500000.0 / EARTH_RADIUS)) * AU_EARTH_RADIUS; if (sbody->GetNumChildren() > 0) orbMaxS = std::min(orbMaxS, fixed(1, 2) * sbody->GetChildren()[0]->GetOrbMinAsFixed()); @@ -1656,17 +1743,17 @@ void PopulateStarSystemGenerator::PopulateAddStations(SystemBody *sbody, StarSys void PopulateStarSystemGenerator::SetSysPolit(RefCountedPtr galaxy, RefCountedPtr system, const fixed &human_infestedness) { SystemPath path = system->GetPath(); - const Uint32 _init[5] = { Uint32(path.sectorX), Uint32(path.sectorY), Uint32(path.sectorZ), path.systemIndex, POLIT_SEED }; + const Uint32 _init[5] = { Uint32(system->GetSeed()), Uint32(path.sectorX), Uint32(path.sectorY), Uint32(path.sectorZ), POLIT_SEED }; Random rand(_init, 5); RefCountedPtr sec = galaxy->GetSector(path); const CustomSystem *customSystem = sec->m_systems[path.systemIndex].GetCustomSystem(); - SysPolit sysPolit; - sysPolit.govType = Polit::GOV_INVALID; + SysPolit sysPolit = system->GetSysPolit(); + + /* sysPolit should already be populated from custom system definition */ + if (!customSystem) + sysPolit.govType = Polit::GOV_INVALID; - /* from custom system definition */ - if (customSystem) - sysPolit.govType = customSystem->govType; if (sysPolit.govType == Polit::GOV_INVALID) { if (path == SystemPath(0, 0, 0, 0)) { sysPolit.govType = Polit::GOV_EARTHDEMOC; @@ -1683,17 +1770,16 @@ void PopulateStarSystemGenerator::SetSysPolit(RefCountedPtr galaxy, RefC } } - if (customSystem && !customSystem->want_rand_lawlessness) - sysPolit.lawlessness = customSystem->lawlessness; - else + if (!customSystem || customSystem->want_rand_lawlessness) sysPolit.lawlessness = Polit::GetBaseLawlessness(sysPolit.govType) * rand.Fixed(); + system->SetSysPolit(sysPolit); } void PopulateStarSystemGenerator::SetCommodityLegality(RefCountedPtr system) { const SystemPath path = system->GetPath(); - const Uint32 _init[5] = { Uint32(path.sectorX), Uint32(path.sectorY), Uint32(path.sectorZ), path.systemIndex, POLIT_SALT }; + const Uint32 _init[5] = { Uint32(system->GetSeed()), Uint32(path.sectorX), Uint32(path.sectorY), Uint32(path.sectorZ), POLIT_SALT }; Random rand(_init, 5); // All legal flags were set to true on initialization @@ -1733,7 +1819,7 @@ bool PopulateStarSystemGenerator::Apply(Random &rng, RefCountedPtr galax { PROFILE_SCOPED() const bool addSpaceStations = !config->isCustomOnly; - Uint32 _init[5] = { system->GetPath().systemIndex, Uint32(system->GetPath().sectorX), Uint32(system->GetPath().sectorY), Uint32(system->GetPath().sectorZ), UNIVERSE_SEED }; + Uint32 _init[5] = { Uint32(system->GetSeed()), Uint32(system->GetPath().sectorX), Uint32(system->GetPath().sectorY), Uint32(system->GetPath().sectorZ), UNIVERSE_SEED }; Random rand; rand.seed(_init, 5); diff --git a/src/galaxy/StarSystemGenerator.h b/src/galaxy/StarSystemGenerator.h index b1511b2e4bf..676e3f190bf 100644 --- a/src/galaxy/StarSystemGenerator.h +++ b/src/galaxy/StarSystemGenerator.h @@ -13,6 +13,10 @@ class StarSystemFromSectorGenerator : public StarSystemGeneratorStage { }; class StarSystemLegacyGeneratorBase : public StarSystemGeneratorStage { +public: + static void PickAtmosphere(SystemBody *sbody); + static void PickRings(SystemBodyData *sbody, bool forceRings = false); + protected: struct StarTypeInfo { int mass[2]; // min,max % sol for stars, unused for planets @@ -22,23 +26,32 @@ class StarSystemLegacyGeneratorBase : public StarSystemGeneratorStage { static const fixed starMetallicities[]; static const StarTypeInfo starTypeInfo[]; - void PickAtmosphere(SystemBody *sbody); - void PickRings(SystemBody *sbody, bool forceRings = false); - fixed CalcHillRadius(SystemBody *sbody) const; + fixedf<48> CalcHillRadius(SystemBody *sbody) const; }; class StarSystemCustomGenerator : public StarSystemLegacyGeneratorBase { public: virtual bool Apply(Random &rng, RefCountedPtr galaxy, RefCountedPtr system, GalaxyGenerator::StarSystemConfig *config); + // returns true if the system is custom, false if the contents should be randomly generated + bool ApplyToSystem(Random &rng, RefCountedPtr system, const CustomSystem *customSys); + private: - void CustomGetKidsOf(RefCountedPtr system, SystemBody *parent, const std::vector &children, int *outHumanInfestedness, Random &rand); + void CustomGetKidsOf(RefCountedPtr system, SystemBody *parent, const std::vector &children, int *outHumanInfestedness); }; class StarSystemRandomGenerator : public StarSystemLegacyGeneratorBase { public: + static constexpr uint32_t BODY_SATELLITE_SALT = 0xf5123a90; + virtual bool Apply(Random &rng, RefCountedPtr galaxy, RefCountedPtr system, GalaxyGenerator::StarSystemConfig *config); + // Calculate the min, max distances from the primary where satellites should be generated + // Returns the mass density of a 2d slice through the center of the shell + fixed CalcBodySatelliteShellDensity(Random &rand, SystemBody *primary, fixed &discMin, fixed &discMax); + SystemBody *MakeBodyInOrbitSlice(Random &rand, StarSystem::GeneratorAPI *system, SystemBody *primary, fixed min, fixed max, fixed discMax, fixed discDensity); + void PickPlanetType(SystemBody *sbody, Random &rand); + private: void MakePlanetsAround(RefCountedPtr system, SystemBody *primary, Random &rand); void MakeRandomStar(SystemBody *sbody, Random &rand); @@ -48,7 +61,6 @@ class StarSystemRandomGenerator : public StarSystemLegacyGeneratorBase { int CalcSurfaceTemp(const SystemBody *primary, fixed distToPrimary, fixed albedo, fixed greenhouse); const SystemBody *FindStarAndTrueOrbitalRange(const SystemBody *planet, fixed &orbMin_, fixed &orbMax_) const; - void PickPlanetType(SystemBody *sbody, Random &rand); }; class PopulateStarSystemGenerator : public StarSystemLegacyGeneratorBase { diff --git a/src/galaxy/SystemBody.cpp b/src/galaxy/SystemBody.cpp index f8fc57521ad..a930b7f8cfd 100644 --- a/src/galaxy/SystemBody.cpp +++ b/src/galaxy/SystemBody.cpp @@ -6,18 +6,14 @@ #include "AtmosphereParameters.h" #include "EnumStrings.h" #include "Game.h" +#include "JsonUtils.h" #include "Lang.h" -#include "Pi.h" -#include "enum_table.h" #include "utils.h" -SystemBody::SystemBody(const SystemPath &path, StarSystem *system) : - m_parent(nullptr), - m_path(path), +SystemBodyData::SystemBodyData() : + m_type(SystemBodyType::TYPE_GRAVPOINT), m_seed(0), m_aspectRatio(1, 1), - m_orbMin(0), - m_orbMax(0), m_rotationalPhaseAtStart(0), m_semiMajorAxis(0), m_eccentricity(0), @@ -25,14 +21,223 @@ SystemBody::SystemBody(const SystemPath &path, StarSystem *system) : m_axialTilt(0), m_inclination(0), m_averageTemp(0), - m_type(TYPE_GRAVPOINT), - m_isCustomBody(false), - m_heightMapFractal(0), - m_atmosDensity(0.0), - m_system(system) + m_heightMapFractal(0) { } +void SystemBodyData::SaveToJson(Json &out) +{ + out["seed"] = m_seed; + out["name"] = m_name; + out["type"] = EnumStrings::GetString("BodyType", m_type); + + out["radius"] = m_radius; + out["aspectRatio"] = m_aspectRatio; + out["mass"] = m_mass; + out["rotationPeriod"] = m_rotationPeriod; + out["rotationPhase"] = m_rotationalPhaseAtStart; + out["humanActivity"] = m_humanActivity; + out["semiMajorAxis"] = m_semiMajorAxis; + out["eccentricity"] = m_eccentricity; + out["orbitalOffset"] = m_orbitalOffset; + out["orbitalPhase"] = m_orbitalPhaseAtStart; + out["axialTilt"] = m_axialTilt; + out["inclination"] = m_inclination; + out["argOfPeriapsis"] = m_argOfPeriapsis; + out["averageTemp"] = m_averageTemp; + + out["metallicity"] = m_metallicity; + out["volcanicity"] = m_volcanicity; + out["volatileLiquid"] = m_volatileLiquid; + out["volatileIces"] = m_volatileIces; + out["atmosDensity"] = m_volatileGas; + out["atmosOxidizing"] = m_atmosOxidizing; + out["atmosColor"] = m_atmosColor; + out["life"] = m_life; + + out["population"] = m_population; + out["agricultural"] = m_agricultural; + + bool hasRings = m_rings.maxRadius != 0; + out["hasRings"] = hasRings; + + if (hasRings) { + out["ringsMinRadius"] = m_rings.minRadius; + out["ringsMaxRadius"] = m_rings.maxRadius; + out["ringsBaseColor"] = m_rings.baseColor; + } + + if (!m_spaceStationType.empty()) + out["spaceStationType"] = m_spaceStationType; + + if (!m_heightMapFilename.empty()) { + out["heightMapFilename"] = m_heightMapFilename; + out["heightMapFractal"] = m_heightMapFractal; + } +} + +void SystemBodyData::LoadFromJson(const Json &obj) +{ + m_seed = obj.value("seed", 0); + m_name = obj.value("name", ""); + + int type = EnumStrings::GetValue("BodyType", obj.value("type", "GRAVPOINT").c_str()); + m_type = SystemBodyType::BodyType(type); + + m_radius = obj.value("radius", 0); + m_aspectRatio = obj.value("aspectRatio", 0); + m_mass = obj.value("mass", 0); + m_rotationPeriod = obj.value("rotationPeriod", 0); + m_rotationalPhaseAtStart = obj.value("rotationPhase", 0); + m_humanActivity = obj.value("humanActivity", 0); + m_semiMajorAxis = obj.value("semiMajorAxis", 0); + m_eccentricity = obj.value("eccentricity", 0); + m_orbitalOffset = obj.value("orbitalOffset", 0); + m_orbitalPhaseAtStart = obj.value("orbitalPhase", 0); + m_axialTilt = obj.value("axialTilt", 0); + m_inclination = obj.value("inclination", 0); + m_argOfPeriapsis = obj.value("argOfPeriapsis", 0); + m_averageTemp = obj.value("averageTemp", 0); + + m_metallicity = obj.value("metallicity", 0); + m_volcanicity = obj.value("volcanicity", 0); + m_volatileLiquid = obj.value("volatileLiquid", 0); + m_volatileIces = obj.value("volatileIces", 0); + m_volatileGas = obj.value("atmosDensity", 0); + m_atmosOxidizing = obj.value("atmosOxidizing", 0); + m_atmosColor = obj.value("atmosColor", 0); + m_life = obj.value("life", 0); + + m_population = obj.value("population", 0); + m_agricultural = obj.value("agricultural", 0); + + if (obj.value("hasRings", false)) { + m_rings.minRadius = obj.value("ringsMinRadius", 0); + m_rings.maxRadius = obj.value("ringsMaxRadius", 0); + m_rings.baseColor = obj.value("ringsBaseColor", {}); + } + + m_spaceStationType = obj.value("spaceStationType", ""); + + m_heightMapFilename = obj.value("heightMapFilename", ""); + m_heightMapFractal = obj.value("heightMapFractal", 0); +} + +SystemBody::SystemBody(const SystemPath &path, StarSystem *system) : + m_parent(nullptr), + m_system(system), + m_path(path), + m_orbMin(0), + m_orbMax(0) +{ +} + +void SystemBody::SetOrbitFromParameters() +{ + // Cannot orbit if no parent to orbit around + if (!m_parent) { + m_orbit = {}; + m_orbMin = 0; + m_orbMax = 0; + + return; + } + + if (m_parent->GetType() == SystemBody::TYPE_GRAVPOINT) // generalize Kepler's law to multiple stars + m_orbit.SetShapeAroundBarycentre(m_semiMajorAxis.ToDouble() * AU, m_parent->GetMass(), GetMass(), m_eccentricity.ToDouble()); + else + m_orbit.SetShapeAroundPrimary(m_semiMajorAxis.ToDouble() * AU, m_parent->GetMass(), m_eccentricity.ToDouble()); + + m_orbit.SetPhase(m_orbitalPhaseAtStart.ToDouble()); + + if (GetType() == SystemBody::TYPE_STARPORT_SURFACE) { + double longitude = m_orbitalOffset.ToDouble(); + double latitude = m_inclination.ToDouble(); + + m_orbit.SetPlane(matrix3x3d::RotateY(longitude) * matrix3x3d::RotateX(-0.5 * M_PI + latitude)); + } else { + // NOTE: rotate -Y == counter-clockwise parameterization of longitude of ascending node + m_orbit.SetPlane( + matrix3x3d::RotateY(-m_orbitalOffset.ToDouble()) * + matrix3x3d::RotateX(-0.5 * M_PI + m_inclination.ToDouble()) * + matrix3x3d::RotateZ(m_argOfPeriapsis.ToDouble())); + } + + // perihelion and aphelion (in AUs) + m_orbMin = m_semiMajorAxis - m_eccentricity * m_semiMajorAxis; + m_orbMax = 2 * m_semiMajorAxis - m_orbMin; +} + +// TODO: a more detailed atmospheric simulation should replace this +double GetSpecificHeat(SystemBody::BodySuperType superType) +{ + if (superType == SystemBody::SUPERTYPE_GAS_GIANT) + return 12950.0; // constant pressure specific heat, for a combination of hydrogen and helium + else + return 1000.5; // constant pressure specific heat, for the combination of gasses that make up air +} + +// TODO: a more detailed atmospheric simulation should replace this +double GetMolarMass(SystemBody::BodySuperType superType) +{ + if (superType == SystemBody::SUPERTYPE_GAS_GIANT) + return 0.0023139903; // molar mass, for a combination of hydrogen and helium + else + // XXX using earth's molar mass of air... + return 0.02897; +} + +double SystemBody::GetAtmPressure(double altitude) const +{ + const double gasMolarMass = GetMolarMass(GetSuperType()); + const double surfaceGravity_g = CalcSurfaceGravity(); + const double lapseRate_L = surfaceGravity_g / GetSpecificHeat(GetSuperType()); // deg/m + const double surfaceTemperature_T0 = GetAverageTemp(); //K + + return m_atmosPressure * pow((1 - lapseRate_L * altitude / surfaceTemperature_T0), + (surfaceGravity_g * gasMolarMass / (GAS_CONSTANT_R * lapseRate_L))); // in ATM since p0 was in ATM +} + +double SystemBody::GetAtmDensity(double altitude, double pressure) const +{ + double gasMolarMass = GetMolarMass(GetSuperType()); + double aerialTemp = GetAtmAverageTemp(altitude); + + return (pressure / (PA_2_ATMOS * GAS_CONSTANT_R * aerialTemp)) * gasMolarMass; +} + +double SystemBody::GetAtmAverageTemp(double altitude) const +{ + // temperature at height + const double lapseRate_L = CalcSurfaceGravity() / GetSpecificHeat(GetSuperType()); // deg/m + return double(GetAverageTemp()) - lapseRate_L * altitude; +} + +void SystemBody::SetAtmFromParameters() +{ + double gasMolarMass = GetMolarMass(GetSuperType()); + + double surfaceDensity = GetAtmSurfaceDensity() / gasMolarMass; // kg / m^3, convert to moles/m^3 + double surfaceTemperature_T0 = GetAverageTemp(); //K + + // surface pressure + //P = density*R*T=(n/V)*R*T + m_atmosPressure = PA_2_ATMOS * ((surfaceDensity) * GAS_CONSTANT_R * surfaceTemperature_T0); // in atmospheres + + double surfaceGravity_g = CalcSurfaceGravity(); + const double lapseRate_L = surfaceGravity_g / GetSpecificHeat(GetSuperType()); // deg/m + + if (m_atmosPressure < 0.002) + m_atmosRadius = 0; // no meaningful radius for atmosphere + else { + //*outPressure = p0*(1-l*h/T0)^(g*M/(R*L); + // want height for pressure 0.001 atm: + // h = (1 - exp(RL/gM * log(P/p0))) * T0 / l + double RLdivgM = (GAS_CONSTANT_R * lapseRate_L) / (surfaceGravity_g * GetMolarMass(GetSuperType())); + m_atmosRadius = (1.0 - exp(RLdivgM * log(0.001 / m_atmosPressure))) * surfaceTemperature_T0 / lapseRate_L; + } +} + bool SystemBody::HasAtmosphere() const { return (m_volatileGas > fixed(1, 100)); @@ -594,7 +799,7 @@ double SystemBody::CalcSurfaceGravity() const { double r = GetRadius(); if (r > 0.0) { - return G * GetMass() / pow(r, 2); + return G * GetMass() / (r * r); } else { return 0.0; } @@ -611,8 +816,8 @@ void SystemBody::Dump(FILE *file, const char *indent) const m_orbit.GetOrbitalPhaseAtStart()); fprintf(file, "%s\torbit a=%.6f, e=%.6f, orbMin=%.6f, orbMax=%.6f\n", indent, m_semiMajorAxis.ToDouble(), m_eccentricity.ToDouble(), m_orbMin.ToDouble(), m_orbMax.ToDouble()); - fprintf(file, "%s\t\toffset=%.6f, phase=%.6f, inclination=%.6f\n", indent, m_orbitalOffset.ToDouble(), m_orbitalPhaseAtStart.ToDouble(), - m_inclination.ToDouble()); + fprintf(file, "%s\t\toffset=%.6f, phase=%.6f, inclination=%.6f, argument=%.6f\n", indent, m_orbitalOffset.ToDouble(), m_orbitalPhaseAtStart.ToDouble(), + m_inclination.ToDouble(), m_argOfPeriapsis.ToDouble()); if (m_type != TYPE_GRAVPOINT) { fprintf(file, "%s\tseed %u\n", indent, m_seed); fprintf(file, "%s\tradius %.6f, aspect %.6f\n", indent, m_radius.ToDouble(), m_aspectRatio.ToDouble()); @@ -624,7 +829,7 @@ void SystemBody::Dump(FILE *file, const char *indent) const m_volatileLiquid.ToDouble() * 100.0, m_volatileIces.ToDouble() * 100.0); fprintf(file, "%s\tlife %.2f\n", indent, m_life.ToDouble() * 100.0); fprintf(file, "%s\tatmosphere oxidizing=%.2f, color=(%hhu,%hhu,%hhu,%hhu), density=%.6f\n", indent, - m_atmosOxidizing.ToDouble() * 100.0, m_atmosColor.r, m_atmosColor.g, m_atmosColor.b, m_atmosColor.a, m_atmosDensity); + m_atmosOxidizing.ToDouble() * 100.0, m_atmosColor.r, m_atmosColor.g, m_atmosColor.b, m_atmosColor.a, m_volatileGas.ToDouble()); fprintf(file, "%s\trings minRadius=%.2f, maxRadius=%.2f, color=(%hhu,%hhu,%hhu,%hhu)\n", indent, m_rings.minRadius.ToDouble() * 100.0, m_rings.maxRadius.ToDouble() * 100.0, m_rings.baseColor.r, m_rings.baseColor.g, m_rings.baseColor.b, m_rings.baseColor.a); fprintf(file, "%s\thuman activity %.2f, population %.0f, agricultural %.2f\n", indent, m_humanActivity.ToDouble() * 100.0, @@ -651,7 +856,7 @@ void SystemBody::ClearParentAndChildPointers() m_children.clear(); } -SystemBody *SystemBody::GetNearestJumpable() +SystemBody *SystemBody::GetNearestJumpable(double atTime) { PROFILE_SCOPED() if (IsJumpable()) return this; @@ -675,7 +880,7 @@ SystemBody *SystemBody::GetNearestJumpable() // if the body has a zero orbit or it is a surface port - we assume that // it is at the same point with the parent, just don't touch pos if (!is_zero_general(s->GetOrbit().GetSemiMajorAxis()) && s->GetType() != SystemBody::TYPE_STARPORT_SURFACE) - pos += s->GetOrbit().OrbitalPosAtTime(Pi::game->GetTime()); + pos += s->GetOrbit().OrbitalPosAtTime(atTime); if (s->IsJumpable()) jumpables.emplace_back(s, pos); else if (s == this) // the current body is definitely not jumpable, otherwise we would not be here diff --git a/src/galaxy/SystemBody.h b/src/galaxy/SystemBody.h index f3c3c11ba29..f599328b809 100644 --- a/src/galaxy/SystemBody.h +++ b/src/galaxy/SystemBody.h @@ -6,6 +6,7 @@ #include "Color.h" #include "IterationProxy.h" +#include "JsonFwd.h" #include "Orbit.h" #include "RefCounted.h" #include "fixed.h" @@ -17,11 +18,11 @@ class StarSystem; struct AtmosphereParameters; -class SystemBody : public RefCounted { +// Enum scoped access pattern base class +// Allows access to e.g. SystemBody::TYPE_GRAVPOINT +class SystemBodyType { public: - SystemBody(const SystemPath &path, StarSystem *system); - - enum BodyType { // + enum BodyType : uint32_t { // TYPE_GRAVPOINT = 0, TYPE_BROWN_DWARF = 1, // L+T Class Brown Dwarfs TYPE_WHITE_DWARF = 2, @@ -71,13 +72,74 @@ class SystemBody : public RefCounted { // XXX need larger atmosphereless thing }; - enum BodySuperType { // + enum BodySuperType : uint32_t { // SUPERTYPE_NONE = 0, SUPERTYPE_STAR = 1, SUPERTYPE_ROCKY_PLANET = 2, SUPERTYPE_GAS_GIANT = 3, SUPERTYPE_STARPORT = 4, }; +}; + +/** + * Base class containing only static data parameters for SystemBody + * + * All "runtime" data parameters are generated in SystemBody.cpp or StarSystemGenerator.cpp + */ +class SystemBodyData { +public: + SystemBodyData(); + + void SaveToJson(Json &out); + void LoadFromJson(const Json &obj); + + std::string m_name; + SystemBodyType::BodyType m_type = SystemBodyType::TYPE_GRAVPOINT; + + Uint32 m_seed; // Planet.cpp can use to generate terrain + fixed m_radius; // in earth radii for planets, sol radii for stars. equatorial radius in case of bodies which are flattened at the poles + fixed m_aspectRatio; // ratio between equatorial and polar radius for bodies with eqatorial bulges + fixed m_mass; // earth masses if planet, solar masses if star + fixed m_rotationPeriod; // in days + fixed m_rotationalPhaseAtStart; // 0 to 2 pi + fixed m_humanActivity; // 0 - 1 + fixed m_semiMajorAxis; // in AUs + fixed m_eccentricity; // 0 - 1 + fixed m_orbitalOffset; // 0 to 2 pi in radians, counterclockwise (long. of ascending node) + fixed m_orbitalPhaseAtStart; // 0 to 2 pi in radians, counterclockwise (true anomaly at epoch) + fixed m_axialTilt; // in radians + fixed m_inclination; // in radians, for surface bodies = latitude + fixed m_argOfPeriapsis; // in radians, counterclockwise + int m_averageTemp; // surface temperature in degrees Kelvin + + /* composition */ + fixed m_metallicity; // (crust) 0.0 = light (Al, SiO2, etc), 1.0 = heavy (Fe, heavy metals) + fixed m_volcanicity; // 0 = none, 1.0 = fucking volcanic + fixed m_volatileLiquid; // 1.0 = 100% ocean cover (earth = 70%) + fixed m_volatileIces; // 1.0 = 100% ice cover (earth = 3%) + fixed m_volatileGas; // 1.225 = earth atmosphere density, kg/m^3 + fixed m_atmosOxidizing; // 0.0 = reducing (H2, NH3, etc), 1.0 = oxidising (CO2, O2, etc) + fixed m_life; // 0.0 = dead, 1.0 = teeming + + /* economy type stuff */ + fixed m_population; + fixed m_agricultural; + + RingStyle m_rings; + Color m_atmosColor; + + std::string m_heightMapFilename; + unsigned int m_heightMapFractal; + + std::string m_spaceStationType; +}; + +class SystemBody : public RefCounted, public SystemBodyType, protected SystemBodyData { +public: + class EditorAPI; // Allows editor API limited access to data members without rewriting entire class + +public: + SystemBody(const SystemPath &path, StarSystem *system); const SystemPath &GetPath() const { return m_path; } SystemBody *GetParent() const { return m_parent; } @@ -86,7 +148,7 @@ class SystemBody : public RefCounted { bool IsMoon() const { return GetSuperType() == SUPERTYPE_ROCKY_PLANET && !IsPlanet(); } // We allow hyperjump to any star of the system bool IsJumpable() const { return GetSuperType() == SUPERTYPE_STAR; } - SystemBody *GetNearestJumpable(); + SystemBody *GetNearestJumpable(double atTime); bool HasChildren() const { return !m_children.empty(); } Uint32 GetNumChildren() const { return static_cast(m_children.size()); } @@ -98,7 +160,7 @@ class SystemBody : public RefCounted { inline const std::string &GetName() const { return m_name; } std::string GetAstroDescription() const; const char *GetIcon() const; - BodyType GetType() const { return m_type; } + BodyType GetType() const { return BodyType(m_type); } BodySuperType GetSuperType() const; bool IsCustomBody() const { return m_isCustomBody; } bool IsCoOrbitalWith(const SystemBody *other) const; //this and other form a binary pair @@ -162,6 +224,7 @@ class SystemBody : public RefCounted { double GetSemiMajorAxis() const { return m_semiMajorAxis.ToDouble(); } fixed GetSemiMajorAxisAsFixed() const { return m_semiMajorAxis; } fixed GetInclinationAsFixed() const { return m_inclination; } + fixed GetArgOfPeriapsisAsFixed() const { return m_argOfPeriapsis; } void SetOrbitPlane(const matrix3x3d &orient) { m_orbit.SetPlane(orient); } int GetAverageTemp() const { return m_averageTemp; } @@ -207,12 +270,26 @@ class SystemBody : public RefCounted { return Color(200, 200, 200, 255); } + // Returns color, density in kg/m^3 at sea level void GetAtmosphereFlavor(Color *outColor, double *outDensity) const { *outColor = m_atmosColor; - *outDensity = m_atmosDensity; + *outDensity = m_volatileGas.ToDouble(); } + double GetAtmSurfaceDensity() const { return m_volatileGas.ToDouble(); } + double GetAtmSurfacePressure() const { return m_atmosPressure; } + double GetAtmRadius() const { return m_atmosRadius; } + + // Calculate atmosphere pressure at given altitude (atm) + double GetAtmPressure(double altitude) const; + + // Calculate atmosphere average temperature at given altitude (deg) + double GetAtmAverageTemp(double altitude) const; + + // Calculate atmosphere density at given altitude and pressure (kg/m^3) + double GetAtmDensity(double altitude, double pressure) const; + AtmosphereParameters CalcAtmosphereParams() const; bool IsScoopable() const; @@ -221,7 +298,7 @@ class SystemBody : public RefCounted { StarSystem *GetStarSystem() const { return m_system; } - const std::string &GetSpaceStationType() const { return m_space_station_type; } + const std::string &GetSpaceStationType() const { return m_spaceStationType; } private: friend class StarSystem; @@ -230,57 +307,33 @@ class SystemBody : public RefCounted { friend class StarSystemCustomGenerator; friend class StarSystemRandomGenerator; friend class PopulateStarSystemGenerator; + friend class CustomSystemsDatabase; + + // Copy-assignment operator from another instance of SystemBodyData + SystemBody &operator=(const SystemBodyData &rhs) + { + *static_cast(this) = rhs; + return *this; + } + + void SetOrbitFromParameters(); + void SetAtmFromParameters(); void ClearParentAndChildPointers(); SystemBody *m_parent; // these are only valid if the StarSystem std::vector m_children; // that create them still exists + StarSystem *m_system; SystemPath m_path; Orbit m_orbit; - Uint32 m_seed; // Planet.cpp can use to generate terrain - std::string m_name; - fixed m_radius; // in earth radii for planets, sol radii for stars. equatorial radius in case of bodies which are flattened at the poles - fixed m_aspectRatio; // ratio between equatorial and polar radius for bodies with eqatorial bulges - fixed m_mass; // earth masses if planet, solar masses if star - fixed m_orbMin, m_orbMax; // periapsism, apoapsis in AUs - fixed m_rotationPeriod; // in days - fixed m_rotationalPhaseAtStart; // 0 to 2 pi - fixed m_humanActivity; // 0 - 1 - fixed m_semiMajorAxis; // in AUs - fixed m_eccentricity; - fixed m_orbitalOffset; - fixed m_orbitalPhaseAtStart; // 0 to 2 pi - fixed m_axialTilt; // in radians - fixed m_inclination; // in radians, for surface bodies = latitude - int m_averageTemp; - BodyType m_type; + fixed m_orbMin, m_orbMax; // periapsism, apoapsis in AUs bool m_isCustomBody; - /* composition */ - fixed m_metallicity; // (crust) 0.0 = light (Al, SiO2, etc), 1.0 = heavy (Fe, heavy metals) - fixed m_volatileGas; // 1.0 = earth atmosphere density - fixed m_volatileLiquid; // 1.0 = 100% ocean cover (earth = 70%) - fixed m_volatileIces; // 1.0 = 100% ice cover (earth = 3%) - fixed m_volcanicity; // 0 = none, 1.0 = fucking volcanic - fixed m_atmosOxidizing; // 0.0 = reducing (H2, NH3, etc), 1.0 = oxidising (CO2, O2, etc) - fixed m_life; // 0.0 = dead, 1.0 = teeming - - RingStyle m_rings; - - /* economy type stuff */ - fixed m_population; - fixed m_agricultural; - - std::string m_heightMapFilename; - unsigned int m_heightMapFractal; - - Color m_atmosColor; - double m_atmosDensity; - - StarSystem *m_system; - - std::string m_space_station_type; + // atmosphere surface pressure, unit: atm + double m_atmosPressure; + // atmosphere radius at 0.01atm, unit: meters + double m_atmosRadius; }; #endif // SYSTEMBODY_H diff --git a/src/gameconsts.h b/src/gameconsts.h index 3b62ce16226..ebb955c3afe 100644 --- a/src/gameconsts.h +++ b/src/gameconsts.h @@ -4,12 +4,14 @@ #ifndef _GAMECONSTS_H #define _GAMECONSTS_H +#include + static const double PHYSICS_HZ = 60.0; static const double MAX_LANDING_SPEED = 30.0; // m/sec static const double LIGHT_SPEED = 3e8; // m/sec -static const Uint32 UNIVERSE_SEED = 0xabcd1234; +static const uint32_t UNIVERSE_SEED = 0xabcd1234; static const double EARTH_RADIUS = 6378135.0; // m static const double EARTH_MASS = 5.9742e24; // Kg @@ -22,4 +24,6 @@ static const double G = 6.67428e-11; static const double EARTH_ATMOSPHERE_SURFACE_DENSITY = 1.225; static const double GAS_CONSTANT_R = 8.3144621; +const double PA_2_ATMOS = 1.0 / 101325.0; // pascal -> atm + #endif /* _GAMECONSTS_H */ diff --git a/src/lua/LuaSystemBody.cpp b/src/lua/LuaSystemBody.cpp index c88b4e41109..c78d7a8ba0f 100644 --- a/src/lua/LuaSystemBody.cpp +++ b/src/lua/LuaSystemBody.cpp @@ -700,7 +700,8 @@ static int l_sbody_attr_children(lua_State *l) static int l_sbody_attr_nearest_jumpable(lua_State *l) { - LuaObject::PushToLua(LuaObject::CheckFromLua(1)->GetNearestJumpable()); + double time = Pi::game->GetTime(); + LuaObject::PushToLua(LuaObject::CheckFromLua(1)->GetNearestJumpable(time)); return 1; } diff --git a/src/pigui/PerfInfo.cpp b/src/pigui/PerfInfo.cpp index c8ab2da3407..5ab6b978219 100644 --- a/src/pigui/PerfInfo.cpp +++ b/src/pigui/PerfInfo.cpp @@ -8,7 +8,9 @@ #include "LuaPiGui.h" #include "Pi.h" #include "Player.h" +#include "SectorView.h" #include "Space.h" +#include "core/Log.h" #include "graphics/Renderer.h" #include "graphics/Stats.h" #include "graphics/Texture.h" @@ -393,6 +395,17 @@ void PerfInfo::DrawWorldViewStats() const auto *sbody = Pi::player->GetNavTarget()->GetSystemBody(); ImGui::TextUnformatted(fmt::format("Name: {}, Population: {}", sbody->GetName(), sbody->GetPopulation() * 1e9).c_str()); } + + if (Pi::GetView() == Pi::game->GetSectorView()) { + if (ImGui::Button("Dump Selected System")) { + SystemPath path = Pi::game->GetSectorView()->GetSelected(); + RefCountedPtr system = Pi::game->GetGalaxy()->GetStarSystem(path); + + if (system) + system->Dump(Log::GetLog()->GetLogFileHandle()); + } + } + } void PerfInfo::DrawInputDebug()