Biomes

As always, we can copy an existing biome to create our custom biome faster. Biome files are located in stonehearth/data/biome.

Adding the biome to the list

First, let's make an alias for our biome in our manifest:

  "aliases" : {
     "biome:silent_woods" : "file(data/biome/silent_woods.json)"
  }

Then, let's add the mixinto so that our biome appears in the list of available biomes:

  "mixintos" : {
     "stonehearth:biome:index" : "file(data/biome/index.json)"
  }

Our index.json mixinto file would look like this:

  {
     "biomes": {
        "silent_woods": "silent_woods:biome:silent_woods"
     }
  }

The key is a custom identifier for our biome, and the alias points to our biome alias preceded by the namespace of our mod plus a colon.

Biome info

The definition of biomes is divided into two files. The first one contains the basic information, and it's the one that we reference in our alias (in our example, silent_woods.json). The second one contains only the data related to the actual terrain generation (in our example, it would be silent_woods_generation_data.json).

The info files contain these fields:

  • "type": "biome" -- this file represents a biome.
  • "alias" -- the alias of our biome, preceded by the namespace of our mod plus a colon.
  • "display_name" -- the localized name for our biome, this is the title that will appear for our biome in the "Select a Biome" list in the UI.
  • "description" -- the localized description that will appear for our biome in the biomes list.
  • "random_location_names" -- an array containing localized location names for our biome. One will be chosen when the player selects to play in our biome, and will appear in the description of the story in the embarkation screen (where we can click to change the biome).
  • "image" -- the path to the image that will be used in the list of biomes in the UI. Usually it's 400x300 pixels.
  • "story_board_image" -- this is the image that will appear at the right side of the first embarkation screen when the player selects our biome.
  • "game_mode_images" -- a map containing aliases of game modes (usually "stonehearth:game_mode:normal" and "stonehearth:game_mode:hard") pointing to images that will layer on top of the story board image. That allows us to use a semitransparent image to add enemies on top of the image when the player selects a non-peaceful game mode.
  • "generation_file" -- a path pointing to the generation data JSON file for our biome.
  • "mining_loot_table" -- optional field. We can link to a file containing a custom mining loot table for our biome (if we don't add this field, the mining service will load the "stonehearth:mining:base_loot_table" by default). An example can be seen in the desert biome (stonehearth/data/biome/desert.json). It mixins the base loot table, to reuse the default loot for the different terrain types, and then adjusts the weight of clay so that in the desert we get more clay by mining.
  • "applied_manifests" -- optional field. We can use it to apply custom manifests only when the player chooses to play in our biome. An example can be seen in the desert biome too. (Hotloading manifests doesn't work for everything. It'll be discussed in another section of the guide).
  • "default_starting_season" -- this field will point to the key of a season defined below. It's used to automatically select a default season in the UI (in the map screen during embarkation).
  • "seasons" -- here we'll define the seasons and weather for our biome. See this page for how to set them up.

The warning about the biome not being the preferred biome for the chosen kingdom will not appear for custom mods, only for the stonehearth one.

Biome generation data

The terrain generation is managed from stonehearth/services/server/world_generation/world_generation_service.lua, with help of all the other files inside the stonehearth/services/server/world_generation directory, although the actual work is done from the C++ side. The terrain itself is generated from stonehearth/data/service/game_creation_service.lua, once the game knows which biome / seed / etc. it has to use.

The terrain service takes care of things like territory and visibility (fog of war). The hydrology service manages the behavior or the water (when it should create waterfalls, the rate at which it should deplete, etc). There are also some related components.

The data used in the generation data files is extracted from the "generation_file" linked in the main biome file. Within it we can determine things like height of the terrain, depth of water, which plants and trees will appear in our custom biome, etc. Some mods override the Lua files that generate the terrain, to allow for a different terrain generation (by default it's based on a noise function). For example, the Archipelago mod, Sky Lands mod, and Rivers mod (which adds rivers to the terrain generation).

The temperate_generation_data.json file has more comments than the corresponding files of the desert and arctic biomes, so it's a good start. Many comments have "Def N", which is the default value for that parameter, if we made some mistake and need to revert back to a reasonable value, or min/max values, so that we don't try to add invalid values. If we add a mixinto to stonehearth/data/terrain/terrain_blocks.json adding more terrain types, we'll be able to use them in our biome as well.

Ideally we'll start defining the shape of the terrain, then adding the trees/plants that we want, and finally double checking any static scenario that we want / don't want our biome to have (such as bunny statues, critter nests, landmarks, etc).

If we used a wrong value in the generation files, an error will pop up in game when generating the map. Sometimes it won't fail there, but only with certain seeds so we can try rerolling the map a few times and see if anything's wrong. Some values might also be affected by other ones, so while they aren't wrong, they might cause issues depending on the other values.

In short, it is recommended to change the values one by one and test a lot, so that we know exactly which value is causing the problem, and how much effect did our last change have in the terrain generation.

Let's explain a bit the different fields that we can find in the generation file:

  • "type": "biome_generation_data" -- this is a biome generation data file.

  • "ring_tesselation" : {} -- these parameters are used for generating the rings we can see on the borders of the grass, which have different colors. The keys inside this field are keys from the block_types section inside terrain_blocks.json. We can only include keys of terrain types that are 1 block thick (like grass and dirt). We can change the width and terrain type of the ring, add more rings, or remove them if we don't want them. The terrain type that they link to ("name") is any key from the block_types section inside terrain_blocks.json.

  • "season" -- this points to the key of a season we defined in the main biome file. It will be used to choose the default palette to generate our biome (so that the UI can load starting colors in the map screen of embarkation, before the player chooses any season).

  • "palettes" -- the keys inside correspond to the seasons we defined for our biome. Each key will then map the terrain types from terrain_blocks.json to colors in hexadecimal format (starting by # and in uppercase by convention). This way the terrain can look different depending on the season. We can also define seasonal variants for plants (see this page).

    • The colors of the terrain in the embarkation map will be picked up from the palette corresponding to the default season, as explained above. But if we want to change them, for example, to edit the color of water or trees, which are not taken from the palette, we can do so in a "minimap" section inside the "palettes" section, before defining any of the palettes. For instance:

      "minimap":{
         "water":"#FF0000",
         "trees":"#AA3800",
         "plains_2":"#00BB00",
         "mountains_1":"#334433"
      }
      

      This will change the color of water, trees, and two of the terrain layers in the embarkation map. We can edit any of the colors that are displayed there by using the terrain type (plains, foothills or mountains) plus an underscore and a number representing the step/layer.

  • "terrain": {} -- the parameters in this section control the shape of the terrain.

    • "minimap" : {} -- optional field. We can map terrain types (water, plains/foothills/mountains plus underscore and step number) to custom names. These names appear on the right side of the map screen when we hover over the terrain. For instance, when hovering over water we can make it say "Ocean" or "Swamp", when hovering over plains we can make it say "Beach", etc.

      "minimap":{
         "water" : "i18n(my_mod:ui.shell.select_settlement.terrain_codes.water)",
         "plains_2" : "i18n(my_mod:ui.shell.select_settlement.terrain_codes.plains_2)"
      }
      
    • "noise_map_settings" : {} -- the terrain is generated using a simplex noise generator. These are the parameters for that generator. Check the comments in the temperate_generation_data.json for an idea on what they can do.

    • "plains_percentage" -- the percentage of plains. Depending on the other values below, it might not feel correct (for example, if we add too many steps for the foothills, they will cover the plains).

    • "height_base": 36 -- this is an offset in regards to where the layers line up. Def 36, must be 1 (for the bedrock, which is the bottom layer that we can't mine) plus a multiple of five (which is related to mining: 1 block for the floor so that hearthlings can stand on it, and 4 blocks so that they have enough head space).

    • "plains": {}, "foothills" : {} and "mountains" : {} -- the parameters to define the quantity of layers of terrain and their height in blocks/world units. Normally we don't edit the values for plains, they can easily cause errors.

  • "terrain_detailer": {} -- these are parameters to define the outcroppings (the bits of terrain that stick off the side of the terrain layers/steps).

    • "mountains" : {}, "foothills" : {} and "plains" : {} -- again, take a look at the comments in temperate_generation_data.json file to know what each parameter does. For example, if we set "layer_count" to 0, the sides of those terrain steps will be completely flat, so the terrain will look like giant rectangular shapes.
  • "landscape": {} -- these are parameters for defining the rest of the landscape:

    • "placement_table": {} -- here we define all of the entities that we want to spawn in our terrain (trees, plants, bushes and boulders). The keys are the URIs of our entities (we can reuse trees etc. from the stonehearth mod, or use our custom entities, or both). For each key we define these properties:

      • "placement_type" -- it can be either "single", "dense" or "pattern".

      • "parameters" : {} -- depending on the placement_type, we'll have different parameters. They refer to "tiles" which are 16x16x16 chunks of terrain (you can visualize them by pressing F1 with debug keys/debugtools enabled). With these fields we can specify how many entities can spawn in a tile, how much space we should leave around the entities so that nothing else spawn there (large trees need a big space for themselves), a radius to prevent trees from spawning semi-floating on cliffs, and values for grid-like patterns based on random chances, so that we can spawn groups of entities.

        Take into account that these values can affect the placement of the entities, as in if they're too clustered it's possible that none of the entities will spawn, or entities will spawn on top of each other (due to their collision boxes). Always make sure to leave some space, otherwise plants won't find a place to spawn in the tiles, since trees occupy quite a big space.

    • "trees": {} -- parameters for tree placement:

      • "sizes" : {} -- here we define the size of trees if we set up their aliases with the size as a suffix (check this other page). If our trees don't have different sizes, we can (and should, since the "trees" section is required) define them with just their alias, and a couple fake sizes like "small": 0, "large": 10, for example. If we used the suffixes method for the aliases (recommended if your trees have different sizes), the ancient percentage will be used over large trees (so a percentage of the large trees will spawn as ancient instead).
      • "weights" : {} -- weights of each tree per terrain elevation. Here the number of entries may vary depending on how many terrain steps we defined before.
      • "noise_map_parameters": {} -- these are parameters for the noise function that generates the forests.
    • "berries" : {} -- parameters for placing bushes. This field is required (otherwise we'll get errors), but doesn't actually have an impact in the game, so we can use any of the trees/plants we defined for our biome in it.

    • "scattered": {} -- parameters for the rest of entities (like plants. If you don't see any trees being placed regardless of the parameters above, try adding them in this section too, like the desert biome does).

      • "plants": {} -- the weights of the plants for the different terrain elevations, and their noise map parameters, so that they can spawn more scattered or more clustered in the different terrain elevations (mind tree placement for this, since if there are too many trees there might not be enough space for plants to appear).
      • "boulders": {} -- parameters for boulder placement. This field is also required. It's based on the same setup than trees, with the suffixes, so for the "namespace" field we should specify the alias without the suffix. Unfortunately for boulders we do have to make them with this suffixes for sizes otherwise they won't appear. We can, however, specify the full alias of any entity there and just add our boulders to the "scattered" weight set of plants.
    • "water" : {} -- parameters for the water. Wrong values will throw an error in the loading screen when the terrain is being generated, not necessarily at the choose map screen. The "deep" value is the number of blocks from the surface to the deep sections of the water (you can see them in big lakes). The "shallow" value is the number of blocks from the surface to the shallow part of the lakes.

      At world generation, water will always be 1.5 blocks lower than the level of the surface where it spawns, unless we specify the "water_height_delta" property inside the "water" section. It can't be negative, and must always be lower than the level of the "shallow" value.

Here's the old Desktop Tuesday about biomes, and this is the one about the Northern Alliance and the arctic biome (after seasons and weather were introduced).

You can find more biome mods at the Steam Workshop here.