Skip to content

Life Cycles

A life-cycle gene defines how a villager or player moves through stages.

Gene type: townstead_roots:life_cycle

For a first custom life cycle, define:

  1. A townstead_roots:life_cycle gene.
  2. A short stages array.
  3. Each stage’s id, label, presents_as, and days.

Add rig, mobile, needs, talkable, death_loot, and custom narrative_age only when basic stage progression works.

FieldTypeRequiredNotes
typestringyestownstead_roots:life_cycle.
stagesarrayyesOrdered life stages.
variancenumbernoPer-stage random age spread at spawn. 0.1 means each stage can roll about 10% shorter or longer. Defaults to 0.
agelessbooleannoIf true, the cycle names stages but calendar progression is frozen at the resolved stage.

Life-cycle genes use replace semantics. A later life-cycle gene with stages replaces the earlier cycle rather than merging stage-by-stage.

{
"schema": "townstead:gene/v2",
"type": "townstead_roots:life_cycle",
"display_name": "Human-Like Life",
"variance": 0.1,
"stages": [
{
"id": "baby",
"label": "Baby",
"presents_as": "baby",
"days": 2
},
{
"id": "adult",
"label": "Adult",
"presents_as": "adult",
"days": 47,
"on_end": "stay"
}
]
}
FieldTypeRequiredNotes
idstringyesStable stage id used for saved data and runtime lookups.
labelcomponentnoDisplay label. Defaults to the stage id.
presents_asstringyesCanonical stage axis.
daysintegernoBase duration of the stage in Townstead life days. Minimum 1, default 1.
on_endstringnonext, stay, die, or loop.
scalenumbernoRender scale for this stage. Defaults from presents_as.
narrative_agenumber arraynoApparent age range, such as [18, 64].
rigidnoStage-specific rig override.
mobilebooleannoWhether the stage can move. Defaults to true.
needsbooleannoWhether hunger/thirst/needs apply. Defaults to true.
talkablebooleannoWhether normal interaction is allowed. Defaults to true.
death_lootarraynoExtra drops added on death for this stage.
ValueMeaning
babyBaby-like behaviour and scale defaults.
toddlerToddler-like behaviour and scale defaults.
childChild-like behaviour and scale defaults.
teenTeen-like behaviour and scale defaults.
adultAdult behaviour. MCA adult gates treat this as adult.
seniorSenior-like behaviour and scale defaults.

days controls biological pacing. It is the base duration of the stage in Townstead life days before variance and aging-scale handling are applied. Life days are the age-progression counter; they are not the apparent age shown to players, and they are protected from calendar relabelling.

variance gives each newly spawned villager slightly different stage lengths. It is rolled separately for each stage. For example, with variance: 0.1, a 10 life-day stage can become about 9 to 11 life days for one villager, while another stage on the same villager rolls separately. Use 0 when every villager should use the exact authored stage lengths.

narrative_age controls what age the villager or player appears to be. If no stage in the cycle uses narrative_age, Townstead derives apparent age from lived days and the configured aging scale. If any stage provides narrative_age, the cycle uses the authored bands and interpolates within the current stage.

on_endBehaviour
nextMove to the next stage.
stayStay in the current stage.
dieDie when the stage ends.
loopLoop back through the cycle.

When on_end is omitted, non-last stages use next and the final stage uses stay.

death_loot entries are added on top of normal drops.

FieldTypeRequiredNotes
itemidyesItem id to drop.
countintegernoExact count. Overrides min and max.
minintegernoMinimum count. Defaults to 1.
maxintegernoMaximum count. Defaults to min.
chancenumbernoDrop chance from 0 to 1. Defaults to 1.

When no effective life-cycle gene is found, Townstead uses:

Stagepresents_asBase Life DaysEnd
Babybaby2next
Toddlertoddler2next
Childchild9next
Teenteen5next
Adultadult47next
Seniorsenior25stay

The default cycle totals 90 apparent years before aging scale is applied.

Mixed founders can blend life cycles only when each selected root has the same compatible stage shape: same stage ids, same order, and compatible canonical presents_as values. If the cycles do not match, the dominant root’s life cycle is used.