[Jongware]
Most recent update (All By Hand(TM)): 9-Aug-2009 19:46

 

As the world turns, I spread like germs
Bless the globe with the pestilence,
the hard-headed never learn
It's my testament to those burned

Wu-Tang Clan, Triumph

 

The Frontier Galaxy V: A Planet By Any Other Name

The first part in this series showed how to create millions upon millions of stars and how to label each and every one of them, albeit with a rather limited routine which could only produce some 32,768 different names. In Part II the trick to name individual stars was revealed: look'em up in a big table. Want more names, expand this table. In the previous part you could trace how the designations of individual planets and moons were constructed, leading to unimaginative names such as "Laaycan E11a" (-73,243). However, as soon as a star gets incorporated into the human sphere of influence, the first thing the intrepid explorer does is to dispatch a high speed courier to the Galactic Planet/Star Database office, registering the claimed planets under a name of his choice.

In the part of space colonized by humans we find individual worlds called "Williams's Legacy" (Miquve (-13,1)), "Ashfield Hollow" (Liaqulia (11,8)) and "New California" (Zevear (4,14)) (some unlucky explorers are commemorated with names such as "Du Prés's Grave" -- well, at least he's got his name into the history books). After a couple of years the population concentrates in dwellings large enough to build starports, and so appear on the galactic map. The richest colonies may even construct orbiters (usually named after the big shot who provided the cash: "Donaldson Orbiter", Andackio (-9,1)).

 

In Part III you could see where the crucial decision is taken to honor a planet with a name, and how it is determined how many towns and orbiters there should be; each one gets its name from the routine GenerateName. The first argument of this routine points to the current list of planets, the second indexes the world of interest. The third argument states what type of name should be returned, and the final argument is one of those unknowns (you'll see how it will be ignored completely later on).

void GenerateName (Planet_t *planet, int Counter, int NameType, int argC)
{
    GSystem_namedobjects++;

    planet[Counter].level |= 0x80;

    if (BasePlayerVariables_government_allegiance == 3)    // Alliance
    {
        if (NameType == NAMETYPE_PROJECT || NameType == NAMETYPE_COLONY)
        {
            Get_any_Name (NAMETYPE_WORLDNAME2, argC, planet[Counter].random_seed,
                planet[Counter].name);
            return;
        }
        if (NameType == NAMETYPE_CITYNAME || NameType == NAMETYPE_ORBITERNAME)
        {
            Get_any_Name (NAMETYPE_WORLDNAME3, argC, planet[Counter].random_seed,
                planet[Counter].name);
            return;
        }
    }
    Get_any_Name (NameType, argC, planet[Counter].random_seed, planet[Counter].name);
}

Every call to GenerateName contains a defined value for the type of name. The list of definitions appears as

#define NAMETYPE_MALEFULLNAME    1
#define NAMETYPE_FEMALEFULLNAME  2
#define NAMETYPE_ANYFULLNAME     3
#define NAMETYPE_CITYNAME        6
#define NAMETYPE_ORBITERNAME     7
#define NAMETYPE_PROJECT         8
#define NAMETYPE_COLONY          9
#define NAMETYPE_WORLD          10
#define NAMETYPE_INTREPID       11
#define NAMETYPE_INDUSTRY       12
#define NAMETYPE_WORLDNAME2     16    // "{16}'s End" etc.
#define NAMETYPE_WORLDNAME3     17    // "New {16}" etc.
#define NAMETYPE_SHIPNAME_AA000 18
#define NAMETYPE_SHIPNAME_000A  19
#define NAMETYPE_SHIPNAME_AA0   20

In the code shown so far not every type of name gets used; NAMETYPE_xxFULLNAME defines are used when interacting with real persons (e.g., talking to them, or exposing them to a megawatt of laser blasts). NAMETYPE_SHIPNAME_xxx codes are used when purchasing new ships or occasionally shooting them down, and NAMETYPE_INDUSTRY is used to chase unreliable executives from various firms.

The code for GenerateName shows that a different strategy is used for worlds alleged to the Alliance. If you are creating a map for First Encounters the Alliance doesn't yet exist, and so the old formula will be used.

Note also that an object-with-a-name (be it planet, city or orbiter) gets a bit set -- probably useful for some shortcut elsewhere in the game.

 

The array with all the names can be downloaded here.

unsigned int name_getrandom (int *shuffle0, int *shuffle1, unsigned int max_value)
{
    (*shuffle0) += (*shuffle1);
    (*shuffle1) = _lrotr (*shuffle1, 27);
    (*shuffle0) += (*shuffle1);
    (*shuffle0) = _lrotr (*shuffle0, 16);
    return ((((*shuffle0) & 0xffff)*max_value) >> 16) & 0xffff;
}

void Get_any_Name (int NameType, int /* ?? */, int code, char *dest)
{
    int l_newcode;
    char buf[32], *ptr;

    l_newcode = _lrotl(code, 16);
    code += l_newcode;
    l_newcode = _lrotr(l_newcode, 25);
    code += l_newcode;
    l_newcode = _lrotr(l_newcode, 28);
    code += l_newcode;

    if (NameType == NAMETYPE_WORLDNAME2 || NameType == NAMETYPE_WORLDNAME3)
    {
        strcpy (buf, Names[name_getrandom(&code,&l_newcode,88)+394]);
    } else
    {
        if ((code & 0xff) < 25)
        {
            strcpy (buf, Names[name_getrandom(&code,&l_newcode,10)+79]);
            strcat (buf, "s");
            if (!(code & 0x800))
                strcat (buf, "on");
        } else
            strcpy (buf, Names[name_getrandom(&code,&l_newcode,138)+89]);
    }

    switch (NameType)
    {
        case  6: strcpy (dest, Names[name_getrandom(&code,&l_newcode,19)+284]); break;
        case  7: strcpy (dest, Names[name_getrandom(&code,&l_newcode,11)+297]); break;
        case  8: strcpy (dest, Names[name_getrandom(&code,&l_newcode,24)+308]); break;
        case  9: strcpy (dest, Names[name_getrandom(&code,&l_newcode,34)+313]); break;
        case 10: strcpy (dest, Names[name_getrandom(&code,&l_newcode,16)+342]); break;
        case 11: strcpy (dest, Names[name_getrandom(&code,&l_newcode,19)+358]); break;
        case 12: strcpy (dest, Names[name_getrandom(&code,&l_newcode, 7)+377]); break;
        case 13: strcpy (dest, Names[name_getrandom(&code,&l_newcode, 9)+384]); break;
        case 14: strcpy (dest, Names[name_getrandom(&code,&l_newcode, 1)+393]); break; // "Police"!
        case 15: strcpy (dest, Names[name_getrandom(&code,&l_newcode,88)+394]); break;
        case 16: strcpy (dest, Names[name_getrandom(&code,&l_newcode,11)+482]); break;
        case 17: strcpy (dest, Names[name_getrandom(&code,&l_newcode,17)+493]); break;
        default: strcpy (dest, "UNDEFINED");    // Never saw this one appear, but what the hell
    }

    ptr = strchr (dest, 0xff);
    if (ptr)
    {
        memmove (ptr+strlen(buf), ptr+3, strlen(ptr)-2);
        memcpy (ptr, buf, strlen(buf));
    }
}

In Get_any_Name not every possible value of NameType gets used; that's because this routine is my own interpretation of a much larger routine, able to produce just about any string in the entire game. I've investigated only the part relating to the galactic map in detail.

The function name_getrandom is a bit-shuffler as seen before, with the addition of a max value. This max value is required because all possible names are contained in a single large list (the Names array); the first element in this list gets added per each individual type of name (the addition after each name_getrandom call).

Some names are not merely copied from the list: a few first names can act as surname with the addition of "s" or "son".

The strchr and string handling at the bottom comes courtesy of a larger scheme of string manipulation. A number of strings contain a variable code, marked by the sequence 0xFF,0x38, followed by a variable identifier. For names this variable is always number 22 (0x16) so no other check is made, and the random name is just moved into its correct position.

In the Names array you can see the different combinations possible. Funny: "David" and "Ian" are in the first names list, and "Bell" is in the surname list but "Braben" is not! Since only surnames are used in the "insert-your-name-here World" scheme, "Bell's World" is possible and "Brabenworld" isn't. (And apparently "Ian Bell" can appear on the BBS but "David Braben" can't.)

Return to the Earth

The name routine can create lots of Havens and Heavens (and the odd Wreck or Grave), but the familiar Venus, Jupiter and Pluto are missing, and if you know your surroundings, so are Merlin and Waypoint. As outlined in PlanetGeneratorMain (Part III), the seed for a solar system might not be a random number at all, but set to zero (though, technically, zero is also a random number), an indication that the data should be copied from a predefined list. This list is created at startup, and is kept in memory somewhere all the time. So let's make it a global array.

Planet_t ManufacturedSystem[147];

The hardcoded planets still need a random seed, since this is used for the layout of cities, the number of rings around planets, and some more items. Jotting down random numbers is a boring job, and one the computer can fully do on its own. (Wherever a specific seed is wanted it is supplied; if it's a zero, a real random is created. Why would one want a specific random number? Well, in the case of New San Francisco, we don't want any random city, it has to be the one with the large bridge. And Jupiter needs a few thin rings, where Saturn wants them big. Probably Dave just generated cities and planets until he found the ones he liked, and copied that random number.)

Two other helper functions are also introduced. Given a (long!) list of parameters, these are stored in the appropriate places of the Planets array. Some of the parameters are also recalculated, presumably from a sensible unit into binary notation. See, for example, the expansion of "normal" longitude and latitude values into word values, and how the temperature apparently is input in Celsius but gets converted to Kelvin.

unsigned int SolarSystem_random = 0x1234;
unsigned int SolarSystem_seed = 0x5678;

void HandcodedRandomizer (void)
{
    SolarSystem_random = ((SolarSystem_random * 3179)/39) & 0xffff;
    SolarSystem_seed   = ((SolarSystem_seed   * 3137)/39) & 0xffff;
}

void CreateSolarSystem_Body (Planet_t *dest,int mass,unsigned int random_seed,short eccentricity,
    int inclination,int orbits_around,int distance,int declination,short surfaceTemp,char Char_24,
    int Dword_28,short description,short ObjectNumber,char *name)
{
    dest->mass = mass+1;
    if (random_seed)
    {
        dest->random_seed = (random_seed<<16) | (random_seed>>16);
    } else
    {
        dest->random_seed = (SolarSystem_random << 16) + (SolarSystem_seed & 0xffff);
        HandcodedRandomizer();
    }
    dest->parent = orbits_around;
    dest->descriptioncode = description;
    dest->model = ObjectNumber;
    dest->orbital_radius = distance;
    dest->latitude = (declination<<16)/360;
    dest->orbital_period = 0;
    dest->tempptr = 0;
    dest->eccentricity = eccentricity;
    dest->longitude = (inclination<<16)/3600;
    dest->temperature = surfaceTemp+273;
    strcpy (dest->name, name);
    dest->field_3A = 0;
    dest->field_3C = 0;
    dest->rotspeed = Char_24;
    dest->level = (orbits_around >= 2) ? 2 : orbits_around;
    dest->field_40 = 0;
    dest->field_42 = (Dword_28<<16)/360;
    dest->field_42 = 0;
}

void CreateSolarSystem_City (Planet_t *dest,int random_seed,int lati,int longi,short on_body,
    short ObjectNumber,char *name)
{
    dest->mass = 0x0FFF60000;
    if (random_seed)
    {
        dest->random_seed = random_seed;
    } else
    {
        dest->random_seed = (SolarSystem_random << 16) + (SolarSystem_seed & 0xffff);
        HandcodedRandomizer();
    }
    dest->parent = on_body;
    dest->descriptioncode = 35;
    dest->model = ObjectNumber;
    dest->orbital_radius = 0;
    dest->latitude = -((lati<<16)/36000)-0x4000;
    dest->orbital_period = 0;
    dest->tempptr = 0;
    dest->eccentricity = 0;
    dest->longitude = (longi<<16)/36000;
    dest->temperature = 293;
    strcpy (dest->name, name);
    dest->field_3A = 0;
    dest->field_3C = 0;
    dest->rotspeed = 0;
    dest->level = 3;
    dest->field_40 = 0;
    dest->field_42 = 0;
}

The entire list of manufactured systems has 147 elements. The first element of each distinct system is found in a list, and the list for this system stops when the mass member is zero. You'll see later on how individual systems are copied; first thing to do, create all those objects:

void CreateHandcodedSystems (void)
{
    CreateSolarSystem_Body(&ManufacturedSystem[0], 200000000, 0, 0, 0, 0, 0, 0, 5800, 2, 0, 22, 141,
        "Sol");
    CreateSolarSystem_Body(&ManufacturedSystem[1], 33, 0, 206, 70, 1, 1725554, 30, 350, 0, 0, 3, 120,
        "Mercury");
    CreateSolarSystem_Body(&ManufacturedSystem[2], 0, 0, 0, 900, 2, 220, 50, 15, 12, 0, 32, 72,
        "Daedalus");
    CreateSolarSystem_Body(&ManufacturedSystem[3], 487, 0, 1, 34, 1, 3224611, 220, 480, 0, 0, 12, 123,
        "Venus");
    CreateSolarSystem_Body(&ManufacturedSystem[4], 598, 0x12340ABC, 17, 0, 1, 4458427, 90, 22, 1, 23, 10,
        129, "Earth");
    CreateSolarSystem_City(&ManufacturedSystem[5], 0, -0x3370, 0x0B34, 5, 84, "London");
    CreateSolarSystem_City(&ManufacturedSystem[6], 0, -14415, 1985, 5, 83, "Paris");
    CreateSolarSystem_City(&ManufacturedSystem[7], 0, -31505, 2390, 5, 90, "Tokyo");
    CreateSolarSystem_City(&ManufacturedSystem[8], 0, 0x0FFFF5E59, 0x285, 5, 84, "New York");
    CreateSolarSystem_City(&ManufacturedSystem[9], 0, 0x0FFFFB738, 0x0FCC, 5, 84, "New Moscow");
    CreateSolarSystem_City(&ManufacturedSystem[10], 0, 0x0FFFF6124, 0x0FFFFEFEC, 5, 90,
        "New San Francisco");
    CreateSolarSystem_City(&ManufacturedSystem[11], 0, 0x0FFFF94A5, 0x0FFFFF636, 5, 0x5A, "Sydney");
    CreateSolarSystem_Body(&ManufacturedSystem[12], 0, 0, 0, 0x0EB, 5, 1257, 0x78, 15, 0x0A, 0, 34, 80,
        "Abraham Lincoln");
    CreateSolarSystem_Body(&ManufacturedSystem[13], 0, 0, 0, 235, 5, 1257, 240, 15, 10, 0, 34, 80,
        "M.Gorbachev");
    CreateSolarSystem_Body(&ManufacturedSystem[14], 0, 0, 0, 235, 5, 1257, 0, 15, 10, 0, 34, 80,
        "Li Qing Jao");
    CreateSolarSystem_Body(&ManufacturedSystem[15], 7, 4660, 54, 51, 5, 11456, 150, -20, 0, 0, 3, 120,
        "Moon");
    CreateSolarSystem_City(&ManufacturedSystem[16], 0, -17127, 871, 16, 96, "Apollonius City");
    CreateSolarSystem_Body(&ManufacturedSystem[17], 0, 0, 0, 0x384, 16, 250, 0, 15, 11, 0, 33, 73,
        "Galileo");
    CreateSolarSystem_Body(&ManufacturedSystem[18], 64, 3168665610, 93, 19, 1, 6791949, 80, 15, 1, 24, 9,
        125, "Mars");
    CreateSolarSystem_City(&ManufacturedSystem[19], 0x0FBFEFF0, 0x0FFFF84EF, 0x956, 0x13, 0x54,
        "Olympus Village");
    CreateSolarSystem_City(&ManufacturedSystem[20], 0x0E1234567, 0x0FFFFCC90, 0x0B34, 0x13, 0x53,
        "Quenisset");
    CreateSolarSystem_Body(&ManufacturedSystem[21], 0, 0, 0, 900, 19, 180, 0, 0x0F, 0x0A, 0, 34, 80,
        "Mars High");
    CreateSolarSystem_Body(&ManufacturedSystem[22], 0, 0x0EF123, 0x15, 0x0B, 0x13, 0x117, 0, 0x0FFFFFFBE,
        5, 0, 1, 112, "Phobos");
    CreateSolarSystem_Body(&ManufacturedSystem[23], 0, 0x56789, 3, 0x12, 0x13, 0x2BC, 0, -66, 4, 0, 1, 112,
        "Deimos");
    CreateSolarSystem_Body(&ManufacturedSystem[24], 1, 0x0EF123, 0x4F, 0x6A, 1, 0x0BABE2E, 0x0C8, -120,
        0, 0, 2, 119, "Ceres");
    CreateSolarSystem_Body(&ManufacturedSystem[25], 189900, 1610617396, 49, 13, 1, 23195147, 300, -150,
        2, 3, 15, 134, "Jupiter");
    CreateSolarSystem_Body(&ManufacturedSystem[26], 0, 0, 3, 36, 26, 5394, 0x0AA, 0x0FFFFFF65, 2, 0, 1, 112,
        "Amalthea");
    CreateSolarSystem_Body(&ManufacturedSystem[27], 9, 0, 4, 31, 26, 0x3121, 0x50, 0x0FFFFFF6F, 0, 0, 5,
        122, "Io");
    CreateSolarSystem_Body(&ManufacturedSystem[28], 0, 0, 0, 0x384, 28, 0x8C, 0x5A, 0x0F, 0x0B, 0, 33, 73,
        "Columbus");
    CreateSolarSystem_Body(&ManufacturedSystem[29], 5, 0, 9, 36, 26, 20000, 270, -159, 0, 0, 2, 119,
        "Europa");
    CreateSolarSystem_Body(&ManufacturedSystem[30], 0x0F, 214375, 2, 33, 26, 31888, 120, -160, 0, 0, 3, 120,
        "Ganymede");
    CreateSolarSystem_Body(&ManufacturedSystem[31], 10, 74565, 7, 0x24, 26, 56028, 10, -160, 0, 0, 3, 120,
        "Callisto");
    CreateSolarSystem_Body(&ManufacturedSystem[32], 56860, 0x0FFA10123, 55, 25, 1, 42527914, 220, -180, 2,
        0x1D, 14, 134, "Saturn");
    CreateSolarSystem_Body(&ManufacturedSystem[33], 1, 0, 0x14, 0, 33, 0x15A7, 0x0FA, -190, 2, 0, 2, 119,
        "Mimas");
    CreateSolarSystem_Body(&ManufacturedSystem[34], 1, 0, 4, 0, 33, 0x1BB4, 0x28, -190, 0, 0x32, 2, 119,
        "Enceladus");
    CreateSolarSystem_Body(&ManufacturedSystem[35], 1, 0, 0, 0, 33, 0x2258, 0x5A, 0x0FFFFFF42, 0, 0, 2,
        119, "Tethys");
    CreateSolarSystem_Body(&ManufacturedSystem[36], 1, 0, 2, 0, 33, 0x2BE3, 0x8C, 0x0FFFFFF42, 0, 0x1E, 2,
        119, "Dione");
    CreateSolarSystem_Body(&ManufacturedSystem[37], 1, 0, 1, 0, 33, 0x3D59, 0x154, 0x0FFFFFF42, 0, 0x5A, 2,
        119, "Rhea");
    CreateSolarSystem_Body(&ManufacturedSystem[38], 14, 0, 0x1D, 0, 33, 0x8E42, 0x0C8, 0x0FFFFFF58, 0, 0, 7,
        124, "Titan");
    CreateSolarSystem_Body(&ManufacturedSystem[39], 0, 0, 0, 0x384, 39, 0x1F4, 0x46, 0x0F, 0x0B, 0, 33, 73,
        "Titan City");
    CreateSolarSystem_Body(&ManufacturedSystem[40], 0, 0, 0x68, 0x126, 0x21, 0x0AC69, 0x50, 0x0FFFFFF41, 0,
        0, 1, 117, "Hyperion");
    CreateSolarSystem_Body(&ManufacturedSystem[41], 1, 0, 0x1C, 0x6E, 0x21, 0x19E8E, 0x78, 0x0FFFFFF41, 0,
        0, 2, 119, "Iapetus");
    CreateSolarSystem_Body(&ManufacturedSystem[42], 8660, 0x7F801234, 0x2F, 8, 1, 85520744, 0x46, -210, 2,
        98, 13, 135, "Uranus");
    CreateSolarSystem_Body(&ManufacturedSystem[43], 0, 0, 0x11, 0x3D4, 0x2B, 0x0F22, 0x122, 0x0FFFFFF29, 0,
        0, 1, 0x75, "Miranda");
    CreateSolarSystem_Body(&ManufacturedSystem[44], 1, 0, 3, 0x3D4, 0x2B, 0x165A, 0x28, 0x0FFFFFF29, 0, 0x5A,
        2, 0x77, "Ariel");
    CreateSolarSystem_Body(&ManufacturedSystem[45], 1, 0, 4, 0x3D4, 0x2B, 0x1F15, 0x64, 0x0FFFFFF29, 0, 0x1E,
        2, 0x77, "Umbriel");
    CreateSolarSystem_Body(&ManufacturedSystem[46], 1, 0, 2, 0x3D4, 0x2B, 0x32FD, 0x96, 0x0FFFFFF29, 0, 0x46,
        2, 0x77, "Titania");
    CreateSolarSystem_Body(&ManufacturedSystem[47], 1, 0, 1, 0x3D4, 0x2B, 0x4438, 0x14, 0x0FFFFFF29, 0, 0x0A,
        2, 119, "Oberon");
    CreateSolarSystem_Body(&ManufacturedSystem[48], 10300, 0x7D40789A, 9, 0x12, 1, 0x7FCD123, 0x96,
        0x0FFFFFF24, 2, 0, 13, 135, "Neptune");
    CreateSolarSystem_Body(&ManufacturedSystem[49], 6, 0x23456, 0, 0x63F, 0x31, 0x2953, 0x136, 0x0FFFFFF42,
        0, 0, 4, 121, "Triton");
    CreateSolarSystem_Body(&ManufacturedSystem[50], 0, 0, 0x2ED, 0x110, 0x31, 0x28780, 0x3C, 0x0FFFFFF1E, 0,
        0, 1, 117, "Nereid");
    CreateSolarSystem_Body(&ManufacturedSystem[51], 1, 0, 250, 172, 1, 175833702, 170, -232, 0, 0, 2, 119,
        "Pluto");
    CreateSolarSystem_Body(&ManufacturedSystem[52], 1, 0, 0, 0x28A, 0x34, 566, 0x14, 0x0FFFFFF18, 0, 0, 2,
        119, "Charon");
    ManufacturedSystem[53].mass = 0;
    ManufacturedSystem[54].mass = 0;
    ManufacturedSystem[55].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[56], 398000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23,
        "Alpha Centauri A,B");
    CreateSolarSystem_Body(&ManufacturedSystem[57], 220000000, 0, 0, 0, 1, 52029761, 0, 5900, 0, 0, 22, 141,
        "Alpha Centauri A");
    CreateSolarSystem_Body(&ManufacturedSystem[58], 178000000, 0, 0, 0, 1, 52029761, 180, 5000, 0, 0, 21,
        140, "Alpha Centauri B");
    CreateSolarSystem_Body(&ManufacturedSystem[59], 180000, 0x0FFFFFFFF, 3, 2, 1, 0x633D282, 0x5A,
        0x0FFFFFF30, 1, 80, 15, 134, "Lagrange");
    CreateSolarSystem_Body(&ManufacturedSystem[60], 6, 0, 3, 0x33A, 4, 0x4E20, 0, 0x0FFFFFF27, 0, 0, 2, 0x77,
        "2042 L1");
    CreateSolarSystem_Body(&ManufacturedSystem[61], 10000, 0, 0x57, 0x7D, 1, 0x35A4E900, 0, 0x0FFFFFF0B, 0,
        0x0AF, 13, 135, "2071 AC3");
    CreateSolarSystem_Body(&ManufacturedSystem[62], 1, 0, 0x0B2, 0x2DA, 6, 0x1E2A, 0, 0x0FFFFFF03, 0, 0, 2,
        119, "2075 AC3a");
    CreateSolarSystem_Body(&ManufacturedSystem[63], 20000000, 0, 450, 800, 1, 0x0FFFFFFFF, 0, 0x0CE4, 2, 75,
        18, 138, "Proxima Centauri");
    CreateSolarSystem_Body(&ManufacturedSystem[64], 456, 0, 10, 772, 8, 400000, 0, 3, 2, 60, 8, 0x80,
        "Eden");
    CreateSolarSystem_Body(&ManufacturedSystem[65], 0, 0, 0, 0, 9, 300, 0, 15, 12, 0, 32, 72,
        "Eden Station");
    CreateSolarSystem_Body(&ManufacturedSystem[66], 6, 0, 350, 758, 8, 1000000, 0, -81, 1, 170, 2, 119,
        "2045 PC2");
    ManufacturedSystem[67].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[68], 658000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23,
        "Sirius, Sirius B");
    CreateSolarSystem_Body(&ManufacturedSystem[69], 462000000, 0, 0, 0, 1, 0x2B01FD6, 0, 0x2328, 2, 0, 24,
        143, "Sirius");
    CreateSolarSystem_Body(&ManufacturedSystem[70], 196000000, 0, 0, 0, 1, 0x2B01FD6, 0x0B4, 0x2AF8, 0x0D, 0,
        25, 148, "Sirius B");
    CreateSolarSystem_Body(&ManufacturedSystem[71], 0x23, 0, 0x0E, 0x186, 3, 0x366C9, 0, 0x9A, 5, 0x78, 5,
        122, "Lucifer");
    CreateSolarSystem_City(&ManufacturedSystem[72], 0, 0x0FFFFAD36, 0x6F8, 4, 96, "Factory Central");
    CreateSolarSystem_Body(&ManufacturedSystem[73], 210000, 0, 0x11B, 0x147, 1, 0x5D0291AA, 0, 0x0FFFFFF06,
        0, 0x5F, 15, 134, "Waypoint");
    ManufacturedSystem[74].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[75], 172000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23,
        "Wolf 630 A,B");
    CreateSolarSystem_Body(&ManufacturedSystem[76], 91200000, 0, 0, 0x3B6, 1, 0x1FE39C6, 0, 0x0D48, 1, 0, 20,
        138, "Wolf 630 A");
    CreateSolarSystem_Body(&ManufacturedSystem[77], 80800000, 0, 0, 0x3B6, 1, 0x1FE39C6, 0x0B4, 0x0D16, 1, 0,
        20, 138, "Wolf 630 B");
    CreateSolarSystem_Body(&ManufacturedSystem[78], 150500000, 1916032639, 0, 0, 1, 773532400, 0x64, 10700,
        0x0C, 0, 25, 148, "Van Biesbroeck 8");
    CreateSolarSystem_Body(&ManufacturedSystem[79], 980000, 0x0FEDCBA98, 0x5A, 0x4B0, 4, 0x1000AE0, 0, 0x4B0,
        3, 0x0A, 0x11, 0x89, "McCarthy");
    CreateSolarSystem_Body(&ManufacturedSystem[80], 620, 0x54321, 0x0E, 0x186, 5, 0x143E9, 0, 0x18, 3, 0x8C,
        0x0A, 0x7E, "Landfall");
    CreateSolarSystem_City(&ManufacturedSystem[81], 0, 0x0FFFFAD36, 0x6F8, 6, 0x53, "Mayflower City");
    CreateSolarSystem_Body(&ManufacturedSystem[82], 0, 0, 0, 0, 6, 0x1F4, 0, 0x0F, 0x0B, 0, 0x21, 0x49,
        "Mayflower High");
    CreateSolarSystem_Body(&ManufacturedSystem[83], 658000000, 0, 0, 0x454, 1, 0x0FFFFFFFF, 0x104, 0, 0, 0,
        0, 0x17, "Wolf 629 A,B");
    CreateSolarSystem_Body(&ManufacturedSystem[84], 41500000, 0, 0, 0x0FFFFFB7F, 9, 0x2C3835, 0, 0x0C80, 1,
        0, 0x14, 0x8A, "Wolf 629 A");
    CreateSolarSystem_Body(&ManufacturedSystem[85], 41000000, 0, 0, 0x0FFFFFB7F, 9, 0x2C3835, 0x0B4, 0x0C4E,
        1, 0, 0x14, 0x8A, "Wolf 629 B");
    ManufacturedSystem[86].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[87], 218000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x17,
        "61 Cygni A,B");
    CreateSolarSystem_Body(&ManufacturedSystem[88], 118000000, 0, 0, 0, 1, 0x0A4A7414, 0, 0x1388, 1, 0, 0x15,
        0x8C, "61 Cygni A");
    CreateSolarSystem_Body(&ManufacturedSystem[89], 100000000, 0, 0, 0, 1, 0x0A4A7414, 0x0B4, 0x1324, 1, 0,
        0x15, 0x8C, "61 Cygni B");
    CreateSolarSystem_Body(&ManufacturedSystem[90], 630, 0, 0x53, 0x0E4, 3, 0x227673, 0, 0x0FFFFFFFB, 2, 0,
        0x0B, 0x7F, "Scott");
    CreateSolarSystem_City(&ManufacturedSystem[91], 0, 0x0FFFFAD36, 0x6F8, 4, 0x55, "Amundsen");
    CreateSolarSystem_Body(&ManufacturedSystem[92], 430000, 0, 0x11B, 0x27A, 1, 0x75357C1A, 0, 0x0FFFFFF06,
        0, 0, 0, 0x17, "61 Cygni A,B 1,2");
    CreateSolarSystem_Body(&ManufacturedSystem[93], 230000, 0, 0, 0x645, 6, 0x340B08, 0, 0x0FFFFFEFC, 1, 0,
        0x0F, 0x86, "61 Cygni A,B 1");
    CreateSolarSystem_Body(&ManufacturedSystem[94], 200000, 0, 0, 0x645, 6, 0x340B08, 0x0B4, 0x0FFFFFEFC, 1,
        0, 0x0F, 0x86, "61 Cygni A,B 2");
    CreateSolarSystem_Body(&ManufacturedSystem[95], 35, 0, 0x54, 0x64C, 8, 0x2EE0, 0, 0x0FFFFFF2B, 2, 0, 5,
        0x7A, "61 Cygni A,B 2a");
    CreateSolarSystem_Body(&ManufacturedSystem[96], 3, 0, 0x5C, 0x6C0, 6, 0x1D46348, 0, 0x0FFFFFEF2, 1, 0, 2,
        0x77, "61 Cygni A,B 1,2a");
    ManufacturedSystem[97].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[98], 201500000, 0, 0, 0, 0, 0, 0, 0x170C, 2, 0, 0x16, 0x8D,
        "Eta Cassiopeia");
    CreateSolarSystem_Body(&ManufacturedSystem[99], 38, 0, 0x102, 0x33E, 1, 0x0FCD22, 0, 0x1CC, 0, 0, 3, 0x78,
        "Rock");
    CreateSolarSystem_Body(&ManufacturedSystem[100], 504000, 0x0CF019875, 0, 0x0A, 1, 0x503CBB, 0, 0x0E, 1,
        0x28, 0x10, 0x88, "Between");
    CreateSolarSystem_Body(&ManufacturedSystem[101], 64, 0x12ABCFE, 0x68, 0x12, 3, 0x0CAED, 0, 0x0F, 1, 0x18,
        9, 0x7D, "Navy Central");
    CreateSolarSystem_Body(&ManufacturedSystem[102], 0, 0, 0, 0x72, 4, 0x0B4, 0, 0x0F, 0x0A, 0, 0x22, 0x50,
        "Morgue's Mortuary");
    CreateSolarSystem_Body(&ManufacturedSystem[103], 610, 0x7834536, 0, 0x0A, 1, 0x503CBB, 0x12C, 0x18, 1,
        0x14, 0x0A, 0x7E, "Trojan");
    CreateSolarSystem_City(&ManufacturedSystem[104], 0, 0x0FFFFCC90, 0x0B34, 6, 0x54, "New Kyoto");
    CreateSolarSystem_City(&ManufacturedSystem[105], 0, 0x0FFFFAD36, 0x6F8, 6, 0x53, "Sunnyville");
    CreateSolarSystem_City(&ManufacturedSystem[106], 0, 0x0FFFF9F83, 0x0FAC, 6, 0x53, "Venice on Land");
    CreateSolarSystem_City(&ManufacturedSystem[107], 0, 0x0FFFF5E59, 0x285, 6, 0x52, "Manchester");
    CreateSolarSystem_City(&ManufacturedSystem[108], 0, 0x0FFFFB739, 0x0FCC, 6, 0x51, "Epping Newtown");
    CreateSolarSystem_Body(&ManufacturedSystem[109], 0, 0, 0, 0x44C, 6, 0x274, 0x78, 0x0F, 0x0A, 0, 0x22,
        0x50, "J.F.Kennedy");
    CreateSolarSystem_Body(&ManufacturedSystem[110], 5, 0x124534, 0x36, 0x33, 6, 0x0D80, 0, 0x0FFFFFFEC, 0,
        0, 2, 0x77, "New Moon");
    CreateSolarSystem_Body(&ManufacturedSystem[111], 505, 0x1CFD75F, 0, 0x0A, 1, 0x503CBB, 0x3C, 0x0A, 1,
        0x0A0, 0x0A, 0x7E, "Feynman");
    CreateSolarSystem_City(&ManufacturedSystem[112], 0, 0x0FFFFC7B1, 0x7C1, 0x0E, 0x54, "Naunton Estates");
    CreateSolarSystem_City(&ManufacturedSystem[113], 0, 0x0FFFF9F83, 0x0FAC, 0x0E, 0x53, "Tarbit's Landing");
    CreateSolarSystem_Body(&ManufacturedSystem[114], 0, 0, 0, 0x9C4, 0x0E, 0x274, 0x78, 0x0F, 0x0B, 0, 0x21,
        0x49, "Angus Manwaring");
    CreateSolarSystem_Body(&ManufacturedSystem[115], 19000000, 0, 0x2D, 0x32, 1, 0x104FAB10, 0x0C8, 0x0C80,
        2, 0x4B, 0x14, 0x8A, "Eta Cassiopeia B");
    CreateSolarSystem_Body(&ManufacturedSystem[116], 9, 0, 350, 758, 0x12, 0x0F4240, 0, 0x0FFFFFFAF, 1,
        0x0AA, 3, 0x78, "Firing Range");
    CreateSolarSystem_Body(&ManufacturedSystem[117], 9000, 0x7F801234, 0x37, 0x0A, 1, 0x389A6E97, 0,
        0x0FFFFFF07, 2, 0x9E, 0x0D, 0x87, "Eta Cassiopeia 5");
    ManufacturedSystem[118].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[119], 19500000, 0, 0x1C2, 0x320, 0, 0, 0, 0x0C80, 0, 0x4B,
        0x12, 0x8A, "Ross 154");
    CreateSolarSystem_Body(&ManufacturedSystem[120], 40, 0, 9, 0x24, 1, 0x6CD92, 0, 0x0FFFFFFCD, 0, 0, 2,
        0x77, "Dust Ball");
    CreateSolarSystem_Body(&ManufacturedSystem[121], 65600, 0x8D4078FF, 9, 0x1C, 1, 0x16AD3E, 0x9B,
        0x0FFFFFFEC, 3, 0, 0x0D, 0x87, "Aster");
    CreateSolarSystem_Body(&ManufacturedSystem[122], 25, 0, 26, 300, 3, 6727, 145, -4, 1, 0x3C, 0x0A,
        0x7F, "Merlin");
    CreateSolarSystem_City(&ManufacturedSystem[123], 0, 0x2145, 0x82D6, 4, 0x55, "Sirocco Station");
    CreateSolarSystem_Body(&ManufacturedSystem[124], 7, 0, 26, 190, 3, 8087, 0, -40, 1, 0x3C, 3, 0x78,
        "Willow");
    CreateSolarSystem_Body(&ManufacturedSystem[125], 29, 0, 146, 53, 1, 13063112, 0, -185, 1, 10, 3, 120,
        "Ross 154 3");
    ManufacturedSystem[126].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[127], 118000000, 0, 0, 0, 0, 0, 0, 0x1388, 0, 0, 0x15,
        0x8C, "Lave");
    CreateSolarSystem_Body(&ManufacturedSystem[128], 598, 19666997, 0x26, 0, 1, 0x2275FB, 0, 0x0E, 1, 0,
        0x0A, 0x7E, "Planet Lave");
    CreateSolarSystem_Body(&ManufacturedSystem[129], 0, 0, 0, 0x9C4, 2, 0x274, 0x78, 0x0F, 0x0C, 0, 0x20,
        0x48, "Lave Station");
    ManufacturedSystem[130].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[131], 200000000, 0, 0, 0, 0, 0, 0, 0x1388, 0, 0, 0x16,
        0x8D, "Diso");
    CreateSolarSystem_Body(&ManufacturedSystem[132], 598, 0x12C1835, 0x26, 0, 1, 0x489B9B, 0, 0x0FFFFFFE8,
        1, 0x1E, 0x0A, 0x7F, "Birmingham");
    CreateSolarSystem_Body(&ManufacturedSystem[133], 0, 0, 0, 0x9C4, 2, 0x274, 0x78, 0x0F, 0x0C, 0, 0x20,
        0x48, "Shifnalport");
    ManufacturedSystem[134].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[135], 20000000, 0, 0, 0, 0, 0, 0, 0x1388, 0, 0, 0x14, 0x8A,
        "Leesti");
    CreateSolarSystem_Body(&ManufacturedSystem[136], 598, 19666997, 0x26, 0, 1, 0x17C79B, 0, 0x10, 1, 0x1E,
        3, 0x78, "Leesti 1");
    CreateSolarSystem_Body(&ManufacturedSystem[137], 0, 0, 0, 0x9C4, 2, 0x274, 0x78, 0x0F, 0x0C, 0, 0x20,
        0x48, "George Lucas");
    ManufacturedSystem[138].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[139], 20000000, 0, 0, 0, 0, 0, 0, 5000, 0, 0, 23, 142,
        "Zaonce");
    CreateSolarSystem_Body(&ManufacturedSystem[140], 598, 19666997, 0x26, 0, 1, 1558427, 0, 0x10, 1, 30,
        3, 120, "Industry");
    CreateSolarSystem_Body(&ManufacturedSystem[141], 0, 0, 0, 2500, 2, 628, 120, 15, 12, 0, 32, 72,
        "Ridley Scott");
    ManufacturedSystem[142].mass = 0;

    CreateSolarSystem_Body(&ManufacturedSystem[143], 200000000, 0, 0, 0, 0, 0, 0, 5000, 0, 0, 22, 141,
        "Riedquat");
    CreateSolarSystem_Body(&ManufacturedSystem[144], 598, 0x12C1835, 0x26, 0, 1, 0x2275FB, 0, 0x1D, 1,
        0x1E, 0x0A, 0x7E, "Waterloo");
    CreateSolarSystem_Body(&ManufacturedSystem[145], 0, 0, 0, 0x9C4, 2, 0x274, 0x78, 0x0F, 0x0B, 0, 33,
        0x49, "La Soeur du Dan Ham");
    ManufacturedSystem[146].mass = 0;
}

We need a way of finding the correct system when its seed is zero. That's done by looping through the following array, checking your system's UniqueId against the first value; the second value is the index into the ManufacturedSystem array for the first object of that system.

dd offsets_of_handcoded_systems[][2] =
{
    0x2A49718,    0,   // Sol
    0x6A49718,   56,   // Alpha Centauri
    0xEA47716,   75,   // Wolf 630
    0x2A4B717,   87,   // 61 Cygni
    0x2A49719,   68,   // Sirius
    0x6A4D718,   98,   // Eta Cassiopeia
    0x6A49717,  119,   // Ross 154
    0x2A3D715,  127,   // Lave
    0x6A3D715,  131,   // Diso
    0xEA3D715,  135,   // Leesti
    0x2A3D716,  139,   // Zaonce
    0, 0
};

Thoroughly creepy is that Riedquat is not in this list! As you can see when searching for the correct system, the search stops when it encounters a zero for map coordinate, and starts copying data from whereever it was at that moment. That's why the pointer to data-to-be-copied is initialized with the objects for Riedquat...

 

The routine CreateHandcodedSystems is executed before any system can be displayed. Strange as it may seem, these values cannot be used immediately. One would expect all values to be copied as-is into the active Planets array, but for some reason this is merely an intermediate list. If your current star system is found in this list, most values are copied but some of them are recalculated.

Copying (and adjusting the odd parameter) is done in PlanetGeneratorMain as explained in Part III. Near the start of this routine you see the lines

    if (gensysparam->seed == 0)
    {
    //  This is a handcoded system
        i = 0;
    //  Code to copy lots and lots of data omitted...
        planets[i].mass = 0;
        return i;
    }

Replace these lines with the following snippet of code.

    if (gensysparam->seed == 0)
    {
    //  This is a handcoded system
        Planet_t *manufactured = &ManufacturedSystem[143];
        i = 0;
        while (offsets_of_handcoded_systems[i][0])
        {
            if (offsets_of_handcoded_systems[i][0] == gensysparam->mapcoordinate)
            {
                manufactured = &ManufacturedSystem[offsets_of_handcoded_systems[i][1]];
                break;
            }
            i++;
        }
        i = 0;
        while (manufactured[i].mass)
        {
            if (manufactured[i].descriptioncode == 35)
            {
                memcpy (&planets[i], &manufactured[i], sizeof(Planet_t));
                GSystem_NumStations++;
            } else
            {
                planets[i].mass = DWordToScaledWord(manufactured[i].mass-1);
                planets[i].mass += 0x3f0000;
                planets[i].random_seed = manufactured[i].random_seed;
                planets[i].parent = manufactured[i].parent;
                planets[i].descriptioncode = manufactured[i].descriptioncode;
                planets[i].orbital_radius = DWordToScaledWord(manufactured[i].orbital_radius);
                planets[i].orbital_radius += 0x190000;
                planets[i].latitude = manufactured[i].latitude;
                planets[i].eccentricity = manufactured[i].eccentricity;
                planets[i].longitude = manufactured[i].longitude;
                planets[i].temperature = manufactured[i].temperature;
                planets[i].field_3A = manufactured[i].field_3A;
                planets[i].field_3C = manufactured[i].field_3C;
                planets[i].rotspeed = manufactured[i].rotspeed;
                planets[i].level = manufactured[i].level;
                planets[i].model = manufactured[i].model;
                GSystem_NumberOfMajorBodies++;
                GSystem_namedobjects++;
                if (manufactured[i].descriptioncode >= 32 && manufactured[i].descriptioncode <= 34)
                {
                    GSystem_NumStations++;
                    GSystem_NumOrbiters++;
                }
                if (planets[i].parent == 0)
                {
                    planets[i].orbital_period = 0;
                } else
                {
                    ScaledWord_t temp;
                    temp.full = getSqrt_adj(FFP_Div(FFP_Mul(FFP_Mul(planets[i].orbital_radius,
                        planets[i].orbital_radius),planets[i].orbital_radius),
                        planets[planets[i].parent-1].mass));
                    temp.w.Base = ((temp.w.Base*0x5EDB) >> 15);
                    planets[i].orbital_period = temp.full;
                }
                strcpy (planets[i].name, manufactured[i].name);
            }
            i++;
        }
        planets[i].mass = 0;
        return i;
    }

