Skip to content

Commit

Permalink
add more ore generator types. (#1104)
Browse files Browse the repository at this point in the history
* add `ClassicVeinGenerator` for the grognards among us

* make VeinGenerator#copy() do deeper copies for safety reasons

* add shortcut in GTOreDefinition for ClassicVeinGenerator

* fix float provider crashing with constant value because of mojank.

* finalize classic generator

* add CuboidVeinGenerator

* finalize cuboid generator

* fix gradual falloff in cuboid vein
  • Loading branch information
screret committed May 2, 2024
1 parent b642edd commit 6ae02f6
Show file tree
Hide file tree
Showing 12 changed files with 628 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,24 @@ public GTOreDefinition veinedVeinGenerator(Consumer<VeinedVeinGenerator> config)
return this;
}

public GTOreDefinition classicVeinGenerator(Consumer<ClassicVeinGenerator> config) {
var veinGenerator = new ClassicVeinGenerator(this);

config.accept(veinGenerator);
this.veinGenerator = veinGenerator;

return this;
}

public GTOreDefinition cuboidVeinGenerator(Consumer<CuboidVeinGenerator> config) {
var veinGenerator = new CuboidVeinGenerator(this);

config.accept(veinGenerator);
this.veinGenerator = veinGenerator;

return this;
}

