package com.hypixel.hytale.builtin.hytalegenerator.scanners; import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; import com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions.BiDouble2DoubleFunction; import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.math.util.FastRandom; import com.hypixel.hytale.math.vector.Vector3i; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class ColumnRandomScanner extends Scanner { private final int minY; private final int maxY; private final boolean isRelativeToPosition; @Nullable private final BiDouble2DoubleFunction bedFunction; private final int resultsCap; @Nonnull private final SeedGenerator seedGenerator; @Nonnull private final ColumnRandomScanner.Strategy strategy; @Nonnull private final SpaceSize scanSpaceSize; public ColumnRandomScanner( int minY, int maxY, int resultsCap, int seed, @Nonnull ColumnRandomScanner.Strategy strategy, boolean isRelativeToPosition, @Nullable BiDouble2DoubleFunction bedFunction ) { if (resultsCap < 0) { throw new IllegalArgumentException(); } else { this.bedFunction = bedFunction; this.minY = minY; this.maxY = maxY; this.isRelativeToPosition = isRelativeToPosition; this.resultsCap = resultsCap; this.seedGenerator = new SeedGenerator(seed); this.strategy = strategy; this.scanSpaceSize = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(1, 0, 1)); } } @Nonnull @Override public List scan(@Nonnull Scanner.Context context) { return switch (this.strategy) { case DART_THROW -> this.scanDartThrow(context); case PICK_VALID -> this.scanPickValid(context); }; } @Nonnull private List scanPickValid(@Nonnull Scanner.Context context) { if (this.resultsCap == 0) { return Collections.emptyList(); } else { int scanMinY; int scanMaxY; if (this.isRelativeToPosition) { scanMinY = Math.max(context.position.y + this.minY, context.materialSpace.minY()); scanMaxY = Math.min(context.position.y + this.maxY, context.materialSpace.maxY()); } else if (this.bedFunction != null) { int bedY = (int)this.bedFunction.apply(context.position.x, context.position.z); scanMinY = Math.max(bedY + this.minY, context.materialSpace.minY()); scanMaxY = Math.min(bedY + this.maxY, context.materialSpace.maxY()); } else { scanMinY = Math.max(this.minY, context.materialSpace.minY()); scanMaxY = Math.min(this.maxY, context.materialSpace.maxY()); } int numberOfPossiblePositions = Math.max(0, scanMaxY - scanMinY); ArrayList validPositions = new ArrayList<>(numberOfPossiblePositions); Vector3i patternPosition = context.position.clone(); Pattern.Context patternContext = new Pattern.Context(patternPosition, context.materialSpace, context.workerId); for (int y = scanMinY; y < scanMaxY; y++) { patternPosition.y = y; if (context.pattern.matches(patternContext)) { Vector3i position = context.position.clone(); position.setY(y); validPositions.add(position); } } if (validPositions.isEmpty()) { return validPositions; } else if (validPositions.size() <= this.resultsCap) { return validPositions; } else { ArrayList usedIndices = new ArrayList<>(this.resultsCap); ArrayList outPositions = new ArrayList<>(this.resultsCap); FastRandom random = new FastRandom(this.seedGenerator.seedAt((long)context.position.x, (long)context.position.y, (long)context.position.z)); for (int i = 0; i < this.resultsCap; i++) { int pickedIndex = random.nextInt(validPositions.size()); if (!usedIndices.contains(pickedIndex)) { usedIndices.add(pickedIndex); outPositions.add(validPositions.get(pickedIndex)); } } return outPositions; } } } @Nonnull private List scanDartThrow(@Nonnull Scanner.Context context) { if (this.resultsCap == 0) { return Collections.emptyList(); } else { int scanMinY = this.isRelativeToPosition ? Math.max(context.position.y + this.minY, context.materialSpace.minY()) : Math.max(this.minY, context.materialSpace.minY()); int scanMaxY = this.isRelativeToPosition ? Math.min(context.position.y + this.maxY, context.materialSpace.maxY()) : Math.min(this.maxY, context.materialSpace.maxY()); int range = scanMaxY - scanMinY; if (range == 0) { return Collections.emptyList(); } else { int TRY_MULTIPLIER = 1; int numberOfTries = range * 1; ArrayList validPositions = new ArrayList<>(this.resultsCap); FastRandom random = new FastRandom(this.seedGenerator.seedAt((long)context.position.x, (long)context.position.y, (long)context.position.z)); ArrayList usedYs = new ArrayList<>(this.resultsCap); Vector3i patternPosition = context.position.clone(); Pattern.Context patternContext = new Pattern.Context(patternPosition, context.materialSpace, context.workerId); for (int i = 0; i < numberOfTries; i++) { patternPosition.y = random.nextInt(range) + scanMinY; if (context.pattern.matches(patternContext) && !usedYs.contains(patternPosition.y)) { usedYs.add(patternPosition.y); Vector3i position = patternPosition.clone(); validPositions.add(position); if (validPositions.size() == this.resultsCap) { break; } } } return validPositions; } } } @Nonnull @Override public SpaceSize scanSpace() { return this.scanSpaceSize.clone(); } public static enum Strategy { DART_THROW, PICK_VALID; } }