Initial release
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package de.nicolasklier.custom_structures;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.nicolasklier.custom_structures.events.PlayerTick;
|
||||
import de.nicolasklier.custom_structures.items.StructureSpawner;
|
||||
|
||||
public class CustomStructures implements ModInitializer {
|
||||
// This logger is used to write text to the console and the log file.
|
||||
// It is considered best practice to use your mod id as the logger's name.
|
||||
// That way, it's clear which mod wrote info, warnings, and errors.
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger("customstructure");
|
||||
|
||||
public static final Item STRUCTURE_SPAWNER = new StructureSpawner(new FabricItemSettings().group(ItemGroup.MISC)
|
||||
.maxCount(1));
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
// This code runs as soon as Minecraft is in a mod-load-ready state.
|
||||
// However, some things (like resources) may still be uninitialized.
|
||||
// Proceed with mild caution.
|
||||
|
||||
Registry.register(Registry.ITEM, new Identifier("customstructures", "structure_spawner"), STRUCTURE_SPAWNER);
|
||||
|
||||
new PlayerTick();
|
||||
}
|
||||
}
|
||||
84
src/main/java/de/nicolasklier/custom_structures/Helper.java
Normal file
84
src/main/java/de/nicolasklier/custom_structures/Helper.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package de.nicolasklier.custom_structures;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.decoration.ItemFrameEntity;
|
||||
import net.minecraft.entity.projectile.ProjectileUtil;
|
||||
import net.minecraft.util.hit.EntityHitResult;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
import net.minecraft.util.math.Box;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.RaycastContext;
|
||||
|
||||
public class Helper {
|
||||
public static HitResult raycastInDirection(MinecraftClient client, float tickDelta, Vec3d direction) {
|
||||
Entity entity = client.getCameraEntity();
|
||||
if (entity == null || client.world == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double reachDistance = 100f; // Custom reach size of 100 blocks.
|
||||
HitResult target = raycast(entity, reachDistance, tickDelta, false, direction);
|
||||
double extendedReach = reachDistance;
|
||||
|
||||
Vec3d cameraPos = entity.getCameraPosVec(tickDelta);
|
||||
|
||||
extendedReach = extendedReach * extendedReach;
|
||||
if (target != null) {
|
||||
extendedReach = target.getPos().squaredDistanceTo(cameraPos);
|
||||
}
|
||||
|
||||
Vec3d vec3d3 = cameraPos.add(direction.multiply(reachDistance));
|
||||
Box box = entity
|
||||
.getBoundingBox()
|
||||
.stretch(entity.getRotationVec(1.0F).multiply(reachDistance))
|
||||
.expand(1.0D, 1.0D, 1.0D);
|
||||
|
||||
EntityHitResult entityHitResult = ProjectileUtil.raycast(
|
||||
entity,
|
||||
cameraPos,
|
||||
vec3d3,
|
||||
box,
|
||||
(entityx) -> !entityx.isSpectator(),
|
||||
extendedReach
|
||||
);
|
||||
|
||||
if (entityHitResult == null) {
|
||||
return target;
|
||||
}
|
||||
|
||||
Entity entity2 = entityHitResult.getEntity();
|
||||
Vec3d vec3d4 = entityHitResult.getPos();
|
||||
double g = cameraPos.squaredDistanceTo(vec3d4);
|
||||
|
||||
if (g > reachDistance) {
|
||||
return null;
|
||||
} else if (g < extendedReach || target == null) {
|
||||
target = entityHitResult;
|
||||
if (entity2 instanceof LivingEntity || entity2 instanceof ItemFrameEntity) {
|
||||
client.targetedEntity = entity2;
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
private static HitResult raycast(
|
||||
Entity entity,
|
||||
double maxDistance,
|
||||
float tickDelta,
|
||||
boolean includeFluids,
|
||||
Vec3d direction
|
||||
) {
|
||||
Vec3d end = entity.getCameraPosVec(tickDelta).add(direction.multiply(maxDistance));
|
||||
return entity.world.raycast(new RaycastContext(
|
||||
entity.getCameraPosVec(tickDelta),
|
||||
end,
|
||||
RaycastContext.ShapeType.OUTLINE,
|
||||
includeFluids ? RaycastContext.FluidHandling.ANY : RaycastContext.FluidHandling.NONE,
|
||||
entity
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package de.nicolasklier.custom_structures.events;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import de.nicolasklier.custom_structures.CustomStructures;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public class PlayerTick {
|
||||
|
||||
public static HashMap<BlockPos, BlockState> queue = new HashMap<>();
|
||||
public static Iterator<BlockPos> queueKeys;
|
||||
|
||||
/**
|
||||
* How many ticks to wait until the next block will be placed.
|
||||
*/
|
||||
public static int placeDelay = 3;
|
||||
private static int tickCount = 0;
|
||||
|
||||
public PlayerTick() {
|
||||
registerClientEndTick();
|
||||
|
||||
CustomStructures.LOGGER.info("Client end tick event registered.");
|
||||
}
|
||||
|
||||
private void registerClientEndTick() {
|
||||
ClientTickEvents.END_CLIENT_TICK.register((instance) -> {
|
||||
if (instance.world == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tickCount < placeDelay) {
|
||||
tickCount++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tickCount = 0;
|
||||
|
||||
if (queue.isEmpty())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
if (queueKeys == null || !queueKeys.hasNext())
|
||||
return;
|
||||
|
||||
BlockPos pos = queueKeys.next();
|
||||
BlockState state = queue.get(pos);
|
||||
|
||||
if (state == null)
|
||||
return;
|
||||
|
||||
//CustomStructures.LOGGER.info("Build at " + pos.toShortString() + "\t Q:" + queue.size());
|
||||
|
||||
instance.getServer().getOverworld().setBlockState(pos, state);
|
||||
instance.world.addBlockBreakParticles(pos, state);
|
||||
instance.world.playSound(pos, SoundEvents.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1f, 1f, true);
|
||||
|
||||
queue.remove(pos);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package de.nicolasklier.custom_structures.items;
|
||||
|
||||
import de.nicolasklier.custom_structures.Helper;
|
||||
import de.nicolasklier.custom_structures.structures.Generations;
|
||||
import de.nicolasklier.custom_structures.structures.StaircaseNoiseOptions;
|
||||
import de.nicolasklier.custom_structures.structures.StaircaseOptions;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
import net.minecraft.util.hit.HitResult.Type;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class StructureSpawner extends Item {
|
||||
|
||||
public StructureSpawner(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
HitResult hit = Helper.raycastInDirection(client, client.getTickDelta(), client.cameraEntity.getRotationVec(client.getTickDelta()));
|
||||
|
||||
Generations gen = new Generations();
|
||||
|
||||
if (hit.getType() == Type.BLOCK) {
|
||||
BlockHitResult result = (BlockHitResult) hit;
|
||||
client.player.sendChatMessage("Coords: " + result.getBlockPos().toShortString(), null);
|
||||
|
||||
// Spawn structure at raycast hit
|
||||
StaircaseOptions options = new StaircaseOptions();
|
||||
options.stretch = 2;
|
||||
options.height = 10;
|
||||
options.noise = new StaircaseNoiseOptions();
|
||||
options.mirror = true;
|
||||
|
||||
gen.spawnStaircase(result.getBlockPos().add(0, 1, 0), options);
|
||||
}
|
||||
|
||||
return TypedActionResult.success(user.getStackInHand(hand));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package de.nicolasklier.custom_structures.mixin;
|
||||
|
||||
import net.minecraft.client.gui.screen.TitleScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import de.nicolasklier.custom_structures.CustomStructures;
|
||||
|
||||
@Mixin(TitleScreen.class)
|
||||
public class ExampleMixin {
|
||||
@Inject(at = @At("HEAD"), method = "init()V")
|
||||
private void init(CallbackInfo info) {
|
||||
CustomStructures.LOGGER.info("This line is printed by an example mod mixin!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package de.nicolasklier.custom_structures.mixin;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
@Mixin(PlayerEntity.class)
|
||||
public class PlayerTickMixin {
|
||||
|
||||
private static HashMap<BlockPos, BlockState> queue = new HashMap<>();
|
||||
|
||||
/**
|
||||
* How many ticks to wait until the next block will be placed.
|
||||
*/
|
||||
private static int placeDelay = 2;
|
||||
private static int tickCount = 0;
|
||||
|
||||
@Inject(method = "tick", at = @At("RETURN"))
|
||||
private void tick(CallbackInfo info) {
|
||||
if (tickCount < placeDelay) {
|
||||
tickCount++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tickCount = 0;
|
||||
|
||||
MinecraftClient instance = MinecraftClient.getInstance();
|
||||
|
||||
if (queue.isEmpty())
|
||||
return;
|
||||
|
||||
BlockPos pos = (BlockPos) queue.keySet().toArray()[0];
|
||||
BlockState state = queue.get(pos);
|
||||
queue.remove(pos);
|
||||
|
||||
System.out.println("Build at " + pos.toShortString());
|
||||
|
||||
instance.world.setBlockState(pos, state);
|
||||
instance.world.addBlockBreakParticles(pos, state);
|
||||
instance.world.playSound(pos, SoundEvents.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1f, 1f, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package de.nicolasklier.custom_structures.mixin;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import de.nicolasklier.custom_structures.worldgen.StairChunkGenerator;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.BuiltinRegistries;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryEntry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.dimension.DimensionOptions;
|
||||
import net.minecraft.world.dimension.DimensionTypes;
|
||||
import net.minecraft.world.gen.WorldPreset;
|
||||
import net.minecraft.world.gen.WorldPresets;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.gen.chunk.FlatChunkGenerator;
|
||||
import net.minecraft.world.gen.chunk.FlatChunkGeneratorConfig;
|
||||
|
||||
@Mixin(WorldPresets.Registrar.class)
|
||||
public abstract class WorldPresetMixin {
|
||||
// defining our registry key. this key provides an Identifier for our preset, that we can use for our lang files and data elements.
|
||||
private static final RegistryKey<WorldPreset> VOID_WORLD = RegistryKey.of(Registry.WORLD_PRESET_KEY, new Identifier("customstructures", "stair_world"));
|
||||
@Shadow protected abstract RegistryEntry<WorldPreset> register(RegistryKey<WorldPreset> key, DimensionOptions dimensionOptions);
|
||||
@Shadow protected abstract DimensionOptions createOverworldOptions(ChunkGenerator chunkGenerator);
|
||||
|
||||
/*private static final DimensionOptions OVERWORLD_OPTIONS = new DimensionOptions(
|
||||
BuiltinRegistries.DIMENSION_TYPE.getOrCreateEntry(DimensionTypes.OVERWORLD),
|
||||
StairChunkGenerator.forOverworld()
|
||||
);*/
|
||||
|
||||
@Inject(method = "initAndGetDefault", at = @At("RETURN"))
|
||||
private void addPresets(CallbackInfoReturnable<RegistryEntry<WorldPreset>> cir) {
|
||||
// the register() method is shadowed from the target class
|
||||
/*this.register(VOID_WORLD, this.createOverworldOptions(
|
||||
// a FlatChunkGenerator is the easiest way to get a void world, but you can replace this FlatChunkGenerator constructor with a NoiseChunkGenerator, or your own custom ChunkGenerator.
|
||||
new FlatChunkGenerator(
|
||||
// passing null will use the default structure set
|
||||
null,
|
||||
new FlatChunkGeneratorConfig(
|
||||
// we don't need to overwrite the structure set
|
||||
Optional.empty(),
|
||||
BuiltinRegistries.BIOME)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
BuiltinRegistries.add(BuiltinRegistries.WORLD_PRESET, new Identifier("customstructures", "stair_world"), new WorldPreset(
|
||||
Map.of(
|
||||
DimensionOptions.OVERWORLD, OVERWORLD_OPTIONS
|
||||
))
|
||||
);*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package de.nicolasklier.custom_structures.structures;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import de.nicolasklier.custom_structures.CustomStructures;
|
||||
import de.nicolasklier.custom_structures.events.PlayerTick;
|
||||
import de.nicolasklier.custom_structures.utils.PerlinNoise;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.Material;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Direction.Axis;
|
||||
|
||||
public class Generations {
|
||||
|
||||
List<Block> fence_whitelist = new ArrayList<>();
|
||||
|
||||
public Generations() {
|
||||
fence_whitelist.add(Blocks.OAK_FENCE);
|
||||
fence_whitelist.add(Blocks.BIRCH_FENCE);
|
||||
fence_whitelist.add(Blocks.SPRUCE_FENCE);
|
||||
fence_whitelist.add(Blocks.JUNGLE_FENCE);
|
||||
}
|
||||
|
||||
public void spawnStaircase(BlockPos at, StaircaseOptions options) {
|
||||
Map<BlockPos, BlockState> placeQueue = new HashMap<>();
|
||||
MinecraftClient instance = MinecraftClient.getInstance();
|
||||
int length = options.height * options.stretch * (options.mirror ? 2 : 1);
|
||||
|
||||
Random rng = new Random();
|
||||
PerlinNoise noise = null;
|
||||
|
||||
int y = -(options.weight);
|
||||
int step = 0;
|
||||
|
||||
if (options.noise != null) {
|
||||
noise = new PerlinNoise(rng, options.noise.roughness, length, length);
|
||||
noise.initialise();
|
||||
}
|
||||
|
||||
// Final length is height times stretch factor. If mirroring is enabled, then it will be twice as long.
|
||||
int x = 0;
|
||||
boolean isMirroring = y > options.height * options.stretch;
|
||||
|
||||
if (step >= options.stretch) {
|
||||
step = 0;
|
||||
|
||||
// Check if we already reached our final height. If yes, go downwards
|
||||
if (isMirroring) {
|
||||
y -= 1;
|
||||
} else {
|
||||
y++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int z = 0; z < options.width; z++) {
|
||||
boolean isNextHeight = true;
|
||||
|
||||
if (options.stretch > 1) {
|
||||
if (isMirroring) {
|
||||
isNextHeight = step == 0;
|
||||
} else {
|
||||
isNextHeight = step + 1 == options.stretch;
|
||||
}
|
||||
}
|
||||
|
||||
Block block = Blocks.COBBLESTONE;
|
||||
|
||||
// Build supporting fence
|
||||
if (x % options.fenceVerticalDensity == 0 && y > 0) {
|
||||
// From left to right
|
||||
for (int z2 = 0; z2 <= options.width; z2 += (options.width - 1) / options.fenceHorizontalDensity) {
|
||||
// Place fence manually on ground because the loop below offsets by one.
|
||||
placeQueue.put(at.add(x + 1, 0, z2), getRandomFance(rng));
|
||||
|
||||
int y2 = y - options.weight;
|
||||
while (true) {
|
||||
Material mat = instance.getServer().getOverworld().getBlockState(new BlockPos(x, y2, z2)).getMaterial();
|
||||
if (mat == Material.AIR || mat == Material.WATER) {
|
||||
placeQueue.put(at.add(x, y2, z2), getRandomFance(rng));
|
||||
placeQueue.put(at.add(x + 1, y2 + 1, z2), getRandomFance(rng));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
y2--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean putStair = false;
|
||||
|
||||
// Decide if there should be mossy cobblestone or glass. If yes, then don't put a stair there.
|
||||
if (rng.nextFloat() < options.chanceMossyCobblestone / 100f) {
|
||||
block = Blocks.MOSSY_COBBLESTONE;
|
||||
} else if (rng.nextFloat() < options.chanceGlass / 100f) {
|
||||
block = Blocks.GLASS;
|
||||
} else {
|
||||
putStair = true;
|
||||
}
|
||||
|
||||
// Build actual block
|
||||
for (int j = 0; j < options.weight; j++) {
|
||||
// Building imperfection
|
||||
if (rng.nextFloat() < 0.001) continue;
|
||||
|
||||
placeQueue.put(at.add(x, y + j, z), block.getDefaultState());
|
||||
}
|
||||
|
||||
Direction facing = isMirroring ? Direction.WEST : Direction.EAST;
|
||||
|
||||
// Build stair or not
|
||||
if (putStair && isNextHeight) {
|
||||
BlockState stairState = Blocks.COBBLESTONE_STAIRS.getDefaultState().with(Properties.HORIZONTAL_FACING, facing);
|
||||
|
||||
if (isMirroring)
|
||||
stairState = stairState.with(Properties.HORIZONTAL_FACING, facing);
|
||||
|
||||
if (rng.nextFloat() < options.chanceSlap / 100f) {
|
||||
placeQueue.put(at.add(x, y + options.weight, z), Blocks.SMOOTH_STONE_SLAB.getDefaultState());
|
||||
} else {
|
||||
CustomStructures.LOGGER.info("Noise [" + x + ", " + y + "]: " + noise.getValueAt(x, y + options.weight));
|
||||
if (noise.getValueAt(x, y + options.weight) < options.chanceWoodStair / 100f) {
|
||||
|
||||
// Override stair as wood stair, copying the facing.
|
||||
stairState = Blocks.OAK_STAIRS.getDefaultState()
|
||||
.with(Properties.HORIZONTAL_FACING, stairState.get(Properties.HORIZONTAL_FACING));
|
||||
} else if (rng.nextFloat() > options.chanceMossyCobblestone / 100f) {
|
||||
stairState = Blocks.MOSSY_COBBLESTONE_STAIRS.getDefaultState()
|
||||
.with(Properties.HORIZONTAL_FACING, stairState.get(Properties.HORIZONTAL_FACING));
|
||||
}
|
||||
|
||||
placeQueue.put(at.add(x, y + options.weight, z), stairState);
|
||||
}
|
||||
} else {
|
||||
if (rng.nextFloat() < 0.04) {
|
||||
placeQueue.put(at.add(x, y + options.weight - 1, z), Blocks.GLOWSTONE.getDefaultState());
|
||||
}
|
||||
}
|
||||
|
||||
// Build wall
|
||||
if ((z == 0 || z == options.width - 1) && options.wallHeight > 0) {
|
||||
for (int y2 = 0; y2 < options.wallHeight; y2++) {
|
||||
placeQueue.put(at.add(x, y + y2 + options.weight, z), Blocks.OAK_PLANKS.getDefaultState());
|
||||
}
|
||||
|
||||
placeQueue.put(at.add(x, y + options.wallHeight + options.weight, z), Blocks.OAK_FENCE.getDefaultState());
|
||||
placeQueue.put(at.add(x, y + options.wallHeight + options.weight + 1, z), Blocks.OAK_FENCE.getDefaultState());
|
||||
placeQueue.put(at.add(x, y + options.wallHeight + options.weight + 2, z), Blocks.OAK_PLANKS.getDefaultState());
|
||||
|
||||
if (isNextHeight) {
|
||||
placeQueue.put(at.add(x, y + options.wallHeight + options.weight + 3, z), Blocks.OAK_STAIRS.getDefaultState()
|
||||
.with(Properties.HORIZONTAL_FACING, facing));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
step++;
|
||||
}
|
||||
|
||||
PlayerTick.queue.putAll(placeQueue);
|
||||
|
||||
Set<BlockPos> set = placeQueue.keySet();
|
||||
List<BlockPos> list = new ArrayList<>(set);
|
||||
//Collections.shuffle(list);
|
||||
PlayerTick.queueKeys = list.iterator();
|
||||
}
|
||||
|
||||
|
||||
private BlockState getRandomFance(Random rng) {
|
||||
return fence_whitelist.get(rng.nextInt(0, fence_whitelist.size() - 1)).getDefaultState();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.nicolasklier.custom_structures.structures;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class StaircaseNoiseOptions {
|
||||
|
||||
public float roughness = 0.1f;
|
||||
|
||||
public Random rng = new Random();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package de.nicolasklier.custom_structures.structures;
|
||||
|
||||
public class StaircaseOptions {
|
||||
/**
|
||||
* Width of the staircase on the Z-axis.
|
||||
*/
|
||||
public int width = 21;
|
||||
|
||||
/**
|
||||
* Thickness of the staircase.
|
||||
*/
|
||||
public int weight = 3;
|
||||
|
||||
/**
|
||||
* Height of the staircase on the Y-axis.
|
||||
*/
|
||||
public int height = 32;
|
||||
|
||||
/**
|
||||
* Padding between the support fences vertically (XY-plane).
|
||||
*/
|
||||
public int fenceVerticalDensity = 10;
|
||||
|
||||
/**
|
||||
* Padding between the support fences horizontally (ZY-plane).
|
||||
*/
|
||||
public int fenceHorizontalDensity = 3;
|
||||
|
||||
/**
|
||||
* Height of the wall left & right. Set to 0 to disable this feature.
|
||||
*/
|
||||
public int wallHeight = 2;
|
||||
|
||||
/**
|
||||
* Scratch the staircase along the X-axis.
|
||||
*/
|
||||
public int stretch = 4;
|
||||
|
||||
/**
|
||||
* Mirror staircase when height is reached. This will double your length.
|
||||
*/
|
||||
public boolean mirror = false;
|
||||
|
||||
/**
|
||||
* Value between 0% and 100% being the chance to spawn glass.
|
||||
*/
|
||||
public short chanceGlass = 10;
|
||||
|
||||
/**
|
||||
* Value between 0% and 100% being the chance to spawn glowstone.
|
||||
*/
|
||||
public short chanceGlowstone = 4;
|
||||
|
||||
/**
|
||||
* Value between 0% and 100% being the chance to spawn mossy cobblestone.
|
||||
*/
|
||||
public short chanceMossyCobblestone = 20;
|
||||
|
||||
/**
|
||||
* Value between 0% and 100% being the chance to spawn a slap instead of an stair.
|
||||
*/
|
||||
public short chanceSlap = 5;
|
||||
|
||||
/**
|
||||
* Value between 0% and 100% being the chance to spawn a wooden stair instead of an cobblestone stair.
|
||||
*/
|
||||
public short chanceWoodStair = 50;
|
||||
|
||||
/**
|
||||
* Adds variation to the stretch of the staircase. This will override `stretch`.
|
||||
* To disable this feature set it to null.
|
||||
*/
|
||||
public StaircaseNoiseOptions noise = null;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package de.nicolasklier.custom_structures.utils;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class PerlinNoise {
|
||||
/** Source of entropy */
|
||||
private Random rand_;
|
||||
|
||||
/** Amount of roughness */
|
||||
float roughness_;
|
||||
|
||||
/** Plasma fractal grid */
|
||||
private float[][] grid_;
|
||||
|
||||
|
||||
/** Generate a noise source based upon the midpoint displacement fractal.
|
||||
*
|
||||
* @param rand The random number generator
|
||||
* @param roughness a roughness parameter
|
||||
* @param width the width of the grid
|
||||
* @param height the height of the grid
|
||||
*/
|
||||
public PerlinNoise(Random rand, float roughness, int width, int height) {
|
||||
roughness_ = roughness / width;
|
||||
grid_ = new float[width][height];
|
||||
rand_ = (rand == null) ? new Random() : rand;
|
||||
}
|
||||
|
||||
|
||||
public void initialise() {
|
||||
int xh = grid_.length - 1;
|
||||
int yh = grid_[0].length - 1;
|
||||
|
||||
// set the corner points
|
||||
grid_[0][0] = rand_.nextFloat() - 0.5f;
|
||||
grid_[0][yh] = rand_.nextFloat() - 0.5f;
|
||||
grid_[xh][0] = rand_.nextFloat() - 0.5f;
|
||||
grid_[xh][yh] = rand_.nextFloat() - 0.5f;
|
||||
|
||||
// generate the fractal
|
||||
generate(0, 0, xh, yh);
|
||||
}
|
||||
|
||||
|
||||
// Add a suitable amount of random displacement to a point
|
||||
private float roughen(float v, int l, int h) {
|
||||
return v + roughness_ * (float) (rand_.nextGaussian() * (h - l));
|
||||
}
|
||||
|
||||
|
||||
// generate the fractal
|
||||
private void generate(int xl, int yl, int xh, int yh) {
|
||||
int xm = (xl + xh) / 2;
|
||||
int ym = (yl + yh) / 2;
|
||||
if ((xl == xm) && (yl == ym)) return;
|
||||
|
||||
grid_[xm][yl] = 0.5f * (grid_[xl][yl] + grid_[xh][yl]);
|
||||
grid_[xm][yh] = 0.5f * (grid_[xl][yh] + grid_[xh][yh]);
|
||||
grid_[xl][ym] = 0.5f * (grid_[xl][yl] + grid_[xl][yh]);
|
||||
grid_[xh][ym] = 0.5f * (grid_[xh][yl] + grid_[xh][yh]);
|
||||
|
||||
float v = roughen(0.5f * (grid_[xm][yl] + grid_[xm][yh]), xl + yl, yh
|
||||
+ xh);
|
||||
grid_[xm][ym] = v;
|
||||
grid_[xm][yl] = roughen(grid_[xm][yl], xl, xh);
|
||||
grid_[xm][yh] = roughen(grid_[xm][yh], xl, xh);
|
||||
grid_[xl][ym] = roughen(grid_[xl][ym], yl, yh);
|
||||
grid_[xh][ym] = roughen(grid_[xh][ym], yl, yh);
|
||||
|
||||
generate(xl, yl, xm, ym);
|
||||
generate(xm, yl, xh, ym);
|
||||
generate(xl, ym, xm, yh);
|
||||
generate(xm, ym, xh, yh);
|
||||
}
|
||||
|
||||
public float getValueAt(int x, int y) {
|
||||
return grid_[x][y];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dump out as a CSV
|
||||
*/
|
||||
public void printAsCSV() {
|
||||
for(int i = 0;i < grid_.length;i++) {
|
||||
for(int j = 0;j < grid_[0].length;j++) {
|
||||
System.out.print(grid_[i][j]);
|
||||
System.out.print(",");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert to a Boolean array
|
||||
* @return the boolean array
|
||||
*/
|
||||
public boolean[][] toBooleans() {
|
||||
int w = grid_.length;
|
||||
int h = grid_[0].length;
|
||||
boolean[][] ret = new boolean[w][h];
|
||||
for(int i = 0;i < w;i++) {
|
||||
for(int j = 0;j < h;j++) {
|
||||
ret[i][j] = grid_[i][j] < 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user