Real badlands clay bands and a real biome temperature table
The bandlands rule cycled four terracotta colours off a per-column random draw. Vanilla generates a 192-entry band table once per world, from a random source named clay_bands, and reads it at the block's height shifted by the clay_bands_offset noise. Brown, red and light grey terracotta were never placed anywhere; the stripes were the wrong thickness and did not line up between neighbouring columns. All seven colours now appear. The temperature condition matched a hand-written list of eleven biome names. Replacing it with the temperature field read out of the jar's 65 biome JSONs fixes one of them: deep_frozen_ocean reads cold by name but its base temperature is 0.5, so vanilla does not freeze it. taiga and the pine taigas were the other way round -- excluded by name, and correctly so, but by coincidence rather than by data. Two parts of the vanilla calculation are left out and documented where they belong: the height adjustment that cools peaks, and the "frozen" modifier that warms scattered patches of frozen ocean. Both need PerlinSimplexNoise. Neither is reachable from the overworld tree in a way that shows: the single condition that consults temperature sits under a frozen_ocean biome check, below a water check, and decides whether a hole in the ocean floor ices over. The snowy mountain tops come from biome selection, not from here -- which is not what the plan for this commit assumed. The per-column *rand.Rand threaded through SurfaceContext goes away with the old bandlands rule; nothing needs it now that vertical_gradient rolls positionally.
This commit is contained in:
parent
c19e5f0e4f
commit
3a255b52e1
8 changed files with 289 additions and 68 deletions
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// surface.go implements the vanilla SurfaceRules interpreter: a rule tree that
|
||||
|
|
@ -59,10 +58,6 @@ type SurfaceContext struct {
|
|||
// the interpolated preliminary surface level plus SurfaceDepth less 8.
|
||||
// above_preliminary_surface tests Y against it.
|
||||
MinSurfaceLevel int
|
||||
// Rng is a per-column deterministic source for the bandlands rule. It is
|
||||
// seeded by the column so results are stable across runs.
|
||||
Rng *rand.Rand
|
||||
|
||||
// noiseValues holds one sample per noise the rule tree's noise_threshold
|
||||
// conditions reference, refreshed once per column by BeginColumn. Vanilla
|
||||
// caches these the same way, through LazyXZCondition.
|
||||
|
|
@ -107,29 +102,13 @@ func (r conditionRule) Apply(ctx *SurfaceContext) (uint16, bool) {
|
|||
return r.then.Apply(ctx)
|
||||
}
|
||||
|
||||
// bandlandsRule reproduces the vanilla badlands coloured-clay banding: a
|
||||
// deterministic per-column pattern of terracotta colours at certain Y bands. We
|
||||
// approximate the 8-band rotation using the column RNG; exact band geometry is
|
||||
// captured well enough to read as badlands.
|
||||
type bandlandsRule struct{}
|
||||
// bandlandsRule reads the world's clay band table at the block's height,
|
||||
// shifted horizontally by the clay_bands_offset noise. It is what stripes the
|
||||
// badlands.
|
||||
type bandlandsRule struct{ bands *clayBands }
|
||||
|
||||
func (bandlandsRule) Apply(ctx *SurfaceContext) (uint16, bool) {
|
||||
orange, _ := surfaceBlockID("minecraft:orange_terracotta", nil)
|
||||
if ctx.Rng == nil {
|
||||
return orange, true
|
||||
}
|
||||
// Vanilla chooses band by Y + a per-column random offset; the rotation
|
||||
// cycles white/orange/yellow/orange terracotta. Pick from the cycle by Y.
|
||||
white, _ := surfaceBlockID("minecraft:white_terracotta", nil)
|
||||
yellow, _ := surfaceBlockID("minecraft:yellow_terracotta", nil)
|
||||
switch (ctx.Y + ctx.Rng.Intn(7)) % 4 {
|
||||
case 0:
|
||||
return white, true
|
||||
case 1, 3:
|
||||
return orange, true
|
||||
default:
|
||||
return yellow, true
|
||||
}
|
||||
func (r bandlandsRule) Apply(ctx *SurfaceContext) (uint16, bool) {
|
||||
return r.bands.bandAt(ctx.X, ctx.Y, ctx.Z), true
|
||||
}
|
||||
|
||||
// ---- Condition tests ---------------------------------------------------
|
||||
|
|
@ -188,27 +167,13 @@ func (t waterTest) Test(ctx *SurfaceContext) bool {
|
|||
return y >= ctx.WaterHeight+t.offset+ctx.SurfaceDepth*t.surfaceDepthMul
|
||||
}
|
||||
|
||||
// temperatureTest passes when the (column) temperature is below freezing — the
|
||||
// snow-at-height rule. We fold temperature into the biome name (snowy_*
|
||||
// biomes) rather than sampling the temperature noise, so pass for cold biomes.
|
||||
// temperatureTest passes where the biome is cold enough for snow and ice
|
||||
// rather than rain. The overworld tree uses it once, to freeze holes in a
|
||||
// frozen ocean floor.
|
||||
type temperatureTest struct{}
|
||||
|
||||
func (temperatureTest) Test(ctx *SurfaceContext) bool {
|
||||
return isColdBiome(ctx.BiomeName)
|
||||
}
|
||||
|
||||
// isColdBiome reports whether the biome should receive snow cover. We use the
|
||||
// biome name rather than the temperature noise for simplicity; this matches
|
||||
// the visible result for the standard overworld biomes.
|
||||
func isColdBiome(name string) bool {
|
||||
switch name {
|
||||
case "minecraft:snowy_plains", "minecraft:snowy_taiga", "minecraft:snowy_beach",
|
||||
"minecraft:snowy_slopes", "minecraft:jagged_peaks", "minecraft:frozen_peaks",
|
||||
"minecraft:frozen_river", "minecraft:frozen_ocean", "minecraft:deep_frozen_ocean",
|
||||
"minecraft:ice_spikes", "minecraft:grove":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return coldEnoughToSnow(ctx.BiomeName)
|
||||
}
|
||||
|
||||
// yAboveTest passes when Y clears an anchor, with optional surface-depth and
|
||||
|
|
@ -445,7 +410,11 @@ func (p *surfaceParser) parseRule(raw json.RawMessage) (SurfaceRule, error) {
|
|||
return conditionRule{test: test, then: then}, nil
|
||||
|
||||
case "minecraft:bandlands":
|
||||
return bandlandsRule{}, nil
|
||||
bands, err := p.loader.clayBands()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bandlandsRule{bands: bands}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("surface: unknown rule type %q", obj.Type)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue