Universe Creation - enenra/x4modding GitHub Wiki

Universe Creation

How to create a universe from scratch and bake an apple pie for X4 Foundations

Concepts

Before doing anything useful I would like to provide some fundamental information without which any attempts at planning will fail miserably and due to specifics of the process it is crucial to do some preparations before delving into stuff as the game tends to take 'fail silently' approach.

  • Base unit of the game is METER which means 1000 is one ingame kilometer.
  • Game accepts scientific floating point notation
  • Rotation is defined by quaternions
  • X is sideways axis(horizontal in relation to default map projection), y is depth and z is frontal axis(vertical in relation to default map projection)

By learning this assumptions it is really easy to plan your universe using any tool which allows to track numeric spatial coordinates, like Inkscape. In case you will be confused by file structure, please, look at appendix 1. It contains filestructure information on how everything should be.

Universe and superglue

By default game inspects index/macros.xml to load all the required lookup information. It is the place where information about location of different stuff is provided. It is mandatory to have that file in your mod. Empty it should look like this:

<?xml version="1.0" encoding="utf-8"?>
<index>
</index>

Don't worry, we will fill it with a lot of stuff later. After that you have to choose an unique prefix which will help the game to select stuff in wildcard mode. In this example it is chosen to be "esc_", but I recommend doing your own to avoid conflict with my mods.

Second, we need to choose a name for our universe, for example: "escalations_universe". Create a folder where all the map data will reside: maps/escalations_universe and create galaxy.xml. This is standard x4/xr macros list file and therefore it should contain those lines:

<?xml version="1.0" encoding="utf-8"?>
<macros>
</macros>

Your definitions will go into section.

Galaxy macro is very straightforward and is the most simple one:

<macro name="esc_universe_macro" class="galaxy">
  <component ref="standardgalaxy"/>
  <connections>
  </connections>
</macro>

After declaring it in galaxy.xml we need to tell the game where to look at it in index/macros.xml:

<entry name="esc_universe_macro" value="extensions\ita_mod_escalations\maps\escalations_universe\galaxy"/>

As you can see there is part of the path "ita_mod_escalations". This should be the name of the mod as it is named in extensions folder. In a nutshell macros.xml tells the game where to look for a files with required definitions in case it will need to load them. Without such instructions your definitions will never be found by the game. As we have created galaxy it doesn't mean we could use it right away: first we need to define at least one cluster with at least one sector and a zone where we will spawn in.

Cluster

Lets create clusters.xml in the directory of the map. It is default macros list so it should like this after creation:

<?xml version="1.0" encoding="utf-8"?>
<macros>
</macros>

We define cluster the same way as the galaxy except it should have a different name and a class, for example:

<macro name="esc_cluster_01_macro" class="cluster">
  <component ref="standardcluster"/>
  <connections>
    <connection ref="content">
      <macro>
        <component connection="space" ref="Cluster_01" />
      </macro>
    </connection>
  </connections>
</macro>

But there is a slight difference! It has a connection of some relation to "content". This tells the game this cluster should be associated with the backdrop graphics of Cluster_01. assets/environments/cluster contains all the graphics Egosoft created for the game and you could choose from any of those. Let's make the game know where it resides to adding an entry to macros.xml:

<entry name="esc_cluster_*" value="extensions\ita_mod_escalations\maps\escalations_universe\clusters"/>

Actually, such an entry will tell the game that all clusters with esc_ prefix are in extensions\ita_mod_escalations\maps\escalations_universe\clusters.xml. So when you add a cluster you don't need to add entry for each one(but you can specify it verbatim without wildcard if you want).

Indeed, we have created the cluster, but it is in total vacuum - we need to make it part of the universe. For that we should return to galaxy.xml and add a connection inside of connections block of our esc_universe_macro galaxy:

<connection name="esc_cluster_01_connection" ref="clusters">
  <offset>
    <position x="0" y="0" z="0" />
  </offset>
  <macro ref="esc_cluster_01_macro" connection="galaxy" />
</connection>

Name of connection could be anything, but I recommend naming them the same as macros except replacing _macro with _connection. In case we would need to reference it later it will make more sense to have it that way. Why we would want to reference them you might ask? For gates and highways of course!

Position is where it will be situated on the map. In case hexagon is misaligned you will get nasty z-fighting on your map.

Deltagraph

This deltagraph is indicating relative coordinates to align your hexes. Take your cluster x and z positions. Lets call them ox and oz (original x and original z). Choose the hex around central one from the deltagraph and take its x and z while multiplying by 1000. Add result to corresponding ox and oz. Total for each coordinate is a new aligned position for your new cluster.

Sector

Empty cluster is useless: it will not show on the map, it couldn't contain interesting stuff so it must have a sector, at least one. There is no other way.

Let's create sectors.xml in out map folder. It is the same macros list. Populate it accordingly. Sector definition is somewhat like of a cluster, but without graphics definition as it will inherit it from owning cluster:

<macro name="esc_cluster_01_sector001_macro" class="sector">
  <component ref="standardsector" />
  <connections>
  </connections>
</macro>

Let's provide information for the game about its location in macros.xml:

<entry name="esc_cluster_01_sector*" value="extensions\ita_mod_escalations\maps\escalations_universe\sectors"/>

As you can see we create a new entry for a group of sectors for each cluster. You could use another naming scheme like esc_sector_* in case you want it all work automatically, but I prefer to stick closely to somewhat vanilla naming.

For that to work we need to let cluster know it contains it. Let's return to clusters.xml and our esc_cluster_01_macro entry. We should add a new connection the same way we have connected cluster to the galaxy, but in this case we connect sector to a cluster:

<connection name="esc_cluster_01_sector001_connection" ref="sectors">
  <macro ref="esc_cluster_01_sector001_macro" connection="cluster" />
</connection>

Trivial? Indeed it is.

Technically, it is all it is need for sector to be fully operational: any object creation in the sector will create a zone, but because it is the only sector we need to define at least one zone so we could spawn player in it...

Fixed Zones

For zones we should create as you may have guessed it another macro list in map directory: zones.xml.

Without further ado let's create a zone for player to spawn in:

<macro name="esc_zone001_cluster_01_sector001_macro" class="zone">
  <component ref="standardzone" />
  <connections>
  </connections>
</macro>

And we should make the game aware about all zones we create that way in macros.xml:

<entry name="esc_zone*" value="extensions\ita_mod_escalations\maps\escalations_universe\zones"/>

Zones are connected to sectors the same way others do connect to larger entity. We should add a connection inside a sector:

<connection name="esc_zone001_cluster_01_sector001_connection" ref="zones">
  <offset>
    <position x="0" y="0" z="0" />
  </offset>
  <macro ref="esc_zone001_cluster_01_sector001_macro" connection="sector" />
</connection>

This zone is now fully operational.

Gamestart

To appear in our new universe we need to create a gamestart(or change existing, but it is not in the scope of this tutorial). To create a new gamestart you have to create a gamestart definitions xml file "libraries/gamestarts.xml". I recommend this one:

<?xml version="1.0" encoding="utf-8"?>
<gamestarts xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="gamestarts.xsd">
  <gamestart id="ita_mod_escalations_main" name="Ren Otani" image="gamestart_1">
    <location galaxy="esc_universe_macro" zone="esc_zone001_cluster_01_sector001_macro">
      <position x="0" y="0" z="0"/>
      <rotation yaw="0" pitch="0" roll="0"/>      
    </location>
    <player macro="character_player_fight_macro" money="5000" name="{1021,302}">
      <ship macro="ship_par_s_fighter_01_a_macro">
        <loadout>
          <macros>
            <engine macro="engine_par_s_combat_01_mk1_macro" path="../con_engine_01" />
            <engine macro="engine_par_s_combat_01_mk1_macro" path="../con_engine_02" />
            <engine macro="engine_par_s_combat_01_mk1_macro" path="../con_engine_03" />
            <weapon macro="weapon_gen_s_laser_01_mk1_macro" path="../con_weapon_01" optional="true" />
            <weapon macro="weapon_gen_s_guided_01_mk1_macro" path="../con_weapon_02" optional="true" />
            <shield macro="shield_par_s_standard_01_mk1_macro" path="../con_shield_01" optional="true" />
          </macros>
          <ammunition>
            <ammunition macro="missile_guided_light_mk1_macro" exact="10" optional="true" />
            <ammunition macro="eq_arg_satellite_01_macro" exact="5" optional="true"/>
            <ammunition macro="env_deco_nav_beacon_t1_macro" exact="5" optional="true"/>
            <ammunition macro="eq_arg_resourceprobe_01_macro" exact="5" optional="true"/>
          </ammunition>
          <software>
            <software ware="software_targetmk1" />
          </software>
          <virtualmacros>
            <thruster macro="thruster_gen_s_allround_01_mk1_macro" />
          </virtualmacros>
        </loadout>
      </ship>
      <inventory>
        <ware ware="weapon_gen_spacesuit_repairlaser_01_mk1" amount="1" />
        <ware ware="software_scannerobjectmk3" amount="1" />
      </inventory>
      <blueprints>
        <ware ware="clothingmod_0001" />
        <ware ware="clothingmod_0002" />
        <ware ware="module_arg_dock_m_01_lowtech" />
        <ware ware="module_par_pier_l_01" />
        <ware ware="module_par_stor_container_s_01" />
        <ware ware="module_par_conn_base_01" />
        <ware ware="module_par_conn_cross_01" />
        <ware ware="module_par_conn_vertical_01" />
        <ware ware="module_gen_prod_energycells_01" />
        <ware ware="module_par_prod_sojabeans_01" />
        <ware ware="paintmod_0006"/>
        <ware ware="paintmod_0048"/>
        <ware ware="paintmod_0049"/>
        <ware ware="paintmod_0050"/>
      </blueprints>
      <research>
        <ware ware="research_radioreceiver" />
        <ware ware="research_sensorbooster" />
        <ware ware="research_tradeinterface" />
      </research>
      <theme paint="painttheme_player_01" />
    </player>
  </gamestart>