Note the difference in copying a city (description code 35, no parameters adjusted) and other bodies, where the odd constant is added to some parameters. My guess is that the original inputs are logical in some system, albeit not actual kms or AUs or something like that.

Even worse is that the values produced by the routine PlanetGeneratorMain, either random generated or taken from the hardcoded systems, are also not in any known metric system. Before you can actually display values for mass, distance, and orbit parameters, you'll have to know how to convert them to the unit of your choice. Let me first show you again the structure where all this information is collected in, then I'll explain all I know about its members.

// Planet_t: sizeof=0X44
// Holds data on each generated star, planet or moon object
// Output of PlanetGenerator
struct {
    dd mass;
    dd random_seed;
    dw parent;
    dw descriptioncode;
    dw model;
    dd orbital_radius;
    dw latitude;
    dd orbital_period;
    dd tempptr;
    dd field_1C;
    dw eccentricity;
    dw longitude;
    dw temperature;    // in Kelvin
    db name[20];
    dw field_3A;
    dw field_3C;
    db rotspeed;
    db level;
    dw field_40;
    dw field_42;
} Planet_t;

mass

The mass of planets and stars is expressed in the games in Earth masses. Converting the binary value to the actual value goes like this:

result = FractionMul(ScaledWordToDword(planets[i].mass-0x3f0000),43866, 0)>>2;

The mass-0x3f0000 strips off a huge chunk of the value's second word (the ScaledWord's Scale member). Where the constant 43866 came from... I have no idea.

The result is the mass in Earth's masses times 100, so you want to convert it to a string using:

printf ("Mass: %d.%02d Earth masses", result/100, result % 100);

To convert it to kilograms, multiply the result by 5.975 x 1024 and divide by 100 (obvious optimization left out for clarity!); at that point you should probably start using real floating point numbers...

A sanity check would be to input the Earth mass as hardcoded above, including the conversion in PlanetGeneratorMain. If the result is not "1.00" something is wrong with your or my code. Surprisingly, before being converted, the input value in CreateHandcodedSystems is "598"; not too distant from the actual value in 1022 kg. The same goes for the other (really existing) objects.

random_seed

This is only useful if you know how to display objects from the 3D Object List. Drawing 3D objects can be influenced by numerous variables — a well-known example is the little church with its real-time clock (the game time is also a variable). Though the number itself is generated at random, for every planet and/or city displayed it is generated in the same way, and thus is constant whenever something is drawn, resulting in the same cities and planets every time you see them.

parent

This number indexes the parent of the object in the same list plus 1. For the main star or main Binary System it is always 0. In the Sol system code above you can see how every planet revolves around object #1 (the Sun), Daedalus orbits #2 (Mercury), and the Moon orbits #5 (the Earth). For cities it is (obviously) the planet on which they are located. Useful if you want to show a hierarchical tree of all objects in a stellar system.

