Hidden SBC tags features - THDigi/SE-ModScript-Examples GitHub Wiki

Not all SBC tags are declared in vanilla files, this list has the ones I found/remembered.

Feel free to ask me in the Keen discord about

NOTE: All numbers are float (can have decimal points) unless otherwise specified in the comment next to it.

NOTE: Boolean values should always be lower case, a False or True will throw XML errors.

Contents


Referencing assets from other mods

(Back to top)

Paths inside SBCs that lead to an asset can take advantage of ..\ to go back one folder into the workshop, from there you can go into anoher mod's folder and reference one of its models, icons, sounds, etc, this works because mods are stored there under their workshopId as their folder name.

An example: <Model>..\WorkshopId\Models\SomeThing.mwm</Model> where WorkshopId is the id (from the link) of the mod you're relying on.

This is very useful if you want to make small tweaks to a mod without having to reupload any of its assets, but instead adding it as a dependency.

To test this locally you will have to temporarily copy the other mod's folder to your %appdata%/SpaceEngineers/Mods/, without renaming it!

A more real example, if one were to change volume of my PaintGun item, it would have:

<Icon>Textures\UI\PaintGun.dds</Icon>
<Model>Models\PaintGun.mwm</Model>

that would look inside the new mod, but one can change those to:

<Icon>..\500818376\Textures\UI\PaintGun.dds</Icon>
<Model>..\500818376\Models\PaintGun.mwm</Model>

(see the id=500818376 in the mod link) So that they're still looking for them in the original mod.

This of course requires the PaintGun mod to be added as a dependency so it's automatically used whenever the item sbc mod is.

NOTE: this is for Steam workshop, I don't know if this works for mod.io ones, I've been told they're as .zip in %appdata%/SpaceEngineers/cache/ so it might work, experiment and tell me so I can update this notice.

This technique should work for console aswell (mind the above note), however for PC there's a way to do partial definition edits with this script mod: Mod Adjuster V2.


Multiple Icons (stacked)

(Back to top)

You can stack multiple icons for the same block, item, etc. This should work on anything that has the <Icon> tag (which is all definitions, but not all of them actually use it). To do it, simply declare <Icon> multiple times, no wrapping tag or anything, example:

<Icon>Textures\Icons\SomeIcon.dds</Icon>
<Icon>Textures\Icons\SomeOverlayedIcon.dds</Icon>

Remove definition

(Back to top)

Mind that this might not always work or it can cause crashes because other things refer to the definition you've removed. It can work in some cases, like respawn ships.
For blocks it depends where else it's referenced, like it might crash if they were referenced in radial menu, which is most vanilla blocks. See below in Make a block unbuildable for hiding blocks efficiently instead.

Using it is simply adding Enabled="false" inside the opening tag of the definition.

Here's an example:

    <Ship Enabled="false">
      <Id>
        <TypeId>RespawnShipDefinition</TypeId>
        <SubtypeId>RespawnSpacePod</SubtypeId>
      </Id>
...

NOTE: do not strip the definition of other tags, they still have to be validly processed and loaded before they're getting removed =) I know, it's weird.


Remove Blueprint from BlueprintClass

(Back to top)

It seems it is possible to remove an Entry from <BlueprintClassEntries> but the Enabled is a normal tag instead of an attribute.

An example removing the gravity generator component blueprint from the assembler:

<?xml version="1.0"?>
<Definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <BlueprintClassEntries>
        <Entry Class="Components" BlueprintSubtypeId="GravityGeneratorComponent">   <!-- mind the ending with > not /> -->
            <Enabled>false</Enabled>
        </Entry>
    </BlueprintClassEntries>
</Definitions>

The tag ending with /> means it self-closes, so a <Tag /> == <Tag></Tag>, but in this case <Enabled> has to go inside the <Entry> tag therefore it should not self-close, like shown above.


Make a block unbuildable

(Back to top)

Blocks can be hidden from build menu by simply <Public>false</Public> in its definition.

Existing blocks in world can only be removed manually or by completely removing the block from existance as shown above in Remove Definition, but that can have problems with blocks depending on its references, try it though.

However, that does not fully prevent players from building it using projectors or if they have it in toolbar already (which they can also add to by using block picker mods).

To prevent those cases too you need to give it an unobtainable component as the first one and make it deconstruct to scrap (in block's definition).

For unobtainable component just make one up (see Components.sbc) and you also have to create a blueprint for it (Blueprints.sbc) otherwise it crashes, you don't need to add the blueprint to any blueprint class however, meaning it remains uncraftable (See zonechip component in both those files for reference).

For the deconstruction, from battery block definition as an example:

            <Components>
                <Component Subtype="SteelPlate" Count="20" />
                <Component Subtype="Construction" Count="10" />
                <Component Subtype="PowerCell" Count="80">   <!-- mind the ending with > not /> -->
                    <DeconstructId>
                        <TypeId>Ore</TypeId>
                        <SubtypeId>Scrap</SubtypeId>
                    </DeconstructId>
                </Component>
                <Component Subtype="Computer" Count="25" />
                <Component Subtype="Construction" Count="20" />
                <Component Subtype="SteelPlate" Count="60" />
            </Components>

The tag ending with /> means it self-closes, so a <Tag /> == <Tag></Tag>, but in this case <DeconstructId> has to go inside the <Component> tag therefore it should not self-close, like shown above.


Random model for a droppable item

(Back to top)

Only works for items.

Usage example:

<Models>
    <Model>Models\SomeModel1.mwm</Model>
    <Model>Models\SomeModel2.mwm</Model>
    <Model>Models\SomeModelWhatever.mwm</Model>
</Models>

If this list is declared, <Model> tag gets ignored.

Changing ore models

Using this <Models> tag on Ore TypeId will replace their dynamic model feature (which uses DebrisModels.sbc, only <Type>Voxel</Type> ones).

Also the game will shader crash with a normal model, remove all textures from your material or see RockDebris .fbx and .xml from SDK.
The game will assign the textures from the voxel material to these models.


Common block tags explained

(Back to top)

A few of the tags that are available on all blocks, their defaults and their function.

<!-- Any string that you can use on 2 blocks to pair them into being toggled between grid sizes with R/same-number.
Should not be used on more than 1 block of each grid size and should not be omitted. The subtype without the size indicator is a good one to use.
Also used by BlockLimits in world settings (usable in DS GUI).
And also used by mechanical blocks (rotors/pistons/suspensions) as a target for <RotorTop>/etc instead of a subtypeId which is
  important for "Add small top" from terminal to work.
-->
<BlockPairName></BlockPairName>

<!-- decides if the block shows up in All Blocks; NOTE: gets overwritten by BlockVariantGroups.sbc -->
<GuiVisible>true</GuiVisible>

<!-- Defines the cell where the block is pivoted from when placing.
If left undefined it will get calculated to a center-enough cell.
WARNING: It should not be equal or larger than <Size>'s respective X/Y/Z values, or you end up with very weird bugs. -->
<Center x="0" y="0" z="0" />

<!-- if false, makes the block not have any collision, also does not provide mass to the grid -->
<HasPhysics>true</HasPhysics>

<IsStandAlone>true</IsStandAlone>
<!-- if set to false, a grid cannot exist if it only has this block (or multiple blocks that also have this false).
e.g. Removing an armor block that holds this block (which has IsStandAlone=false) would make this block vanish.
NOTE: If HasPhysics is false then IsStandAlone should also be false! Otherwise you end up with non-grindable floating blocks. -->

<!--
This is one of those that has multiple purposes:
- Damage from other thrusters: if it's <= 0.25 then the block doesn't get damaged by SmallGrid thrusters (LargeGrid ones damage everything).
- For volumetric explosions: this multiplied by GeneralDamageMultiplier is used to divide block's integrity by it and then checks
    against some  damage value (unclear if the raw one) before deciding if it gets deleted.
    Also this is before DamageMultiplierExplosion is applied to the damage (which considering its default value makes it important).
- For armor deformations: gets used as a multiplier of sorts.
-->
<DeformationRatio>1</DeformationRatio>

<!-- A multiplier to grinding time (which is already half the welding time, so here 2 would make it identical) -->
<DisassembleRatio>1</DisassembleRatio>

<!-- a multiplier to all damage dealt to this block -->
<GeneralDamageMultiplier>1</GeneralDamageMultiplier>

<!-- a multiplier to explosion damage in some cases, see DeformationRatio's explanation above. -->
<DamageMultiplierExplosion>7.0</DamageMultiplierExplosion>

<!-- changes how subparts are targetted by using mostly detector area for line intersection... easier to see it in action in game -->
<EnableUseObjectSimpleTargeting>false</EnableUseObjectSimpleTargeting>

<!-- Another one that affects multiple things:
- Placing this block: `false` would collide with nearby objects using the boundingbox; `true` would use the visual model which allows tighter fit.
- Placing on this block: `false` would use a mix of collider+model to determine if you're aiming at it; `true` uses only the visual model.
Downside of `true` is that you'll have a harder time aiming at some models (e.g. conveyor tube being hollow will require you to aim at the frame).
Also causes a fake error model error in log/F11 menu =)
-->
<UseModelIntersection>false</UseModelIntersection>

<!-- Usable by deformable armor to provide the model set for edges. See Edges.sbc for values (their subtypes). -->
<EdgeType></EdgeType>

<!-- This defaults to null if it's not declared so here's the non-null syntax instead.
It offsets where blocks aim at this block.
For example used by vanilla artillery because it's shorter than the boundingbox center so turrets would miss trying to shoot it.
-->
<AimingOffset>
  <X>0</X>
  <Y>0</Y>
  <Z>0</Z>
</AimingOffset>

<!-- This is a 3-value bool:
null (undeclared) = uses mount points for airtightness
true = makes block airtight on all sides
false = makes block not airtight at all
-->
<IsAirTight>false</IsAirTight>

Tags that do nothing

These exist in game code and might be in some SBCs, they however do absolutely nothing.
As of SE v203 those are: <DamageThreshold>, <DetonateChance>


<WheelPlacementCollider> explained

(Back to top)

Even though this is declared for all blocks, it's only used by blocks attached to MotorSuspension.
This defines a fake collision cylinder for determining what other blocks can be placed nearby as well as if the wheel can spawn when surrounded by blocks.
This is used instead of the actual wheel collider because that one has to be a sphere for performance and smoothness reasons.

Defaults to blank, instead I'm showing the possible tags and their defaults.

You can also see the fake collider by activating F11's Debug Draw (also turn on draw physics then off again to fix shenanigans) and try to place other blocks near suspension wheels.

<WheelPlacementCollider>
  <!-- fake wheel diameter, ratio of largest between X and Z from the metric boundingbox -->
  <ColliderDiameter>1.0</ColliderDiameter>
  <!-- fake wheel thickness, ratio of Y from the metric boundingbox -->
  <ColliderHeight>1.0</ColliderHeight>
  <!-- fake wheel offset on Y axis, ratio of Y from the metric boundingbox -->
  <ColliderOffset>0.0</ColliderOffset>
</WheelPlacementCollider>

Basically if Diameter and Height are both 1 and offset 0, then the fake wheel will perfectly fit inside the block boundingbox.

If the explanations don't make sense, here's an example of the math involved:
Given a wheel block that is LargeGrid 3x1x3, its metric boundingbox would be 2.5*3 = 7.5m X and Z, with 2.5*1 = 2.5m Y.
Which means:

  • ColliderDiameter of 0.5 would be 7.5 * 0.5 = 3.75m for the fake wheel diameter
  • ColliderHeight of 0.25 would be 2.5 * 0.25 = 0.625m for the fake wheel thickness

Undeclared tags on Ammo (Ammos.sbc)

(Back to top)

Straightforward, here's all the tags available to ammo.

Shared tags (<BasicProperties>):

<DesiredSpeed>0</DesiredSpeed> <!-- (both) max speed -->
<SpeedVariance>0</SpeedVariance> <!-- (projectiles) acts as +/- random to DesiredSpeed, only positive numbers work. -->
<MaxTrajectory>0</MaxTrajectory> <!-- (both) max travel distance before it self-detonates/vanishes -->
<BackkickForce>0</BackkickForce> <!-- (both) newtons applied as impulse to ship/player in opposite direction (can be negative to flip direction) -->
<PhysicalMaterial></PhysicalMaterial> <!-- (both) subtypeId from PhysicalMaterials.sbc, also see MaterialProperties.sbc for behaviors -->
<ExplosiveDamageMultiplier>1</ExplosiveDamageMultiplier> <!-- (both) as a magazine in a container, this determines if that container explodes when destroyed -->
<IsExplosive>false</IsExplosive> <!-- not used by game code -->
<EndOfLifeEffect></EndOfLifeEffect> <!-- particle subtypeId that triggers when projectile/missile reaches its end of travel without hitting anything -->
<EndOfLifeSound></EndOfLifeSound> <!-- same trigger as above but an audio subtypeId instead -->

Projectile ammo (<ProjectileProperties>):

<ProjectileCount>1</ProjectileCount> <!-- integer value. amount of projectiles to spawn, each will be subject to gun's DeviateShotAngle allowing for shotguns -->
<ProjectileHealthDamage>0</ProjectileHealthDamage> <!-- damage to characters -->
<HeadShot>false</HeadShot>
<ProjectileHeadShotDamage>120</ProjectileHeadShotDamage> <!-- replaced damage if it hits a character's head, only used if HeadShot is true -->
<ProjectileMassDamage>0</ProjectileMassDamage> <!-- damage to everything else -->
<ProjectileExplosionDamage>0</ProjectileExplosionDamage>
<ProjectileExplosionRadius>0</ProjectileExplosionRadius> <!-- meters -->
<ProjectileHitImpulse>0</ProjectileHitImpulse> <!-- in newton-seconds; half to everythong except characters which get x50. just keen things -->
<ProjectileOnHitEffectName>Hit_BasicAmmoSmall</ProjectileOnHitEffectName> <!-- particle subtypeId -->
<ProjectileTrailColor x="1" y="1" z="1" /> <!-- X/Y/Z == R/G/B, can go past 1 to create glow/bloom -->
<ProjectileTrailMaterial></ProjectileTrailMaterial> <!-- transparent material subtypeId, if empty then uses ProjectileTrailLine -->
<ProjectileTrailProbability>1.0</ProjectileTrailProbability> <!-- 0.0 to 1.0 as chance percentage -->
<ProjectileTrailScale>0.1</ProjectileTrailScale> <!-- has a small random and scales with FOV a bit too, it does not have a unit, eyeball it -->

Missile ammo (<MissileProperties>):

<MissileSkipAcceleration>false</MissileSkipAcceleration> <!-- if true, uses ammo's DesiredSpeed as the constant speed -->
<MissileAcceleration>0</MissileAcceleration> <!-- m/s/s, only works if MissileSkipAcceleration is false; max speed is DesiredSpeed, capped spherically -->
<MissileExplosionDamage>0</MissileExplosionDamage>
<MissileExplosionRadius>0</MissileExplosionRadius> <!-- meters -->
<MissileGravityEnabled>false</MissileGravityEnabled> <!-- whether missile is affected by natural gravity. artificial is always ignored. NOTE: gravity is retrieved only once on missile spawn. -->
<MissileHealthPool>0</MissileHealthPool> <!-- block health it can penetrate -->
<MissileInitialSpeed>0</MissileInitialSpeed> <!-- m/s -->
<MissileMass>0</MissileMass> <!-- in kg like everywhere else -->
<MissileModelName></MissileModelName> <!-- path to model .mwm, defaults null but should probably not be left as such -->
<MissileTrailEffect>Smoke_Missile</MissileTrailEffect> <!-- particle subtypeId -->
<!-- these are defaults if null or undeclared; see explanations below. -->
<ExplosionFlags>AFFECT_VOXELS APPLY_FORCE_AND_DAMAGE CREATE_DECALS CREATE_SHRAPNELS APPLY_DEFORMATION CREATE_PARTICLE_DEBRIS</ExplosionFlags>

These flags affect the explosion caused by <MissileExplosionDamage>. Penetration pool (<MissileHealthPool>) is not affected by these flags.

All possible values: CREATE_DEBRIS AFFECT_VOXELS APPLY_FORCE_AND_DAMAGE CREATE_DECALS FORCE_DEBRIS CREATE_PARTICLE_EFFECT CREATE_SHRAPNELS APPLY_DEFORMATION CREATE_PARTICLE_DEBRIS FORCE_CUSTOM_END_OF_LIFE_EFFECT

Each flag's purpose:

  • APPLY_FORCE_AND_DAMAGE determines if the explosion deals any damage.
    Unsure what "force" means here, it doesn't prevent <BackkickForce> nor the physical impact of the missile from pushing things.
  • AFFECT_VOXELS damages voxels but only if APPLY_FORCE_AND_DAMAGE is also present and voxel destruction is allowed in world.
    NOTE: AdaptiveSimulation world setting can make explosions not damage voxels if CPU is >90%.
  • CREATE_PARTICLE_EFFECT decides if the explosion makes a primary particle effect.
    NOTE: this flag seems to be automatically added if the missile is not destroyed by other means (impacting safezone, destroyed by other explosions, destroyed by mods).
  • CREATE_PARTICLE_DEBRIS spawns a secondary particle effect: Explosion_Debris (hardcoded).
  • CREATE_DEBRIS spawns some model debris flying randomly outwards, only on hitting an entity. Amount of them depends on the explosion volume.
  • FORCE_CUSTOM_END_OF_LIFE_EFFECT allows <EndOfLifeEffect> and <EndOfLifeSound> to be used.
  • Seem to be not used: CREATE_DECALS, FORCE_DEBRIS, CREATE_SHRAPNELS

TargetLockingComponent (EntityComponents.sbc)

(Back to top)

EDIT: v203 added all the tags to the sbc alongside adding Character-suffixed ones.

This for some unknown reason has no tags declared in the SBC, but the definition actually has 4 tags that one can edit:

<LockingModifierSmallGrid>2</LockingModifierSmallGrid>
<LockingModifierLargeGrid>1</LockingModifierLargeGrid>
<LockingModifierCharacter>2</LockingModifierCharacter>

<LockingTimeMin>1</LockingTimeMin> <!-- seconds -->
<LockingTimeMax>10</LockingTimeMax>
<LockingTimeMinCharacter>1</LockingTimeMinCharacter>
<LockingTimeMaxCharacter>10</LockingTimeMaxCharacter>

<LockingModifierDistance>10</LockingModifierDistance>
<LockingModifierDistanceCharacter>10</LockingModifierDistanceCharacter>

The other component, TargetFocusComponent, does have both its tags mentioned in that file.

The way these are used, pseudocode:

DistanceRatio = DistanceBetweenShips / TargetFocusComponent.FocusSearchMaxDistance
SizeModifier = LockingModifierSmallGrid or LockingModifierSmallGrid depending on target grid size

TimeToLock = (DistanceRatio * LockingModifierDistance * SizeModifier) and limited between LockingTimeMin and LockingTimeMax, inclusive

Use the Character-suffixed ones if the target is not a grid (code does not specifically check for character here, but it probably limits it elsewhere).


Animated panels (MaintenancePanelComponent, EntityComponents.sbc)

(Back to top)

The access panel blocks introduced in v202 brought an animated panel component which can be used on probably any block type (only tested on refinery so far).

This door panel has no physics, it's handled by the GPU and therefore should not impact simspeed.

While the opening/closing is synchronized, it does not offer a way to open/close it from terminal.

Here's all the tags the definition supports with their defaults and comments from testing and digging in game code:

<EntityComponent xsi:type="MyObjectBuilder_MaintenancePanelComponentDefinition">
  <Id>
    <TypeId>MaintenancePanelComponent</TypeId>
    <SubtypeId>SomeSubtypeIdHere</SubtypeId>
  </Id>

  <!-- if nothing entered it uses the first detector_maintenance empty it finds -->
  <DetectorName></DetectorName>

  <!-- if nothing entered it uses the first subpart_ empty it finds -->
  <SubpartName></SubpartName>

  <!-- false is rotation mode, true is translation mode (linear movement); true seems currently broken -->
  <SimpleOpen>false</SimpleOpen>

  <!-- Number values on OpenDirection depend on the mode that SimpleOpen is in:
    SimpleOpen=false -> 0=AxisX, 1=AxisY, 2=AxisZ, 3=CustomAxis
    SimpleOpen=true  -> 0=Forward, 1=Backward, 2=Left, 3=Right, 4=Up, 5=Down -->
  <OpenDirection>0</OpenDirection>

  <CloseSpeed>1</CloseSpeed>
  <OpenSpeed>1</OpenSpeed>
  <SpinUpSpeed>0</SpinUpSpeed> <!-- supposedly accelerating as it opens, seems to actually do nothing. -->
  <MinPosition>0</MinPosition> <!-- degrees in rotation mode, meters in translation mode -->
  <MaxPosition>0</MaxPosition>

  <!-- seems to only be used by rotation mode, uncertain what it actually does, seems nothing -->
  <DefaultOpeningDirectionRevert>false</DefaultOpeningDirectionRevert>

  <!-- supposedly used in rotation mode by CustomAxis (OpenDirection=3), but does not seem to open at all -->
  <HingePosition>
      <X>0</X>
      <Y>0</Y>
      <Z>0</Z>
  </HingePosition>

  <!-- offset to UseObject's position (the detector empty) -->
  <UseObjectPositionMultipliers>
      <X>0</X>
      <Y>0</Y>
      <Z>0</Z> <!-- this axis is flipped, positive values go front instead of back -->
  </UseObjectPositionMultipliers>

  <!-- available on all components; setting it to false allows this component to exist multiple times on the same entity -->
  <RemoveExistingComponentOnNewInsert>true</RemoveExistingComponentOnNewInsert>
</EntityComponent>

Undeclared tags on Radio Antenna

(Back to top)

and their defaults:

<MaxBroadcastRadius>50000</MaxBroadcastRadius>
<LightningRodRadiusLarge>10</LightningRodRadiusLarge>
<LightningRodRadiusSmall>2</LightningRodRadiusSmall>

The way LightningRod ones work is it simply chooses the one that matches the grid size, there's nothing fancy about it.


Undeclared tags on Decoy

(Back to top)

<LightningRodRadiusLarge>50</LightningRodRadiusLarge>
<LightningRodRadiusSmall>10</LightningRodRadiusSmall>

The way LightningRod ones work is it simply chooses the one that matches the grid size, there's nothing fancy about it.


Undeclared tags on ship welder & grinder

(Back to top)

<!-- Shifts center of welding/grinding sphere forwards/backwards.
If model's detector_shiptool is scaled this will get affected by it (making it no longer in meters) -->
<SensorOffset>0</SensorOffset>

Undeclared tags for Medical Room

(Back to top)

The self-explanatory ones along with their defaults:

<RespawnAllowed>true</RespawnAllowed>
<HealingAllowed>true</HealingAllowed>
<RefuelAllowed>true</RefuelAllowed>
<SuitChangeAllowed>true</SuitChangeAllowed>
<SpawnWithoutOxygenEnabled>true</SpawnWithoutOxygenEnabled>
<ForceSuitChangeOnRespawn>false</ForceSuitChangeOnRespawn>
<RespawnSuitName></RespawnSuitName> <!-- character's <Name> not subtypeId -->

You can also define what "suits" (characters) can be equipped using this block:

<CustomWardrobesEnabled>true</CustomWardrobesEnabled> <!-- this is default false, true enables the below list filter -->
<CustomWardRobeNames>
  <Name>SomeCharacterName</Name> <!-- from character's <Name> tag, not SubTypeId -->
  <Name>AndMore...</Name>
</CustomWardRobeNames>

Now for the mess that is this offset... first this is the tag and defaults:

<WardrobeCharacterOffset>
  <X>-1.35</X>
  <Y>-0.9</Y>
  <Z>1.7</Z>
</WardrobeCharacterOffset>

If detector_wardrobe exists in the model it will be used for the player position, it however needs to be 0.98m above the floor where the character's feet would be.
Otherwise without the dummy the <WardrobeCharacterOffset> is the position of the character but Z is flipped, it's right/up/forwards instead of right/up/back as everything else is in this engine.

Then it will calculate this continuously:
dist = Distance(PlayerFeetPosition, BlockCenterPosition) - WardrobeCharacterOffset.Length
Length of WardrobeCharacterOffset is basically distance from 0,0,0 that the vector represents.
If this dist gets higher than 0.5 for a second or so, it'll kick you off the wardrobe, pretty nasty.

Then the camera is placed relative to <WardrobeCharacterOffset> aswell, again with Z flipped, and they're adding -1.15, 0.7, 1.5 to each axis, then the final Z is multiplied by 70 / ClientFieldOfView.

Then the problem is with leaving the wardrobe, if <RespawnAllowed> is true it will use the same logic as respawning for placing the player, but if that is false it will do bad math and place you, from block center (character's feet are their position, so already we're starting head-first in the ceiling) it offsets towards block's forward by <Size> tag's highest number. So for a 1x1x1 largegrid, 1m, for a 5x2x3 smallgrid, 5m (the grid size does not matter, which is the issue with that math).

TL;DR, the math is all weird and wrong in places, what I suggest is:

  • Have a detector_wardrobe where the player's feet are supposed to be placed, then move it 0.98m upwards.
  • Set <WardrobeCharacterOffset> to the same coordinates as the detector_wardrobe, flip Z then add 0.45 to Y, this is so that the camera isn't in the floor (you can freely tweak Z within the regular value and the flipped value, as long as you don't go over those to make the Length larger causing it to kick you off the wardrobe).
  • If respawn is turned off, unfortunately there's no solution for the bad exit position.

Undeclared tags for Respawn Ships

(Back to top)

I'm just gonna post all of them, with their types and default values:

<Prefab></Prefab> <!-- prefab SubTypeId (never file name), defaults to null. Infinite streaming means it can't find prefab. -->
<!-- Side Note: prefabs that get spawned will generate a .sbcB5 (in downloaded mods too) and then subsequent loads will read only the sbcB5 -->

<CooldownSeconds>0</CooldownSeconds> <!-- integer value, see details below on how cooldown works. -->
<SpawnWithDefaultItems>true</SpawnWithDefaultItems> <!-- character spawns with welder/grinder/etc; world setting forces this off on all respawnships -->

<PlanetDeployAltitude>500</PlanetDeployAltitude> <!-- float, meters. Used for any kind of planetary spawn (including SpawnPosition). -->

<!-- starting movement and rotational velocities, relative to grid orientation (see pivot in info tab for its orientation) -->
<InitialLinearVelocity x="0" y="0" z="0" />
<InitialAngularVelocity x="0" y="0" z="0" />

<!-- string value, defaults null; can be language ID or the text directly, shows in a popup when selected.
Note that the popup is not visible for planets that have more than 1 respawnship available for them. -->
<HelpTextLocalizationId></HelpTextLocalizationId>

<UseForSpace>false</UseForSpace>

<UseForPlanetsWithoutAtmosphere>false</UseForPlanetsWithoutAtmosphere>

<UseForPlanetsWithAtmosphere>false</UseForPlanetsWithAtmosphere>
<MinimalAirDensity>0.7</MinimalAirDensity> <!-- only used if above tag is true; this is the required Atmosphere Density at spawn position. -->

<!-- only spawns on these planets and ignores the above UseFor* rules. Defaults to all planets if undeclared -->
<PlanetTypes>
  <PlanetType>SomePlanetSubtype</PlanetType>
  <PlanetType>SomeOtherSubtype</PlanetType>
  <PlanetType>etc...</PlanetType>
</PlanetTypes>

<!-- Specific spawn world position, ignores all other rules.
If a planet is near the position it will spawn on the surface of that planet from the position given, at PlanetDeployAltitude altitude. --->
<SpawnPosition x="0" y="0" z="0" /> <!-- defaults to null, which you can achieve by removing the tag or using: <SpawnPosition xsi:nil="true" />  -->
<SpawnPositionDispersionMin>0</SpawnPositionDispersionMin>
<SpawnPositionDispersionMax>10000</SpawnPositionDispersionMax> <!-- min&max sphere radius where it can randomly spawn at, around the SpawnPosition -->
<SpawnNearProceduralAsteroids>true</SpawnNearProceduralAsteroids> <!-- only used for SpawnPosition -->

Note that planet-supporting respawnships don't show up individually in the respawn screen.
The planets themselves show up and when you click Respawn it picks a random respawnship that is usable on that planet.
While space respawnships all show up individually.

Creative Mode or Scenario Mode would not show any respawnships, same as disabling EnableRespawnShips world setting.

How cooldowns work

If enabled in world settings (default is disabled), when you spawn with any respawnship it triggers cooldown on absolutely all of them (tracked per player).

For planets that have multiple respawn ships it will use the earliest cooldown (Visual example), but once that planet becomes available it will still give you a random ship! Not just the shorter cooldown one.

How game picks which seat on the ship

It picks from a sorted list of functional (built above red line) seats on all grids, the way they sort them:

  1. Main cockpits
  2. Cockpits that can control ship
  3. Any other kind of seat (passenger, cryo, couch, etc)

Picks first seat from the first group it can find, for example if there's 0 main, 2 regular, 2 cryos, it goes with one of the regular ones, likely first declared in sbc (it's up to the sorting algorithm really).

If it fails to find a cockpit, it probably spawns character near the ship.


Undeclared Ratio for reactor

(Back to top)

In reactors' <FuelInfo> tag you can also have a <Ratio> tag:

<FuelInfos>
    <FuelInfo>
        <Id>
            <TypeId>Ingot</TypeId>
            <SubtypeId>Uranium</SubtypeId>
        </Id>
        <Ratio>1</Ratio>
    </FuelInfo>
</FuelInfos>

There's also <FuelProductionToCapacityMultiplier> that is default 3600 if undeclared (which is not declared for reactor).

And while we're here... Reactor fuel formula:

MaxFuelPerSecond = ((MaxPowerOutput / FuelProductionToCapacityMultiplier * Ratio) / FuelItem_Mass)

These are all in reactor's definition except for FuelItem_Mass which is the physical item's <Mass>. Ingots have 1kg and it's best to not mess with that because the game was designed with ores and ingots amounts being equivalent to mass.

Hydrogen engine fuel formula:

MaxFuelPerSecond = MaxPowerOutput / FuelProductionToCapacityMultiplier

Both of these scale with current power output percentage.

Also NOTE: even though <FuelInfos> supports more than one fuel, hydrogen engine only uses the first declared.


Thrusters can only use one resource

(Back to top)

For example, hydrogen thrusters only consume hydrogen, they do not (and cannot) consume electricity.

Here's the formula used by thrusters:

FuelPerSec = Thruster_MaxPowerConsumption / (Fuel_EnergyDensity * Thruster_Efficiency)

The EnergyDensity is in GasProperties.sbc for the chosen fuel.

For electric thrusters, it will just be the Min/MaxPowerConsumption tags in MW.


Inventory size? Railgun charge? EntityComponents.sbc!

(Back to top)

EntityComponents.sbc is just a declaration of components, the EntityContainers.sbc is what links entities (blocks, characters, etc) to entity components.

Some vanilla cargo containers have their inventory volume declared there aswell as railguns' charge and capacity.

For inventories the <Size x="0.4" y="1" z="1" /> tag is simply 3D volume, it will get the final volume from X * Y * Z which in this case is 0.4 m3.

And here's all the tags supported by MyObjectBuilder_InventoryComponentDefinition:

<Size x="1" y="1" z="1" /> <!-- nullable Vector3, defaults to null -->
<Volume>3.40282347e+38</Volume> <!-- default is float.MaxValue; if Size is declared then it will override this -->
<Mass>3.40282347e+38</Mass> <!-- default is float.MaxValue -->
<RemoveEntityOnEmpty>false</RemoveEntityOnEmpty> <!-- used by temporary inventories like dead character's backpack -->

<!-- turning this off *should* prevent volume and mass from being affected by multiplier (ship or character, depending on which it's applied to) -->
<MultiplierEnabled>true</MultiplierEnabled>
<MaxItemCount>2147483647</MaxItemCount> <!-- default is int.MaxValue -->

<!-- this defaults to null which means allow all items. instead I'll just show all the possible options: -->
<InputConstraint Whitelist="true" Description="Tooltip here" Icon="Texture\SomeIcon.dds">
    <!-- example of all magazines regardless of subtypes -->
    <Entry Type="AmmoMagazine" />

    <!-- example of a specific type+subtype -->
    <Entry>
        <TypeId>Component</TypeId>
        <SubtypeId>SteelPlate</SubtypeId>
    </Entry>
</InputConstraint>

The railgun component (MyObjectBuilder_EntityCapacitorComponentDefinition) already shows both its tags in vanilla sbc.

Speaking of railguns, here's some handy formulas to calculate one of Capacity/RechargeDraw/Time by inputting the other 2:

Time = (Capacity * 60 * 60) / (RechargeDraw * 0.8)
Capacity = (RechargeDraw * 0.8) * (Time / 60 / 60)
RechargeDraw = ((Capacity * 60 * 60) / Time) * (1 / 0.8)

The 0.8 being the charge loss which is hardcoded.


Change render distance on multi-LCD blocks

(Back to top)

For standalone LCD (<TypeId>TextPanel</TypeId>) it has the render distance right in the block definition:

<MaxScreenRenderDistance>180</MaxScreenRenderDistance>

(and that is the default if undeclared)

For blocks that have a different purpose and just so happen to support LCDs, they don't have a setting in the block definition, however using EntityComponents one can change it.

Here's an example of doing it to the big-screen cockpit:

<?xml version="1.0" encoding="utf-8" ?>
<Definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <EntityContainers>
        <Container>
            <Id>
                <TypeId>Cockpit</TypeId>
                <SubtypeId>LargeBlockCockpit</SubtypeId>
            </Id>
            <DefaultComponents>
                <Component BuilderType="MyObjectBuilder_LcdSurfaceComponent" ForceCreate="true"/>
                <Component BuilderType="MyObjectBuilder_MultiTextPanelComponent" SubtypeId="LCD_500m" ForceCreate="true"/>
            </DefaultComponents>
        </Container>
    </EntityContainers>

    <EntityComponents>
        <EntityComponent xsi:type="MyObjectBuilder_MultiTextPanelComponentDefinition">
            <Id>
                <TypeId>MultiTextPanelComponent</TypeId>
                <SubtypeId>LCD_500m</SubtypeId>
            </Id>
            <MaxRenderDistance>500</MaxRenderDistance>
        </EntityComponent>
    </EntityComponents>

</Definitions>

Audio.sbc tags explained

(Back to top)

All the tags, their defaults and explanations on the ones I researched... but brace yourself, there's various quirks which is why there's comments galore below.

<Sound>
    <Id>
        <TypeId>MyObjectBuilder_AudioDefinition</TypeId>
        <SubtypeId>ArcGreatSoundTM</SubtypeId>
<!-- Something important about audio subtypes is the Arc or Real prefix, they pair 2 sounds into one!

From the perspective of something that plays sound you can have 2 approaches:

1. Can give it the exact subtypeId and it will use that one sound for both realistic and arcade contexts.
  The game applies filters to sounds played in realistic mode so it is perfectly fine to have just one sound.

2. Can also have 2 different audio definitions in that one pair.
To do this you input the sound without the Arc or Real prefix and it will look for the name with those prefixes.

To summarize with an example:
If a block looks for GreatSoundTM, then the game first looks for that exact ID,
  if it can't find it then it grabs ArcGreatSoundTM and RealGreatSoundTM (no matter if they exist, so careful there).

NOTE: The sound stored as arcade is still used if realistic sound is enabled in world, when sound needs
  to be crystal clear (atmosphere, pressurized area, etc).
-->
    </Id>

<!-- Can't tell what this affects, aside from 2 specific values: Music (exact case) and HUD (case insensitive).
Music it will enable <DynamicMusic*> tags from below.
Then there's 3 internal pools of playback (which probably control how many sounds can be played at the same time?),
  2 of which are HUD and Music, the 3rd is everything else. -->
    <Category>BLOCK</Category> <!-- string -->

<!-- Multiplier to the volume provided by the files.
Can get overwritten by the sound emitter (via CustomVolume). 
Additive with <VolumeVariation> and it can't be smaller than 0 or larger than 1 after that. -->
    <Volume>1.0</Volume>

<!-- Random -1 to 1 multiplied by this then multiplied by 0.07.
Then is added to <Volume> (or custom volume) and capped between 0 and 1. -->
    <VolumeVariation>0</VolumeVariation>

<!-- Whether the sound will play <Loop> forever until stopped -->
    <Loopable>true</Loopable>

<!-- Max distance that the sound will be heard from -->
    <MaxDistance>100</MaxDistance>

<!-- Sound will not start playing if source is further than this -->
    <UpdateDistance>110</UpdateDistance>


<!-- The actual files that are going to be played.
It's a list so you can add however many <Wave ...> you want. -->
    <Waves>
<!-- If there's multiple <Wave> tags then it will pick one at random per playback. To increase chance of some, define it/them multiple times.
There's only 2 values for Type attribute:
- "D3" is for sound in 3D space, requires mono.
- "D2" is for sound that is played in both speakers with no distance, can be stereo. -->
        <Wave Type="D3">

<!-- The following tags are all optional but you should have at least one.
For looping sounds: <Start> triggers once when it begins, then <Loop> gets repeated, and when stopped it plays <End>.
For non-looping just use <Start> only

They're what point to the actual .wav or .xwm files and path starts from the mod's folder (for audio files in the mod itself).
e.g. <Start>Audio\YourFile.wav</Start> for %appdata%\SpaceEngineers\Mods\YourMod\Audio\YourFile.wav

However if you wish to use an audio file from the game, because of weird design you cannot without tricks, and even then you can only reference .wav files.
To do that you have to use the same relative path as in their audio SBC (without Audio\ prefix) and simply remove .wav extension from the file.
This will trigger a warning in F11 menu but there's really no other way to do this short of copying the file which is not ideal. -->
            <Start></Start>
            <Loop></Loop>
            <End></End>

        </Wave>
    </Waves>


<!-- Different audio definitions that play when source is far.
It's a list so you can add however many <DistantSound ... /> you want.
NOTE: Defining order is important, smallest distances first! -->
    <DistantSounds>

<!-- Distance = minimum distance for the sound to be replaced by this entry.
DistanceCrossfade = minimum distance to play both sounds mixed, volume of each depends on distance ratio.
  DistanceCrossfade should be smaller than Distance.
Sound = audio definition subtypeId to play (same rules with Arc/Real prefix as explained in SubtypeId above).
-->
        <DistantSound Distance="50" DistanceCrossfade="-1" Sound="" />

    </DistantSounds>


<!-- Time (in game ticks) that this same sound cannot be played again within.
When 2+ sounds play at the same time synchronized their volume increases, this is to mitigate that.
-1 to disable, 0 to prevent this sound from playing again within 16.667ms of starting, 2 = 33.333ms, etc.
Because game runs at 60 ticks per game second.
NOTE: Might not do anything if multiple sounds come from the same position. -->
    <PreventSynchronization>-1</PreventSynchronization> <!-- integer -->

<!-- Prevents this sound from having this many active playbacks at once, 0 = infinite. -->
    <SoundLimit>0</SoundLimit> <!-- integer -->

<!-- Offsets the sound's pitch in semitones, 0 to not change. -->
    <Pitch>0.0</Pitch>

<!-- A random value from -1 to 1 is multiplied by this value then divided by 100.
The resulting value is in semitones and added to the same value that <Pitch> is used on. -->
    <PitchVariation>0.0</PitchVariation>

<!-- If set to true, it ignores both <Pitch> and <PitchVariation> -->
    <DisablePitchEffects>false</DisablePitchEffects>

<!-- When this sound plays, it adds the amount towards the specified music category.
Once the <DynamicMusicCategory> reaches some internal counter it triggers a random music from that.
The values that can be used as music category here are hardcoded: LightFight, HeavyFight, Building, Danger
Light and heavy fight ones trigger when their respective amount reaches 100, but heavy adds to both light and heavy internal counters.
Building internal counter needs to reach 7000 to trigger.
Danger one ignores <DynamicMusicAmount> and seems related to meteor shower.
For this to be used, <DynamicMusicCategory> needs to be not empty and <DynamicMusicAmount> to be higher than 0 (even if using Danger category).
-->
    <DynamicMusicCategory></DynamicMusicCategory> <!-- values: LightFight, HeavyFight, Building, Danger -->
    <DynamicMusicAmount>0</DynamicMusicAmount> <!-- integer -->

<!-- For these to be used, <Category> needs to be set to Music
Both of these names can be invented from what I can tell, aside from that I don't know how these work, see Audio_music.sbc for hints. -->
    <MusicCategory></MusicCategory> <!-- string, inventable -->
    <TransitionCategory></TransitionCategory> <!-- string, inventable -->

<!-- Likely useful for large sounds to avoid storing them in memory and instead read them live from the drive. -->
    <StreamSound>false</StreamSound>

<!-- They're pointing to subtypes in AudioEffects.sbc and the Arcade/Realistic part is for the specific sound mode in the world -->
    <ArcadeFilter></ArcadeFilter> <!-- string, subtypeId -->
    <RealisticFilter></RealisticFilter> <!-- string, subtypeId -->
<!-- NOTE: the game automatically adds some filters to sounds in realistic sound mode regardless of these -->

<!-- Makes it always pick the realistic from the sound pair (see subtype description above to understand) -->
    <AlwaysUseOneMode>false</AlwaysUseOneMode>

<!-- Likely related to the automatic filters provided by realistic sound mode -->
    <CanBeSilencedByVoid>true</CanBeSilencedByVoid>
    <ModifiableByHelmetFilters>true</ModifiableByHelmetFilters>

<!-- A multiplier to <Volume> only if the sound itself is realistic (Real prefix)... seems pointless? -->
    <RealisticVolumeChange>1.0</RealisticVolumeChange>


<!-- Seem to not be used at all by the game, they still exist in the definition code so here they are xD -->
    <VolumeCurve>Custom_1</VolumeCurve> <!-- Available values: Linear, Quadratic, Poly2, Custom_1 -->
    <UseOcclusion>false</UseOcclusion>
    <Alternative2D></Alternative2D> <!-- string -->
</Sound>

A few other things worth mentioning about sounds in SE:

  • Can use either .wav or .xwm, wav is not compressed while .xwm is but it's not ideal for seamless loops because the compression leaves some empty sound at the end.
  • If you want to use .xwm then use the xWMAencode.exe that comes with SE or SE SDK, don't use Skyrim's or whatever else.

Mountpoint features

(Back to top)

There's a few mountpoint things that aren't immediately obvious, here's all the available attributes one has and their defaults:

<MountPoint Side="Right" StartX="0" StartY="0" EndX="0" EndY="0" Default="false" Enabled="true" PressurizedWhenOpen="true" PropertiesMask="0" ExclusionMask="0" CouplingTag="" AllowCouplingWithItself="false" />

The required ones are Side, Start* and End* which define which side of the block it's on and in cell-units how far it spans, for a 1x1x1 block the values are from 0 to 1, for a 2x2x2 block they're from 0 to 2, and so on.

Now onto optional ones:

Default specifies this mountpoint's side as the aim-with side for auto-rotate feature when building.

Enabled if the mountpoint is used for mounting. Setting this to false allows you to specify airtight faces without them being also mountable.

Reminder that block definition's <IsAirTight> must not be declared for mountpoints to be used for airtightness.

PressurizedWhenOpen defaults to true which is nothing special with this value.
When set to false it then depends on the block type it's used on:

  • any kind of door type: it makes the cells covered by this mountpoint airtight only when the door is closed.
  • everything else: cells covered by it are not marked airtight, can be used to have full-side mountpoints that are not airtight.

This exists because Door (and AdvancedDoor too) TypeId have airtight-toggled sides for any side that has zero mountpoints, which was a problem when Keen added the Frostbite gate.

Other doors have hardcoded airtight-toggled sides: AirtightHangarDoor: front and back, AirtightSlideDoor: front.


PropertiesMask = what this mountpoint is
ExclusionMask = what properties to not allow to mount to this mountpoint
Both default to 0 which means no properties and no exclusion.

The values for either mask tags can be one or all of these (by adding the numbers):

  • 1 = Extruding (classic door's side bits go outside of its boundingbox and use this, same for interior lights)
  • 2 = Thin/small (catwalks, lights, etc)
  • 4, 8, 16, 32, 64, 128 = Not used by Keen (up to SE v202).

The way these work is when a block is checked if its mounts overlap another's, they're also checking BlockA's PropertiesMask vs BlockB's ExclusionMask and BlockA's ExclusionMask vs BlockB's PropertiesMask, if either of these checks match then it does not allow the block to mount.
Also if BlockA's <Id> is the same as BlockB's <Id> then these masks are ignored.
(source)

Here's a practical example from game's usage of it:

  • Interior light has its only mountpoint as ExclusionMask="3" (1+2) which means it can't be placed on extruding nor thin mountpoints.
  • It also has PropertiesMask="3" (1+2) which means itself is both thin and extruding.
  • Then classic door has PropertiesMask="1" on the sides because they poke outside the block quite significantly.
  • This means you cannot place interior light on the sides of classic door because 1, because interior lights exclude 1+2 and classic door has 1.
  • But classic door's side can mount to another classic door's side because they're the same block <Id> which ignores these masks.

CouplingTag can be any string you want or empty to disable.
If not empty, makes the mountpoint only mountable to other mountpoints that contain this string in their CouplingTag, or vice-versa.

Example, BlockA has CouplingTag="Water Ice Fire" and:

  • BlockB has CouplingTag="Fire" then they can mount, because B's tag is contained in A's tag.
  • BlockB has CouplingTag="Fire Ice" then they cannot mount, because it does not process the string in any way.
  • BlockB has CouplingTag="" (same as undeclared) then they also cannot mount.

AllowCouplingWithItself allows mounting the same block ID to itself with this mount, false does not allow that.
Only works if CouplingTag is not empty.

NOTE: Conveyor/upgrade/etc ports do not care about mountpoints.


Disabling all mountpoints

(Back to top)

You cannot disable all mountpoints like keen does with an empty MountPoints list, I've no idea why but they have a special check for it, if it's a modded one and empty tags, it will auto-generate it as if it's not declared at all.

To work around this you can declare a disabled mountpoint:

<MountPoints>
  <MountPoint Side="Back" StartX="0" StartY="0" EndX="0" EndY="0" Enabled="false" />
</MountPoints>

Different mountpoints on build stages

(Back to top)

This feature is from Medieval Engineers where they prevent the top part of a block from being mountable until the block is more constructed or even fully built.

It seems to work in SE but as with all features not directly used by Keen in SE, they tend to be buggy(er) or could break in the future.

To use it you have to first open up the Model tag from <BuildProgressModels>, as in all definitions it's self-closing.

That is done by changing /> to > and then add a regular closing tag:

<Model BuildPercentUpperBound="0.33" File="Models\Cubes\Large\Armor\SquarePlateConstruction_1.mwm" />

to

<Model BuildPercentUpperBound="0.33" File="Models\Cubes\Large\Armor\SquarePlateConstruction_1.mwm">
</Model>

Very important to change the /> to > !

Then inside that model tag you can add <MountPointOverrides> which has the same exact layout as <MountPoints>.

However it works quite differently:

  • it starts off with a clone of the regular mountpoints.
  • declaring a Side in <MountPointOverrides> will first remove all mountpoints for that side then it adds yours.
  • any following build progresss models that have <MountPointOverrides> will inherit the previous one's changes.

Here's what I played around with, I added comments of what the result is:

<!-- for reference, the regular mountpoints -->
<MountPoints>
    <MountPoint Side="Back" StartX="0" StartY="0" EndX="2" EndY="4" />
    <MountPoint Side="Bottom" StartX="0" StartY="1" EndX="2" EndY="2" Default="true" />
    <MountPoint Side="Top" StartX="0" StartY="0" EndX="2" EndY="1" />
    <MountPoint Side="Left" StartX="1" StartY="0" EndX="2" EndY="4" />
    <MountPoint Side="Right" StartX="0" StartY="0" EndX="1" EndY="4" />
</MountPoints>

<BuildProgressModels>
    <Model BuildPercentUpperBound="0.16" File="Models\Cubes\Large\RefineryConstruction_1.mwm">
        <MountPointOverrides>
            <MountPoint Side="Back" Enabled="false" />
            <MountPoint Side="Top" Enabled="false" />
            <MountPoint Side="Left" Enabled="false" />
            <MountPoint Side="Right" Enabled="false" />
            <!-- results in only bottom side remaining -->
        </MountPointOverrides>
    </Model>
    
    <Model BuildPercentUpperBound="0.33" File="Models\Cubes\Large\RefineryConstruction_2.mwm">
        <MountPointOverrides>
            <!-- results in identical mountpoints as previous build stage -->
        </MountPointOverrides>
    </Model>
    
    <Model BuildPercentUpperBound="0.50" File="Models\Cubes\Large\RefineryConstruction_3.mwm">
        <MountPointOverrides>
            <MountPoint Side="Back" StartX="0" StartY="0" EndX="2" EndY="4" />
            <!-- results in bottom + back side -->
        </MountPointOverrides>
    </Model>
    
    <Model BuildPercentUpperBound="0.83" File="Models\Cubes\Large\RefineryConstruction_4.mwm">
        <MountPointOverrides>
            <!-- results in identical mountpoints as previous build stage -->
        </MountPointOverrides>
    </Model>
    
    <!-- this one has no override so it just uses the regular full mountpoints -->
    
    <Model BuildPercentUpperBound="1.00" File="Models\Cubes\Large\RefineryConstruction_5.mwm" />
</BuildProgressModels>

And here's the mountpoints in visual form, using my BuildInfo mod to show construction stages (alt R) and mountpoint overlays (ctrl +):


<TieredUpdateTimes> explained

(Back to top)

You might've seen these tags on some blocks:

<TieredUpdateTimes>
    <unsignedInt>60</unsignedInt>
    <unsignedInt>120</unsignedInt>
    <unsignedInt>240</unsignedInt>
</TieredUpdateTimes>

What it affects is how frequent the block updates server-side depending on player-nearby-ness (in case server is also player) and if grid is streamed to any MP client. This of course impacts performance so if you're uncertain then don't change them.

You also don't need to declare them, they have defaults per block type.

The <unsignedInt> tags in order are the tiers:

  1. Normal - local player is nearby or grid streamed to at least one player.
  2. Tier1 - local player not nearby and grid is not streamed to anyone.
  3. Tier2 - same as Tier1 but 10 minutes passed since that turned to Tier1.

Some block types will also care if any other grid is within 3km of its own grid, a few cases I found (SE v202):

  • Regular turrets: if a grid is nearby they use Normal instead
  • Thrusts: if they're on Tier2 and a grid is nearby, they'll use Tier1 instead
  • Connector: if a grid is nearby they use Normal instead

The values are ticks between updates (game runs at 60 ticks per game second), and 0 can be used to completely pause the block's update, only if it supports this system of course.

So for the above snippet, the refinery updates once per second if any player is within sync distance.

In SE v202 the following block types use this system:

  • 3 values: TurretControlBlock, Reactor, Thrust, Refinery, Assembler, SurvivalKit, GasGenerator, Connector
  • 2 values: All regular turrets
  • 1 value: Searchlight

Planet CloudLayers and fake errors

(Back to top)

CloudLayers section from planet definitions is such a nightmare, let me break it down:

  • You're supposed to give it a Path\TextureName.dds and it automatically looks for Path\TextureName_cm.dds and Path\TextureName_alphamask.dds.
  • It cares not for your suffixes, if you give it Texture_cm.dds it will look for Texture_cm_cm.dds and Texture_cm_alphamask.dds.
  • It only reads the first <Texture> so don't bother listing more than one per CloudLayer.
  • You get an error in F11 menu for the Path\TextureName.dds not existing, which you should ignore as long as the texture actually shows up in game.
  • Depending on the definition style at the begining and end, can have different behaviors:
    • Using the old <Definition xsi:type="PlanetGeneratorDefinition">:
      • does not forcefully append mod folder to CloudLayers (see below for example).
      • can use textures from game folder.
      • can add a fake texture to get rid of the error (only if you have the real textures in your mod too).
      • can't read textures from mods unless you add the fake texture to get rid of the error.
    • Using the new <PlanetGeneratorDefinitions> + <PlanetGeneratorDefinition>:
      • forcefully adds mod folder to CloudLayers textures (regardless of them existing in mod folder or not).
        • (e.g. Textures\Things\Texture.dds becomes C:\Steam\WorkshopOrWhatever\YourModId\Textures\Things\Texture.dds)
      • because of the above, cannot use textures from game folder.
      • cannot add a fake texture to get rid of the error because then mod path is added twice, breaking it.
      • textures in mods are automatically found because of the first point, but previous point breaks it.

PlanetGeneratorDefinitions.sbc has the old definitions style.
Triton.sbc, Pertam.sbc etc have the new definition style.
Don't forget that the ending of the file also has to be changed to match.

So TL;DR my recomandation for linking CloudLayer textures:

  • Use old style definition (<Definition xsi:type="PlanetGeneratorDefinition">)
  • Depending on where you want to use textures from:
    • Game folder: just have the texture name as normal (without the suffixes), you can't avoid the missing texture error though, just ignore it and test it visually.
    • Mod folder: you must add a fake texture (a renamed empty .txt file works) so that the game finds it and appends the mod folder path to its path internally, otherwise it remains relative and looks in game folder. Bonus that the missing texture error will be gone too.

Contract custom icon/header

(Back to top)

They expect exactly 2 textures:

<Icon>Textures\GUI\Icons\Contracts\RepairContract.dds</Icon> <!-- actual icon -->
<Icon>Textures\GUI\Icons\Contracts\RepairContractHeader.dds</Icon> <!-- banner when selected -->

However the game does not properly load icons from contracts. They need to be preloaded by something else and as Patrick found the ideal way is a fake item per icon (because it only preloads its first icon).

Example:

<?xml version="1.0"?>
<Definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <PhysicalItems>
        <PhysicalItem xsi:type="MyObjectBuilder_PhysicalItemDefinition">
            <Id>
                <TypeId>PhysicalObject</TypeId>
                <SubtypeId>YourModName_ContractA_Icon</SubtypeId>
            </Id>
            <Icon>Textures\Icons\ContractA_Icon.dds</Icon>
            <DisplayName>Contract Icon Preloader</DisplayName>
            <Public>false</Public>
            <CanPlayerOrder>false</CanPlayerOrder>
            <CanSpawnFromScreen>false</CanSpawnFromScreen>
        </PhysicalItem>
        <PhysicalItem xsi:type="MyObjectBuilder_PhysicalItemDefinition">
            <Id>
                <TypeId>PhysicalObject</TypeId>
                <SubtypeId>YourModName_ContractA_Header</SubtypeId>
            </Id>
            <Icon>Textures\Icons\ContractA_Header.dds</Icon>
            <DisplayName>Contract Icon Preloader</DisplayName>
            <Public>false</Public>
            <CanPlayerOrder>false</CanPlayerOrder>
            <CanSpawnFromScreen>false</CanSpawnFromScreen>
        </PhysicalItem>
        <!-- etc... -->
    </PhysicalItems>
</Definitions>

Block <VoxelPlacement> explained

(Back to top)

This part of a block definition:

<VoxelPlacement>
    <!--Possible settings Both,InVoxel,OutsideVoxel,Volumetric. If volumetric set than MaxAllowed and MinAllowed will be used.-->
    <StaticMode>
        <PlacementMode>Volumetric</PlacementMode>
        <MaxAllowed>0.2</MaxAllowed>
        <MinAllowed>0</MinAllowed>
    </StaticMode>
    <DynamicMode>
        <PlacementMode>Volumetric</PlacementMode>
        <MaxAllowed>0.2</MaxAllowed>
        <MinAllowed>0</MinAllowed>
    </DynamicMode>
</VoxelPlacement>

Is what determines how the block can be placed inside voxels or outside.

If this tag is not defined on a block it will fall back to the settings from SessionComponents.sbc.

Now to explain how it works.

For <PlacementMode> values:

  • None = can't be placed regardless of voxels.
  • Both = fully permissive, allowed both inside and outside of voxels.
  • InVoxel = can only be placed if any boundingbox corner is inside voxels.
  • OutsideVoxel = can only be placed if none of its boundingbox corners are inside voxels.
    NOTE: it can still be placed inside with its middle if the block is large enough, if you wish for any part to not touch voxels use Volumetric with both min and max as 0 instead.
  • Volumetric = determines how much inside or outside voxels it can be, configured using <MinAllowed>/<MaxAllowed> tags, see blow for details.

For the <MinAllowed>/<MaxAllowed> tags:

Only used if <PlacementMode>Volumetric</PlacementMode>.
Range for these tags are 0.0 to 1.0 equivalent to 0% to 100% of block's volume intersecting voxels.
NOTE: the code calculating volume ratio only gets to 0.5-0.75 tops (jitters between these) in best case scenario, but with cubebuilder raycasting to surface it realistically goes up to 0.5.

  • Min is how much is required be inside voxels meaning it can't be placed outside of voxels if this is above 0.
  • Max is how much to allow inside voxels.
    Both 0 will act similar to OutsideVoxel but with a volume check instead of checking corners.

What the <StaticMode> vs <DynamicMode> mean:

  • It has nothing to do with a cubegrid's physical static-ness.
  • Static is used when aiming at a cubegrid or at a voxel with "local grid mode", or basically if your block is restricted to a 3D grid then it's likely using Static mode in this.
  • Dynamic is opposite, if you can freely move the block without a 3D grid, even when aiming at a voxel, then it uses Dynamic mode.

I recommend to not care about the distinction between Static or Dynamic and set both to the same exact settings. This is what Keen also does.


Conveyor & LCD support by block TypeID

(Back to top)

Only specific block types can connect to conveyor network and can have LCDs.

As of SE v203 those are:

Conveyor = Can connect to conveyor network (if model has conveyor ports).
LCD = Supports having LCDs.
LCD ModFix = Can use Fix LCD support mod (and caution part explained there).
- = Does not have that thing.

TypeId Conveyor LCD
AdvancedDoor - LCD ModFix
AirtightHangarDoor - LCD ModFix
AirtightSlideDoor - LCD ModFix
AirVent Conveyor LCD ModFix+Caution
Assembler Conveyor LCD
BasicMissionBlock - LCD ModFix
BatteryBlock - LCD ModFix
Beacon - LCD ModFix
ButtonPanel - LCD
CameraBlock - LCD ModFix
CargoContainer Conveyor -
Cockpit Conveyor LCD
Collector Conveyor LCD ModFix
ContractBlock Conveyor LCD
Conveyor Conveyor -
ConveyorConnector Conveyor -
ConveyorSorter Conveyor LCD ModFix
CryoChamber Conveyor LCD
Decoy - LCD ModFix
DefensiveCombatBlock - LCD ModFix
Door - LCD ModFix
Drill Conveyor LCD ModFix+Caution
EmissiveBlock - -
EmotionControllerBlock - LCD
EventControllerBlock - LCD ModFix
ExhaustBlock - LCD ModFix
ExtendedPistonBase Conveyor LCD ModFix
FlightMovementBlock - LCD ModFix
FunctionalBlock - LCD ModFix
GravityGenerator - LCD ModFix
GravityGeneratorSphere - LCD ModFix
Gyro - LCD ModFix
HeatVentBlock - LCD ModFix+Caution
HydrogenEngine Conveyor LCD ModFix+Caution
InteriorLight - LCD ModFix+Caution
InteriorTurret - LCD ModFix+Caution
Jukebox - LCD
JumpDrive - LCD ModFix
Kitchen - -
Ladder2 - -
LandingGear - LCD ModFix
LargeGatlingTurret Conveyor LCD ModFix+Caution
LargeMissileTurret Conveyor LCD ModFix
LaserAntenna - LCD ModFix
LCDPanelsBlock - LCD
MedicalRoom Conveyor LCD
MergeBlock - LCD ModFix
MotorAdvancedRotor Conveyor -
MotorAdvancedStator Conveyor LCD ModFix
MotorRotor - -
MotorStator Conveyor LCD ModFix
MotorSuspension - LCD ModFix
MyProgrammableBlock - LCD
OffensiveCombatBlock - LCD ModFix
OreDetector - LCD ModFix
OxygenFarm Conveyor LCD ModFix
OxygenGenerator Conveyor LCD ModFix
OxygenTank Conveyor LCD ModFix
Parachute Conveyor LCD ModFix
Passage - -
PathRecorderBlock - LCD ModFix
PistonTop Conveyor -
Planter - -
Projector - LCD
RadioAntenna - LCD ModFix
Reactor Conveyor LCD ModFix
Refinery Conveyor LCD
ReflectorLight - LCD ModFix+Caution
RemoteControl - -
SafeZoneBlock Conveyor LCD
Searchlight - LCD ModFix+Caution
SensorBlock - LCD ModFix
ShipConnector Conveyor LCD ModFix
ShipGrinder Conveyor LCD ModFix+Caution
ShipWelder Conveyor LCD ModFix
SmallGatlingGun Conveyor LCD ModFix+Caution
SmallMissileLauncher Conveyor LCD ModFix
SmallMissileLauncherReload Conveyor LCD ModFix
SolarPanel - LCD ModFix
SoundBlock - LCD ModFix
SpaceBall - LCD ModFix
StoreBlock Conveyor LCD
SurvivalKit Conveyor LCD
TargetDummyBlock Conveyor -
TerminalBlock - -
TextPanel - LCD
Thrust Conveyor LCD ModFix+Caution
TimerBlock - LCD ModFix
TurretControlBlock - LCD
UpgradeModule - LCD ModFix
VendingMachine Conveyor LCD
VirtualMass - LCD ModFix
Warhead - -
Wheel - -
WindTurbine - LCD ModFix+Caution
⚠️ **GitHub.com Fallback** ⚠️