filename:
common/src/main/java/rearth/oritech/init/world/features/uranium/UraniumPatchFeature.java
branch:
1.21
back to repo
package rearth.oritech.init.world.features.uranium;
import com.mojang.serialization.Codec;
import net.minecraft.block.AmethystClusterBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.registry.Registries;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3i;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.StructureWorldAccess;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.util.FeatureContext;
import org.joml.Vector2d;
import rearth.oritech.init.BlockContent;
import rearth.oritech.util.Geometry;
import java.util.List;
public class UraniumPatchFeature extends Feature<UraniumPatchFeatureConfig> {
public UraniumPatchFeature(Codec<UraniumPatchFeatureConfig> configCodec) {
super(configCodec);
}
private static boolean isAirOrWater(BlockState state) {
return state.isAir() || state.isOf(Blocks.WATER);
}
@Override
public boolean generate(FeatureContext<UraniumPatchFeatureConfig> context) {
var world = context.getWorld();
var origin = context.getOrigin();
if (world.isClient()) return false;
var testPos = new BlockPos(origin.down(3));
if (isAirOrWater(world.getBlockState(testPos)))
placeStructure(testPos, context);
return false;
}
private void placeStructure(BlockPos pos, FeatureContext<UraniumPatchFeatureConfig> context) {
var random = context.getRandom();
var config = context.getConfig();
var state = Registries.BLOCK.get(config.blockId()).getDefaultState();
var world = context.getWorld();
var range = config.number();
var closestWall = pos;
// find closest wall
for (var candidate : BlockPos.iterateOutwards(pos, range, range, range)) {
var candidateState = world.getBlockState(candidate);
if (isAirOrWater(candidateState)) continue;
closestWall = candidate;
break;
}
if (closestWall.equals(pos)) return;
var closestWallDir = closestWall.subtract(pos);
var forward = getBiggestDirection(closestWallDir);
var facing = Direction.fromVector(forward.getX(), forward.getY(), forward.getZ());
if (facing == null) return;
var right = Geometry.getRight(facing);
var up = Geometry.getUp(facing);
var veinCount = 3;
for (int i = 0; i < veinCount; i++) {
var randomDir = new Vector2d(random.nextFloat() * 2 - 1, random.nextFloat() * 2 - 1).normalize();
var veinLength = random.nextBetween(5, 9);
// move along vein
for (int j = 0; j < veinLength; j++) {
var test = pos.add(right.multiply((int) (randomDir.x * j))).add(up.multiply((int) (randomDir.y * j)));
var test2 = pos.add(right.multiply((int) (randomDir.x * j + 0.5))).add(up.multiply((int) (randomDir.y * j + 0.5)));
// project onto first non-air block in forward direction
for (int k = 0; k < 5; k++) {
var projected = test.add(forward.multiply(k));
var projected2 = test2.add(forward.multiply(k));
var testState = world.getBlockState(projected);
var testState2 = world.getBlockState(projected2);
if (isValidReplacementBloc(testState)) {
createCrystals(projected, world, random);
world.setBlockState(projected, state, Block.NOTIFY_LISTENERS, 0);
break;
}
if (isValidReplacementBloc(testState2)) {
world.setBlockState(projected2, state, Block.NOTIFY_LISTENERS, 0);
break;
}
}
randomDir = randomDir.add(random.nextFloat() * 0.2, random.nextFloat() * 0.2).normalize();
}
}
}
private boolean isValidReplacementBloc(BlockState state) {
return state.isIn(BlockTags.DEEPSLATE_ORE_REPLACEABLES);
}
private void createCrystals(BlockPos pos, StructureWorldAccess world, Random random) {
for (var neighborPos : getNeighbors(pos)) {
var neighborState = world.getBlockState(neighborPos);
var isValid = neighborState.isAir() || neighborState.isOf(Blocks.WATER);
if (!isValid || random.nextFloat() < 0.7) continue;
var waterLogged = neighborState.isOf(Blocks.WATER);
var facing = Geometry.fromVector(neighborPos.subtract(pos));
if (facing == null) continue;
var targetState = BlockContent.URANIUM_CRYSTAL.getDefaultState()
.with(AmethystClusterBlock.WATERLOGGED, waterLogged)
.with(AmethystClusterBlock.FACING, facing);
world.setBlockState(neighborPos, targetState, Block.NOTIFY_LISTENERS, 0);
}
}
private List<BlockPos> getNeighbors(BlockPos pos) {
return List.of(pos.down(), pos.up(), pos.north(), pos.east(), pos.south(), pos.west());
}
private Vec3i getBiggestDirection(Vec3i source) {
var x = Math.abs(source.getX());
var y = Math.abs(source.getY());
var z = Math.abs(source.getZ());
if (x > y && x > z) {
return new Vec3i(Math.clamp(source.getX(), -1, 1), 0, 0);
} else if (y > x && y > z) {
return new Vec3i(0, Math.clamp(source.getY(), -1, 1), 0);
} else {
return new Vec3i(0, 0, Math.clamp(source.getZ(), -1, 1));
}
}
}