From 674d0192c95300887f51f8c4e13af176ff3baab4 Mon Sep 17 00:00:00 2001 From: Daniar Mannanov Date: Tue, 11 Aug 2026 09:42:11 +0300 Subject: [PATCH] Match vanilla placement height sampling --- internal/worldgen/features.go | 20 +++++++++++++------- internal/worldgen/features_test.go | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/internal/worldgen/features.go b/internal/worldgen/features.go index 7dc4d5c..6125bf0 100644 --- a/internal/worldgen/features.go +++ b/internal/worldgen/features.go @@ -176,18 +176,24 @@ func (p PlacementPlan) SampleY(r RandomSource, minY, height int) int { } span := hi - lo + 1 if p.HeightDistribution == "minecraft:trapezoid" { - plateau := 0 - triangle := span - plateau - return lo + int(r.NextIntN(int32((triangle+1)/2))) + int(r.NextIntN(int32(triangle/2+1))) + // Vanilla's TrapezoidHeight works with the inclusive range distance, + // then performs two inclusive random draws around the midpoint. + rangeSize := span - 1 + if rangeSize <= 0 { + return lo + } + left := rangeSize / 2 + right := rangeSize - left + return lo + int(r.NextIntN(int32(right+1))) + int(r.NextIntN(int32(left+1))) } if p.HeightDistribution == "minecraft:very_biased_to_bottom" { const inner = 8 - outer := span - inner + 1 - if outer <= 0 { + if span-inner <= 0 { return lo } - bound := int(r.NextIntN(int32(outer))) + inner - return lo + int(r.NextIntN(int32(bound))) + first := lo + inner + int(r.NextIntN(int32(span-inner))) + second := lo + int(r.NextIntN(int32(first-lo))) + return lo + int(r.NextIntN(int32(second-lo+inner))) } return lo + int(r.NextIntN(int32(span))) } diff --git a/internal/worldgen/features_test.go b/internal/worldgen/features_test.go index e7832ff..6720fda 100644 --- a/internal/worldgen/features_test.go +++ b/internal/worldgen/features_test.go @@ -80,3 +80,20 @@ func TestFeatureStepsRejectsCycles(t *testing.T) { t.Fatal("cyclic feature order succeeded") } } + +func TestPlacementHeightDistributionsStayWithinInclusiveBounds(t *testing.T) { + r := NewLegacy(12345) + for _, distribution := range []string{"minecraft:trapezoid", "minecraft:very_biased_to_bottom", "minecraft:uniform"} { + plan := PlacementPlan{ + HeightDistribution: distribution, + MinY: HeightProvider{Absolute: intPtr(-20)}, + MaxY: HeightProvider{Absolute: intPtr(20)}, + } + for i := 0; i < 1000; i++ { + got := plan.SampleY(r, -64, 384) + if got < -20 || got > 20 { + t.Fatalf("%s sample %d outside [-20,20]: %d", distribution, i, got) + } + } + } +}