Skip to content

Inheritance

Roots uses a layered genome and a diploid inheritance model. The important distinction is that a root can say what a founder should be seeded with, while an individual stores what they actually carry.

The short version:

  1. A root builds a starting gene list from species, ancestry, and lineage data.
  2. Founders roll carried gene copies from that list.
  3. Children inherit one copy from each parent.
  4. Heritage fractions are averaged from the parents.
  5. The game reads the expressed genes after dominance and tie-breaking.

You can understand most pack behaviour from those five steps. The rest of this page explains the edge cases.

When Townstead resolves a root, it reads the supporting species, ancestry, and lineage data referenced by that root. Species, ancestry, and lineage provide the inherited gene layers.

Townstead reads the root’s ancestry, the root’s lineage, and the lineage’s ancestry before composing the effective genome.

When the same gene id appears more than once, the more identity-specific entry replaces the shared entry.

After the layered genome is built, Townstead also collapses genes by locus for founder seeding and display. If two genes share a locus, the later and more specific gene wins. Genes without a locus are kept independently.

A locus is the inheritance slot for related genes. Some gene types provide a default locus. A gene file can also set its own locus.

{
"schema": "townstead:gene/v2",
"type": "pheno:attribute",
"locus": "my_pack:strength",
"attribute": "minecraft:generic.attack_damage",
"amount": 2.0
}

Genes at the same locus compete like variants of the same inherited trait. Genes at different loci can both be expressed.

Most genes are diploid. A diploid locus stores two alleles, one from each parent.

An allele can be:

FormMeaning
gene_idA single-variant gene allele.
gene_id#variantA named variant of a gene.
~Empty/wild allele.

Founders receive two independently rolled alleles at each diploid locus. Children draw one allele from each parent at each locus.

The following gene types are not stored as normal diploid allele pairs:

Gene TypeReason
pheno:body_metricApplied as founder body ranges and inherited through MCA body data.
pheno:proportionsApplied as founder body/render ranges and free-form proportions.
townstead_roots:trait_occurrenceRolled at spawn to grant traits.
townstead_roots:life_cycleResolved as a cycle definition rather than a pair of alleles.

When a gene has a variants object, each variant can have its own config and weight.

{
"schema": "townstead:gene/v2",
"type": "townstead_roots:eye_color",
"display_name": "Eye Colour",
"variants": {
"blue": {
"display_name": "Blue",
"weight": 3,
"tint": "#66aaff"
},
"gold": {
"display_name": "Gold",
"weight": 1,
"tint": "#f6c04a"
}
}
}

Founder rolling chooses among variants by weight. If the gene has no variants object, the whole file behaves as one implicit variant.

At each diploid locus, Townstead chooses one expressed allele from the two carried alleles.

SituationResult
One allele is emptyTownstead expresses the real allele if it passes its penetrance roll. Otherwise nothing is expressed.
Both alleles are emptyNothing is expressed at that locus.
Different genes, and only one passes its penetrance rollTownstead expresses the gene that passes.
One allele’s gene is dominant and the other is recessiveTownstead expresses the dominant allele.
Same gene, different variantsTownstead expresses the variant with the higher weight.
Different genes at same locus with equal dominanceTownstead expresses the gene with the higher weight.

dominance defaults to dominant. Set "dominance": "recessive" when a gene should only win against empty or weaker recessive competition.

weight defaults to 1. Use it when one gene or variant should be expressed more often than another. If everything is still tied, the individual’s expression seed settles it. The result is stable for that villager but can differ between siblings, so two Fire x Ice children needn’t share every trait.

penetrance (0 to 1) is the chance that a gene shows when its bearer carries a single copy. Two copies of the same gene always show. A gene without the field always shows from one copy.

{
"schema": "townstead:gene/v2",
"type": "townstead_roots:attachment",
"locus": "townstead_roots:snout",
"penetrance": 0.3,
"attachment": "my_pack:cat_muzzle"
}

The roll is settled once per individual by its expression seed, so a villager never flickers between looks. Siblings can differ.

Penetrance also decides a contest between two different genes at the same locus. If one copy fails its roll and the other passes, Townstead expresses the copy that passes. For example, if the cat coat fails its roll, the child shows the human parent’s skin. If both fail, ordinary dominance and weight decide, so a cat x dog child still grows one of its parents’ muzzles.

