filename:
common/src/main/java/rearth/oritech/init/world/features/resourcenode/ResourceNodeFeature.java
branch:
1.21
back to repo
package rearth.oritech.init.world.features.resourcenode;
import com.mojang.serialization.Codec;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction.Axis;
import net.minecraft.util.math.noise.PerlinNoiseSampler;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.util.FeatureContext;
import rearth.oritech.Oritech;
import java.util.List;
public class ResourceNodeFeature extends Feature<ResourceNodeFeatureConfig> {
public ResourceNodeFeature(Codec<ResourceNodeFeatureConfig> configCodec) {
super(configCodec);
}
@Override
public boolean generate(FeatureContext<ResourceNodeFeatureConfig> context) {
var world = context.getWorld();
var origin = context.getOrigin();
if (world.isClient()) return false;
var solidBlockFound = false;
var testPos = new BlockPos(origin);
var deepNodePos = testPos;
var boulderPos = testPos;
for (int y = origin.getY(); y > world.getBottomY(); y--) {
var downPos = testPos.down();
var testState = world.getBlockState(downPos);
if (testState.isOf(Blocks.BEDROCK)) {
deepNodePos = testPos;
break;
} else if (testState.isSolidBlock(world, downPos) && !solidBlockFound) {
boulderPos = testPos = downPos;
solidBlockFound = true;
} else {
testPos = downPos;
}
}
// edge case: if no solid block was found, or the boulder is too close to the deep node, don't generate
if (!solidBlockFound || boulderPos.getY() < (deepNodePos.getY() + 10))
return false;
if (Oritech.CONFIG.easyFindFeatures())
placeSurfaceBoulder(boulderPos, context);
placeBedrockNode(deepNodePos, context);
Oritech.LOGGER.debug("placing resource node at " + boulderPos + " with deep " + deepNodePos);
return true;
}
private BlockState getRandomBlockFromList(List<Identifier> list, Random random) {
return Registries.BLOCK.get(getRandomFromList(list, random)).getDefaultState();
}
private Identifier getRandomFromList(List<Identifier> list, Random random) {
return list.get(random.nextInt(list.size()));
}
private void placeBedrockNode(BlockPos startPos, FeatureContext<ResourceNodeFeatureConfig> context) {
var world = context.getWorld();
var random = context.getRandom();
var ores = context.getConfig().nodeOres();
var radius = context.getConfig().nodeSize();
var overlayBlock = Registries.BLOCK.get(context.getConfig().overlayBlock()).getDefaultState();
var overlayHeight = context.getConfig().overlayHeight();
var noise = new PerlinNoiseSampler(random);
// the bottom of the "bowl" should start below the top layer of bedrock
BlockPos centerPos = startPos.up(radius - 2);
for (BlockPos pos : BlockPos.iterateOutwards(centerPos, radius, radius, radius)) {
// skip anything outside the radius, or outside the vertical cutoff
if (Math.sqrt(pos.getSquaredDistance(centerPos)) + noise.sample(pos.getX(), pos.getY(), pos.getZ()) > radius
|| pos.getY() >= startPos.getY() + overlayHeight + 3 + noise.sample(pos.getX(), pos.getY() + 2, pos.getZ())) continue;
// randomly replace some blocks below bedrock level with resource nodes
if (pos.getY() <= startPos.getY() + 1 && random.nextDouble() <= context.getConfig().nodeOreChance()) {
world.setBlockState(pos, getRandomBlockFromList(ores, random), 0x10);
// set blocks between bedrock and bedrock + overlayHeight to overlayBlock
} else if (pos.getY() > startPos.getY() + 1 && pos.getY() <= startPos.getY() + overlayHeight + 1) {
world.setBlockState(pos, overlayBlock, 0x10);
// set anything between overlay and vertical cutoff to air
} else if (pos.getY() > startPos.getY() + 1) {
world.setBlockState(pos, Blocks.AIR.getDefaultState(), 0x10);
}
}
}
private void placeSurfaceBoulder(BlockPos startPos, FeatureContext<ResourceNodeFeatureConfig> context) {
var world = context.getWorld();
var random = context.getRandom();
var radius = context.getConfig().boulderRadius();
var movedCenter = startPos.offset(Axis.pickRandomAxis(random), random.nextBetween(0, radius-1));
var ores = context.getConfig().boulderOres();
var noise = new PerlinNoiseSampler(random);
for (BlockPos pos : BlockPos.iterateOutwards(movedCenter, radius, radius, radius)) {
if (Math.sqrt(pos.getSquaredDistance(movedCenter)) > radius + noise.sample(pos.getX(), pos.getY(), pos.getZ())) continue;
world.setBlockState(pos, getRandomBlockFromList(ores, random), 0x10);
}
}
}