</gamestarts>

It is a default Paranid start in the new galaxy. The part which is of interest for us is

<location galaxy="esc_universe_macro" zone="esc_zone001_cluster_01_sector001_macro">

It tells the name of the galaxy and the name of the destination zone.

After that you are set to test it!

Lore and garbage

As you may have noted it is not as good looking as it should - it doesn't have fancy names! Those are unlike XR defined separately from map: in libraries/mapdefaults.xml. You should check how vanilla are working to bind to localization and voice, but for now there is a quick and dirty example for you:

<?xml version="1.0" encoding="utf-8"?>
<defaults xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="libraries.xsd">
  <dataset macro="esc_cluster_01_macro">
    <properties>
      <identification name="Test" description="Total Test"/>
      <sounds>
        <music ref="music_cluster_01"/>
      </sounds>
      <area sunlight="1" economy="0.5" security="0.25"/>
    </properties>
  </dataset>
  <dataset macro="esc_cluster_01_sector001_macro">
    <properties>
      <identification name="Test sector" description="Total sector Test"/>
      <sounds>
        <music ref="music_cluster_01_sector_01"/>
      </sounds>
    </properties>
  </dataset>  
</defaults>

Advanced - Gates

Gates have to be in the zone. We should create a zone in each sector we would like to connect with gates.

<macro name="esc_zone001_cluster_05_sector_001_macro" class="zone">
  <component ref="standardzone"/>
  <connections>
    <connection name="esc_gate_c05_c06_positive_connection" ref="gates">
      <offset>
        <position x="0" y="500" z="0" />
      </offset>
      <macro ref="props_gates_orb_accelerator_01_macro" connection="space" />
     </connection>
  </connections>
</macro>

As you can see I create the first zone for cluster 5 and its only sector. Then I add connection in the sector to this zone. Zone connections have a gate: terran orbital accelerator(props_gates_orb_accelerator_01_macro) in this case. There are two known types of gates:

  • props_gates_orb_accelerator_01_macro
  • props_gates_anc_gate_macro

Position is in relation to the zone center. As you can see it is 500 meter higher than ecliptic plane: this is how vanilla does it, probably, to keep highways nice and low without interfering with large traffic getting through. I use different naming scheme from vanilla and call gates positive or negative depending on which side of their destination they are.

Another zone in destination is almost the same:

<macro name="esc_zone001_cluster_06_sector_001_macro" class="zone">
  <component ref="standardzone"/>
  <connections>
    <connection name="esc_gate_c05_c06_negative_connection" ref="gates">
      <offset>
        <position x="0" y="500" z="0" />
      </offset>
      <macro ref="props_gates_orb_accelerator_01_macro" connection="space" />
     </connection>
  </connections>
</macro>

It should be connected to only sector of cluster 6, obviously. If you run the game at this point you will notice that there are two gates which claim to be inactive because there is no active connection yet. props_gates_orb_accelerator_01_macro will look operational, but props_gates_anc_gate_macro has a special look when it is inactive. Using props_gates_anc_gate_macro is safe if you want to have some unconnected gates for immersion or later growth of your mod(or even addons!).

Actual connections are defined in galaxy.xml. Let's take a look:

<connection name="esc_gate_c05_c06" ref="destination" path="../esc_cluster_05_connection/esc_cluster_05_sector001_connection/esc_zone001_cluster_05_sector_001_connection/esc_gate_c05_c06_positive_connection">
  <macro connection="destination" path="../../../../../esc_cluster_06_connection/esc_cluster_06_sector_001_connection/esc_zone001_cluster_06_sector_001_connection/esc_gate_c05_c06_negative_connection" />
</connection>

That is why we needed adequate connection naming: we define paths through all the connections starting from galaxy itself towards the gate connection in the zone. Gates will not be operational if there is at least one problem in each of paths!

Appendix 1: Paths

\extensions\ita_mod_escalations\index\macros.xml
\extensions\ita_mod_escalations\maps\escalations_universe\galaxy.xml
\extensions\ita_mod_escalations\maps\escalations_universe\clusters.xml
\extensions\ita_mod_escalations\maps\escalations_universe\sectors.xml
\extensions\ita_mod_escalations\maps\escalations_universe\zones.xml
\extensions\ita_mod_escalations\libraries\gamestarts.xml
\extensions\ita_mod_escalations\libraries\mapdefaults.xml
⚠️ **GitHub.com Fallback** ⚠️