A copy that doesn’t show is still carried and can pass to children. The heritage screen lists it greyed out.

Villagers from saves made before expression seeds existed show every copy, so they keep their current look.

A child of two different peoples carries one copy of each parent’s traits. Penetrance, pheno:gene, and pheno:heritage decide which of those copies show:

GoalTool
Signature parts (ears, tail, horns) always come throughNo penetrance.
Eyes, wings, markings, and colouring come through sometimespenetrance between 0.3 and 0.9.
A trait only appears on top of another, such as a muzzle over a coatA gene condition of type pheno:gene naming the coat genes or their locus.
A trait needs both copies, such as a clutch of two eggs"penetrance": 0.
A trait needs a share of the bloodlineA gene condition of type pheno:heritage, on gene types that honour condition.

It helps to order the traits: ears and tail first, then eyes and hand fur, then coat and nose, and the muzzle last. Give earlier traits higher penetrance, and make later traits require the one before. A half-breed then reads as a believable step between its parents, never a muzzle on bare human skin.

Give species colouring a weight above the human default skin. Then Townstead expresses the colouring each time it passes its roll.

A founder is a naturally spawned villager that is not already the result of a parented birth.

For founders, Townstead:

  1. Selects a root from spawn bias, village restrictions, and fallback rules.
  2. Optionally creates a same-species mixed founder if the species allows admixture.
  3. Stores root and heritage.
  4. Applies personality policy.
  5. Applies body metrics and proportions.
  6. Rolls trait occurrence genes.
  7. Seeds genotype from the effective inherited genes.
  8. Rolls life-stage timing.

If no weighted root can be selected, Townstead falls back to the default root.

Species can set admixture_chance. When it succeeds, Townstead builds a same-species mix from compatible roots.

Mixed founders receive:

SystemMixed Behaviour
Root idThe dominant fraction’s root.
HeritageWeighted combination of each selected root’s seed heritage.
GenotypeRolled from selected roots according to their mix fractions.
Body metricsFraction-weighted blended ranges.
Trait occurrenceFraction-weighted chance.
Life cycleBlended only when the candidate cycles have the same stage shape; otherwise the dominant root’s cycle wins.
PersonalityAssigned from the dominant root.

Children inherit from parents.

Parents need to share a species to produce children through the Roots breeding model. Ancestries and lineages can mix inside that shared species, but species do not interbreed. This is both a biological boundary and an asset boundary: species own the rig/body base, textures, face layout, overlays, attachments, equipment anchors, and other authored presentation data. Townstead does not generate hybrid rigs or hybrid asset sets from incompatible parents.

At each diploid locus:

  1. Townstead gathers every locus carried by either parent.
  2. The child draws one allele from the first parent and one allele from the second parent.
  3. Missing loci contribute an empty allele.
  4. Expressed genes are recomputed from the child’s genotype.

Child heritage is the average of the parents’ heritage maps. The child’s displayed root follows the dominant ancestry in the child heritage when a parent root matches that dominant ancestry; otherwise the mother’s root is preferred as the tie/default.

Players also carry a persisted root id and genotype, so player genetics can participate in inheritance.

Seed heritage is derived from the root:

Root ShapeSeed Heritage
Root with ancestry and lineage100% that ancestry.
Legacy or malformed root without ancestry/lineageEmpty heritage map.

Heritage profiles from data/<namespace>/heritage/ can name fraction ranges. If no profile matches, near-pure heritage keeps the dominant root or ancestry name. Mixed heritage gets a generated label from its two largest ancestry fractions. Each ancestry is named by its main lineage when one carries most of it. See Heritage.

Townstead expresses the genes that a heritage profile grants in addition to the inherited genes. A granted gene replaces the inherited gene at its locus. Townstead does not add it next to the inherited gene.

Body metrics and proportions are applied when a founder is created. For children, MCA’s body inheritance and blend data carries the family resemblance forward.

Life cycles are not inherited as allele pairs. Townstead resolves the effective cycle gene from the root’s layered genome, using the most specific and strongest applicable life-cycle gene.