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
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
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
| Combination | Outcome | Size | Typical |
|---|---|---|---|
n/n | wild | unchanged | 1.00× |
Big/n | giant | one copy's percentage | ~1.10× |
Big/Big | double-giant | both percentages, added | ~1.20× |
Small/n | tiny | one copy's percentage off | ~0.90× |
Small/Small | double-tiny | both percentages off | ~0.80× |
Big/Small | balanced | the 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.
@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.
| Draw | One copy | Two copies |
|---|---|---|
| −6σ (the floor) | 1.01× | 1.02× |
| −1σ | 1.03× | 1.06× |
| mean | 1.10× | 1.20× |
| +1σ | 1.17× | 1.34× |
| +3σ | 1.31× | 1.62× |
| +6σ (the bound) | 1.52× | 2.04× |
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.
common/genetics/genes/MagicSizeGene.java,
common/trait/GeneEpigenetics.java