descriptioncode

The text string to be displayed if you want information on any object. Take this string from the array StellarObjectNames in Part III. The "City" string is included by me for sake of completeness (you can't select individual cities in the games).

model

The index of the 3D model in the 3D Object List. Different from descriptioncode, because there are different models for cities and — off the top of my head — a couple of planets. I've not included the actual model to be used in the function defWorld_CreateTowns, but it seems that the model taken depends on the external conditions of the planet (eg., terraformed, oxygen atmosphere, or airlocks needed), the tech level, and — indeed — whether it belongs to the Alliance, which has an architectural style of its own.

orbital_radius

The distance from this to its parent object. Converted using

result = ScaledWordToDword(FFP_Mul (planets[i].orbital_radius, 0xFFDB759A));

The result is the distance in 1000ths of an AU (Astronomical Unit). To get a real idea of the distances involved, multiply this number with 149597870 and divide by 1000; that's in km. For miles, multiply with 92955807 and divide by 1000.

Displaying the distances as in the games goes like this:

printf ("Distance: %d.%03d A.U.", result/1000, result % 1000);

latitude

For cities this is the latitude, the angle between its vertical position and the equator. If a city is located on the Southern Hemisphere it is negative! It gets converted on input from 1/100ths of a degree to a binary value; check over there if you want to convert it back.

For planets this should probably be labelled "declination" ("the angular distance on the celestial sphere north or south of the celestial equator" — ssd.jpl.nasa.gov/glossary.html). Don't know how to use it for display, though.

See also longitude below.

orbital_period

The time it takes for this object to revolve once around its parent. The range of possible values is quite large, so there are three different equations:

if (planets[i].orbital_period >= 0x1c0000)
{
    result = ScaledWordToDword(FFP_Mul (planets[i].orbital_period, 0xffeb551f));
    printf ("%d.%d years", result/10, result % 10);
} else
{
    if (planets[i].orbital_period >= 0x140000)
    {
        result = ScaledWordToDword(FFP_Mul (planets[i].orbital_period, 0xfff06117));
        printf ("%d days", result);
    } else
    {
        result = ScaledWordToDword(FFP_Mul (planets[i].orbital_period, 0xfff548d1));
        printf ("%d hours", result);
    }
}

tempptr

A temporary pointer to an instance of the actual 3D object to be used to display this object. Not used in my code.

field_1C

Ahhh, those unknowns. As said before, most likely the infrared output of the planet or star. Used only to calculate the temperature of random generated planets and stars. In the predefined systems all temperatures are hardcoded so it's not used there.

eccentricity

The amount in which the perfect orbit circle is flattened towards an ellipse. Look it up on the web if you want to know more about it. Though this is given sensible values it is not visible in the game, all orbit lines are drawn as perfect circles.

Nitpick: due to the way the orbit lines are drawn, they are not perfect circles. Anyhow, they are not adjusted for eccentricity, so this statement still is true (for a given value of "true").

longitude

For cities: the horizontal angle towards some arbritary vertical line. For planets it should probably be called "inclination" ("The angle between the vectors normal to the body's orbit plane and the specified reference plane" — again from the NASA glossary). The vertical plane for our solar system is Earth's orbit, other planets are tilted above and below that. Visible in the game! Also no idea how to calculate and display in 3D.

temperature

The surface temperature in Kelvin. Subtract 273 to display in Celsius, or perform your own calculation to convert to Fahrenheit.

name

Being only maximally 20 bytes long, it's just enough for "La Soeur du Dan Ham". As soon as planet and moon numbers are glued at the end of a star name you'll run into problems with names as "Groombridge 34", that's why they are ruthlessly snipped in the planet generator code.

field_3A, field_3C, rotspeed, field_40, field_42

To be honest, all unknowns. I once had an idea that rotspeed is the rotational period of a planet around its own axis, but had to give up on that. Read the code if you want to know where these values are used.

level

The level of nesting of moonlets into moons into planets into stars, eg., Sol has nesting level 0, Earth is 1, the Moon 2 and Galileo 3, and continuing on that, a spaceship orbiting Galileo would be 4 (and ejected cargo still in the gravity field of your ship would be 5). Useful to display a hierarchical tree of all objects in a single system.

Wrapping things up

Congratulations! As far as I can see, you can build your own universe from now on. (Should I have left out some crucial function or data array, just let me know!)

If all's well you should end up with exactly the same map as in Frontier:First Encounters. There are some differences with Frontier: Elite 2 which aren't pursued in full. Examples of these are: In FE2, the star Olphiso (3,0) displays a small moon orbiting Olphiso 5, in FFE this moon disappeared miraculously (try fitting that in a story line). The planet numbering in complicated systems such as Enethze (-3,3) has changed, in FE2 the first planet orbiting Enethze C is called Fisher's Wreck, and the next planets would have to be numbered on from C2. So they are. In FFE the numbering — as shown in my code — for the next planets starts at C3.

 

There are numerous optimizations possible; I just converted the original assembler to be working, not to be a prime example of coding. If you think a particular part could be rewritten in a somewhat clearer way, I urge you to do so but first of all test your new code! If there is the slightest possibility that the results are different anywhere in Frontier's Galaxy I will not use it!

 

The experienced coder can try to add and/or modify parameters. A well-known patch changes the Earth from a drab brown blob to something resembling a terraformed world (now you can see why). It is even fairly easy to add entirely new predefined systems and planets, and it would be fun to see those patched into, for example, JJFFE's sources. (You'll have to find the original routines in there first.) If you stick to just using it as an external map, FFEStarsys style, you can add new astronomical objects, such as Black holes, nebulae, Ringworld, or all kinds of alien planets. It is also easy to expand Human Space; one of my more successful patches to JJFFE reintroduced the colonized wormhole systems (and they appear exactly as in FE2), but, sadly, since I'm unable to retrace the wormhole jump bug, I can see them on the map but going there is a no-no...

If you fully understand how the Human Systems are constructed you could invent an alien race (let's call them "Thargoids", shall we), pick any random star as "home base", set the parameters which govern their expansion and type of worlds they colonize. Make up a routine for the names of stars, planets, and cities ("hives"?), and include these new routines in your galaxy map. All fairly easy; it gets interesting if this alien sphere intersects the Human Colonized space! "Disputed system" would get a whole new meaning...

A final note!

Minor adjustments to the code will unevitably lead to a universe very similar to Frontier's, and if anyone is going to use it in a commercial project it would be interesting to see how much you'll have to change before Frontier Developments reckons it's not worth a law suit.
<< Previous :: Next >>

[Jongware]

Based on original data and algorithms from Frontier:Elite 2 and Frontier:First Encounters by David Braben (Frontier Developments)

Original copyright holders:
Elite 4: The Next Encounter David Braben 2011?
First Encounters David Braben 1995
Frontier David Braben 1993
Elite David Braben and Ian Bell 1984

 

For any real questions -- I will not provide the complete source code! -- you can drop me a mail at jongware.