@Tolerate
@Nullable
public VeinGenerator veinGenerator(ResourceLocation id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import java.util.function.Function;

@SuppressWarnings("unused")
public class VeinGenerators {
public static final Codec<NoopVeinGenerator> NO_OP = register(GTCEu.id("no_op"), NoopVeinGenerator.CODEC, entry -> NoopVeinGenerator.INSTANCE);

Expand All @@ -19,6 +20,8 @@ public class VeinGenerators {
public static final Codec<GeodeVeinGenerator> GEODE = register(GTCEu.id("geode"), GeodeVeinGenerator.CODEC, GeodeVeinGenerator::new);
public static final Codec<DikeVeinGenerator> DIKE = register(GTCEu.id("dike"), DikeVeinGenerator.CODEC, DikeVeinGenerator::new);
public static final Codec<VeinedVeinGenerator> VEINED = register(GTCEu.id("veined"), VeinedVeinGenerator.CODEC, VeinedVeinGenerator::new);
public static final Codec<ClassicVeinGenerator> CLASSIC = register(GTCEu.id("classic"), ClassicVeinGenerator.CODEC, ClassicVeinGenerator::new);
public static final Codec<CuboidVeinGenerator> CUBOID = register(GTCEu.id("cuboid"), CuboidVeinGenerator.CODEC, CuboidVeinGenerator::new);

public static <T extends VeinGenerator> Codec<T> register(ResourceLocation id, Codec<T> codec, Function<GTOreDefinition, ? extends VeinGenerator> function) {
WorldGeneratorUtils.VEIN_GENERATORS.put(id, codec);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public class SurfaceIndicatorGenerator extends IndicatorGenerator {
public static final Codec<SurfaceIndicatorGenerator> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.either(BlockState.CODEC, GTCEuAPI.materialManager.codec()).fieldOf("block").forGetter(ext -> ext.block),
IntProvider.codec(1, 32).fieldOf("radius").forGetter(ext -> ext.radius),
FloatProvider.codec(0.0f, 1.0f).fieldOf("density").forGetter(ext -> ext.density),
StringRepresentable.fromEnum(IndicatorPlacement::values).fieldOf("placement").forGetter(ext -> ext.placement)
FloatProvider.codec(0.0f, 2.0f).fieldOf("density").forGetter(ext -> ext.density),
IndicatorPlacement.CODEC.fieldOf("placement").forGetter(ext -> ext.placement)
).apply(instance, SurfaceIndicatorGenerator::new));

private Either<BlockState, Material> block = Either.left(Blocks.AIR.defaultBlockState());
Expand Down Expand Up @@ -206,6 +206,8 @@ public enum IndicatorPlacement implements StringRepresentable {
block -> getBlockState(block, Direction.UP)
);

public static final Codec<IndicatorPlacement> CODEC = StringRepresentable.fromEnum(IndicatorPlacement::values);

public final TriFunction<WorldGenLevel, BulkSectionAccess, BlockPos, BlockPos> resolver;
public final Function<Either<BlockState, Material>, BlockState> stateTransformer;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
package com.gregtechceu.gtceu.api.data.worldgen.generator.veins;

import com.google.common.base.Preconditions;
import com.gregtechceu.gtceu.api.GTCEuAPI;
import com.gregtechceu.gtceu.api.data.chemical.ChemicalHelper;
import com.gregtechceu.gtceu.api.data.chemical.material.Material;
import com.gregtechceu.gtceu.api.data.worldgen.GTOreDefinition;
import com.gregtechceu.gtceu.api.data.worldgen.generator.VeinGenerator;
import com.gregtechceu.gtceu.api.data.worldgen.ores.OreBlockPlacer;
import com.gregtechceu.gtceu.api.data.worldgen.ores.OreVeinUtil;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.AllArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.BulkSectionAccess;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguration;
import net.minecraft.world.level.levelgen.structure.templatesystem.RuleTest;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

@Accessors(fluent = true, chain = true)
public class ClassicVeinGenerator extends VeinGenerator {
public static final Codec<ClassicVeinGenerator> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Layer.CODEC.fieldOf("primary").forGetter(val -> val.primary),
Layer.CODEC.fieldOf("secondary").forGetter(val -> val.secondary),
Layer.CODEC.fieldOf("between").forGetter(val -> val.between),
Layer.CODEC.fieldOf("sporadic").forGetter(val -> val.sporadic),
ExtraCodecs.POSITIVE_INT.optionalFieldOf("y_radius", 3).forGetter(val -> val.yRadius)
).apply(instance, ClassicVeinGenerator::new));

private Layer primary;
private Layer secondary;
private Layer between;
private Layer sporadic;
private int yRadius = 3;

// Provided for readability
private int sporadicDivisor;
private int startPrimary;
private int startBetween;

@Setter
private RuleTest[] rules;

public ClassicVeinGenerator(GTOreDefinition entry) {
super(entry);
}

public ClassicVeinGenerator(Layer primary, Layer secondary, Layer between, Layer sporadic, int yRadius) {
this.primary = primary;
this.secondary = secondary;
this.between = between;
this.sporadic = sporadic;
this.yRadius = yRadius;
}

@Override
public List<Map.Entry<Either<BlockState, Material>, Integer>> getAllEntries() {
List<Map.Entry<Either<BlockState, Material>, Integer>> result = new ArrayList<>();
int totalWeight = primary.layers + secondary.layers + between.layers;
primary.target
.map(blockStates -> blockStates.stream().map(state -> Either.<BlockState, Material>left(state.state)),
material -> Stream.of(Either.<BlockState, Material>right(material)))
.forEach(entry -> result.add(Map.entry(entry, primary.layers / totalWeight)));
secondary.target
.map(blockStates -> blockStates.stream().map(state -> Either.<BlockState, Material>left(state.state)),
material -> Stream.of(Either.<BlockState, Material>right(material)))
.forEach(entry -> result.add(Map.entry(entry, secondary.layers / totalWeight)));
between.target
.map(blockStates -> blockStates.stream().map(state -> Either.<BlockState, Material>left(state.state)),
material -> Stream.of(Either.<BlockState, Material>right(material)))
.forEach(entry -> result.add(Map.entry(entry, between.layers / totalWeight)));
sporadic.target
.map(blockStates -> blockStates.stream().map(state -> Either.<BlockState, Material>left(state.state)),
material -> Stream.of(Either.<BlockState, Material>right(material)))
.forEach(entry -> result.add(Map.entry(entry, 1)));
return result;
}

@Override
public Map<BlockPos, OreBlockPlacer> generate(WorldGenLevel level, RandomSource random, GTOreDefinition entry, BlockPos origin) {
Map<BlockPos, OreBlockPlacer> generatedBlocks = new Object2ObjectOpenHashMap<>();

int radius = entry.clusterSize();
int ySize = radius / 2;
int xy2 = radius * radius * ySize * ySize;
int xz2 = radius * radius * radius * radius;
int yz2 = ySize * ySize * radius * radius;
int xyz2 = xy2 * radius * radius;

int xPos = origin.getX();
int yPos = origin.getY();
int zPos = origin.getZ();

int max = Math.max(ySize, radius);
int yMax = Math.min(max, yRadius);
BlockPos minPos = new BlockPos(xPos - max, yPos - yMax, zPos - max);

for (int xOffset = -max; xOffset <= max; xOffset++) {
int xr = yz2 * xOffset * xOffset;
if (xr > xyz2) continue;
for (int yOffset = -yMax; yOffset <= yMax; yOffset++) {
int yr = xr + xz2 * yOffset * yOffset + xy2;
if (yr > xyz2) continue;
if (level.isOutsideBuildHeight(yOffset + yPos))
continue;
for (int zOffset = -max; zOffset <= max; zOffset++) {
int zr = yr + xy2 * zOffset * zOffset;
if (zr > xyz2) continue;

final var randomSeed = random.nextLong(); // Fully deterministic regardless of chunk order
BlockPos currentPos = new BlockPos(xOffset + xPos, yOffset + yPos, zOffset + zPos);
generatedBlocks.put(currentPos, (access, section) ->
placeBlock(access, section, randomSeed, entry, currentPos, minPos)
);
}
}
}
return generatedBlocks;
}

private void placeBlock(BulkSectionAccess access, LevelChunkSection section, long randomSeed,
GTOreDefinition entry,
BlockPos blockPos, BlockPos lowestPos) {
RandomSource random = new XoroshiroRandomSource(randomSeed);
int x = SectionPos.sectionRelative(blockPos.getX());
int y = SectionPos.sectionRelative(blockPos.getY());
int z = SectionPos.sectionRelative(blockPos.getZ());

BlockState blockState = section.getBlockState(x, y, z);
int layer = blockPos.getY() - lowestPos.getY();

// First try to spawn "between"
if (layer >= startBetween && layer - startBetween + 1 <= between.layers) {
if (random.nextFloat() <= entry.density() / 2) {
between.place(blockState, access, section, randomSeed, entry, blockPos);
return;
}
}

// Then try primary/secondary
if (layer >= startPrimary) {
if (random.nextFloat() <= entry.density()) {
primary.place(blockState, access, section, randomSeed, entry, blockPos);
return;
}
} else {
if (random.nextFloat() <= entry.density()) {
secondary.place(blockState, access, section, randomSeed, entry, blockPos);
return;
}
}

// Then lastly, try sporadic
if (random.nextFloat() <= entry.density() / sporadicDivisor) {
sporadic.place(blockState, access, section, randomSeed, entry, blockPos);
}
}

@Override
public VeinGenerator build() {
primary.layers = primary.layers == -1 ? 4 : primary.layers;
secondary.layers = secondary.layers == -1 ? 3 : secondary.layers;
between.layers = between.layers == -1 ? 3 : between.layers;

// Ensure "between" is not more than the total primary and secondary layers
Preconditions.checkArgument(primary.layers + secondary.layers >= between.layers,
"Error: cannot be more \"between\" layers than primary and secondary layers combined!");

this.sporadicDivisor = primary.layers + secondary.layers - 1;
this.startPrimary = secondary.layers;
this.startBetween = secondary.layers - between.layers / 2;
return this;
}

@Override
public VeinGenerator copy() {
return new ClassicVeinGenerator(this.primary.copy(), this.secondary.copy(), this.between.copy(), this.sporadic.copy(), this.yRadius);
}

@Override
public Codec<? extends VeinGenerator> codec() {
return CODEC;
}

public ClassicVeinGenerator primary(Consumer<Layer.Builder> builder) {
Layer.Builder layerBuilder = new Layer.Builder(rules);
builder.accept(layerBuilder);
primary = layerBuilder.build();
return this;
}

public ClassicVeinGenerator secondary(Consumer<Layer.Builder> builder) {
Layer.Builder layerBuilder = new Layer.Builder(rules);
builder.accept(layerBuilder);
secondary = layerBuilder.build();
return this;
}

public ClassicVeinGenerator between(Consumer<Layer.Builder> builder) {
Layer.Builder layerBuilder = new Layer.Builder(rules);
builder.accept(layerBuilder);
between = layerBuilder.build();
return this;
}

public ClassicVeinGenerator sporadic(Consumer<Layer.Builder> builder) {
Layer.Builder layerBuilder = new Layer.Builder(rules);
builder.accept(layerBuilder);
sporadic = layerBuilder.build();
return this;
}

@AllArgsConstructor
public static class Layer {
public static final Codec<Layer> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.either(OreConfiguration.TargetBlockState.CODEC.listOf(), GTCEuAPI.materialManager.codec()).fieldOf("targets").forGetter(layer -> layer.target),
ExtraCodecs.intRange(-1, Integer.MAX_VALUE).optionalFieldOf("layers", -1).forGetter(layer -> layer.layers)
).apply(instance, Layer::new));

public final Either<List<OreConfiguration.TargetBlockState>, Material> target;
public int layers;

public void place(BlockState blockState, BulkSectionAccess access, LevelChunkSection section, long randomSeed, GTOreDefinition entry, BlockPos pos) {
RandomSource random = new XoroshiroRandomSource(randomSeed);
int x = SectionPos.sectionRelative(pos.getX());
int y = SectionPos.sectionRelative(pos.getY());
int z = SectionPos.sectionRelative(pos.getZ());

target.ifLeft(blockStates -> {
for (OreConfiguration.TargetBlockState targetState : blockStates) {
if (!OreVeinUtil.canPlaceOre(blockState, access::getBlockState, random, entry, targetState, pos))
continue;
if (targetState.state.isAir())
continue;
section.setBlockState(x, y, z, targetState.state, false);
break;
}
}).ifRight(material -> {
if (!OreVeinUtil.canPlaceOre(blockState, access::getBlockState, random, entry, pos))
return;
BlockState currentState = access.getBlockState(pos);
var prefix = ChemicalHelper.getOrePrefix(currentState);
if (prefix.isEmpty()) return;
Block toPlace = ChemicalHelper.getBlock(prefix.get(), material);
if (toPlace == null || toPlace.defaultBlockState().isAir())
return;
section.setBlockState(x, y, z, toPlace.defaultBlockState(), false);
});
}

public Layer copy() {
return new Layer(this.target.mapBoth(ArrayList::new, Function.identity()), layers);
}

public static class Builder {
private Either<List<OreConfiguration.TargetBlockState>, Material> target;
private int size = 1;
private final RuleTest[] rules;

protected Builder(RuleTest... rules) {
this.rules = rules;
}

public Layer.Builder block(Supplier<? extends Block> block) {
return state(block.get().defaultBlockState());
}

public Layer.Builder state(Supplier<? extends BlockState> state) {
return state(state.get());
}

public Layer.Builder state(BlockState state) {
this.target = Either.left(Arrays.stream(this.rules).map(rule -> OreConfiguration.target(rule, state)).toList());
return this;
}

public Layer.Builder mat(Material material) {
this.target = Either.right(material);
return this;
}

public Layer.Builder size(int size) {
this.size = size;
return this;
}

public Layer build() {
return new Layer(target, size);
}
}
}
}
Loading

0 comments on commit 6ae02f6

Please sign in to comment.