package world import ( "strconv" "regionio/internal/worldgen" ) func (r *decorationRegion) placeScheduledVegetationPatches(seed int64) error { set, err := worldgen.LoadFeatureSet() if err != nil { return err } if err := r.ensureSourceNeighborhood(); err != nil { return err } schedule, err := set.FeatureSchedule(possibleBiomeOrder(), r.sourceBiomes(), vegetationStage) if err != nil { return err } random, decorationSeed := worldgen.DecorationRandom(seed, int(r.sourceX), int(r.sourceZ)) origin := worldgen.FeaturePosition{X: int(r.sourceX) << 4, Y: MinY, Z: int(r.sourceZ) << 4} for _, scheduled := range schedule { placed, ok := set.Placed[scheduled.Name] if !ok { continue } configured, ok := set.Configured[placed.Feature] if !ok { continue } random.SetFeatureSeed(decorationSeed, scheduled.Index, vegetationStage) context := r.placementContext(func(position worldgen.FeaturePosition) bool { return r.biomeAllowsFeature(set, scheduled.Name, vegetationStage, position) }) switch configured.Type { case "minecraft:vegetation_patch": config, err := set.VegetationPatch(placed.Feature) if err != nil { return err } if err := set.ForEachPlacementPosition(scheduled.Name, random, origin, context, func(position worldgen.FeaturePosition) error { r.placeVegetationPatch(random, position, config, set) return nil }); err != nil { return err } case "minecraft:kelp": if err := set.ForEachPlacementPosition(scheduled.Name, random, origin, context, func(position worldgen.FeaturePosition) error { r.placeKelp(random, position, set) return nil }); err != nil { return err } case "minecraft:seagrass": config, err := set.Probability(placed.Feature) if err != nil { return err } if err := set.ForEachPlacementPosition(scheduled.Name, random, origin, context, func(position worldgen.FeaturePosition) error { r.placeSeagrass(random, position, config.Probability, set) return nil }); err != nil { return err } } } return nil } func (r *decorationRegion) placeKelp(random worldgen.RandomSource, position worldgen.FeaturePosition, set *worldgen.FeatureSet) bool { position.Y = r.heightAt("OCEAN_FLOOR", position.X, position.Z) if r.getBlock(position.X, position.Y, position.Z) != StateWater { return false } plant, plantOK := nameToStateID("minecraft:kelp_plant", nil) if !plantOK { return false } height := 1 + int(random.NextIntN(10)) placed := false for i := 0; i <= height; i++ { y := position.Y + i if r.getBlock(position.X, y, position.Z) == StateWater && r.getBlock(position.X, y+1, position.Z) == StateWater && r.canKelpSurvive(position.X, y, position.Z, set) { if i == height { age := 20 + int(random.NextIntN(4)) head, ok := nameToStateID("minecraft:kelp", map[string]string{"age": strconv.Itoa(age)}) if !ok || !r.setBlock(position.X, y, position.Z, head) { break } } else if !r.setBlock(position.X, y, position.Z, plant) { break } placed = true continue } if i > 0 { belowY := y - 1 if r.canKelpSurvive(position.X, belowY, position.Z, set) && r.getBlock(position.X, belowY-1, position.Z) != plant { age := 20 + int(random.NextIntN(4)) head, ok := nameToStateID("minecraft:kelp", map[string]string{"age": strconv.Itoa(age)}) if ok { placed = r.setBlock(position.X, belowY, position.Z, head) || placed } } } break } return placed } func (r *decorationRegion) canKelpSurvive(x, y, z int, set *worldgen.FeatureSet) bool { below := r.getBlock(x, y-1, z) state, ok := stateByID(below) if !ok || blockTagContains(set, "minecraft:cannot_support_kelp", state.Name) { return false } return state.Name == "minecraft:kelp" || state.Name == "minecraft:kelp_plant" || fullSolidState(below) } func (r *decorationRegion) placeSeagrass(random worldgen.RandomSource, position worldgen.FeaturePosition, probability float32, set *worldgen.FeatureSet) bool { position.X += int(random.NextIntN(8)) - int(random.NextIntN(8)) position.Z += int(random.NextIntN(8)) - int(random.NextIntN(8)) position.Y = r.heightAt("OCEAN_FLOOR", position.X, position.Z) if r.getBlock(position.X, position.Y, position.Z) != StateWater { return false } tall := random.NextDouble() < float64(probability) if tall { lower, lowerOK := nameToStateID("minecraft:tall_seagrass", map[string]string{"half": "lower"}) upper, upperOK := nameToStateID("minecraft:tall_seagrass", map[string]string{"half": "upper"}) if !lowerOK || !upperOK || r.getBlock(position.X, position.Y+1, position.Z) != StateWater || !r.canSeagrassSurvive(position.X, position.Y, position.Z, set) { return false } return r.setBlock(position.X, position.Y, position.Z, lower) && r.setBlock(position.X, position.Y+1, position.Z, upper) } short, ok := nameToStateID("minecraft:seagrass", nil) if !ok || !r.canSeagrassSurvive(position.X, position.Y, position.Z, set) { return false } return r.setBlock(position.X, position.Y, position.Z, short) } func (r *decorationRegion) canSeagrassSurvive(x, y, z int, set *worldgen.FeatureSet) bool { below := r.getBlock(x, y-1, z) state, ok := stateByID(below) return ok && fullSolidState(below) && !blockTagContains(set, "minecraft:cannot_support_seagrass", state.Name) } func blockTagContains(set *worldgen.FeatureSet, tag, name string) bool { for _, member := range flattenBlockTag(set, tag, nil) { if member == name { return true } } return false } func (r *decorationRegion) placeVegetationPatch(random worldgen.RandomSource, origin worldgen.FeaturePosition, config worldgen.VegetationPatchFeatureConfig, set *worldgen.FeatureSet) bool { radiusX := config.XZRadiusMin + 1 if config.XZRadiusMax > config.XZRadiusMin { radiusX += int(random.NextIntN(int32(config.XZRadiusMax - config.XZRadiusMin + 1))) } radiusZ := config.XZRadiusMin + 1 if config.XZRadiusMax > config.XZRadiusMin { radiusZ += int(random.NextIntN(int32(config.XZRadiusMax - config.XZRadiusMin + 1))) } direction := -1 if config.Surface == "ceiling" { direction = 1 } replaceable := geodeTagIDs(set, config.ReplaceableTag) ground, groundOK := nameToStateID(config.Ground.Name, config.Ground.Properties) if !groundOK { return false } placed := false var groundPositions [][3]int for dx := -radiusX; dx <= radiusX; dx++ { for dz := -radiusZ; dz <= radiusZ; dz++ { onXEdge := dx == -radiusX || dx == radiusX onZEdge := dz == -radiusZ || dz == radiusZ if onXEdge && onZEdge { continue } if (onXEdge || onZEdge) && random.NextFloat() >= config.ExtraEdgeColumnChance { continue } p := origin p.X += dx p.Z += dz current := r.getBlock(p.X, p.Y, p.Z) steps := 0 for current == StateAir && steps < config.VerticalRange { p.Y += direction current = r.getBlock(p.X, p.Y, p.Z) steps++ } steps = 0 for current != StateAir && steps < config.VerticalRange { p.Y -= direction current = r.getBlock(p.X, p.Y, p.Z) steps++ } // Vanilla requires the candidate cell to be empty and the adjacent // surface block to expose a sturdy face toward the patch. if r.getBlock(p.X, p.Y, p.Z) != StateAir { continue } groundPos := p groundPos.Y += direction if !fullSolidState(r.getBlock(groundPos.X, groundPos.Y, groundPos.Z)) { continue } depth := config.DepthMin if config.DepthMax > config.DepthMin { depth += int(random.NextIntN(int32(config.DepthMax - config.DepthMin + 1))) } if config.ExtraBottomBlockChance > 0 && random.NextFloat() < config.ExtraBottomBlockChance { depth++ } columnPlaced := false for i := 0; i < depth; i++ { gx, gy, gz := groundPos.X, groundPos.Y+direction*i, groundPos.Z if !replaceable[r.getBlock(gx, gy, gz)] { break } if r.setBlock(gx, gy, gz, ground) { placed = true columnPlaced = true } } if columnPlaced { groundPositions = append(groundPositions, [3]int{p.X, p.Y, p.Z}) } } } // Keep the confirmed RNG contract while the nested placed-feature pass is // independently validated against the full fixture. for range groundPositions { if config.VegetationChance > 0 { random.NextFloat() } } return placed } // placeSimpleBlockFeature implements the simple_block feature variants used // by the moss vegetation patches. State-provider selection is weighted and // consumes the same feature RNG that the configured feature receives. func (r *decorationRegion) placeSimpleBlockFeature(random worldgen.RandomSource, position worldgen.FeaturePosition, config worldgen.SimpleBlockFeatureConfig, set *worldgen.FeatureSet) bool { total := 0 for _, entry := range config.States { if entry.Weight > 0 { total += entry.Weight } } if total <= 0 || r.getBlock(position.X, position.Y, position.Z) != StateAir { return false } roll := int(random.NextIntN(int32(total))) chosen := worldgen.BlockState{} for _, entry := range config.States { if entry.Weight <= 0 { continue } if roll < entry.Weight { chosen = entry.State break } roll -= entry.Weight } state, ok := nameToStateID(chosen.Name, chosen.Properties) if !ok || !r.canVegetationSurvive(position, chosen.Name, set) { return false } if chosen.Name == "minecraft:tall_grass" && chosen.Properties["half"] == "lower" { upper, upperOK := nameToStateID(chosen.Name, map[string]string{"half": "upper"}) if !upperOK || r.getBlock(position.X, position.Y+1, position.Z) != StateAir { return false } if !r.setBlock(position.X, position.Y, position.Z, state) { return false } return r.setBlock(position.X, position.Y+1, position.Z, upper) } return r.setBlock(position.X, position.Y, position.Z, state) } func (r *decorationRegion) canVegetationSurvive(position worldgen.FeaturePosition, name string, set *worldgen.FeatureSet) bool { if position.Y <= MinY { return false } below := r.getBlock(position.X, position.Y-1, position.Z) if name == "minecraft:moss_carpet" || name == "minecraft:pale_moss_carpet" { return fullSolidState(below) } for _, supported := range flattenBlockTag(set, "minecraft:supports_vegetation", nil) { if state, ok := stateByID(below); ok && state.Name == supported { return true } } return false }