Gameplay / inheritance
Breeding carrots & the gene database
The mod’s first mechanism for influencing inheritance, and the knowledge layer that gates it. Built 2026-09-05, not play-tested — checklist in verification §0f. This page is the source of truth for the mechanism; the recipes are on items, and what is still missing is on the roadmap.
The one rule
A fed carrot changes only what a parent draws and passes on for
one foal. It never rewrites the parent’s own alleles — that
would silently mutate a horse the player bred for generations, and make
its displayed code stop matching its history. Every effect below is a
modifier on the draw, expressed as
common/genetics/GameteBias.
GameteBias.NONE on both parents is bit-for-bit
the plain Genome.breedWith — no extra RNG draws —
so a carrot that is not fed changes nothing. That is pinned by a test and
by coat-golden.txt staying untouched.
A splice that lands marks the foal for good
A gene splice carrot whose allele actually reaches the foal changes that foal’s
breed to Spliced (its breed), and the mark never washes out: it passes
down the whole line, and no amount of breeding back removes it. See
crosses and mixes for the combination rules —
they are the cross rules, because splice is simply a reserved lineage
component and a spliced Friesian is the pair {friesian, splice}.
“Actually reaches the foal” is SpliceOutcome.spliceReached, and
it asks a question about the foal rather than about the carrot:
at a locus a carrot substituted, does the foal carry an allele neither parent
had? Neither, not “the fed one”, and that is not a shortcut. A mare fed
a Known Gene Splice for G whose stallion is already G/G was
going to produce a G-carrying foal whichever copy she passed on, so nothing
outside can say whether the carrot did anything — and nothing was added, so
nothing is claimed.
Feeding a splice is a gamble and the carrot is spent either way, so a spectacular spliced horse is cheap in effort and says so on its own label. Anybody can splice a Friesian into something remarkable; only a breeder can get there without. Before this, a spliced line and a bred one were indistinguishable a generation later.
The carrot window
Feeding a carrot to a breedable adult horse
(server/BreedingCarrotHandler) puts a temporary
CarrotWindowAttachment on it — the carrot’s effect
tokens plus a lapse time — and also sets the horse into breeding mode,
the same as a golden carrot. The window is 30 seconds
(vanilla love length), is stored on the horse and part of the breeding
draw’s input while live, and is consumed when the pair
breeds. There is no permanent stored carrot state. Each type shows a
differently-coloured particle burst when fed.
On a foal, HorseBreedingHandler reads both parents’ live
windows, folds each into one GameteBias
(CarrotEffect.fold), threads them through
GeneticCodeCombiner.combine, and clears both windows.
The four breeding carrots
Two are random - "Unknown" - and one is targeted (§below) - "Known". The random gene splice also comes in five themed forms (§below), which are the same carrot pointed at one slice of the pool. Naming:
| Carrot | What it does to the gamete |
|---|---|
| Unknown Epigenetic Splice | Every allele copy this parent passes on gets freshly rolled values instead of the parent copy’s own. The alleles are untouched — the foal has the same genotype an un-fed pairing would give, only re-rolled bay-point heights / grey dapples / splash edges. The re-roll runs in a second pass, after every allele is locked, so it can never move a foal’s genotype. (CarrotEffect.EpigeneticSplice.) |
| Stabilizer | At every heterozygous locus, contribute the earlier-declared allele (the variant / dominant one) rather than the 50/50 coin. A no-op at a homozygous locus — which is the “does nothing when the parent has no dominant allele” the carrot is documented to have; the feed handler says so. |
| Magnifier | The mirror — contribute the later-declared (baseline / recessive) copy. Same no-op rule. |
| Unknown Gene Splice | Pick one random gene and draw this parent’s gamete for it from that gene’s splice distribution (or a uniform draw over its viable pairs if it declares none). The gene and the pair are rolled at breeding time off the foal’s deterministic RNG. The pool is filtered: never the sex locus, and never a locus that could kill the foal or cost it hearts. (CarrotEffect.GeneSplice.) |
The Unknown Gene Splice blacklist
A player feeding a random-splice carrot has chosen a surprise. They have not chosen to kill the foal. So the carrot may never roll a locus that makes a horse worse off:
- nothing that kills — the four recessive foal lethals,
MET’s lethal-at-conception, milk’sWatr/Lava, EDNRB’s overo lethal white; - nothing that costs hearts — the dwarfisms,
silver’s MCOA, and
MSTN, which trades health for speed and carries no
Conditionat all; - nothing merely impairing either. “It only takes two hearts” is still a horse the player did not ask to damage.
It stops there, deliberately. A slower horse is not a damaged one —
HMGA2’s pony allele and magic speed’s
Sluggish both come out of the carrot, because a surprise you did not want
is exactly what a random splice is for. Hearts are the line.
A hand-written list of gene keys is wrong the day someone adds a gene and forgets
it, and wrong silently — the failure is a dead foal in someone’s
world, not a build error. So SpliceSafety works the pool out from
what the genes actually do: every combination the carrot could roll is resolved
through HorseTraits on an otherwise wild-type horse, and the locus is
dropped if any of them produces a condition worse than
INFORMATIONAL or leaves the horse short of baseline health. A new
health gene is excluded the moment it is registered, by having done its job.
Informational conditions are kept: splash deafness and the
leopard complex’s night blindness cost the horse nothing, and excluding them
would delete most of the white-pattern loci from the carrot for no protection at
all. How many loci the rule drops is Genes.codeOrder().size() minus
SpliceSafety.pool().size() — a number that moves with every
gene added, and deliberately not written down here. None of them is named
anywhere in the file either.
The Known Gene Splice carrot is deliberately not filtered. There the player names the gene, has researched it and has read what it does — splicing a horse into a lethal genotype on purpose is allowed, and is the player’s business. The blacklist is about the random draw only.
The five themed splices
A player who wants a surprise colour should not be handed a jumping stat. So the Unknown Gene Splice carrot has five narrowed forms, each rolling one slice of the same safe pool. Crafted by refining the plain one - an Unknown Gene Splice carrot plus a themed reagent - because that is what they are.
| Carrot | Rolls | Reagent |
|---|---|---|
| Dilution Gene Splice | The loci that change what colour the pigment is: cream / pearl, champagne, dun, flaxen, mushroom, grey, tiger eye. Silver is not on it — MCOA costs hearts, so the blacklist had already taken it. | light grey dye |
| White Gene Splice | The loci that take pigment away in patches: KIT, tobiano, MITF / PAX3 splash, manchado, roan, and the leopard complex with PATN1 and PATN2. EDNRB is not on it — overo lethal white. | bone meal |
| Marking Gene Splice | Patterned or shaded pigment that is neither white nor a dilution: sooty, pangaré, primitive striping, rabicano, brindle. | ink sac |
| Performance Gene Splice | The natural loci that move speed, health or jump — and only upward. See the note below. | rabbit’s foot |
| Magical Gene Splice | Everything Gene.isNatural() says is not real, whatever it does — the colour genes, the body-stat genes, particles, the cutie mark, lycanthropy. | amethyst shard |
Every one of the five starts from SpliceSafety.pool() and removes
from it. So the blacklist above covers all six random
carrots at once, and a health gene registered tomorrow is out of every one of
them the moment it exists. Nothing in this family can hand a player a damaged
foal; the deliberate route into a lethal genotype is still the
Known carrot.
The blacklist stops at hearts on purpose — a slower horse is not a damaged one, so HMGA2’s pony allele and LCORL’s short copy stay in the general carrot. A carrot sold as a positive health splice cannot roll them. So the performance carrot is the one theme that narrows the combination as well as the locus: it draws only pairs that beat the baseline horse on speed, max health or jump and lose to it on none of the three.
Size is read neither way. A draught horse is not a better horse
than a pony, so a locus that only moves inches contributes nothing — which
is why LCORL is on this carrot for its speed and
HMGA2, whose every variant is a trade, drops off it entirely.
Magical and performance are properties the code can read —
isNatural(), and whether any combination beats baseline. Dilution /
white / marking is a taxonomy a person applies: dun dilutes the
body and draws a dorsal stripe, roan removes pigment without being a
spotting pattern, brindle paints white streaks and is still a striping gene. No
measurement of the finished coat sorts those the way a horse person would, and
being “derived” would not make a wrong answer right.
So those three are a table in SpliceCategory, written against the
Genes constants rather than key strings — a renamed or retired
gene breaks the compile instead of quietly dropping out of a carrot. A gene not
in the table falls to marking if it paints and to no theme at
all if it does not, so a drop-in gene from the creator lands somewhere harmless.
Extension, agouti and shade are in none of the five. They are the base coat rather than a marking on it, and a carrot sold as “markings” that turned a black horse chestnut would be answering a question nobody asked. They stay on the unthemed carrot, which promises nothing.
Known Gene Splice carrots
One per gene, and you pick which. Feeding it makes the game
treat that parent as n<Gene> (heterozygous, the default)
or <Gene><Gene> (homozygous, if the gene’s
carrot.behaviour says so) for that one gene’s gamete;
normal Mendelian rules apply from there, so two heterozygous-carrot parents
give the ordinary 25/50/25. If the homozygote cannot occur (a KIT
lethal, met/met) it falls back to heterozygous.
(CarrotEffect.KnownGeneSplice, effect token
known:<geneKey>:het|hom.)
The recipe is one parameterised CustomRecipe
(server/recipe/KnownGeneSpliceRecipe), not N generated recipes:
exactly four items — golden carrot + a
research paper + a hair item (horse_hair
or hair_cloth) + the rarity item for that gene’s tier
→ a known_gene_splice_carrot carrying the paper’s
gene. The recipe reads the gene off the paper at craft time, so a drop-in
gene file gets its carrot the moment it registers — no datapack.
The design originally called for a fifth “flavour” slot, letting each
gene name an ingredient that reads as it. The recipe now requires
exactly the four, and per-gene flavour lists are still unwritten — a
carrot.flavour block exists in the
gene format and no built-in fills it. See
the roadmap.
| Rarity tier | Item (server/recipe/RarityItems) |
|---|---|
| common | iron ingot |
| uncommon (default — the gold-ingot tier) | gold ingot |
| rare | diamond |
| epic | emerald |
| legendary | netherite ingot |
| mythic | nether star |
A gene opts out of having a Known Gene Splice carrot with
carrot.enabled: false. Built-in exceptions: the
sex locus, the recessive disorders
(RecessiveDisorderGene), and extension + agouti.
wiki/gene-carrot/ is a card that any gene page carries in one
line, and it asks the mod for the four things that differ
per gene: whether a carrot exists at all, the rarity tier that prices it,
heterozygous or homozygous, and whether the
Unknown splice may land on the locus. It runs the
same wasm the horse designer does,
so nothing about a gene is written into a page and there is no per-gene
recipe to keep in step.
Two things about the recipe are not per-gene and so are held in
the card itself: the four-slot layout, whose canonical form is
menu/SpliceRecipeDisplay (what the browser ghosts into the
grid), and the tier→item table above, whose authority is
server/recipe/RarityItems. Change either and
wiki/gene-carrot/gene-carrot.js changes with it.
Combining carrots
server/recipe/CarrotCombineRecipe — two or more breeding
carrots crafted together become one carrot carrying all their effects, as a
merged carrot_effects component list. No visual change, no cap
on count. Contradictions are rejected at craft time —
a stabilizer with a magnifier, or two Known Gene Splice carrots for the same gene that
disagree on het/hom — so the recipe simply does not resolve, which the
player can see, rather than a silent last-one-wins.
The gene database & research papers
data/GeneDatabaseData is a server-global, per-player
SavedData: per gene, the allele variants the player has seen,
when it was first met, and whether its gene-carrot recipe is unlocked. It
is filled by:
- Taming a horse that carries a non-baseline allele at a
gene (
server/GeneDiscoveryHandler, onAnimalTameEvent); - Breeding a foal with one (from
applyBredFoal, credited to the breeder); - Reading a research paper.
It is a plain progressive record. The info panel, the pen signs and the genotype code keep showing every gene a horse carries, always. The only thing discovery gates is the display of a gene’s gene-carrot line in the browser — and even the recipe itself is gated only by needing a paper as an ingredient, not by a database check.
A research paper (item/ResearchPaperItem)
carries a research_gene component. Right-clicking it adds the
gene to the reader’s database, unlocks its carrot line, and consumes
the paper. Three sources: chest loot
(data/loot/AddResearchPaperModifier, a global loot modifier that
seeds papers into ~8 chest tables weighted by GeneRarity),
other players, and writing one from your own database at the
cost of a book — but you do not choose which gene. See below.
A book on a horse
Right-click a horse holding a book and you get a research paper for one gene that horse carries, chosen at random from every locus where it is not simply the plain baseline. An entirely ordinary horse has nothing to teach and costs you no book.
It is random on purpose. The old route was a Craft research paper button in the Horse Browser: pick the gene, spend a book, get exactly that paper — which meant a paper was never a discovery, only paperwork once you already knew. Now the horse decides, so a paper is something an animal told you. Discovery is not required: reading the paper is what adds the gene to your database, so gating it on the database would make it unable to teach you anything.
The other half of the loop is the Equine Research Shelf,
where a paper you own becomes one you can copy. The browser’s
Recipes tab is now a reference only — it makes
nothing; menu/SpliceRecipeDisplay is still the canonical splice
layout, shared by that reference and the recipe so the two cannot disagree.
The gene metadata it all reads
Everything above reads fields that no gene had before this pass (roadmap
§19). They are three defaults on Gene plus a spec format
bump — full shapes on the gene format
page:
rarity()— aGeneRaritytier (defaultUNCOMMON, the gold-ingot tier).hasGeneCarrot()/geneCarrotHomozygous()— the opt-out flag and the het/hom behaviour.spliceTable()— anOptional<FounderTable>, the Unknown Gene Splice carrot’s per-gene distribution.description()— already existed; a spec gene now fills it from an optionalblurb.
Deferred
The mechanism is complete; what is missing is content and reach. All of it is on the roadmap:
- No built-in gene declares a
splicetable or acarrotflavour list. Every one falls back to the minimal recipe and a uniform draw over its viable pairs — which on a forty-allele locus like particle is a near-guaranteed oddity. That is now the only remaining complaint: the “hands a foal a birth-lethal genotype” half is fixed. - An Epigenetic Stabilizer carrot — the opposite end of the dial from Unknown Epigenetic Splice, forcing every passed-on copy to be inherited verbatim. Only meaningful once epigenetic drift exists.
- Carrot effects on a seed jar, captured at collection and applied at impregnation. None are carried today.
- The Horse Master villager selling papers and carrots.
- The §19 metadata fields as gene-creator form inputs
— the creator emits
format: 3but has no UI forrarity/carrot/splice/blurb. - A database check inside the recipe itself; today the paper ingredient is the whole gate.
common/genetics/CarrotEffect,
common/genetics/GameteBias,
server/BreedingCarrotHandler,
server/recipe/, data/GeneDatabaseData.