Layers

Generate Minecraft worlds by map images!

  1. Height Layer
  2. Weight Layer
  3. Sea Layer
  4. Surface Layer
  5. Registering Block Layer Types

Block Layers define what Block Placer should be used for which block position (e.g. -100, 0, 234). It’s recommended to have layers with the following names: deepslate, stone, dirt, surface, sea.. Those layer names are also used in the implemented Zones. By default, CTGen ships for different layer types, explained below.

Height Layer

This layer type depends on the absolute y value of the block, e.g. -64 to 256.

{
  "type": "ctgen:height_layer",
  "min": -65,
  "max": 0,
  "limit_to_surface": true,
  "shift": true,
  "name": "deepslate",
  "has_caves": true,
  "fallback": "minecraft:deepslate"
}
  • min - min y where the layer is applied
  • max - max y where the layer is applied
  • limit_to_surface - whether the layer should stop on the surface level, if the surface is below “max”
  • shift - whether the layer transition should be “shifted” for each block. adds a bit more beautiful noise to it
  • name - the name of the layer so Zones can modify it
  • has_caves - whether carvers should be applied within this layer
  • fallback - a fallback block player in case the current zone doesn’t have a custom one

The exact same layer will look like this if coded in Java:

BlockLayer layer = new HeightLayer(-65, 0, true, true, "deepslate", true, new BasicPlacer(Blocks.DEEPSLATE));

Weight Layer

This layer type depends on the relative y - y in percent between minY and surface height. If the minimum y is -64 and the surface is at 70, then y = -64 will be 0, y = 70 will be 1.

{
  "type": "ctgen:weight_layer",
  "min_percentage": 0,
  "max_percentage": 0.96,
  "shift": true,
  "name": "stone",
  "has_caves": true,
  "fallback": "minecraft:stone"
}
  • min_percentage - min y in percent between the minimum y and the surface
  • max_percentage - max y in percent between the minimum y and the surface
  • shift - whether the layer transition should be “shifted” for each block. adds a bit more beautiful noise to it
  • name - the name of the layer so Zones can modify it
  • has_caves - whether carvers should be applied within this layer
  • fallback - a fallback block player in case the current zone doesn’t have a custom one

The exact same layer will look like this if coded in Java:

BlockLayer layer = new WeightLayer(0D, 0.96D, true, "stone", true, new BasicPlacer(Blocks.STONE));

Sea Layer

This layer type fills every block between the surface and the sea level, if the surface is below the sea level.

{
  "type": "ctgen:sea_layer",
  "name": "sea",
  "fallback": "minecraft:water"
}
  • name - the name of the layer so Zones can modify it
  • fallback - a fallback block player in case the current zone doesn’t have a custom one

The exact same layer will look like this if coded in Java:

BlockLayer layer = new SeaLayer("sea", new BasicPlacer(Blocks.WATER));

Surface Layer

This layer type only applies on the surface block.

{
  "type": "ctgen:surface_layer",
  "name": "surface",
  "fallback": "minecraft:grass_block"
}
  • name - the name of the layer so Zones can modify it
  • fallback - a fallback block player in case the current zone doesn’t have a custom one

The exact same layer will look like this if coded in Java:

BlockLayer layer = new SurfaceLayer("surface", new BasicPlacer(Blocks.GRASS_BLOCK));

Registering Block Layer Types

You can also register a custom block layer using code similar to this one:

public class HeightLayer extends BlockLayer {
    private final int min;
    private final int max;
    private final boolean limitedToSurface;
    private final boolean hasShift;

    public HeightLayer(int minY, int maxY, boolean limitedToSurface, boolean hasShift, String name, boolean hasCaves, BlockPlacer fallback) {
        super(name, hasCaves, fallback);
        this.min = minY;
        this.max = maxY;
        this.hasShift = hasShift;
        this.limitedToSurface = limitedToSurface;
    }

    @Override
    public boolean is(SimplexNoise noise, int x, int y, int z, Zone zone, int minY, int seaLevel, double surfaceHeight, int genHeight, int shift) {
        int y2 = hasShift ? y + shift : y;
        return y2 >= min - 1 && y2 <= max && (!limitedToSurface || y < surfaceHeight);
    }

    public static final Codec<HeightLayer> CODEC = RecordCodecBuilder.create(instance -> instance.group(
            Codec.INT.fieldOf("min").forGetter(o -> o.min),
            Codec.INT.fieldOf("max").forGetter(o -> o.max),
            Codec.BOOL.optionalFieldOf("limit_to_surface", true).forGetter(o -> o.limitedToSurface),
            Codec.BOOL.optionalFieldOf("shift", true).forGetter(o -> o.hasShift),
            Codec.STRING.fieldOf("name").forGetter(BlockLayer::getName),
            Codec.BOOL.optionalFieldOf("has_caves", true).forGetter(BlockLayer::hasCaves),
            BlockPlacer.CODEC.fieldOf("fallback").forGetter(BlockLayer::getFallback)
    ).apply(instance, instance.stable(HeightLayer::new)));

    public static final ResourceLocation ID = new ResourceLocation(YourMod.MODID, "height_layer");

    @Override
    protected Codec<HeightLayer> codec() {
        return CODEC;
    }
}

To register the layer, just use this snippet:

CTRegistries.BLOCK_LAYER.register(HeightLayer.ID, HeightLayer.CODEC);

Continue with Placer