Preserve trapezoid height plateaus
This commit is contained in:
parent
4fa0a5e554
commit
2cf6ab4775
2 changed files with 62 additions and 11 deletions
|
|
@ -122,6 +122,7 @@ type PlacementPlan struct {
|
||||||
Count CountProvider
|
Count CountProvider
|
||||||
RarityChance int
|
RarityChance int
|
||||||
HeightDistribution string
|
HeightDistribution string
|
||||||
|
HeightPlateau int
|
||||||
MinY HeightProvider
|
MinY HeightProvider
|
||||||
MaxY HeightProvider
|
MaxY HeightProvider
|
||||||
}
|
}
|
||||||
|
|
@ -195,13 +196,15 @@ func (p PlacementPlan) SampleY(r RandomSource, minY, height int) int {
|
||||||
}
|
}
|
||||||
span := hi - lo + 1
|
span := hi - lo + 1
|
||||||
if p.HeightDistribution == "minecraft:trapezoid" {
|
if p.HeightDistribution == "minecraft:trapezoid" {
|
||||||
// Vanilla's TrapezoidHeight works with the inclusive range distance,
|
|
||||||
// then performs two inclusive random draws around the midpoint.
|
|
||||||
rangeSize := span - 1
|
rangeSize := span - 1
|
||||||
if rangeSize <= 0 {
|
if rangeSize <= 0 {
|
||||||
return lo
|
return lo
|
||||||
}
|
}
|
||||||
left := rangeSize / 2
|
plateau := p.HeightPlateau
|
||||||
|
if plateau < 0 || plateau > rangeSize {
|
||||||
|
return lo
|
||||||
|
}
|
||||||
|
left := (rangeSize - plateau) / 2
|
||||||
right := rangeSize - left
|
right := rangeSize - left
|
||||||
return lo + int(r.NextIntN(int32(right+1))) + int(r.NextIntN(int32(left+1)))
|
return lo + int(r.NextIntN(int32(right+1))) + int(r.NextIntN(int32(left+1)))
|
||||||
}
|
}
|
||||||
|
|
@ -500,6 +503,7 @@ func (s *FeatureSet) Placement(name string) (PlacementPlan, error) {
|
||||||
var value struct {
|
var value struct {
|
||||||
Height struct {
|
Height struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
Plateau int `json:"plateau"`
|
||||||
Min json.RawMessage `json:"min_inclusive"`
|
Min json.RawMessage `json:"min_inclusive"`
|
||||||
Max json.RawMessage `json:"max_inclusive"`
|
Max json.RawMessage `json:"max_inclusive"`
|
||||||
} `json:"height"`
|
} `json:"height"`
|
||||||
|
|
@ -519,7 +523,7 @@ func (s *FeatureSet) Placement(name string) (PlacementPlan, error) {
|
||||||
value.Height.Type != "minecraft:very_biased_to_bottom" {
|
value.Height.Type != "minecraft:very_biased_to_bottom" {
|
||||||
return PlacementPlan{}, fmt.Errorf("worldgen: %s unsupported height distribution %q", name, value.Height.Type)
|
return PlacementPlan{}, fmt.Errorf("worldgen: %s unsupported height distribution %q", name, value.Height.Type)
|
||||||
}
|
}
|
||||||
plan.HeightDistribution, plan.MinY, plan.MaxY = value.Height.Type, min, max
|
plan.HeightDistribution, plan.HeightPlateau, plan.MinY, plan.MaxY = value.Height.Type, value.Height.Plateau, min, max
|
||||||
case "minecraft:in_square", "minecraft:biome", "minecraft:surface_water_depth_filter",
|
case "minecraft:in_square", "minecraft:biome", "minecraft:surface_water_depth_filter",
|
||||||
"minecraft:heightmap", "minecraft:block_predicate_filter", "minecraft:noise_threshold_count",
|
"minecraft:heightmap", "minecraft:block_predicate_filter", "minecraft:noise_threshold_count",
|
||||||
"minecraft:random_offset":
|
"minecraft:random_offset":
|
||||||
|
|
@ -746,6 +750,7 @@ func placementHeightPlan(raw json.RawMessage) (PlacementPlan, error) {
|
||||||
var value struct {
|
var value struct {
|
||||||
Height struct {
|
Height struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
Plateau int `json:"plateau"`
|
||||||
Min json.RawMessage `json:"min_inclusive"`
|
Min json.RawMessage `json:"min_inclusive"`
|
||||||
Max json.RawMessage `json:"max_inclusive"`
|
Max json.RawMessage `json:"max_inclusive"`
|
||||||
} `json:"height"`
|
} `json:"height"`
|
||||||
|
|
@ -765,7 +770,7 @@ func placementHeightPlan(raw json.RawMessage) (PlacementPlan, error) {
|
||||||
value.Height.Type != "minecraft:very_biased_to_bottom" {
|
value.Height.Type != "minecraft:very_biased_to_bottom" {
|
||||||
return PlacementPlan{}, fmt.Errorf("worldgen: unsupported height distribution %q", value.Height.Type)
|
return PlacementPlan{}, fmt.Errorf("worldgen: unsupported height distribution %q", value.Height.Type)
|
||||||
}
|
}
|
||||||
return PlacementPlan{HeightDistribution: value.Height.Type, MinY: min, MaxY: max}, nil
|
return PlacementPlan{HeightDistribution: value.Height.Type, HeightPlateau: value.Height.Plateau, MinY: min, MaxY: max}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseIntProvider(raw json.RawMessage) (CountProvider, error) {
|
func parseIntProvider(raw json.RawMessage) (CountProvider, error) {
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,35 @@ package worldgen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestTrapezoidPlateauDiagnostic(t *testing.T) {
|
||||||
|
if os.Getenv("REGIONIO_TRAPEZOID_DIAGNOSTIC") != "1" {
|
||||||
|
t.Skip("set REGIONIO_TRAPEZOID_DIAGNOSTIC=1 to list non-zero height plateaus")
|
||||||
|
}
|
||||||
|
set, err := LoadFeatureSet()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for name, placed := range set.Placed {
|
||||||
|
for _, modifier := range placed.Placement {
|
||||||
|
if modifier.Type != "minecraft:height_range" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
plan, err := placementHeightPlan(modifier.Raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: %v", name, err)
|
||||||
|
}
|
||||||
|
if plan.HeightDistribution == "minecraft:trapezoid" && plan.HeightPlateau != 0 {
|
||||||
|
t.Logf("%s plateau=%d", name, plan.HeightPlateau)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFeatureDatapackLoadsAndLinks(t *testing.T) {
|
func TestFeatureDatapackLoadsAndLinks(t *testing.T) {
|
||||||
set, err := LoadFeatureSet()
|
set, err := LoadFeatureSet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -157,6 +182,27 @@ func TestVeryBiasedToBottomConsumesVanillaDraws(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrapezoidHeightHonorsPlateau(t *testing.T) {
|
||||||
|
plan := PlacementPlan{
|
||||||
|
HeightDistribution: "minecraft:trapezoid",
|
||||||
|
HeightPlateau: 4,
|
||||||
|
MinY: HeightProvider{Absolute: intPtr(0)},
|
||||||
|
MaxY: HeightProvider{Absolute: intPtr(20)},
|
||||||
|
}
|
||||||
|
gotRandom := NewLegacy(12345)
|
||||||
|
wantRandom := NewLegacy(12345)
|
||||||
|
for sample := 0; sample < 16; sample++ {
|
||||||
|
left := (20 - 4) / 2
|
||||||
|
want := int(wantRandom.NextIntN(int32(20-left+1))) + int(wantRandom.NextIntN(int32(left+1)))
|
||||||
|
if got := plan.SampleY(gotRandom, 0, 21); got != want {
|
||||||
|
t.Fatalf("sample %d = %d, want %d", sample, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if got, want := gotRandom.NextLong(), wantRandom.NextLong(); got != want {
|
||||||
|
t.Fatalf("random state after samples = %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPlacementPositionsPreservesModifierOrder(t *testing.T) {
|
func TestPlacementPositionsPreservesModifierOrder(t *testing.T) {
|
||||||
modifier := func(raw string) PlacementModifier {
|
modifier := func(raw string) PlacementModifier {
|
||||||
var value PlacementModifier
|
var value PlacementModifier
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue