Tech Tree and upgrades.yaml - DomeKeeperMods/Docs GitHub Wiki

Table of Contents


Tech Tree Generation

Tech trees in Dome Keeper are generated using information provided in a YAML file.

The vanilla "gadgets", which include Domes, Keepers, Game Modes, and Gadgets from a code perspective, are defined in the upgrades.yaml file, located in the project's top-level folder. In this context, when we mention gadgets, we refer to "top-level" elements in the upgrades.yaml that are then saved in Data.gadgets.

This file essentially contains a collection of tree-like data structures. When an upgrades.yaml file (either the vanilla version or a new one) is parsed by Data.parseUpgradesYaml(path:String), the parser seeks to identify the "roots" of these trees.

Let's consider the following example to understand how these top-level elements are identified:

dome1:
  type: dome
  slots: [primary, cellar, cellar, shaft, domesurface, backside]
  propertyChanges: 
    - dome.maxhealth = 800
    - dome.meleedamageReduction = 0.0/h
    - dome.healthbar = 0/h
  primaryWeapon: laser

Here, all properties of dome1 (Laser Dome) are defined, including slots for gadgets, stats like default max health, and the primary weapon.

Now, let's examine the data for the Upgrade "Dome Health Meter":

domeHealthMeter:
  limit: [hostile]
  cost: iron=1
  predecessors: [dome1,dome2,dome3]
  predecessorsAny: true
  hud: content/hud/healthmeter/HealthMeter.tscn

There are a few key differences here. Most importantly, this object has a predecessors property and no type property. This informs Dome Keeper that this is not a top-level (or root) element, and also defines where it will be located in the tech tree. In this case, the predecessors are all of the game's domes.

To qualify as a top-level element, an element has to have a valid type property!

Once the game has finished parsing a YAML file, it will have identified all top-level elements, their descendants, and their relationships. This results in a structure that can be visualized as follows:

dome1 (Top Level / Root = "gadget")
├─── domewavemeter
|    ├─── domewavewarner
|    |    └─── domewavepathtodome
├─── domeHealthMeter
|    └─── domesandrepair
|         ├─── dome1health1
|         |    └─── domeautorepair
|         |    └─── dome1health2
|         └─── dome1damageReduction1
|              └─── dome1damageReduction2
└─── domeResourceCounters
     └─── domewavecount

This results in the following tech tree:

wiki_techtree_2

Icons and Texts

While observing the tech tree in-game, you'll notice several texts and icons. Upon inspecting upgrades.yaml, you may notice that no icons are defined and no descriptive texts are included.

Icons

The game identifies icons using a specific file path pattern:

res://content/icons/id.png

Here, id is the key of the element in the upgrades.yaml and is converted to lowercase for the filename. If an element has the shadowing property, the shadowed id is used for the icon path.

If an icon is not found, an error message will be generated, detailing the path where Dome Keeper is looking for the icon.

Examples:

id file name
dome1 res://content/icons/dome1.png
dome2Health2 res://content/icons/dome2health2.png

💡 Custom Mod Icons: To use custom icons, the file names need to match this pattern! The easiest way to place your new icons in this path is to use overwrites. An example for an icon is on the overwrites wiki page.

Properties and Translations

The texts in a tech tree are sourced from a translations file. If your mod introduces new elements to the tech tree, Dome Keeper will attempt to locate those new texts using a specific pattern.

wiki_techtree_3

type resulting key name example example value
upgrade title upgrades.id.title upgrades.jetpackspeed1.title Speed 1
upgrade description upgrades.id.desc upgrades.jetpackspeed1.desc Improve the jetpack to move faster.
properties properties.top_level_id.property_name properties.keeper1.maxspeed Top Speed {}

As you can see, properties have a unique case with their values/translations. Curly braces {} indicate the position of the value in the translation, and you have several options for additional styling. Here are some examples:

placeholder example value result
Top Speed {} 10 Top Speed 10
Effect {.percent} 0.35 Effect 35%
Length {}s 12.5 Effect 12.5s
Total Life {}hp 800 Total Life 800hp

Properties

Entries in the upgrades.yaml file can have different properties. These are parsed and transformed to provide the game with additional information. This includes details on what to display, the placement of the element in the tech tree, and any new HUD elements to be added, among other things.

Let's examine the key properties that influence the behavior of a tech tree or how an element will be affected.

type (String)

This property describes the type of the upgrade/item.

📛 This property is required for an item to work as a predecessor. 📛

The available types that are properly parsed by the game are:

  • gamemode
  • dome
  • keeper
  • supplement
  • weapon
  • primarygadget
  • gadget
  • worldmodifier

Examples:

dome1:
  type: dome
  ...

keeper1:
  type: keeper
  ...

predecessors (Array)

This is an array of IDs from which the current element descends.

Example: domesandrepair is a descendant of domeHealthMeter.

wiki_techtree_1

If there are multiple elements in predecessors, all predecessors must be fulfilled for an upgrade to become available. If the element should become available after fulfilling any one of the predecessors, the predecessorsAny property must be set to true.

Example: domeHealthMeter will be available for all domes.

domeHealthMeter:
  limit: [hostile]
  cost: iron=1
  predecessors: [dome1,dome2,dome3]
  predecessorsAny: true

propertyChange (Dictionary)

This dictionary defines the properties that will change in Data when this element is acquired.

These properties can be either pre-existing or new, and their values can be set or have an operation performed on them (+=, *=, -=).

Hiding Properties in the Tech Tree

Properties in this list can be suffixed with /h to hide them in the tech tree's visual representation. This is useful when you want a specific property to be initialized with a default value at the top-level element, but only show it when an upgrade changes this property.

Examples:

keeper1:
  propertyChanges:
    - keeper1.drillStrength = 2
    - keeper1.maxSpeed = 56
    ...

drill1:
  propertyChanges: 
    - keeper1.drillStrength += 4
  predecessors: [keeper1]

cost (String)

This property defines the resource cost of an upgrade. The available resources are iron (Iron), water (Water), and sand (❗ Cobalt).

For an element that costs more than one type of resource, separate the different resources with a comma: iron=1, water=2

Examples:

domewavewarner:
  cost: iron=2

shieldstrength1:
  cost: iron=4, water=2

limit (Array)

This limits the appearance of an element to specific environments. These are defined by an array of strings and are combined using the AND operator.

When a game mode is initiated, GameWorld.gd calls the function setUpgradeLimitAvailable(id:String) to establish different limits for different game modes.

Here are some examples:

  • hostile - Game mode has enemies
  • nocobalt - Map has no cobalt
  • mining - Game mode has mining

You can also define your own limit id, e.g., for your own game mode. A good place to do this would be at prepareGameMode().

shadowing (String)

The shadowing property allows an element to adopt the visual appearance of another element. This can be useful, for example, when an upgrade is available with another limit but behaves similarly to another one and therefore can have the same appearance.

A good example is domesandrepairstrong which is only available in nocobalt prestige and behaves differently than the standard repair.

domesandrepairstrong:
  limit: [nocobalt, hostile]
  shadowing: domesandrepair

repeatable (bool)

If an element has the repeatable property and it's set to true, the upgrade can be purchased multiple times.

Example:

domesandrepair:
  ...
  repeatable: true

repeatable (Dictionary)

The repeatable property can also be used as a dictionary. In this case, the details of the upgrades can be fine-tuned per upgrade level of the element.

The dictionary contains several commonly used properties that the game can parse:

  • times - The number of times the upgrade can be purchased (e.g., times = 3)
  • cost.x - The cost change for each upgrade level (e.g., cost.iron += 3)
  • property.x - The change in other properties for each upgrade level (e.g., property.keeper1.drillStrength *= 2.2)

Example:

drill4:
  cost: iron=20
  repeatable:
    - times = 3
    - cost.iron += 10
    - property.keeper1.drillStrength *= 2.2
  propertyChanges: 
    - keeper1.drillStrength += 40
  predecessors: [drill3]