Magical gene / no coat effect

Magic body size

The magical version of LCORL and HMGA2. Most horses in the world carry it, with one copy, worth about a tenth either way — enough that a paddock has a visible range of sizes and no single horse looks odd. Two copies of the same kind add, and no wild horse is ever born with two.

Crossing two of them

Only heterozygotes are born wild

The founder table lists Big/n, Small/n and n/n and nothing else. It is written out rather than derived from Hardy-Weinberg, because random mating is exactly what this table is not:

private final FounderTable founders = FounderTable.builder()
        .weight(Big,   n, 40.0)
        .weight(Small, n, 40.0)
        .weight(n,     n, 20.0)
        .build();

So every doubled horse in the world, in either direction, is something somebody bred. That is the same rule the health loci use — nothing affected is ever wild-caught — pointed at something worth having instead of something to avoid. And since the percentage travels with the copy, breeding two good copies together is a real project with a real result.

Its gene carrot

A server may switch the resizing off

This locus is one of the ones that moves a horse’s size, and vanilla scales the hitbox from the model — so a world that would rather have tack and hitboxes sit exactly where vanilla puts them can set body.size false. The gene is still inherited, still reported and still worth breeding for; only the entity is held at 1.0×.

Gene key
horsegenetics.body_size
Priority
140 (magical band)
Alleles
Big Small n
Combinations
6, all carryable — 6 outcomes, one each
Inheritance
codominant — both copies contribute and the percentages add
Default allele
n
In the wild
heterozygotes only — 40% Big/n, 40% Small/n, 20% n/n
Founder draws
1 × nextFloat()
Epigenetic draws
12 × nextFloat() per copy (one nextGaussian())
Per copy
normal, mean 10%, σ 7%, floor 1%
Coat effect
none, ever
Built 2026-09-04, not yet play-tested

Nothing about a scaled horse has been seen: not the model, not the hitbox, not the rider position. Checklist on To be verified.

The combination table

CombinationOutcomeSizeTypical
n/nwildunchanged1.00×
Big/ngiantone copy's percentage~1.10×
Big/Bigdouble-giantboth percentages, added~1.20×
Small/ntinyone copy's percentage off~0.90×
Small/Smalldouble-tinyboth percentages off~0.80×
Big/Smallbalancedthe two nearly cancel~1.00×

Six combinations, six outcomes. That is unusual in this model — most genes collapse several combinations onto one row — and it is exactly what codominance is: every combination genuinely differs, so sharing any two rows would be a lie about the gene.

The percentages add

Every allele copy carries a percentage, and both copies contribute: the horse's size is one plus the sum of them, Big counting positive and Small negative. Two big copies of +18% and +21% make a horse nearly 40% over — far outside anything the wild population contains.

common/genetics/genes/MagicSizeGene.java
@Override
public void contribute(AllelePair pair, Genotype genotype, GeneEpigenetics epigenetics,
                       TraitBuilder out) {
    double sum = signedDelta(pair.first(),  epigenetics.copy(0))
               + signedDelta(pair.second(), epigenetics.copy(1));
    if (sum != 0.0) {
        out.multiplyScaleUnclamped(1.0 + sum);
    }
}

Signing the contribution rather than branching on the combination is what makes the table fall out of one loop: Big/Small cancels because a positive and a negative are added, not because anything checks for it.

Big/Small is near 1.0, not exactly on it

The two percentages are independent draws off two different allele copies, so they cancel exactly only by coincidence. That is the honest consequence of addition — forcing an exact zero would need a special case that contradicts the rule — and the residual is worth having: a horse a hair off ordinary size is usually carrying both extremes.

It needs both copies' epigenetics

Every other epigenetic gene asks for "the copy that expresses", because one locus produces one result. A codominant one cannot: asking for the expressed copy would count one allele twice and the other not at all. So this gene takes an GeneEpigenetics and asks it for copy(0) and copy(1) — the trait-side twin of the accessor mane colour needed for the same reason on the coat side.

A normal distribution, centred on a tenth

One copy's percentage is a normal draw with mean 10% and standard deviation 7%. So about two thirds of carriers sit between 1.03× and 1.17× (or 0.83× and 0.97×), and the tails reach further.

The shape matters because almost every wild horse carries this gene. If a single copy were dramatic then dramatic would be the baseline and nothing would read as unusual. Instead the population has a quiet, continuous spread of sizes — you notice it across a paddock rather than on any one horse — and the striking animals are the ones somebody bred.

DrawOne copyTwo copies
−6σ (the floor)1.01×1.02×
−1σ1.03×1.06×
mean1.10×1.20×
+1σ1.17×1.34×
+3σ1.31×1.62×
+6σ (the bound)1.52×2.04×
The Gaussian is bounded, and that is deliberate

Rng.nextGaussian() is the Irwin-Hall construction — twelve uniform draws, minus six — rather than Box-Muller. Two properties earn the twelve calls. Its tails stop at ±6σ, so a trait that decides how big a horse is has a hard bound on absurdity and the guard clamps never actually fire. And all-0.5 inputs give exactly 0, so MidpointRng — the "what does this genotype do on average" source — lands on the mean. Box-Muller would hand it a value 1.18σ below the mean, which is not a midpoint of anything.

A copy's percentage is also floored at 1%. A Big allele that rolled negative would be a big allele making a horse smaller — not a distribution tail, a bug in the reader's head.

Past the natural clamp, on purpose

HorseTraits.MIN_SCALE / MAX_SCALE (0.45–1.75) exist so that no amount of stacking real size loci and dwarfism can produce a horse that is not a horse. A magical gene is allowed to produce exactly that, so it applies its factor after that clamp through TraitBuilder.multiplyScaleUnclamped, bounded only by MAGICAL_MIN_SCALE / MAGICAL_MAX_SCALE (0.1–10).

With a bounded normal distribution the gene now reaches about 2.04× at the absolute limit rather than the 10× of the first draft — and it takes two good copies to get past even the natural clamp, which is the point of making it codominant. The magical bounds stay as guards rather than as the design.

The two stages compose the way you would want: the natural loci settle the horse's own size first and the magic multiplies whatever that turned out to be, so a magically enormous pony is still smaller than a magically enormous draught horse.

It needed a rendering fix

Vanilla advances a horse's leg-swing phase from the ground distance it moved and nothing else. The only size compensation anywhere in it is a hard-coded "a foal's legs cycle three times as fast"; nothing consults Attributes.SCALE, because before this gene nothing changed it.

So a scaled horse walked with its feet sliding — longer legs swinging at the ordinary rate over the ordinary ground. GeneticHorseRenderer now divides walkAnimationPos by the render scale, which makes a bigger horse take proportionally longer, slower strides. See stretchGaitToSize.

Deliberately size only

It moves no other number — not speed, not health, not jump. A large horse is a spectacle and a stable problem, not a better horse, and the natural loci already own the trade between size and everything else.

Everything visible about it comes free from one attribute: Attributes.SCALE scales the model and the hitbox.

Source: common/genetics/genes/MagicSizeGene.java, common/trait/GeneEpigenetics.java