From f0157fa09ac0876b02a0789f27116a74c405be44 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 11 Aug 2023 23:35:49 -0400 Subject: [PATCH 01/29] CustomSystem: load individual system from file --- src/galaxy/CustomSystem.cpp | 40 +++++++++++++++++++++++++++++++++---- src/galaxy/CustomSystem.h | 7 +++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index 93d183cb204..4f75a08a988 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -629,10 +629,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 +658,17 @@ void CustomSystemsDatabase::Load() RegisterCustomSystemsAPI(L); - LUA_DEBUG_CHECK(L, 0); + LUA_DEBUG_END(L, 0); + return L; +} + +void CustomSystemsDatabase::Load() +{ + 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 +676,28 @@ 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]; +} + CustomSystemsDatabase::~CustomSystemsDatabase() { for (SectorMap::iterator secIt = m_sectorMap.begin(); secIt != m_sectorMap.end(); ++secIt) { @@ -688,6 +718,8 @@ const CustomSystemsDatabase::SystemList &CustomSystemsDatabase::GetCustomSystems void CustomSystemsDatabase::AddCustomSystem(const SystemPath &path, CustomSystem *csys) { + csys->systemIndex = m_sectorMap[path].size(); + m_lastAddedSystem = SystemIndex(path, csys->systemIndex); m_sectorMap[path].push_back(csys); } diff --git a/src/galaxy/CustomSystem.h b/src/galaxy/CustomSystem.h index b4e976ba030..6ff1ff693ae 100644 --- a/src/galaxy/CustomSystem.h +++ b/src/galaxy/CustomSystem.h @@ -80,6 +80,7 @@ class CustomSystem { SystemBody::BodyType primaryType[4]; unsigned numStars; int sectorX, sectorY, sectorZ; + Uint32 systemIndex; vector3f pos; Uint32 seed; bool want_rand_explored; @@ -105,6 +106,8 @@ class CustomSystemsDatabase { void Load(); + const CustomSystem *LoadSystem(std::string_view filepath); + 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; @@ -113,10 +116,14 @@ class CustomSystemsDatabase { 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 }; From 1d9f28c13674fc1b59aa33323d6515408f20fd88 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 11 Aug 2023 23:40:25 -0400 Subject: [PATCH 02/29] CustomSystem: apply generator to individual system - Disable 1.25x multiplier to atmospheric density in StarSystemCustomGenerator - Should be replaced with changes to individual system definitions --- src/galaxy/StarSystemGenerator.cpp | 94 +++++++++++++++++------------- src/galaxy/StarSystemGenerator.h | 3 + 2 files changed, 58 insertions(+), 39 deletions(-) diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 71931cb8d2c..9c97942ad3c 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -419,7 +419,7 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrm_metallicity = csbody->metallicity; //multiple of Earth's surface density - kid->m_volatileGas = csbody->volatileGas * fixed(1225, 1000); + kid->m_volatileGas = csbody->volatileGas; // * fixed(1225, 1000); // XXX breaks round-trip from custom system definitions kid->m_volatileLiquid = csbody->volatileLiquid; kid->m_volatileIces = csbody->volatileIces; kid->m_volcanicity = csbody->volcanicity; @@ -500,50 +500,66 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtr system, const CustomSystem *customSys) +{ + system->SetCustom(true, false); + system->SetNumStars(customSys->numStars); + 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->want_rand_lawlessness ? + Polit::GetBaseLawlessness(sysPolit.govType) * rng.Fixed() : + customSys->lawlessness; + + system->SetSysPolit(sysPolit); + + if (customSys->IsRandom()) + return false; + + system->SetCustom(true, 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()); + return true; +} + bool StarSystemCustomGenerator::Apply(Random &rng, RefCountedPtr galaxy, RefCountedPtr system, GalaxyGenerator::StarSystemConfig *config) { 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()); - return true; - } - } + if (const CustomSystem *customSys = sec->m_systems[system->GetPath().systemIndex].GetCustomSystem()) + config->isCustomOnly = ApplyToSystem(rng, system, customSys); + return true; } diff --git a/src/galaxy/StarSystemGenerator.h b/src/galaxy/StarSystemGenerator.h index b1511b2e4bf..b336383e719 100644 --- a/src/galaxy/StarSystemGenerator.h +++ b/src/galaxy/StarSystemGenerator.h @@ -31,6 +31,9 @@ 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); }; From f5d15387f06e9842f542794bc918164b924c0ee4 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 11 Aug 2023 23:46:51 -0400 Subject: [PATCH 03/29] CustomSystem: don't override root body seed savegame bump --- src/galaxy/StarSystemGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 9c97942ad3c..a08c4993e71 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -525,7 +525,7 @@ bool StarSystemCustomGenerator::ApplyToSystem(Random &rng, RefCountedPtrm_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_seed = rng.Int32(); // XXX breaks manual seed set on root body rootBody->m_radius = csbody->radius; rootBody->m_aspectRatio = csbody->aspectRatio; rootBody->m_mass = csbody->mass; From 7e4a1301ca941707c8a1aed0df9e54d71a4122bf Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Mon, 14 Aug 2023 17:56:06 -0400 Subject: [PATCH 04/29] Split sector name generation into separate file --- src/galaxy/NameGenerator.cpp | 178 +++++++++++++++++++++++++++++++++ src/galaxy/NameGenerator.h | 27 +++++ src/galaxy/SectorGenerator.cpp | 174 +------------------------------- 3 files changed, 208 insertions(+), 171 deletions(-) create mode 100644 src/galaxy/NameGenerator.cpp create mode 100644 src/galaxy/NameGenerator.h diff --git a/src/galaxy/NameGenerator.cpp b/src/galaxy/NameGenerator.cpp new file mode 100644 index 00000000000..9ab176379a3 --- /dev/null +++ b/src/galaxy/NameGenerator.cpp @@ -0,0 +1,178 @@ +// 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" + }; + 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 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..7908031e69a 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; @@ -265,16 +106,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]; From e5763616b27f8e82646723f4fb2234f31ebe09f1 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Tue, 15 Aug 2023 19:17:42 -0400 Subject: [PATCH 05/29] Replace system index with system seed in procgen Avoiding the system index allows generating a system without needing to know the context of how many other systems exist in the sector, which is important for consistent results in the SystemEditor. --- src/CityOnPlanet.cpp | 1 + src/galaxy/CustomSystem.cpp | 2 ++ src/galaxy/CustomSystem.h | 1 + src/galaxy/GalaxyGenerator.cpp | 4 ++-- src/galaxy/SectorGenerator.cpp | 5 +++++ src/galaxy/StarSystemGenerator.cpp | 10 +++++----- 6 files changed, 16 insertions(+), 7 deletions(-) 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/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index 4f75a08a988..a87bba7f89a 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -386,6 +386,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; } @@ -727,6 +728,7 @@ CustomSystem::CustomSystem() : sBody(nullptr), numStars(0), seed(0), + want_rand_seed(true), want_rand_explored(true), faction(nullptr), govType(Polit::GOV_INVALID), diff --git a/src/galaxy/CustomSystem.h b/src/galaxy/CustomSystem.h index 6ff1ff693ae..baae0316b33 100644 --- a/src/galaxy/CustomSystem.h +++ b/src/galaxy/CustomSystem.h @@ -83,6 +83,7 @@ class CustomSystem { Uint32 systemIndex; vector3f pos; Uint32 seed; + bool want_rand_seed; bool want_rand_explored; bool explored; const Faction *faction; 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/SectorGenerator.cpp b/src/galaxy/SectorGenerator.cpp index 7908031e69a..fc5c07a5e13 100644 --- a/src/galaxy/SectorGenerator.cpp +++ b/src/galaxy/SectorGenerator.cpp @@ -42,6 +42,9 @@ bool SectorCustomSystemsGenerator::Apply(Random &rng, RefCountedPtr gala } s.m_customSys = cs; s.m_seed = cs->seed; + if (cs->want_rand_seed) + s.m_seed = rng.Int32(); + if (cs->want_rand_explored) { /* * 0 - ~500ly from sol: explored @@ -321,6 +324,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/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index a08c4993e71..16d07ecb234 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -1364,7 +1364,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; @@ -1527,7 +1527,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; @@ -1672,7 +1672,7 @@ 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); @@ -1709,7 +1709,7 @@ void PopulateStarSystemGenerator::SetSysPolit(RefCountedPtr galaxy, RefC 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 @@ -1749,7 +1749,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); From 0a8092d149ef8f0e2db1feee56e57752e35c2690 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Tue, 29 Aug 2023 21:35:02 -0400 Subject: [PATCH 06/29] SystemBody: fully specify orbit SystemBody did not have any concept of Argument of Periapsis, limiting the orbits that could be specified. Random generation has not been adjusted to generate orbits with random argument of periapsis. --- src/galaxy/CustomSystem.cpp | 3 +++ src/galaxy/CustomSystem.h | 3 +++ src/galaxy/StarSystemGenerator.cpp | 14 +++++++++----- src/galaxy/SystemBody.h | 12 +++++++----- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index a87bba7f89a..0d753be8ee8 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -186,6 +186,7 @@ static int l_csb_orbital_phase_at_start(lua_State *L) 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->want_rand_phase = false; lua_settop(L, 1); return 1; } @@ -755,6 +756,8 @@ CustomSystemBody::CustomSystemBody() : aspectRatio(fixed(1, 1)), averageTemp(1), want_rand_offset(true), + want_rand_arg_periapsis(true), + want_rand_phase(true), latitude(0.0), longitude(0.0), volatileGas(0), diff --git a/src/galaxy/CustomSystem.h b/src/galaxy/CustomSystem.h index baae0316b33..cdeb2edff49 100644 --- a/src/galaxy/CustomSystem.h +++ b/src/galaxy/CustomSystem.h @@ -29,7 +29,10 @@ class CustomSystemBody { fixed eccentricity; fixed orbitalOffset; fixed orbitalPhaseAtStart; // mean anomaly at start 0 to 2 pi + fixed argOfPeriapsis; bool want_rand_offset; + bool want_rand_phase; + bool want_rand_arg_periapsis; // for orbiting things, latitude = inclination float latitude, longitude; // radians fixed rotationPeriod; // in days diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 16d07ecb234..dcd9780f1ed 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -429,8 +429,9 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrm_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_orbitalOffset = csbody->want_rand_offset ? fixed::FromDouble(rand.Double(2 * M_PI)) : csbody->orbitalOffset; + kid->m_orbitalPhaseAtStart = csbody->want_rand_phase ? fixed::FromDouble(rand.Double(2 * M_PI)) : csbody->orbitalPhaseAtStart; + kid->m_argOfPeriapsis = csbody->want_rand_arg_periapsis ? fixed::FromDouble(rand.Double(2 * M_PI)) : csbody->argOfPeriapsis; kid->m_axialTilt = csbody->axialTilt; kid->m_inclination = fixed(csbody->latitude * 10000, 10000); if (kid->GetType() == SystemBody::TYPE_STARPORT_SURFACE) @@ -447,7 +448,7 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrm_orbit.SetShapeAroundPrimary(csbody->semiMajorAxis.ToDouble() * AU, parent->GetMass(), csbody->eccentricity.ToDouble()); - kid->m_orbit.SetPhase(csbody->orbitalPhaseAtStart.ToDouble()); + kid->m_orbit.SetPhase(kid->m_orbitalPhaseAtStart.ToDouble()); if (kid->GetType() == SystemBody::TYPE_STARPORT_SURFACE) { kid->m_orbit.SetPlane(matrix3x3d::RotateY(csbody->longitude) * matrix3x3d::RotateX(-0.5 * M_PI + csbody->latitude)); @@ -462,8 +463,11 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrname.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)); + // NOTE: rotate -Y == counter-clockwise parameterization of longitude of ascending node + kid->m_orbit.SetPlane( + matrix3x3d::RotateY(-kid->m_orbitalOffset.ToDouble()) * + matrix3x3d::RotateX(-0.5 * M_PI + kid->m_inclination.ToDouble()) * + matrix3x3d::RotateZ(kid->m_argOfPeriapsis.ToDouble())); } if (kid->GetSuperType() == SystemBody::SUPERTYPE_STARPORT) { (*outHumanInfestedness)++; diff --git a/src/galaxy/SystemBody.h b/src/galaxy/SystemBody.h index f3c3c11ba29..8b410da55bd 100644 --- a/src/galaxy/SystemBody.h +++ b/src/galaxy/SystemBody.h @@ -162,6 +162,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; } @@ -248,11 +249,12 @@ class SystemBody : public RefCounted { 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 + 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; BodyType m_type; bool m_isCustomBody; From e66771b685c49a4133c61c80bd4d841d8fdcfcc8 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Thu, 31 Aug 2023 04:19:01 -0400 Subject: [PATCH 07/29] StarSystem: round-trip to JSON - Add JSON-based serialization to SystemBody and StarSystem. - Will replace lua-based serialization for custom systems - Move responsibility for generating body orbit to SystemBody - Hacked in support for using both Lua and Json-based custom system formats; intend to fully deprecate Lua-based --- src/galaxy/CustomSystem.cpp | 206 +++++++++++++++++++++++++++++ src/galaxy/CustomSystem.h | 29 ++++ src/galaxy/StarSystem.cpp | 59 ++++++++- src/galaxy/StarSystem.h | 8 ++ src/galaxy/StarSystemGenerator.cpp | 29 +--- src/galaxy/SystemBody.cpp | 128 ++++++++++++++++++ src/galaxy/SystemBody.h | 7 + 7 files changed, 443 insertions(+), 23 deletions(-) diff --git a/src/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index 0d753be8ee8..544bc5ecf2f 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -8,10 +8,13 @@ #include "core/LZ4Format.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/SystemBody.h" #include "lua/LuaConstants.h" #include "lua/LuaFixed.h" #include "lua/LuaUtils.h" @@ -315,6 +318,54 @@ static luaL_Reg LuaCustomSystemBody_meta[] = { { 0, 0 } }; +void CustomSystemBody::LoadFromJson(const Json &obj) +{ + // XXX: this is copied from SystemBody::LoadFromJson because this architecture is a bit of a mess + + seed = obj.value("seed", 0); + name = obj.value("name", ""); + + int typeVal = EnumStrings::GetValue("BodyType", obj.value("type", "GRAVPOINT").c_str()); + type = SystemBody::BodyType(typeVal); + + radius = obj.value("radius", 0); + aspectRatio = obj.value("aspectRatio", 0); + mass = obj.value("mass", 0); + rotationPeriod = obj.value("rotationPeriod", 0); + // humanActivity = obj.value("humanActivity", 0); + semiMajorAxis = obj.value("semiMajorAxis", 0); + eccentricity = obj.value("eccentricity", 0); + orbitalOffset = obj.value("orbitalOffset", 0); + orbitalPhaseAtStart = obj.value("orbitalPhase", 0); + axialTilt = obj.value("axialTilt", 0); + latitude = obj.value("inclination", 0).ToDouble(); + argOfPeriapsis = obj.value("argOfPeriapsis", 0); + averageTemp = obj.value("averageTemp", 0); + // isCustomBody = obj.value("isCustom", false); + + metallicity = obj.value("metallicity", 0); + volatileGas = obj.value("volatileGas", 0); + volatileLiquid = obj.value("volatileLiquid", 0); + volatileIces = obj.value("volatileIces", 0); + volcanicity = obj.value("volcanicity", 0); + atmosOxidizing = obj.value("atmosOxidizing", 0); + atmosDensity = obj.value("atmosDensity", 0); + atmosColor = obj.value("atmosColor", 0); + life = obj.value("life", 0); + population = obj.value("population", 0); + agricultural = obj.value("agricultural", 0); + + spaceStationType = obj.value("spaceStationType", ""); + + heightMapFilename = obj.value("heightMapFilename", ""); + heightMapFractal = obj.value("heightMapFractal", 0); + + want_rand_arg_periapsis = !obj.count("argOfPeriapsis"); + want_rand_offset = !obj.count("orbitalOffset"); + want_rand_phase = !obj.count("orbitalPhase"); + want_rand_seed = !obj.count("seed"); +} + // ------- CustomSystem -------- static const char LuaCustomSystem_TypeName[] = "CustomSystem"; @@ -607,6 +658,80 @@ static luaL_Reg LuaCustomSystem_meta[] = { { 0, 0 } }; +void CustomSystem::LoadFromJson(const Json &systemdef) +{ + name = systemdef["name"].get(); + + 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())); + } + + sectorX = systemdef["sectorX"]; + sectorY = systemdef["sectorY"]; + sectorZ = systemdef["sectorZ"]; + + pos = systemdef["pos"]; + 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["sectorX"] = sectorX; + obj["sectorY"] = sectorY; + obj["sectorZ"] = sectorZ; + + obj["pos"] = pos; + + if (!want_rand_seed) + 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) @@ -700,6 +825,87 @@ const CustomSystem *CustomSystemsDatabase::LoadSystem(std::string_view filepath) return m_sectorMap[m_lastAddedSystem.first][m_lastAddedSystem.second]; } +const CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view filename, const Json &systemdef) +{ + 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.", + 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, filename); + sys->faction = nullptr; + } + } + } + + 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->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->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]); + } + + } + + SystemPath path(sys->sectorX, sys->sectorY, sys->sectorZ, 0, 0); + AddCustomSystem(path, 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) { diff --git a/src/galaxy/CustomSystem.h b/src/galaxy/CustomSystem.h index cdeb2edff49..4b6a214aa15 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" @@ -30,6 +31,7 @@ class CustomSystemBody { fixed orbitalOffset; fixed orbitalPhaseAtStart; // mean anomaly at start 0 to 2 pi fixed argOfPeriapsis; + // TODO: these are only to be used for Lua system generation bool want_rand_offset; bool want_rand_phase; bool want_rand_arg_periapsis; @@ -40,8 +42,11 @@ class CustomSystemBody { fixed axialTilt; // in radians std::string heightMapFilename; int heightMapFractal; + // TODO: these two are separate implementations to handle Lua/Json based systems + std::vector childIndicies; 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 @@ -51,6 +56,12 @@ class CustomSystemBody { fixed atmosOxidizing; // 0.0 = reducing (H2, NH3, etc), 1.0 = oxidising (CO2, O2, etc) fixed life; // 0.0 = dead, 1.0 = teeming + double atmosDensity; + Color atmosColor; + + fixed population; + fixed agricultural; + /* rings */ enum RingStatus { WANT_RANDOM_RINGS, @@ -69,10 +80,13 @@ class CustomSystemBody { void SanityChecks(); + void LoadFromJson(const Json &obj); + }; class CustomSystem { public: + static const int CUSTOM_ONLY_RADIUS = 4; CustomSystem(); ~CustomSystem(); @@ -80,12 +94,22 @@ class CustomSystem { std::string name; std::vector other_names; 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; @@ -99,6 +123,9 @@ class CustomSystem { void SanityChecks(); bool IsRandom() const { return !sBody; } + + void LoadFromJson(const Json &systemdef); + void SaveToJson(Json &obj); }; class CustomSystemsDatabase { @@ -112,6 +139,8 @@ class CustomSystemsDatabase { const CustomSystem *LoadSystem(std::string_view filepath); + const CustomSystem *LoadSystemFromJSON(std::string_view filename, const Json &systemdef); + 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; diff --git a/src/galaxy/StarSystem.cpp b/src/galaxy/StarSystem.cpp index b7205c0c2a9..0852838774f 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,60 @@ 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["sectorX"] = m_path.sectorX; + obj["sectorY"] = m_path.sectorY; + obj["sectorZ"] = m_path.sectorZ; + obj["pos"] = m_pos; + + 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 dcd9780f1ed..36d93bd5b93 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -443,42 +443,27 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrm_heightMapFractal = csbody->heightMapFractal; } - 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()); - - kid->m_orbit.SetPhase(kid->m_orbitalPhaseAtStart.ToDouble()); + kid->SetOrbitFromParameters(); - 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)", csbody->name.c_str(), kid->GetOrbit().GetSemiMajorAxis(), lowestOrbit.ToFloat()); } } else { - if (kid->m_orbit.GetSemiMajorAxis() < 1.2 * parent->GetRadius()) { + if (kid->GetOrbit().GetSemiMajorAxis() < 1.2 * parent->GetRadius()) { Error("%s's orbit is too close to its parent", csbody->name.c_str()); } } - // NOTE: rotate -Y == counter-clockwise parameterization of longitude of ascending node - kid->m_orbit.SetPlane( - matrix3x3d::RotateY(-kid->m_orbitalOffset.ToDouble()) * - matrix3x3d::RotateX(-0.5 * M_PI + kid->m_inclination.ToDouble()) * - matrix3x3d::RotateZ(kid->m_argOfPeriapsis.ToDouble())); } + 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 @@ -508,6 +493,7 @@ bool StarSystemCustomGenerator::ApplyToSystem(Random &rng, RefCountedPtrSetCustom(true, false); system->SetNumStars(customSys->numStars); + system->SetPosition(customSys->pos); 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); @@ -529,7 +515,6 @@ bool StarSystemCustomGenerator::ApplyToSystem(Random &rng, RefCountedPtrm_type = csbody->type; rootBody->m_parent = 0; rootBody->m_seed = csbody->want_rand_seed ? rng.Int32() : csbody->seed; - // rootBody->m_seed = rng.Int32(); // XXX breaks manual seed set on root body rootBody->m_radius = csbody->radius; rootBody->m_aspectRatio = csbody->aspectRatio; rootBody->m_mass = csbody->mass; diff --git a/src/galaxy/SystemBody.cpp b/src/galaxy/SystemBody.cpp index f8fc57521ad..bdef8afe729 100644 --- a/src/galaxy/SystemBody.cpp +++ b/src/galaxy/SystemBody.cpp @@ -6,6 +6,7 @@ #include "AtmosphereParameters.h" #include "EnumStrings.h" #include "Game.h" +#include "JsonUtils.h" #include "Lang.h" #include "Pi.h" #include "enum_table.h" @@ -33,6 +34,133 @@ SystemBody::SystemBody(const SystemPath &path, StarSystem *system) : { } +void SystemBody::SaveToJson(Json &out) +{ + // NOTE: we intentionally do not store information about parent/children/path + // CustomSystem will be responsible for serializing the whole-system state + + // XXX: if you change anything in this function, ensure you update + // CustomSystemBody::LoadFromJson as well + + 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["isCustom"] = m_isCustomBody; + + out["metallicity"] = m_metallicity; + out["volatileGas"] = m_volatileGas; + out["volatileLiquid"] = m_volatileLiquid; + out["volatileIces"] = m_volatileIces; + out["volcanicity"] = m_volcanicity; + out["atmosOxidizing"] = m_atmosOxidizing; + out["atmosDensity"] = m_atmosDensity; + out["atmosColor"] = m_atmosColor; + out["life"] = m_life; + out["population"] = m_population; + out["agricultural"] = m_agricultural; + + out["spaceStationType"] = m_space_station_type; + + if (!m_heightMapFilename.empty()) { + out["heightMapFileName"] = m_heightMapFilename; + out["heightMapFractal"] = m_heightMapFractal; + } +} + +void SystemBody::LoadFromJson(const Json &obj) +{ + // NOTE: we intentionally do not load parent/children/path in this function + + m_seed = obj.value("seed", 0); + m_name = obj.value("name", ""); + + int type = EnumStrings::GetValue("BodyType", obj.value("type", "GRAVPOINT").c_str()); + m_type = 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_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_isCustomBody = obj.value("isCustom", false); + + m_metallicity = obj.value("metallicity", 0); + m_volatileGas = obj.value("volatileGas", 0); + m_volatileLiquid = obj.value("volatileLiquid", 0); + m_volatileIces = obj.value("volatileIces", 0); + m_volcanicity = obj.value("volcanicity", 0); + m_atmosOxidizing = obj.value("atmosOxidizing", 0); + m_atmosDensity = obj.value("atmosDensity", 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); + + m_space_station_type = obj.value("spaceStationType", ""); + + m_heightMapFilename = obj.value("heightMapFilename", ""); + m_heightMapFractal = obj.value("heightMapFractal", 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; +} + bool SystemBody::HasAtmosphere() const { return (m_volatileGas > fixed(1, 100)); diff --git a/src/galaxy/SystemBody.h b/src/galaxy/SystemBody.h index 8b410da55bd..fa25bc238fe 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" @@ -231,6 +232,12 @@ class SystemBody : public RefCounted { friend class StarSystemCustomGenerator; friend class StarSystemRandomGenerator; friend class PopulateStarSystemGenerator; + friend class CustomSystemsDatabase; + + void SaveToJson(Json &out); + void LoadFromJson(const Json &obj); + + void SetOrbitFromParameters(); void ClearParentAndChildPointers(); From b01ba87794c54049b617710f946873d6e744196b Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Thu, 31 Aug 2023 04:20:28 -0400 Subject: [PATCH 08/29] Fix invalid serialization in from_json Codepath was never exercised, assumed fixed number was serialized as numerator/denominator. Corrected to load whole/fractional parts as intended. --- src/JsonUtils.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/JsonUtils.cpp b/src/JsonUtils.cpp index a0f195f7abb..18046a409df 100644 --- a/src/JsonUtils.cpp +++ b/src/JsonUtils.cpp @@ -538,14 +538,14 @@ 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); // 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); @@ -553,7 +553,7 @@ void from_json(const Json &obj, fixed &f) 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(numerator << f.FRAC | denominator); } } From 04b5e136b9e6fa9785c2b43270ad936e2885eb89 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Thu, 31 Aug 2023 22:48:15 -0400 Subject: [PATCH 09/29] CustomSystem: generate consistent random body seed - Generate all "missing value" random body parameters using consistent per-body hash value. - Avoids implicit dependency on system load order or body layout order. - Semantically separates body parameters which the game should generate for gameplay reasons from body parameters which were not listed in the lua custom system definition. --- src/galaxy/CustomSystem.cpp | 76 ++++++++++++++++++++++++------ src/galaxy/CustomSystem.h | 2 + src/galaxy/SectorGenerator.cpp | 4 +- src/galaxy/StarSystemGenerator.cpp | 16 +++---- src/galaxy/StarSystemGenerator.h | 2 +- 5 files changed, 73 insertions(+), 27 deletions(-) diff --git a/src/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index 544bc5ecf2f..cdcc6643336 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -5,6 +5,7 @@ #include "Galaxy.h" #include "SystemPath.h" +#include "core/FNV1a.h" #include "core/LZ4Format.h" #include "../gameconsts.h" @@ -321,8 +322,7 @@ static luaL_Reg LuaCustomSystemBody_meta[] = { void CustomSystemBody::LoadFromJson(const Json &obj) { // XXX: this is copied from SystemBody::LoadFromJson because this architecture is a bit of a mess - - seed = obj.value("seed", 0); + seed = obj["seed"]; name = obj.value("name", ""); int typeVal = EnumStrings::GetValue("BodyType", obj.value("type", "GRAVPOINT").c_str()); @@ -359,11 +359,6 @@ void CustomSystemBody::LoadFromJson(const Json &obj) heightMapFilename = obj.value("heightMapFilename", ""); heightMapFractal = obj.value("heightMapFractal", 0); - - want_rand_arg_periapsis = !obj.count("argOfPeriapsis"); - want_rand_offset = !obj.count("orbitalOffset"); - want_rand_phase = !obj.count("orbitalPhase"); - want_rand_seed = !obj.count("seed"); } // ------- CustomSystem -------- @@ -528,7 +523,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); @@ -547,12 +542,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; @@ -592,8 +589,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; @@ -624,10 +623,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; @@ -681,11 +682,11 @@ void CustomSystem::LoadFromJson(const Json &systemdef) sectorZ = systemdef["sectorZ"]; pos = systemdef["pos"]; - seed = systemdef.value("seed", 0); + seed = systemdef["seed"]; explored = systemdef.value("explored", true); lawlessness = systemdef.value("lawlessness", 0); - want_rand_seed = !systemdef.count("seed"); + want_rand_seed = false; want_rand_explored = !systemdef.count("explored"); want_rand_lawlessness = !systemdef.count("lawlessness"); @@ -717,9 +718,8 @@ void CustomSystem::SaveToJson(Json &obj) obj["sectorZ"] = sectorZ; obj["pos"] = pos; + obj["seed"] = seed; - if (!want_rand_seed) - obj["seed"] = seed; if (!want_rand_explored) obj["explored"] = explored; if(!want_rand_lawlessness) @@ -931,6 +931,52 @@ void CustomSystemsDatabase::AddCustomSystem(const SystemPath &path, CustomSystem m_sectorMap[path].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->name.data(), body->name.size()); + body->seed = rand.Int32(); + } + + if (!(body->want_rand_offset || body->want_rand_phase || body->want_rand_arg_periapsis)) + continue; + + // Generate body orbit parameters from its seed + _init[0] = body->seed; + rand.seed(_init, 5); + + if (body->want_rand_offset) + body->orbitalOffset = fixed::FromDouble(rand.Double(2 * M_PI)); + + if (body->want_rand_phase) + body->orbitalPhaseAtStart = fixed::FromDouble(rand.Double(2 * M_PI)); + + if (body->want_rand_arg_periapsis) + body->argOfPeriapsis = fixed::FromDouble(rand.Double(2 * M_PI)); + + } +} + CustomSystem::CustomSystem() : sBody(nullptr), numStars(0), diff --git a/src/galaxy/CustomSystem.h b/src/galaxy/CustomSystem.h index 4b6a214aa15..892df837558 100644 --- a/src/galaxy/CustomSystem.h +++ b/src/galaxy/CustomSystem.h @@ -147,6 +147,8 @@ class CustomSystemsDatabase { 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; diff --git a/src/galaxy/SectorGenerator.cpp b/src/galaxy/SectorGenerator.cpp index fc5c07a5e13..e6555040044 100644 --- a/src/galaxy/SectorGenerator.cpp +++ b/src/galaxy/SectorGenerator.cpp @@ -33,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++) { @@ -42,8 +42,6 @@ bool SectorCustomSystemsGenerator::Apply(Random &rng, RefCountedPtr gala } s.m_customSys = cs; s.m_seed = cs->seed; - if (cs->want_rand_seed) - s.m_seed = rng.Int32(); if (cs->want_rand_explored) { /* diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 36d93bd5b93..77896021820 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -380,7 +380,7 @@ 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 @@ -406,7 +406,7 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrNewBody(); kid->m_type = csbody->type; kid->m_parent = parent; - kid->m_seed = csbody->want_rand_seed ? rand.Int32() : csbody->seed; + kid->m_seed = csbody->seed; kid->m_radius = csbody->radius; kid->m_aspectRatio = csbody->aspectRatio; kid->m_averageTemp = csbody->averageTemp; @@ -429,9 +429,9 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrm_rotationPeriod = csbody->rotationPeriod; kid->m_rotationalPhaseAtStart = csbody->rotationalPhaseAtStart; kid->m_eccentricity = csbody->eccentricity; - kid->m_orbitalOffset = csbody->want_rand_offset ? fixed::FromDouble(rand.Double(2 * M_PI)) : csbody->orbitalOffset; - kid->m_orbitalPhaseAtStart = csbody->want_rand_phase ? fixed::FromDouble(rand.Double(2 * M_PI)) : csbody->orbitalPhaseAtStart; - kid->m_argOfPeriapsis = csbody->want_rand_arg_periapsis ? fixed::FromDouble(rand.Double(2 * M_PI)) : csbody->argOfPeriapsis; + kid->m_orbitalOffset = csbody->orbitalOffset; + kid->m_orbitalPhaseAtStart = csbody->orbitalPhaseAtStart; + kid->m_argOfPeriapsis = csbody->argOfPeriapsis; kid->m_axialTilt = csbody->axialTilt; kid->m_inclination = fixed(csbody->latitude * 10000, 10000); if (kid->GetType() == SystemBody::TYPE_STARPORT_SURFACE) @@ -485,7 +485,7 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrchildren, outHumanInfestedness, rand); + CustomGetKidsOf(system, kid, csbody->children, outHumanInfestedness); } } @@ -514,7 +514,7 @@ bool StarSystemCustomGenerator::ApplyToSystem(Random &rng, RefCountedPtrNewBody(); rootBody->m_type = csbody->type; rootBody->m_parent = 0; - rootBody->m_seed = csbody->want_rand_seed ? rng.Int32() : csbody->seed; + rootBody->m_seed = csbody->seed; rootBody->m_radius = csbody->radius; rootBody->m_aspectRatio = csbody->aspectRatio; rootBody->m_mass = csbody->mass; @@ -527,7 +527,7 @@ bool StarSystemCustomGenerator::ApplyToSystem(Random &rng, RefCountedPtrSetRootBody(rootBody); int humanInfestedness = 0; - CustomGetKidsOf(system, rootBody, csbody->children, &humanInfestedness, rng); + CustomGetKidsOf(system, rootBody, csbody->children, &humanInfestedness); unsigned countedStars = 0; for (RefCountedPtr b : system->GetBodies()) { if (b->GetSuperType() == SystemBody::SUPERTYPE_STAR) { diff --git a/src/galaxy/StarSystemGenerator.h b/src/galaxy/StarSystemGenerator.h index b336383e719..7928e1dc7b9 100644 --- a/src/galaxy/StarSystemGenerator.h +++ b/src/galaxy/StarSystemGenerator.h @@ -35,7 +35,7 @@ class StarSystemCustomGenerator : public StarSystemLegacyGeneratorBase { 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 { From d9e40f331b4509fb4a53be8c4aff3fdde2765e0b Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Thu, 31 Aug 2023 23:49:15 -0400 Subject: [PATCH 10/29] Add StarSystem dump to Ctrl+I window --- src/core/Log.h | 1 + src/galaxy/SystemBody.cpp | 4 ++-- src/pigui/PerfInfo.cpp | 13 +++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) 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/SystemBody.cpp b/src/galaxy/SystemBody.cpp index bdef8afe729..cd893a11230 100644 --- a/src/galaxy/SystemBody.cpp +++ b/src/galaxy/SystemBody.cpp @@ -739,8 +739,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()); diff --git a/src/pigui/PerfInfo.cpp b/src/pigui/PerfInfo.cpp index b0c63b11f80..f439e1f6856 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" @@ -392,6 +394,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() From 180fe43519618042b68f252b8ea895ace4f9ec6f Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 1 Sep 2023 06:43:06 -0400 Subject: [PATCH 11/29] SystemGen: separate body gen, improve heuristics Add several new heuristics controlling generation of planets and moons around primaries. - Scale body mass and orbit eccentricity based on its distance from the primary; generates more "realistic-looking" results. - Start generating bodies closer to primary (moons of gas giants look closer to Jupiter/Saturn) - More straightforward Hill Radius handling for generating moons. Add orbit generation method for inserting a new body between two existing bodies - Designed to be consumed by the SystemEditor for "random guided" body creation. Generated body orbits are fully parameterized with offset, inclination, and argument of periapsis. - Now supports (rare) retrograde orbits --- src/Random.h | 19 +++ src/galaxy/StarSystemGenerator.cpp | 215 +++++++++++++++++++---------- src/galaxy/StarSystemGenerator.h | 7 +- 3 files changed, 167 insertions(+), 74 deletions(-) diff --git a/src/Random.h b/src/Random.h index 163fda19819..9318e4b21d8 100644 --- a/src/Random.h +++ b/src/Random.h @@ -233,6 +233,25 @@ class Random : public RefCounted { return o; } + // interval (-1,1) + // triangle distribution at p=1 + // increasing steepness of normal distribution at p > 1 + inline fixed SFixed(int p) + { + fixed o = Fixed(); + o -= Fixed(); + while (--p > 0) + o *= (fixed(1, 4) + Fixed() * fixed(3, 4)); + return o; + } + + // interval (mean - stddev, mean + stddev) + // this is not a true gaussian distribution + inline fixed NormFixed(fixed mean, fixed stddev) + { + return mean + SFixed(2) * stddev; + } + const pcg32 &GetPCG() const { return mPCG; } private: diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 77896021820..884570f2ed1 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -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(); } 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)); @@ -904,13 +907,8 @@ 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) { @@ -932,10 +930,9 @@ void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtrGetMassAsFixed()); } - // 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)); + + // 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 @@ -943,78 +940,68 @@ void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtrGetStarSystem(); if (system->GetNumStars() >= 3) { discMax = std::min(discMax, fixed(5, 100) * system->GetRootBody()->GetChildren()[0]->m_orbMin); } + + // having limited discMin by bin-separation/fake roche, and + // discMax by some relation to star mass, we can now compute + // disc density + return get_disc_density(primary, discMin, discMax, fixed(2, 100)); } 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, 10); + 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 discMin; + fixed discMax; + fixed discDensity = CalcBodySatelliteShellDensity(rand, primary, discMin, discMax); + + // random density averaging 1/2 the maximum mass distribution + // discDensity *= rand.NormFixed(2, fixed(1, 2), fixed(1, 2)); //fixed discDensity = 20*rand.NFixed(4); - //Output("Around %s: Range %f -> %f AU\n", primary->GetName().c_str(), discMin.ToDouble(), discMax.ToDouble()); + // Output("Around %s: Range %f -> %f AU, Density %g\n", primary->GetName().c_str(), discMin.ToDouble(), discMax.ToDouble(), discDensity.ToDouble()); - fixed initialJump = rand.NFixed(5); - fixed pos = (fixed(1, 1) - initialJump) * discMin + (initialJump * discMax); + fixed initialJump = rand.NFixed(5) * discMax; + fixed pos = discMin + rand.NormFixed(fixed(3, 1), fixed(25, 10)) * discMin + 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; + // Generating a body can fail if there is a small distance between pos and discMax + uint32_t numTries = 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); + while (pos < discMax && numTries++ < 30) { + SystemBody *planet = MakeBodyInOrbitSlice(rand, system.Get(), primary, pos, fixed(0), discMax, discDensity); - 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)); + 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)) { @@ -1025,8 +1012,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; @@ -1051,6 +1038,88 @@ 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 + + // periapsis, apoapsis = closest, farthest distance in orbit + fixed periapsis = min_slice + min_slice * fixed(1, 2) * rand.NFixed(3); + + // 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); + + // reduce mass of bodies between 0 .. 0.2(discMax) + fixed inner_factor = std::min(semiMajorAxis / (discMax * fixed(1, 5)), fixed(1, 1)); + + // random mass averaging ~1/2 the density distribution for this slice + fixed mass = mass_from_disk_area(min_slice, max_slice_eff, discMax) * + rand.NormFixed(fixed(1, 2), fixed(1, 2)) * discDensity * inner_factor; + + if (mass < 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)); + } + assert(mass >= 0); + + 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 + planet->m_inclination = rand.SFixed(5).Abs() * FIXED_PI * fixed(1, 2); + // argument of periapsis, interval -PI .. PI + planet->m_argOfPeriapsis = rand.SFixed(1) * 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(); + return planet; +} + void StarSystemRandomGenerator::MakeStarOfType(SystemBody *sbody, SystemBody::BodyType type, Random &rand) { PROFILE_SCOPED() @@ -1526,7 +1595,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()); diff --git a/src/galaxy/StarSystemGenerator.h b/src/galaxy/StarSystemGenerator.h index 7928e1dc7b9..6107d0e0b1f 100644 --- a/src/galaxy/StarSystemGenerator.h +++ b/src/galaxy/StarSystemGenerator.h @@ -24,7 +24,7 @@ class StarSystemLegacyGeneratorBase : public StarSystemGeneratorStage { 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 { @@ -42,6 +42,11 @@ class StarSystemRandomGenerator : public StarSystemLegacyGeneratorBase { public: 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 &rng, SystemBody *primary, fixed &discMin, fixed &discMax); + SystemBody *MakeBodyInOrbitSlice(Random &rng, StarSystem::GeneratorAPI *system, SystemBody *primary, fixed min, fixed max, fixed discMax, fixed discDensity); + private: void MakePlanetsAround(RefCountedPtr system, SystemBody *primary, Random &rand); void MakeRandomStar(SystemBody *sbody, Random &rand); From 48bcfc81d9f34ea783f8a491a0c52985f01945cd Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Sat, 2 Sep 2023 04:38:25 -0400 Subject: [PATCH 12/29] Move atmosphere pressure calculation to SystemBody - SystemBody is now responsible for information about its atmospheric pressure, density, and height of tropopause (0.01 atm height) - Removed old implementation from Planet.cpp --- src/Planet.cpp | 87 ++++-------------------------- src/galaxy/CustomSystem.cpp | 3 ++ src/galaxy/StarSystemGenerator.cpp | 3 ++ src/galaxy/SystemBody.cpp | 72 ++++++++++++++++++++++++- src/galaxy/SystemBody.h | 21 +++++++- src/gameconsts.h | 6 ++- 6 files changed, 111 insertions(+), 81 deletions(-) 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/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index cdcc6643336..b938411bae1 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -958,6 +958,9 @@ void CustomSystemsDatabase::RunLuaSystemSanityChecks(CustomSystem *csys) body->seed = rand.Int32(); } + if (body->volatileGas != 0) + body->volatileGas *= fixed(1225, 1000); // lua volatile gas treated as 1.0 = 1.225kg/m^3 + if (!(body->want_rand_offset || body->want_rand_phase || body->want_rand_arg_periapsis)) continue; diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 884570f2ed1..8c903719d6c 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -447,6 +447,7 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrSetOrbitFromParameters(); + kid->SetAtmFromParameters(); if (kid->GetType() != SystemBody::TYPE_STARPORT_SURFACE) { if (kid->GetSuperType() == SystemBody::SUPERTYPE_STARPORT) { @@ -1117,6 +1118,8 @@ SystemBody *StarSystemRandomGenerator::MakeBodyInOrbitSlice(Random &rand, StarSy planet->m_orbitalPhaseAtStart = rand.Fixed() * 2 * FIXED_PI; planet->SetOrbitFromParameters(); + planet->SetAtmFromParameters(); + return planet; } diff --git a/src/galaxy/SystemBody.cpp b/src/galaxy/SystemBody.cpp index cd893a11230..7f12181c06a 100644 --- a/src/galaxy/SystemBody.cpp +++ b/src/galaxy/SystemBody.cpp @@ -161,6 +161,76 @@ void SystemBody::SetOrbitFromParameters() 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)); @@ -722,7 +792,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; } diff --git a/src/galaxy/SystemBody.h b/src/galaxy/SystemBody.h index fa25bc238fe..f6708f95ec5 100644 --- a/src/galaxy/SystemBody.h +++ b/src/galaxy/SystemBody.h @@ -215,6 +215,19 @@ class SystemBody : public RefCounted { *outDensity = m_atmosDensity; } + 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; @@ -238,6 +251,7 @@ class SystemBody : public RefCounted { void LoadFromJson(const Json &obj); void SetOrbitFromParameters(); + void SetAtmFromParameters(); void ClearParentAndChildPointers(); @@ -268,7 +282,7 @@ class SystemBody : public RefCounted { /* 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_volatileGas; // 1.225 = earth atmosphere density, kg/m^3 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 @@ -287,6 +301,11 @@ class SystemBody : public RefCounted { Color m_atmosColor; double m_atmosDensity; + // atmosphere surface pressure, unit: atm + double m_atmosPressure; + // atmosphere radius at 0.01atm, unit: meters + double m_atmosRadius; + StarSystem *m_system; std::string m_space_station_type; 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 */ From a3f1609ecefbd7fb9b70520a7e999a1aee4d4964 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Sat, 2 Sep 2023 04:40:46 -0400 Subject: [PATCH 13/29] SystemGen: consistent body satellite shell density - Ensure body satellite shell density is reproducible outside the initial generation pass. - Add std::initializer_list constructor/seed operation to Random.h - Avoid segfault if asking for satellite shell density for a gravpoint without children --- src/Random.h | 13 +++++++++++++ src/galaxy/StarSystemGenerator.cpp | 13 +++++++++++-- src/galaxy/StarSystemGenerator.h | 8 +++++--- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/Random.h b/src/Random.h index 9318e4b21d8..862468e4063 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) { diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 8c903719d6c..83e4b6a295a 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" @@ -915,7 +916,11 @@ fixed StarSystemRandomGenerator::CalcBodySatelliteShellDensity(Random &rand, Sys 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 = 0; } else { /* correct thing is roche limit, but lets ignore that because * it depends on body densities and gives some strange results */ @@ -973,9 +978,13 @@ void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtrGetSuperType(); + // 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 }; + fixed discMin; fixed discMax; - fixed discDensity = CalcBodySatelliteShellDensity(rand, primary, discMin, discMax); + fixed discDensity = CalcBodySatelliteShellDensity(rng, primary, discMin, discMax); // random density averaging 1/2 the maximum mass distribution // discDensity *= rand.NormFixed(2, fixed(1, 2), fixed(1, 2)); diff --git a/src/galaxy/StarSystemGenerator.h b/src/galaxy/StarSystemGenerator.h index 6107d0e0b1f..428d5426c3d 100644 --- a/src/galaxy/StarSystemGenerator.h +++ b/src/galaxy/StarSystemGenerator.h @@ -40,12 +40,15 @@ class StarSystemCustomGenerator : public StarSystemLegacyGeneratorBase { 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 &rng, SystemBody *primary, fixed &discMin, fixed &discMax); - SystemBody *MakeBodyInOrbitSlice(Random &rng, StarSystem::GeneratorAPI *system, SystemBody *primary, fixed min, fixed max, fixed discMax, fixed discDensity); + 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); @@ -56,7 +59,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 { From 2e1a1af959ab98513bcf28c2a08806f46c38c64b Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Sun, 3 Sep 2023 22:53:57 -0400 Subject: [PATCH 14/29] SystemGen: realism + avoid body mass overflow - Increase realism of body arrangements generated through additional post-processing factors. - Fix overflow in mass_from_disk_area (don't take the cube of the orbit apoapsis when not needed) - Fix dense stars generating large numbers of unnaturally-close gas giants - Increase mass falloff towards edge of body satellite radius --- src/galaxy/StarSystemGenerator.cpp | 92 ++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 83e4b6a295a..44ef5f6f94d 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -890,8 +890,20 @@ 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) @@ -920,12 +932,18 @@ fixed StarSystemRandomGenerator::CalcBodySatelliteShellDensity(Random &rand, Sys discMin = primary->m_children[0]->m_orbMax * SAFE_DIST_FROM_BINARY; /* empty gravpoint, should only be encountered while creating custom system */ else - discMin = 0; + 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 @@ -934,9 +952,17 @@ fixed StarSystemRandomGenerator::CalcBodySatelliteShellDensity(Random &rand, Sys discMax = 100 * rand.NFixed(2); // rand-splitting again discMax *= fixed::SqrtOf(fixed(1, 2) + fixed(8, 1) * rand.Fixed()); } else { - discMax = 100 * rand.NFixed(2) * fixed::SqrtOf(primary->GetMassAsFixed()); + discMax = 100 * rand.SFixed(2).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 + 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 @@ -951,10 +977,8 @@ fixed StarSystemRandomGenerator::CalcBodySatelliteShellDensity(Random &rand, Sys discMax = std::min(discMax, fixed(5, 100) * system->GetRootBody()->GetChildren()[0]->m_orbMin); } - // having limited discMin by bin-separation/fake roche, and - // discMax by some relation to star mass, we can now compute - // disc density - return get_disc_density(primary, discMin, discMax, fixed(2, 100)); + return discDensity; + } else { fixed primary_rad = primary->GetRadiusAsFixed() * AU_EARTH_RADIUS; discMin = 4 * primary_rad; @@ -966,7 +990,7 @@ fixed StarSystemRandomGenerator::CalcBodySatelliteShellDensity(Random &rand, Sys // 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, 10); + 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)); @@ -986,6 +1010,9 @@ void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtr discMax || discDensity <= 0) + return; // can't make planets here, outside of Hill radius + // random density averaging 1/2 the maximum mass distribution // discDensity *= rand.NormFixed(2, fixed(1, 2), fixed(1, 2)); @@ -993,11 +1020,16 @@ void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtr %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)) * discMin + initialJump; + fixed pos = discMin + rand.NormFixed(fixed(3, 1), fixed(25, 10)) * flatJump + initialJump; const RingStyle &ring = primary->GetRings(); const bool hasRings = primary->HasRings(); + assert(pos >= 0); + // Generating a body can fail if there is a small distance between pos and discMax uint32_t numTries = 0; @@ -1068,8 +1100,20 @@ SystemBody *StarSystemRandomGenerator::MakeBodyInOrbitSlice(Random &rand, StarSy } 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 / max_slice); + slice_bump += bump_factor * bump_factor * min_slice; + } + // periapsis, apoapsis = closest, farthest distance in orbit - fixed periapsis = min_slice + min_slice * fixed(1, 2) * rand.NFixed(3); + fixed periapsis = min_slice + slice_bump * rand.SFixed(2).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; @@ -1089,17 +1133,29 @@ SystemBody *StarSystemRandomGenerator::MakeBodyInOrbitSlice(Random &rand, StarSy fixed inv_eccentricity = fixed(1,1) - eccentricity; fixed max_slice_eff = min_slice + (max_slice - min_slice) * (inv_eccentricity * inv_eccentricity); - // reduce mass of bodies between 0 .. 0.2(discMax) - fixed inner_factor = std::min(semiMajorAxis / (discMax * fixed(1, 5)), fixed(1, 1)); - // random mass averaging ~1/2 the density distribution for this slice - fixed mass = mass_from_disk_area(min_slice, max_slice_eff, discMax) * - rand.NormFixed(fixed(1, 2), fixed(1, 2)) * discDensity * inner_factor; + 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; - if (mass < 0) { // hack around overflow + 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)); + // mass = fixed(Sint64(0x7fFFffFFffFFffFFull)); + fflush(Log::GetLog()->GetLogFileHandle()); } + assert(mass >= 0); SystemBody *planet = system->NewBody(); From 130a60730725a7c4f4e4f2648f7a4095dd498b50 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Sun, 3 Sep 2023 22:54:59 -0400 Subject: [PATCH 15/29] Improve SystemGen documentation comments --- src/galaxy/StarSystemGenerator.cpp | 63 ++++++++++++++++++------------ 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 44ef5f6f94d..4b1c7888182 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -557,30 +557,6 @@ bool StarSystemCustomGenerator::Apply(Random &rng, RefCountedPtr galaxy, 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) -{ - const double total_solar_emission = boltzman_const * - star_temp*star_temp*star_temp*star_temp* - 4*M_PI*star_radius*star_radius; - - return total_solar_emission / (4*M_PI*object_dist*object_dist); -} - -// 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; -} -*/ -/* - * Instead we use these butt-ugly overflow-prone spat of ejaculate: - */ /* * star_radius in sol radii * star_temp in kelvin, @@ -589,6 +565,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; @@ -659,6 +639,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 } @@ -683,6 +676,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() From dd8313aa072b00369f62fcb029dbddb87e814f37 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Wed, 6 Sep 2023 02:39:42 -0400 Subject: [PATCH 16/29] SystemGen: handle other names, rand lawlessness - Load other_names from custom system definition - Move random generation of system lawlessless to PopulateStarSystemGenerator - Removes all random generation from StarSystemCustomGenerator --- src/galaxy/StarSystemGenerator.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 4b1c7888182..64bc0edaf37 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -499,15 +499,15 @@ bool StarSystemCustomGenerator::ApplyToSystem(Random &rng, RefCountedPtrSetCustom(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->want_rand_lawlessness ? - Polit::GetBaseLawlessness(sysPolit.govType) * rng.Fixed() : - customSys->lawlessness; + sysPolit.lawlessness = customSys->lawlessness; system->SetSysPolit(sysPolit); @@ -1824,6 +1824,7 @@ void PopulateStarSystemGenerator::SetSysPolit(RefCountedPtr galaxy, RefC /* 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; @@ -1840,9 +1841,7 @@ 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); } From 1c3a0b9d34a7ea81c8d94e65e734e1aa5fb7bfef Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Wed, 6 Sep 2023 03:46:53 -0400 Subject: [PATCH 17/29] CustomSystem: clean round-trip to Json for bodies - Ensure bodies are loaded with exactly the same values as they were serialized with - Remove latitude/longitude fields from CustomSystemBody --- src/galaxy/CustomSystem.cpp | 14 +++++++------- src/galaxy/CustomSystem.h | 3 ++- src/galaxy/StarSystemGenerator.cpp | 4 +--- src/galaxy/SystemBody.cpp | 3 ++- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index b938411bae1..52ded90b28b 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -133,8 +133,7 @@ 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(inclination, inclination) CSB_FIELD_SETTER_FIXED(rotation_period, rotationPeriod) CSB_FIELD_SETTER_FIXED(axial_tilt, axialTilt) CSB_FIELD_SETTER_FIXED(metallicity, metallicity) @@ -298,10 +297,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 }, @@ -332,13 +331,14 @@ void CustomSystemBody::LoadFromJson(const Json &obj) aspectRatio = obj.value("aspectRatio", 0); mass = obj.value("mass", 0); rotationPeriod = obj.value("rotationPeriod", 0); + rotationalPhaseAtStart = obj.value("rotationPhase", 0); // humanActivity = obj.value("humanActivity", 0); semiMajorAxis = obj.value("semiMajorAxis", 0); eccentricity = obj.value("eccentricity", 0); orbitalOffset = obj.value("orbitalOffset", 0); orbitalPhaseAtStart = obj.value("orbitalPhase", 0); axialTilt = obj.value("axialTilt", 0); - latitude = obj.value("inclination", 0).ToDouble(); + inclination = obj.value("inclination", 0); argOfPeriapsis = obj.value("argOfPeriapsis", 0); averageTemp = obj.value("averageTemp", 0); // isCustomBody = obj.value("isCustom", false); @@ -1013,7 +1013,7 @@ CustomSystemBody::CustomSystemBody() : want_rand_offset(true), want_rand_arg_periapsis(true), want_rand_phase(true), - latitude(0.0), + inclination(0.0), longitude(0.0), volatileGas(0), ringStatus(WANT_RANDOM_RINGS), diff --git a/src/galaxy/CustomSystem.h b/src/galaxy/CustomSystem.h index 892df837558..c90582a585d 100644 --- a/src/galaxy/CustomSystem.h +++ b/src/galaxy/CustomSystem.h @@ -36,7 +36,8 @@ class CustomSystemBody { bool want_rand_phase; bool want_rand_arg_periapsis; // for orbiting things, latitude = inclination - float latitude, longitude; // radians + fixed inclination; // radians + fixed longitude; // radians fixed rotationPeriod; // in days fixed rotationalPhaseAtStart; // 0 to 2 pi fixed axialTilt; // in radians diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 64bc0edaf37..49adf19939f 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -437,9 +437,7 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrm_orbitalPhaseAtStart = csbody->orbitalPhaseAtStart; kid->m_argOfPeriapsis = csbody->argOfPeriapsis; 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_inclination = csbody->inclination; kid->m_semiMajorAxis = csbody->semiMajorAxis; if (csbody->heightMapFilename.length() > 0) { diff --git a/src/galaxy/SystemBody.cpp b/src/galaxy/SystemBody.cpp index 7f12181c06a..7533ba8400f 100644 --- a/src/galaxy/SystemBody.cpp +++ b/src/galaxy/SystemBody.cpp @@ -77,7 +77,7 @@ void SystemBody::SaveToJson(Json &out) out["spaceStationType"] = m_space_station_type; if (!m_heightMapFilename.empty()) { - out["heightMapFileName"] = m_heightMapFilename; + out["heightMapFilename"] = m_heightMapFilename; out["heightMapFractal"] = m_heightMapFractal; } } @@ -96,6 +96,7 @@ void SystemBody::LoadFromJson(const Json &obj) 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); From 9e76c5d6e5b63651e26a16702e177a687613a978 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Wed, 6 Sep 2023 04:50:39 -0400 Subject: [PATCH 18/29] Explicit starting spaceport seeds - Starports in starting systems have "wandered" due to implicit seed changes affecting placement algorithm. Attempt to control that by restoring old seed value. --- data/systems/00_sol.lua | 3 +++ data/systems/01_epsilon_eridani.lua | 1 + 2 files changed, 4 insertions(+) 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') From dbee4b41968bb25af9d9401290bc8c13a25cb4d8 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Tue, 12 Sep 2023 22:18:25 -0400 Subject: [PATCH 19/29] SystemBody: split underlying data from interface - SystemBodyData can now be copied by value for use in CustomSystemBody - SystemBody retains the same public interface. --- src/galaxy/StarSystemGenerator.cpp | 4 +- src/galaxy/SystemBody.cpp | 78 +++++++++-------- src/galaxy/SystemBody.h | 133 +++++++++++++++++------------ src/lua/LuaSystemBody.cpp | 3 +- 4 files changed, 125 insertions(+), 93 deletions(-) diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 49adf19939f..201b8ed4e2d 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -213,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: diff --git a/src/galaxy/SystemBody.cpp b/src/galaxy/SystemBody.cpp index 7533ba8400f..a930b7f8cfd 100644 --- a/src/galaxy/SystemBody.cpp +++ b/src/galaxy/SystemBody.cpp @@ -8,17 +8,12 @@ #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), @@ -26,22 +21,12 @@ 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 SystemBody::SaveToJson(Json &out) +void SystemBodyData::SaveToJson(Json &out) { - // NOTE: we intentionally do not store information about parent/children/path - // CustomSystem will be responsible for serializing the whole-system state - - // XXX: if you change anything in this function, ensure you update - // CustomSystemBody::LoadFromJson as well - out["seed"] = m_seed; out["name"] = m_name; out["type"] = EnumStrings::GetString("BodyType", m_type); @@ -60,21 +45,30 @@ void SystemBody::SaveToJson(Json &out) out["inclination"] = m_inclination; out["argOfPeriapsis"] = m_argOfPeriapsis; out["averageTemp"] = m_averageTemp; - out["isCustom"] = m_isCustomBody; out["metallicity"] = m_metallicity; - out["volatileGas"] = m_volatileGas; + out["volcanicity"] = m_volcanicity; out["volatileLiquid"] = m_volatileLiquid; out["volatileIces"] = m_volatileIces; - out["volcanicity"] = m_volcanicity; + out["atmosDensity"] = m_volatileGas; out["atmosOxidizing"] = m_atmosOxidizing; - out["atmosDensity"] = m_atmosDensity; out["atmosColor"] = m_atmosColor; out["life"] = m_life; + out["population"] = m_population; out["agricultural"] = m_agricultural; - out["spaceStationType"] = m_space_station_type; + 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; @@ -82,15 +76,13 @@ void SystemBody::SaveToJson(Json &out) } } -void SystemBody::LoadFromJson(const Json &obj) +void SystemBodyData::LoadFromJson(const Json &obj) { - // NOTE: we intentionally do not load parent/children/path in this function - m_seed = obj.value("seed", 0); m_name = obj.value("name", ""); int type = EnumStrings::GetValue("BodyType", obj.value("type", "GRAVPOINT").c_str()); - m_type = BodyType(type); + m_type = SystemBodyType::BodyType(type); m_radius = obj.value("radius", 0); m_aspectRatio = obj.value("aspectRatio", 0); @@ -106,26 +98,40 @@ void SystemBody::LoadFromJson(const Json &obj) m_inclination = obj.value("inclination", 0); m_argOfPeriapsis = obj.value("argOfPeriapsis", 0); m_averageTemp = obj.value("averageTemp", 0); - m_isCustomBody = obj.value("isCustom", false); m_metallicity = obj.value("metallicity", 0); - m_volatileGas = obj.value("volatileGas", 0); + m_volcanicity = obj.value("volcanicity", 0); m_volatileLiquid = obj.value("volatileLiquid", 0); m_volatileIces = obj.value("volatileIces", 0); - m_volcanicity = obj.value("volcanicity", 0); + m_volatileGas = obj.value("atmosDensity", 0); m_atmosOxidizing = obj.value("atmosOxidizing", 0); - m_atmosDensity = obj.value("atmosDensity", 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); - m_space_station_type = obj.value("spaceStationType", ""); + 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 @@ -823,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, @@ -850,7 +856,7 @@ void SystemBody::ClearParentAndChildPointers() m_children.clear(); } -SystemBody *SystemBody::GetNearestJumpable() +SystemBody *SystemBody::GetNearestJumpable(double atTime) { PROFILE_SCOPED() if (IsJumpable()) return this; @@ -874,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 f6708f95ec5..f599328b809 100644 --- a/src/galaxy/SystemBody.h +++ b/src/galaxy/SystemBody.h @@ -18,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, @@ -72,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; } @@ -87,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()); } @@ -99,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 @@ -209,10 +270,11 @@ 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(); } @@ -236,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; @@ -247,8 +309,12 @@ class SystemBody : public RefCounted { friend class PopulateStarSystemGenerator; friend class CustomSystemsDatabase; - void SaveToJson(Json &out); - void LoadFromJson(const Json &obj); + // Copy-assignment operator from another instance of SystemBodyData + SystemBody &operator=(const SystemBodyData &rhs) + { + *static_cast(this) = rhs; + return *this; + } void SetOrbitFromParameters(); void SetAtmFromParameters(); @@ -257,58 +323,17 @@ class SystemBody : public RefCounted { 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; // 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; - 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.225 = earth atmosphere density, kg/m^3 - 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; - // atmosphere surface pressure, unit: atm double m_atmosPressure; // atmosphere radius at 0.01atm, unit: meters double m_atmosRadius; - - StarSystem *m_system; - - std::string m_space_station_type; }; #endif // SYSTEMBODY_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; } From 47f484bdd810027060c4094db2ef7087b466ab61 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Tue, 12 Sep 2023 23:18:49 -0400 Subject: [PATCH 20/29] Remove CustomSystemBody data members - All data handled via SystemBodyData child - Per-member setters removed from StarSystemGenerator - Random rings now determined during CustomSystem load step --- src/galaxy/CustomSystem.cpp | 232 +++++++++++++---------------- src/galaxy/CustomSystem.h | 69 ++------- src/galaxy/StarSystemGenerator.cpp | 102 +++---------- src/galaxy/StarSystemGenerator.h | 6 +- 4 files changed, 138 insertions(+), 271 deletions(-) diff --git a/src/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index 52ded90b28b..ee640f34508 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -15,6 +15,7 @@ #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" @@ -61,8 +62,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; } @@ -84,18 +85,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) \ @@ -128,22 +130,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_FIXED(inclination, inclination) -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 @@ -154,7 +156,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; } @@ -162,7 +164,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; @@ -174,7 +176,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; @@ -188,7 +190,7 @@ 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; @@ -202,7 +204,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; } @@ -216,8 +218,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; } @@ -236,11 +238,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); @@ -251,7 +253,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; @@ -272,12 +274,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."); } @@ -318,49 +320,6 @@ static luaL_Reg LuaCustomSystemBody_meta[] = { { 0, 0 } }; -void CustomSystemBody::LoadFromJson(const Json &obj) -{ - // XXX: this is copied from SystemBody::LoadFromJson because this architecture is a bit of a mess - seed = obj["seed"]; - name = obj.value("name", ""); - - int typeVal = EnumStrings::GetValue("BodyType", obj.value("type", "GRAVPOINT").c_str()); - type = SystemBody::BodyType(typeVal); - - radius = obj.value("radius", 0); - aspectRatio = obj.value("aspectRatio", 0); - mass = obj.value("mass", 0); - rotationPeriod = obj.value("rotationPeriod", 0); - rotationalPhaseAtStart = obj.value("rotationPhase", 0); - // humanActivity = obj.value("humanActivity", 0); - semiMajorAxis = obj.value("semiMajorAxis", 0); - eccentricity = obj.value("eccentricity", 0); - orbitalOffset = obj.value("orbitalOffset", 0); - orbitalPhaseAtStart = obj.value("orbitalPhase", 0); - axialTilt = obj.value("axialTilt", 0); - inclination = obj.value("inclination", 0); - argOfPeriapsis = obj.value("argOfPeriapsis", 0); - averageTemp = obj.value("averageTemp", 0); - // isCustomBody = obj.value("isCustom", false); - - metallicity = obj.value("metallicity", 0); - volatileGas = obj.value("volatileGas", 0); - volatileLiquid = obj.value("volatileLiquid", 0); - volatileIces = obj.value("volatileIces", 0); - volcanicity = obj.value("volcanicity", 0); - atmosOxidizing = obj.value("atmosOxidizing", 0); - atmosDensity = obj.value("atmosDensity", 0); - atmosColor = obj.value("atmosColor", 0); - life = obj.value("life", 0); - population = obj.value("population", 0); - agricultural = obj.value("agricultural", 0); - - spaceStationType = obj.value("spaceStationType", ""); - - heightMapFilename = obj.value("heightMapFilename", ""); - heightMapFractal = obj.value("heightMapFractal", 0); -} - // ------- CustomSystem -------- static const char LuaCustomSystem_TypeName[] = "CustomSystem"; @@ -570,7 +529,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); @@ -581,7 +540,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) @@ -864,14 +823,14 @@ const CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view f sys->bodies.emplace_back(new CustomSystemBody()); CustomSystemBody *body = sys->bodies.back(); - body->LoadFromJson(bodynode); + 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->name, filename, childIndex.get()); + body->bodyData.m_name, filename, childIndex.get()); continue; } @@ -954,28 +913,43 @@ void CustomSystemsDatabase::RunLuaSystemSanityChecks(CustomSystem *csys) // Generate the body's seed if missing if (body->want_rand_seed) { - _init[0] = hash_32_fnv1a(body->name.data(), body->name.size()); - body->seed = rand.Int32(); + _init[0] = hash_32_fnv1a(body->bodyData.m_name.data(), body->bodyData.m_name.size()); + body->bodyData.m_seed = rand.Int32(); } - if (body->volatileGas != 0) - body->volatileGas *= fixed(1225, 1000); // lua volatile gas treated as 1.0 = 1.225kg/m^3 + if (body->bodyData.m_volatileGas != 0) + body->bodyData.m_volatileGas *= fixed(1225, 1000); // lua volatile gas treated as 1.0 = 1.225kg/m^3 - if (!(body->want_rand_offset || body->want_rand_phase || body->want_rand_arg_periapsis)) + 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->seed; + _init[0] = body->bodyData.m_seed; rand.seed(_init, 5); if (body->want_rand_offset) - body->orbitalOffset = fixed::FromDouble(rand.Double(2 * M_PI)); + body->bodyData.m_orbitalOffset = fixed::FromDouble(rand.Double(2 * M_PI)); if (body->want_rand_phase) - body->orbitalPhaseAtStart = fixed::FromDouble(rand.Double(2 * M_PI)); + body->bodyData.m_orbitalPhaseAtStart = fixed::FromDouble(rand.Double(2 * M_PI)); if (body->want_rand_arg_periapsis) - body->argOfPeriapsis = fixed::FromDouble(rand.Double(2 * M_PI)); + 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; + } + } } } @@ -1008,17 +982,11 @@ void CustomSystem::SanityChecks() } CustomSystemBody::CustomSystemBody() : - aspectRatio(fixed(1, 1)), - averageTemp(1), want_rand_offset(true), - want_rand_arg_periapsis(true), want_rand_phase(true), - inclination(0.0), - longitude(0.0), - volatileGas(0), - ringStatus(WANT_RANDOM_RINGS), - seed(0), - want_rand_seed(true) + want_rand_arg_periapsis(true), + want_rand_seed(true), + ringStatus(WANT_RANDOM_RINGS) { } @@ -1033,38 +1001,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.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.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.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.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.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.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.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.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_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 c90582a585d..100441b974c 100644 --- a/src/galaxy/CustomSystem.h +++ b/src/galaxy/CustomSystem.h @@ -17,72 +17,29 @@ 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 - fixed argOfPeriapsis; - // TODO: these are only to be used for Lua system generation - bool want_rand_offset; - bool want_rand_phase; - bool want_rand_arg_periapsis; - // for orbiting things, latitude = inclination - fixed inclination; // radians - fixed longitude; // radians - fixed rotationPeriod; // in days - fixed rotationalPhaseAtStart; // 0 to 2 pi - fixed axialTilt; // in radians - std::string heightMapFilename; - int heightMapFractal; - // TODO: these two are separate implementations to handle Lua/Json based systems - std::vector childIndicies; - 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 - - double atmosDensity; - Color atmosColor; - - fixed population; - fixed agricultural; - - /* rings */ enum RingStatus { WANT_RANDOM_RINGS, WANT_RINGS, WANT_NO_RINGS, WANT_CUSTOM_RINGS }; - RingStatus ringStatus; - fixed ringInnerRadius; - fixed ringOuterRadius; - Color ringColor; - Uint32 seed; - bool want_rand_seed; - std::string spaceStationType; + CustomSystemBody(); + ~CustomSystemBody(); - void SanityChecks(); + SystemBodyData bodyData; + // TODO: these two are separate implementations to handle Lua/Json based systems + std::vector children; + std::vector childIndicies; - void LoadFromJson(const Json &obj); + // 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; + RingStatus ringStatus; + void SanityChecks(); }; class CustomSystem { diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 201b8ed4e2d..654d9bd160c 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -274,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; @@ -293,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(); @@ -301,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; @@ -318,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(); @@ -390,59 +389,24 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrGetType() == 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; + 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->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); // XXX breaks round-trip from custom system definitions - 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_argOfPeriapsis = csbody->argOfPeriapsis; - kid->m_axialTilt = csbody->axialTilt; - kid->m_inclination = csbody->inclination; - kid->m_semiMajorAxis = csbody->semiMajorAxis; - - if (csbody->heightMapFilename.length() > 0) { - kid->m_heightMapFilename = csbody->heightMapFilename; - kid->m_heightMapFractal = csbody->heightMapFractal; + *kid = csbody->bodyData; + + // parent gravpoint mass = sum of masses of its children + if (parent->GetType() == SystemBody::TYPE_GRAVPOINT) { + if (kid->m_type >= SystemBody::TYPE_STAR_MIN && kid->m_type <= SystemBody::TYPE_STAR_MAX) + parent->m_mass += kid->m_mass; + else + parent->m_mass += kid->m_mass / SUN_MASS_TO_EARTH_MASS; } kid->SetOrbitFromParameters(); @@ -452,11 +416,11 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrGetSuperType() == SystemBody::SUPERTYPE_STARPORT) { fixed lowestOrbit = fixed().FromDouble(parent->CalcAtmosphereParams().atmosRadius + 500000.0 / EARTH_RADIUS); if (kid->GetOrbit().GetSemiMajorAxis() < lowestOrbit.ToDouble()) { - Error("%s's orbit is too close to its parent (%.2f/%.2f)", csbody->name.c_str(), kid->GetOrbit().GetSemiMajorAxis(), lowestOrbit.ToFloat()); + 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->GetOrbit().GetSemiMajorAxis() < 1.2 * parent->GetRadius()) { - Error("%s's orbit is too close to its parent", csbody->name.c_str()); + Error("%s's orbit is too close to its parent", kid->m_name.c_str()); } } } @@ -469,25 +433,6 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrringStatus) { - 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); } } @@ -513,20 +458,14 @@ bool StarSystemCustomGenerator::ApplyToSystem(Random &rng, RefCountedPtrSetCustom(true, true); + const CustomSystemBody *csbody = customSys->sBody; + SystemBody *rootBody = system->NewBody(); - rootBody->m_type = csbody->type; + *rootBody = csbody->bodyData; rootBody->m_parent = 0; - rootBody->m_seed = csbody->seed; - 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; @@ -539,6 +478,7 @@ bool StarSystemCustomGenerator::ApplyToSystem(Random &rng, RefCountedPtrGetNumStars()); return true; } diff --git a/src/galaxy/StarSystemGenerator.h b/src/galaxy/StarSystemGenerator.h index 428d5426c3d..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,8 +26,6 @@ class StarSystemLegacyGeneratorBase : public StarSystemGeneratorStage { static const fixed starMetallicities[]; static const StarTypeInfo starTypeInfo[]; - void PickAtmosphere(SystemBody *sbody); - void PickRings(SystemBody *sbody, bool forceRings = false); fixedf<48> CalcHillRadius(SystemBody *sbody) const; }; From 04af47fd29c8dfedf1b958d3721e1cdfa3a5b505 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Thu, 14 Sep 2023 23:58:23 -0400 Subject: [PATCH 21/29] JsonPatch: support patching arrays - Json Merge-Patch completely overwrites arrays rather than modifying them - Top-level keys in a .patch file starting with '$' can use Json Pointer semantics to refer to an object to modify - Patched values are applied using merge-patch semantics --- src/JsonUtils.cpp | 22 ++++++++++++++++++++-- src/JsonUtils.h | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/JsonUtils.cpp b/src/JsonUtils.cpp index 18046a409df..82b3381adf1 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 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. From f6305540e469fc1148c1a6e475ea9c0365e32f03 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 15 Sep 2023 00:47:54 -0400 Subject: [PATCH 22/29] Galaxy: load Json-format systems --- src/galaxy/CustomSystem.cpp | 69 +++++++++++++++++++++++++++++++++---- src/galaxy/CustomSystem.h | 6 +++- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index ee640f34508..ab6a6e6cbad 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -7,6 +7,8 @@ #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" @@ -14,7 +16,6 @@ #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" @@ -22,6 +23,8 @@ #include "lua/LuaUtils.h" #include "lua/LuaVector.h" +#include "profiler/Profiler.h" + #include const CustomSystemsDatabase::SystemList CustomSystemsDatabase::s_emptySystemList; // see: Null Object pattern @@ -381,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]); @@ -621,6 +626,7 @@ static luaL_Reg LuaCustomSystem_meta[] = { 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"]) @@ -750,6 +756,29 @@ lua_State *CustomSystemsDatabase::CreateLoaderState() void CustomSystemsDatabase::Load() { + PROFILE_SCOPED() + + LoadAllLuaSystems(); + + auto enumerator = FileSystem::FileEnumerator(FileSystem::gameDataFiles, m_customSysDirectory, FileSystem::FileEnumerator::Recurse); + for (auto &file : enumerator) { + if (!ends_with_ci(file.GetPath(), ".json")) + continue; + + PROFILE_SCOPED_DESC("Load Custom System File") + + const Json fileData = JsonUtils::LoadJsonDataFile(file.GetPath()); + CustomSystem *sys = LoadSystemFromJSON(file.GetPath(), fileData); + + SystemPath path(sys->sectorX, sys->sectorY, sys->sectorZ); + AddCustomSystem(path, sys); + } +} + +void CustomSystemsDatabase::LoadAllLuaSystems() +{ + PROFILE_SCOPED() + assert(!s_activeCustomSystemsDatabase); s_activeCustomSystemsDatabase = this; lua_State *L = CreateLoaderState(); @@ -784,8 +813,10 @@ const CustomSystem *CustomSystemsDatabase::LoadSystem(std::string_view filepath) return m_sectorMap[m_lastAddedSystem.first][m_lastAddedSystem.second]; } -const CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view filename, const Json &systemdef) +CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view filename, const Json &systemdef) { + PROFILE_SCOPED() + CustomSystem *sys = new CustomSystem(); try { @@ -852,9 +883,6 @@ const CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view f } - SystemPath path(sys->sectorX, sys->sectorY, sys->sectorZ, 0, 0); - AddCustomSystem(path, sys); - return sys; } catch (Json::out_of_range &e) { @@ -885,9 +913,35 @@ const CustomSystemsDatabase::SystemList &CustomSystemsDatabase::GetCustomSystems void CustomSystemsDatabase::AddCustomSystem(const SystemPath &path, CustomSystem *csys) { - csys->systemIndex = m_sectorMap[path].size(); + 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 a fully-defined + // system with the same name + if (csys->IsRandom()) { + delete csys; + return; + } + + // Fully-defined custom systems override partially-defined 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); - m_sectorMap[path].push_back(csys); + sectorSystems.push_back(csys); } void CustomSystemsDatabase::RunLuaSystemSanityChecks(CustomSystem *csys) @@ -958,6 +1012,7 @@ CustomSystem::CustomSystem() : sBody(nullptr), numStars(0), seed(0), + nameHash(0), want_rand_seed(true), want_rand_explored(true), faction(nullptr), diff --git a/src/galaxy/CustomSystem.h b/src/galaxy/CustomSystem.h index 100441b974c..da393b8df82 100644 --- a/src/galaxy/CustomSystem.h +++ b/src/galaxy/CustomSystem.h @@ -51,6 +51,8 @@ class 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 @@ -95,9 +97,11 @@ class CustomSystemsDatabase { void Load(); + + void LoadAllLuaSystems(); const CustomSystem *LoadSystem(std::string_view filepath); - const CustomSystem *LoadSystemFromJSON(std::string_view filename, const Json &systemdef); + CustomSystem *LoadSystemFromJSON(std::string_view filename, const Json &systemdef); typedef std::vector SystemList; // XXX this is not as const-safe as it should be From 2e3e958f46a7b77d8b3bd1204840ecb42761db82 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 15 Sep 2023 01:07:23 -0400 Subject: [PATCH 23/29] FileSystem: add recursive enumerator helper --- src/FileSystem.h | 5 +++++ 1 file changed, 5 insertions(+) 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: From 1423709e822746e77af6e7d9851d1d88ef2b38ab Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 15 Sep 2023 01:27:14 -0400 Subject: [PATCH 24/29] CustomSystem: support loading Json partial systems - Migration path for old 02_local_stars.lua - CustomSystem definitions use nicer storage values for sector position - Loads full system definitions from systems/custom - Loads partial system definitions from systems/partial - Add example Json-based partial system def --- data/systems/02_local_stars.lua | 2 +- data/systems/partial/02_local_stars.json | 3 + src/galaxy/CustomSystem.cpp | 96 +++++++++++++++++------- src/galaxy/CustomSystem.h | 2 +- src/galaxy/Factions.cpp | 6 +- src/galaxy/StarSystem.cpp | 6 +- 6 files changed, 80 insertions(+), 35 deletions(-) create mode 100644 data/systems/partial/02_local_stars.json 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/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/galaxy/CustomSystem.cpp b/src/galaxy/CustomSystem.cpp index ab6a6e6cbad..939f0ae8f0b 100644 --- a/src/galaxy/CustomSystem.cpp +++ b/src/galaxy/CustomSystem.cpp @@ -642,16 +642,21 @@ void CustomSystem::LoadFromJson(const Json &systemdef) primaryType[starIdx++] = SystemBody::BodyType(EnumStrings::GetValue("BodyType", type.get().c_str())); } - sectorX = systemdef["sectorX"]; - sectorY = systemdef["sectorY"]; - sectorZ = systemdef["sectorZ"]; + const Json §or = systemdef["sector"]; + sectorX = sector[0].get(); + sectorZ = sector[1].get(); + sectorY = sector[2].get(); - pos = systemdef["pos"]; - seed = systemdef["seed"]; + 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 = false; + want_rand_seed = !systemdef.count("seed"); want_rand_explored = !systemdef.count("explored"); want_rand_lawlessness = !systemdef.count("lawlessness"); @@ -678,11 +683,8 @@ void CustomSystem::SaveToJson(Json &obj) obj["numStars"] = numStars; - obj["sectorX"] = sectorX; - obj["sectorY"] = sectorY; - obj["sectorZ"] = sectorZ; - - obj["pos"] = pos; + obj["sector"] = Json::array({ sectorX, sectorY, sectorZ }); + obj["pos"] = Json::array({ pos.x, pos.y, pos.z }); obj["seed"] = seed; if (!want_rand_explored) @@ -760,18 +762,37 @@ void CustomSystemsDatabase::Load() LoadAllLuaSystems(); - auto enumerator = FileSystem::FileEnumerator(FileSystem::gameDataFiles, m_customSysDirectory, FileSystem::FileEnumerator::Recurse); - for (auto &file : enumerator) { + // 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 Custom System File") - + PROFILE_SCOPED_DESC("Load Partial System List") const Json fileData = JsonUtils::LoadJsonDataFile(file.GetPath()); - CustomSystem *sys = LoadSystemFromJSON(file.GetPath(), fileData); + for (const Json &sysdef : fileData) { + if (!sysdef.is_object()) + continue; - SystemPath path(sys->sectorX, sys->sectorY, sys->sectorZ); - AddCustomSystem(path, sys); + 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())); } } @@ -813,7 +834,7 @@ const CustomSystem *CustomSystemsDatabase::LoadSystem(std::string_view filepath) return m_sectorMap[m_lastAddedSystem.first][m_lastAddedSystem.second]; } -CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view filename, const Json &systemdef) +CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view filename, const Json &systemdef, bool mergeWithGalaxy) { PROFILE_SCOPED() @@ -826,8 +847,8 @@ CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view filenam // 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.", - filename, 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; } @@ -839,12 +860,30 @@ CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view filenam } else { sys->faction = GetGalaxy()->GetFactions()->GetFaction(factionName); if (sys->faction->idx == Faction::BAD_FACTION_IDX) { - Log::Warning("Unknown faction {} for custom system {}.", factionName, filename); + 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); @@ -860,8 +899,8 @@ CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view filenam for (const Json &childIndex : bodynode["children"]) { if (childIndex >= numBodies) { - Log::Warning("Body {} in system {} has out-of-range child index {}", - body->bodyData.m_name, filename, childIndex.get()); + Log::Warning("Body {} in system {} ({}) has out-of-range child index {}", + body->bodyData.m_name, sys->name, filename, childIndex.get()); continue; } @@ -883,6 +922,9 @@ CustomSystem *CustomSystemsDatabase::LoadSystemFromJSON(std::string_view filenam } + if (mergeWithGalaxy) + AddCustomSystem(SystemPath(sys->sectorX, sys->sectorY, sys->sectorZ), sys); + return sys; } catch (Json::out_of_range &e) { @@ -923,14 +965,14 @@ void CustomSystemsDatabase::AddCustomSystem(const SystemPath &path, CustomSystem if (system->name != csys->name) continue; - // Partially-defined systems are ignored if there is a fully-defined - // system with the same name + // 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 partially-defined systems + // Fully-defined custom systems override existing systems csys->systemIndex = system->systemIndex; m_lastAddedSystem = SystemIndex(path, csys->systemIndex); diff --git a/src/galaxy/CustomSystem.h b/src/galaxy/CustomSystem.h index da393b8df82..ef9a7fa9d5e 100644 --- a/src/galaxy/CustomSystem.h +++ b/src/galaxy/CustomSystem.h @@ -101,7 +101,7 @@ class CustomSystemsDatabase { void LoadAllLuaSystems(); const CustomSystem *LoadSystem(std::string_view filepath); - CustomSystem *LoadSystemFromJSON(std::string_view filename, const Json &systemdef); + 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 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/StarSystem.cpp b/src/galaxy/StarSystem.cpp index 0852838774f..8d53b466fb6 100644 --- a/src/galaxy/StarSystem.cpp +++ b/src/galaxy/StarSystem.cpp @@ -573,10 +573,8 @@ void StarSystem::DumpToJson(Json &obj) for (size_t idx = 0; idx < m_numStars; idx++) out_types.push_back(EnumStrings::GetString("BodyType", m_stars[idx]->GetType())); - obj["sectorX"] = m_path.sectorX; - obj["sectorY"] = m_path.sectorY; - obj["sectorZ"] = m_path.sectorZ; - obj["pos"] = m_pos; + 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; From 56bdbe95ad4daed90de7a9276b33e9bc9d9053a9 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 15 Sep 2023 01:27:43 -0400 Subject: [PATCH 25/29] Migrate Sol system to Json system format --- data/systems/custom/00_sol.json | 2762 +++++++++++++++++++++++++++++++ 1 file changed, 2762 insertions(+) create mode 100644 data/systems/custom/00_sol.json 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 From c09da8924a877689033180052fd7774d9ec78319 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 6 Oct 2023 19:31:38 -0400 Subject: [PATCH 26/29] Rename fixedp serialization to integer/fractional --- src/JsonUtils.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/JsonUtils.cpp b/src/JsonUtils.cpp index 82b3381adf1..68e6e0dff5b 100644 --- a/src/JsonUtils.cpp +++ b/src/JsonUtils.cpp @@ -560,18 +560,18 @@ void from_json(const Json &obj, fixed &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 (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 << f.FRAC | denominator); + f = fixed(integer << f.FRAC | fractional); } } From c94fe57825059e6eb115f8578a3b69432adef3d5 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 6 Oct 2023 22:50:19 -0400 Subject: [PATCH 27/29] Remove Random::SFixed, improve Random::NormFixed --- src/Random.h | 24 +++++++++++------------- src/galaxy/StarSystemGenerator.cpp | 11 ++++++----- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Random.h b/src/Random.h index 862468e4063..ea94dc7d82a 100644 --- a/src/Random.h +++ b/src/Random.h @@ -246,23 +246,21 @@ class Random : public RefCounted { return o; } - // interval (-1,1) - // triangle distribution at p=1 - // increasing steepness of normal distribution at p > 1 - inline fixed SFixed(int p) + // Returns an approximation of a normal distribution in the bounded interval + // (-1, 1) + inline fixed NormFixed() { - fixed o = Fixed(); - o -= Fixed(); - while (--p > 0) - o *= (fixed(1, 4) + Fixed() * fixed(3, 4)); - return o; + // 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 - stddev, mean + stddev) - // this is not a true gaussian distribution - inline fixed NormFixed(fixed mean, fixed stddev) + // 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 + SFixed(2) * stddev; + return mean + maxdev * NormFixed(); } const pcg32 &GetPCG() const { return mPCG; } diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 654d9bd160c..1127ef1c316 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -365,7 +365,7 @@ fixedf<48> StarSystemLegacyGeneratorBase::CalcHillRadius(SystemBody *sbody) cons 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 @@ -905,7 +905,7 @@ fixed StarSystemRandomGenerator::CalcBodySatelliteShellDensity(Random &rand, Sys discMax = 100 * rand.NFixed(2); // rand-splitting again discMax *= fixed::SqrtOf(fixed(1, 2) + fixed(8, 1) * rand.Fixed()); } else { - discMax = 100 * rand.SFixed(2).Abs() * fixed::SqrtOf(primary->GetMassAsFixed()); + discMax = 100 * rand.NormFixed().Abs() * fixed::SqrtOf(primary->GetMassAsFixed()); } // having limited discMin by bin-separation/fake roche, and @@ -1063,7 +1063,7 @@ SystemBody *StarSystemRandomGenerator::MakeBodyInOrbitSlice(Random &rand, StarSy } // periapsis, apoapsis = closest, farthest distance in orbit - fixed periapsis = min_slice + slice_bump * rand.SFixed(2).Abs(); + fixed periapsis = min_slice + slice_bump * rand.NormFixed().Abs(); if (periapsis > discMax) return nullptr; @@ -1124,9 +1124,10 @@ SystemBody *StarSystemRandomGenerator::MakeBodyInOrbitSlice(Random &rand, StarSy // 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 - planet->m_inclination = rand.SFixed(5).Abs() * FIXED_PI * fixed(1, 2); + 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.SFixed(1) * FIXED_PI; + planet->m_argOfPeriapsis = rand.NormFixed() * FIXED_PI; // rare chance of reversed orbit if (rand.Fixed() < fixed(1, 20)) From 74640d0c33eeac7b31d46e32d3f715a25e4bcb32 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 6 Oct 2023 23:04:17 -0400 Subject: [PATCH 28/29] Cleanup StarSystemGenerator - Fix some unintended divide-by-zero occurrences - Remove leftover code from debugging - Ensure system lawlessness value is correctly setup --- src/galaxy/StarSystemGenerator.cpp | 33 +++++++++++------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/galaxy/StarSystemGenerator.cpp b/src/galaxy/StarSystemGenerator.cpp index 1127ef1c316..cf749a39333 100644 --- a/src/galaxy/StarSystemGenerator.cpp +++ b/src/galaxy/StarSystemGenerator.cpp @@ -386,11 +386,10 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtr &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) { + + // 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; @@ -403,7 +402,7 @@ void StarSystemCustomGenerator::CustomGetKidsOf(RefCountedPtrGetType() == SystemBody::TYPE_GRAVPOINT) { - if (kid->m_type >= SystemBody::TYPE_STAR_MIN && kid->m_type <= SystemBody::TYPE_STAR_MAX) + if (kid->GetSuperType() == SystemBody::SUPERTYPE_STAR) parent->m_mass += kid->m_mass; else parent->m_mass += kid->m_mass / SUN_MASS_TO_EARTH_MASS; @@ -861,7 +860,7 @@ static fixed mass_from_disk_area(fixed a, fixed b, fixed max) 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; } @@ -966,11 +965,6 @@ void StarSystemRandomGenerator::MakePlanetsAround(RefCountedPtr discMax || discDensity <= 0) return; // can't make planets here, outside of Hill radius - // random density averaging 1/2 the maximum mass distribution - // discDensity *= rand.NormFixed(2, fixed(1, 2), fixed(1, 2)); - - //fixed discDensity = 20*rand.NFixed(4); - // 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 ? @@ -1058,7 +1052,7 @@ SystemBody *StarSystemRandomGenerator::MakeBodyInOrbitSlice(Random &rand, StarSy // 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 / max_slice); + fixed bump_factor = (fixed(1, 1) - min_slice / discMax); slice_bump += bump_factor * bump_factor * min_slice; } @@ -1105,12 +1099,9 @@ SystemBody *StarSystemRandomGenerator::MakeBodyInOrbitSlice(Random &rand, StarSy 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)); - fflush(Log::GetLog()->GetLogFileHandle()); + mass = fixed(Sint64(0x7fFFffFFffFFffFFull)); } - assert(mass >= 0); - SystemBody *planet = system->NewBody(); planet->m_semiMajorAxis = semiMajorAxis; planet->m_eccentricity = eccentricity; @@ -1757,12 +1748,11 @@ void PopulateStarSystemGenerator::SetSysPolit(RefCountedPtr galaxy, RefC 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(); - /* from custom system definition */ - if (customSystem) - sysPolit.govType = customSystem->govType; + /* sysPolit should already be populated from custom system definition */ + if (!customSystem) + sysPolit.govType = Polit::GOV_INVALID; if (sysPolit.govType == Polit::GOV_INVALID) { if (path == SystemPath(0, 0, 0, 0)) { @@ -1782,6 +1772,7 @@ void PopulateStarSystemGenerator::SetSysPolit(RefCountedPtr galaxy, RefC if (!customSystem || customSystem->want_rand_lawlessness) sysPolit.lawlessness = Polit::GetBaseLawlessness(sysPolit.govType) * rand.Fixed(); + system->SetSysPolit(sysPolit); } From c7681e7fe00e41909824fe7cc3cebdb67e4aa0a8 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Fri, 6 Oct 2023 23:14:02 -0400 Subject: [PATCH 29/29] Minor additions to system name generation lists --- src/galaxy/NameGenerator.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/galaxy/NameGenerator.cpp b/src/galaxy/NameGenerator.cpp index 9ab176379a3..c35ef79e236 100644 --- a/src/galaxy/NameGenerator.cpp +++ b/src/galaxy/NameGenerator.cpp @@ -27,7 +27,8 @@ namespace FrontierNames { "in", "ti", "qu", "so", "ed", "ess", "ex", "io", "ce", "ze", "fa", "ay", - "wa", "de", "ack", "gre" + "wa", "de", "ack", "gre", + "le", "du", "do", "ne" }; static const unsigned int SYS_NAME_FRAGS = ((unsigned int)(sizeof(sys_names) / sizeof(char *))); @@ -53,6 +54,7 @@ namespace HybridNames { "ed", "ess", "ex", "io", "ce", "ze", "fa", "ay", "wa", "de", "ack", "gre", + "le", "du", "do", "ne", //Doomdark-esque additions "img", "or", "ir", "dol", @@ -91,7 +93,7 @@ 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" + "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 *))); @@ -101,7 +103,7 @@ namespace Doomdark { 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" + "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 *)));