Collections

Optional

Games are full of lists: technologies, recipes, cards, levels. A collection keeps one kind of content in one folder, with one JSON file per item. The example is the technology tree of a survival crafting game.

collections/, at the package root

collections/
  technologies/
    fire-making.json
    kiln-building.json
    stone-tools.json
  recipes/
    recipe-charcoal.json
  items/
    charcoal.json

Each folder under collections/ is one collection. The folder name is its id. Each JSON file in the folder is one record, named by its filename. The one exception is the optional schema file _collection.json, shown below. Making the folder is enough: there is no index file to update.

A collection cannot contain subfolders. To group records, use several collections side by side, such as enemies-bosses and enemies-minions.

collections/technologies/kiln-building.json, one record

{
  "title": "Kiln Building",
  "era_order": 2,
  "note": "Fire held in a shape, instead of loose on the ground."
}

The fields are yours. title is what the game shows; kiln-building is the id the package refers to. In prose:

  • collections.technologies means the whole collection;
  • collections.technologies.kiln-building means this record;
  • collections.technologies.kiln-building.era_order means one field of it. The field must be in the record, or in the schema shown below.

If you rename a record, the validator (the checking tool) lists every reference that still uses the old id.

Records may use whatever fields they need. When every record should use the same fields, add the optional schema file to the folder.

collections/technologies/_collection.json

{
  "record": {
    "title": {
      "type": "string",
      "required": true,
      "description": "the name shown in the technology tree"
    },
    "era_order": {
      "type": "integer",
      "required": true,
      "unique": true,
      "description": "the technology's place in the era, counting from 1"
    }
  }
}

Each description says how to use the field. With a schema, the validator reports missing fields, wrong value types, unexpected fields, and repeated values in a field marked unique.

A field can point at another record. The Links chapter shows the link field type and its checks.

Records are Fixed: the builder follows each record exactly. The builder is the person, team or AI that turns your package into a game. There are two exceptions: an open field, which the next section explains, and records that a > DELEGATED: passage leaves to the builder.

In such a passage, cite the collection or the records, and say what the builder may do, such as leave a record out or change it. A record that the passage covers is then a suggestion inside the limits you state. When the change affects how the game plays, also state its target. The Who decides chapter explains Delegated passages and targets.

Numbers you have not decided#

A number that belongs to one technology, such as its research time, is written in that technology's record. When you do not know those numbers yet, mark the field open in the schema. The builder then finds values that work.

collections/technologies/_collection.json, one more field inside record

{
  "research_seconds": {
    "type": "number",
    "open": true,
    "description": "how long researching this technology takes"
  }
}

In an open field, each record says one of three things:

  • a number is your guess: stone-tools.json holds "research_seconds": 40;
  • null means you have no guess yet: kiln-building.json holds "research_seconds": null;
  • a missing field means the field does not apply: fire-making.json has no research_seconds, because the player knows fire from the start.

Only a top-level number or integer field can be open, and an open field cannot also be required. The builder's report of a build records the value used for every open field that applies. An integer field stays a whole number.

Rules across records#

A rule in tuning.json can read a record's top-level number field by its full address. The field must be present in that record. This rule protects a new player:

tuning.json, under rules

{
  "tools-before-first-night": "collections.technologies.stone-tools.research_seconds < day.length_seconds"
}

If stone tools took longer than one day, a new player would meet the first night without tools. day.length_seconds is a shared number in the values part of tuning.json. A rule can also compare two records. As with the open numbers in the Tuning chapter, a guess that breaks a rule gives a warning. A rule that reads a null has no number to use, so the validator skips it while you write. The exception is a number that an equality rule decides, as shown below. Every rule is checked against the build.

In an open field, every record's number is a guess. When playtesting shows the right number for one record, decide it with an equality rule:

tuning.json, under rules

{
  "stone-tools-takes-40": "collections.technologies.stone-tools.research_seconds == 40"
}

This rule decides the number for that one record. The other records stay open. From then on, the validator checks your other rules with 40 as a decided number, so a rule it breaks is an error. An equality rule also decides the number of a record that holds null. Two equality rules for the same record field must give the same number, and an integer field must get a whole number.

To decide a number, the other side of == may hold only plain numbers and numbers you have decided. A number that another equality rule decided does not count, so the order of your rules never matters. If the other side names an open number, or a number that another equality rule decided, the rule decides nothing. The validator checks it like any other rule.

Full rules: OpenGDD specification, §1b.

← Acceptance tests · All chapters · Links →