105 lines
3.3 KiB
Go
105 lines
3.3 KiB
Go
package world
|
|
|
|
import "regionio/internal/worldgen"
|
|
|
|
// placeScheduledDisks replays the vanilla disk features from one source
|
|
// center into the mutable decoration region. Disks are in the underground-ore
|
|
// feature stage (6), after the ore entries in the 26.1.2 overworld datapack.
|
|
func (r *decorationRegion) placeScheduledDisks(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(), undergroundOresStage)
|
|
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 || configured.Type != "minecraft:disk" {
|
|
continue
|
|
}
|
|
config, err := set.Disk(placed.Feature)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
random.SetFeatureSeed(decorationSeed, scheduled.Index, undergroundOresStage)
|
|
context := r.placementContext(func(position worldgen.FeaturePosition) bool {
|
|
return r.biomeAllowsFeature(set, scheduled.Name, undergroundOresStage, position)
|
|
})
|
|
if err := set.ForEachPlacementPosition(scheduled.Name, random, origin, context, func(position worldgen.FeaturePosition) error {
|
|
return r.placeDisk(set, random, position, config)
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *decorationRegion) placeDisk(set *worldgen.FeatureSet, random worldgen.RandomSource, position worldgen.FeaturePosition, config worldgen.DiskFeatureConfig) error {
|
|
radius := config.RadiusMin
|
|
if config.RadiusMax > config.RadiusMin {
|
|
radius += int(random.NextIntN(int32(config.RadiusMax - config.RadiusMin + 1)))
|
|
}
|
|
fallback, ok := nameToStateID(config.Fallback.Name, config.Fallback.Properties)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
ruleStates := make([]uint16, len(config.Rules))
|
|
for i, rule := range config.Rules {
|
|
state, ok := nameToStateID(rule.Then.Name, rule.Then.Properties)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
ruleStates[i] = state
|
|
}
|
|
targets := make(map[uint16]bool, len(config.Targets))
|
|
for _, name := range config.Targets {
|
|
if id, ok := nameToStateID(name, nil); ok {
|
|
targets[id] = true
|
|
}
|
|
}
|
|
if len(targets) == 0 {
|
|
return nil
|
|
}
|
|
// DiskFeature walks BlockPos.betweenClosed: X is the innermost coordinate,
|
|
// then Y, then Z. Each disk column is therefore placed top-down before the
|
|
// next column is visited. This is observable for rule-based providers whose
|
|
// predicate inspects the just-updated block above.
|
|
for dz := -radius; dz <= radius; dz++ {
|
|
for dx := -radius; dx <= radius; dx++ {
|
|
if dx*dx+dz*dz > radius*radius {
|
|
continue
|
|
}
|
|
for y := position.Y + config.HalfHeight; y >= position.Y-config.HalfHeight; y-- {
|
|
x, z := position.X+dx, position.Z+dz
|
|
current := r.getBlock(x, y, z)
|
|
if !targets[current] {
|
|
continue
|
|
}
|
|
placeState := fallback
|
|
for i, rule := range config.Rules {
|
|
matched, err := r.testBlockPredicate(set, rule.IfTrue, worldgen.FeaturePosition{X: x, Y: y, Z: z})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if matched {
|
|
placeState = ruleStates[i]
|
|
break
|
|
}
|
|
}
|
|
r.setBlock(x, y, z, placeState)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|