Skip to content

How to customize the look of modded buildings

Joonho Hwang edited this page Feb 25, 2025 · 6 revisions

1. Somewhere in your mod, create a blueprint inheriting Object and name it CartographOverrideData.

image

image

2. Now you need to choose the variables you want to override.

As of 0.9.0, overrideable variables are:

Variable List

// Custom texture override for each Buildable.
TMap<TSoftClassPtr<AFGBuildable>, TSoftObjectPtr<UTexture2D>> BuildableIconOverrideMap;


// Rectangle color data override for each BuildCategory.
TMap<TSoftClassPtr<UFGBuildCategory>, FCategoryData> BuildCategoryDataMap;
// Rectangle color data override for each Buildable.
TMap<TSoftClassPtr<AFGBuildable>, FCategoryData> BuildableBuildCategoryDataOverrideMap;
// Rectangle color data override for each Material.
TMap<TSubclassOf<UFGFactoryCustomizationDescriptor_Material>, FCategoryData> MaterialBuildCategoryDataOverrideMap;


// Size override for each Buildable.
TMap<TSoftClassPtr<AFGBuildable>, FVector2D> BuildableSizeOverrideMap;
// Extra rotation for each Buildable.
TMap<TSoftClassPtr<AFGBuildable>, FRotator> BuildableExtraRotationMap;


// Spline data for each Buildable that implements SplineBuildableInterface.
TMap<TSoftClassPtr<AFGBuildable>, FSplineData> BuildableSplineDataMap;
// Wire/Beam data for each Buildable that inherits BuildableWire/BuildableBeam.
TMap<TSoftClassPtr<AFGBuildable>, FWireData> BuildableWireDataMap;


// Buildable class redirects. If A->B, A buildings will be treated as if it were B in Cartograph.
TMap<TSoftClassPtr<AFGBuildable>, TSoftClassPtr<AFGBuildable>> BuildableClassRedirectMap;
// Buildables to not draw on the map.
TSet<TSoftClassPtr<AFGBuildable>> BuildableToIgnore;


// Layer categories to add for the map menu.
TArray<FLayerCategoryData> LayerCategories;
// Layer category override for each BuildCategory.
TMap<TSoftClassPtr<UFGBuildCategory>, FBuildLayerData> BuildLayerDataMap;
// Layer category override for each Buildable.
TMap<TSoftClassPtr<AFGBuildable>, FBuildLayerData> BuildableBuildLayerDataOverrideMap;
// Layer category override for each Material.
TMap<TSubclassOf<UFGFactoryCustomizationDescriptor_Material>, FBuildLayerData> MaterialBuildLayerDataOverrideMap;

The custom data structures used above are defines as following:

Custom Type Definition

struct FCategoryData
{
    FLinearColor MainColor;
    FLinearColor OutlineColor;
    float OutlineThickness;
};


struct FSplineData
{
    FLinearColor Color;
    float Thickness;
    /** As you can't add new configs, it should be one of ConveyorBeltSplineSegments, PipelineSplineSegments, RailwaySegments, or HypertubeSegments **/
    FName SegmentsConfigName;
    bool UseTangents;
};


struct FWireData
{
    FLinearColor Color;
    float Thickness;
};


struct FLayerSubCategoryData
{
    FName Name;
    FText DisplayName;
    /** Lower = Earlier in the list **/
    int Priority;
};


struct FLayerCategoryData
{
    FName Name;
    FText DisplayName;
    /** Lower = Earlier in the list **/
    int Priority;
    TArray<FLayerSubCategoryData> SubCategories;
};


struct FBuildLayerData
{
    /** <MainCategoryDataName>/[SubCategoryDataName] **/
    // Main and Sub category name joined with a slash. Subcategory is optional, so you can write the main category name only.
    FString Category;
};

3. Now, open the blueprint you created. For each variable you want to override, create a variable with the same name and type.

image

  • Note: You need to strictly follow the soft-ness of the pointers. For TSoftClassPtr, use Soft Class Reference. For TSubclassOf, use Class Reference. You can switch between them at the bottom of the variable type selection window:

image

But how about the custom structures?

Create a Blueprint Structure and add fields accordingly. The name of the structure doesn't matter, as long as the name and type of the fields are matching.

image

image

4. Compile the blueprint, and edit the default value of the variables.

image

Debugging

It leaves logs while discovering the override data.

If the name and type of the property are matching, it'll say Found (struct) property: [property name].

If the name is matching but the type is mismatching, it'll say so.

It'll print every detail of the layer category data you've added.

[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1793)Override Data Found: /ExampleMod/CartographTest/CartographOverrideData
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1539)Found Property: BuildCategoryDataMap
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1510)Found struct property: MainColor
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1600)Added 1 elements
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1539)Found Property: BuildableIconOverrideMap
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1600)Added 3 elements
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1539)Found Property: BuildableSizeOverrideMap
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1600)Added 2 elements
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1624)Found Property: BuildableToIgnore
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1650)Added 2 elements
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1539)Found Property: BuildableBuildLayerDataOverrideMap
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1510)Found struct property: Category
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1600)Added 3 elements
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1669)Found Property: LayerCategories
[2025.02.23-19.44.54:698][  0]LogCartograph: Display: (1510)Found struct property: Name
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: (1510)Found struct property: DisplayName
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: (1510)Found struct property: Priority
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: (1696)Found struct property: SubCategories
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: (1510)Found struct property: Name
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: (1510)Found struct property: DisplayName
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: (1510)Found struct property: Priority
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Categories: 2
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: MainCategory #0
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Name: One
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: DisplayName: First
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Priority: -100
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: SubCategories: 2
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: SubCategory #0
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Name: OneOne
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: DisplayName: First First
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Priority: 0
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: SubCategory #1
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Name: OneTwo
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: DisplayName: First Second
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Priority: 10
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: MainCategory #1
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Name: Two
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: DisplayName: Second
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Priority: -50
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: SubCategories: 1
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: SubCategory #0
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Name: TwoOne
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: DisplayName: Second First
[2025.02.23-19.44.54:699][  0]LogCartograph: Display: Priority: 0

Notes

  • You can also override the vanilla buildings data and Cartograph's default data, but be mindful that if 2 or more mods try to override the same key, the order is not guaranteed.

  • The logic to decide how to draw a building is:

  1. If it has its own texture (BuildableIconOverrideMap), use that texture.
  2. If it has its own rectangle data (BuildableBuildCategoryDataOverrideMap), use that data.
  3. If its subcategory is found in BuildCategoryDataMap, use that data.
  4. If its main category is found in BuildCategoryDataMap, use that data.

MaterialBuildCategoryDataOverrideMap works as if all the buildings of the Material are registered in BuildableBuildCategoryDataOverrideMap. Other Material***Overrides work like this as well.

Splines, wires, and beams are treated separately.

  • For the rectangle size of a building, it first checks if it has a clearance box (M Clearance Data) and uses that if found. If not, it'll try to calculate from the bounds of the components. If you don't like the calculated size, use the size override.