diff --git a/build.gradle b/build.gradle index c18d7f0..10ee2c4 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,11 @@ repositories { // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. // See https://docs.gradle.org/current/userguide/declaring_repositories.html // for more information about repositories. + + maven { + name = "CottonMC" + url = "https://server.bbkr.space/artifactory/libs-release" + } } dependencies { @@ -31,6 +36,7 @@ dependencies { // These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time. // modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}" + modImplementation include("io.github.cottonmc:LibGui:6.3.0+1.19") } processResources { diff --git a/src/main/java/de/nicolasklier/custom_structures/CustomStructures.java b/src/main/java/de/nicolasklier/custom_structures/CustomStructures.java index 40aa40b..801662d 100644 --- a/src/main/java/de/nicolasklier/custom_structures/CustomStructures.java +++ b/src/main/java/de/nicolasklier/custom_structures/CustomStructures.java @@ -1,18 +1,19 @@ 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; +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.screen.ScreenHandlerType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +@SuppressWarnings("deprecation") 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. diff --git a/src/main/java/de/nicolasklier/custom_structures/events/PlayerTick.java b/src/main/java/de/nicolasklier/custom_structures/events/PlayerTick.java index e85781f..d92be4f 100644 --- a/src/main/java/de/nicolasklier/custom_structures/events/PlayerTick.java +++ b/src/main/java/de/nicolasklier/custom_structures/events/PlayerTick.java @@ -5,6 +5,7 @@ import java.util.Iterator; import de.nicolasklier.custom_structures.CustomStructures; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvents; @@ -56,7 +57,7 @@ public class PlayerTick { //CustomStructures.LOGGER.info("Build at " + pos.toShortString() + "\t Q:" + queue.size()); - instance.getServer().getOverworld().setBlockState(pos, state); + instance.getServer().getOverworld().setBlockState(pos, state, Block.NOTIFY_NEIGHBORS); instance.world.addBlockBreakParticles(pos, state); instance.world.playSound(pos, SoundEvents.BLOCK_STONE_PLACE, SoundCategory.BLOCKS, 1f, 1f, true); diff --git a/src/main/java/de/nicolasklier/custom_structures/guis/SettingsGui.java b/src/main/java/de/nicolasklier/custom_structures/guis/SettingsGui.java new file mode 100644 index 0000000..1f5a16b --- /dev/null +++ b/src/main/java/de/nicolasklier/custom_structures/guis/SettingsGui.java @@ -0,0 +1,41 @@ +package de.nicolasklier.custom_structures.guis; + +import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription; +import io.github.cottonmc.cotton.gui.widget.WButton; +import io.github.cottonmc.cotton.gui.widget.WGridPanel; +import io.github.cottonmc.cotton.gui.widget.WLabel; +import io.github.cottonmc.cotton.gui.widget.WSlider; +import io.github.cottonmc.cotton.gui.widget.WSprite; +import io.github.cottonmc.cotton.gui.widget.data.Axis; +import io.github.cottonmc.cotton.gui.widget.data.Insets; +import net.minecraft.client.gui.hud.MessageIndicator.Icon; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class SettingsGui extends LightweightGuiDescription { + + public SettingsGui() { + WGridPanel root = new WGridPanel(); + setRootPanel(root); + root.setSize(256, 240); + root.setInsets(Insets.ROOT_PANEL); + + WLabel title = new WLabel(Text.of("Custom Structures"), 0x000000); + root.add(title, 0, 0, 2, 1); + + //WSprite icon = new WSprite(new Identifier("minecraft:textures/item/redstone.png")); + //root.add(icon, 0, 2, 1, 1); + + WLabel label_width = new WLabel(Text.of("Width"), 0x000000); + root.add(label_width , 0, 2, 2, 1); + + WSlider slider = new WSlider(0, 100, Axis.HORIZONTAL); + root.add(slider, 0, 4, 5, 1); + + WButton button = new WButton(Text.of("Save")); + root.add(button, 0, 10, 4, 1); + + root.validate(this); + + } +} diff --git a/src/main/java/de/nicolasklier/custom_structures/guis/SettingsScreen.java b/src/main/java/de/nicolasklier/custom_structures/guis/SettingsScreen.java new file mode 100644 index 0000000..fd3b43d --- /dev/null +++ b/src/main/java/de/nicolasklier/custom_structures/guis/SettingsScreen.java @@ -0,0 +1,12 @@ +package de.nicolasklier.custom_structures.guis; + +import io.github.cottonmc.cotton.gui.GuiDescription; +import io.github.cottonmc.cotton.gui.client.CottonClientScreen; + +public class SettingsScreen extends CottonClientScreen { + + public SettingsScreen(GuiDescription description) { + super(description); + } + +} diff --git a/src/main/java/de/nicolasklier/custom_structures/items/StructureSpawner.java b/src/main/java/de/nicolasklier/custom_structures/items/StructureSpawner.java index 309b6ce..6040bb5 100644 --- a/src/main/java/de/nicolasklier/custom_structures/items/StructureSpawner.java +++ b/src/main/java/de/nicolasklier/custom_structures/items/StructureSpawner.java @@ -1,6 +1,8 @@ package de.nicolasklier.custom_structures.items; import de.nicolasklier.custom_structures.Helper; +import de.nicolasklier.custom_structures.guis.SettingsGui; +import de.nicolasklier.custom_structures.guis.SettingsScreen; import de.nicolasklier.custom_structures.structures.Generations; import de.nicolasklier.custom_structures.structures.StaircaseNoiseOptions; import de.nicolasklier.custom_structures.structures.StaircaseOptions; @@ -23,23 +25,34 @@ public class StructureSpawner extends Item { @Override public TypedActionResult use(World world, PlayerEntity user, Hand hand) { + System.out.println("Used item!"); + 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); + // If the player is sneaking, we open a setting menu. + if (client.player.isSneaking()) { + client.setScreen(new SettingsScreen(new SettingsGui())); + } else { + HitResult hit = Helper.raycastInDirection(client, client.getTickDelta(), client.cameraEntity.getRotationVec(client.getTickDelta())); - // Spawn structure at raycast hit - StaircaseOptions options = new StaircaseOptions(); - options.stretch = 2; - options.height = 10; - options.noise = new StaircaseNoiseOptions(); - options.mirror = true; + Generations gen = new Generations(); - gen.spawnStaircase(result.getBlockPos().add(0, 1, 0), options); + 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.width = 5; + options.height = 20; + options.noise = new StaircaseNoiseOptions(); + options.noise.threshold = 0.64f; + options.minecartTrack = true; + options.mirror = true; + + gen.spawnStaircase(result.getBlockPos().add(0, 1, 0), options); + } } return TypedActionResult.success(user.getStackInHand(hand)); diff --git a/src/main/java/de/nicolasklier/custom_structures/structures/Generations.java b/src/main/java/de/nicolasklier/custom_structures/structures/Generations.java index f155698..7b68898 100644 --- a/src/main/java/de/nicolasklier/custom_structures/structures/Generations.java +++ b/src/main/java/de/nicolasklier/custom_structures/structures/Generations.java @@ -1,16 +1,16 @@ 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 org.spongepowered.noise.module.source.Perlin; + 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; @@ -19,7 +19,6 @@ 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 { @@ -38,53 +37,106 @@ public class Generations { int length = options.height * options.stretch * (options.mirror ? 2 : 1); Random rng = new Random(); - PerlinNoise noise = null; + Perlin noise = null; + + int minecartTrackPosition = options.minecartTrack + && options.width > 2 + && options.weight > 2 + ? options.width / 2 + : 0; + int minecartRedstoneTorchStep = 0; int y = -(options.weight); int step = 0; if (options.noise != null) { - noise = new PerlinNoise(rng, options.noise.roughness, length, length); - noise.initialise(); + noise = new Perlin(); + noise.setSeed(rng.nextInt()); + noise.setOctaveCount(3); + noise.setFrequency(0.3); + noise.setPersistence(0.3); } // 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; + + boolean reachedTop = false; + + // Very dangerous. But since we use noise to control the stretch of the staircase we cannot know how long it will be at the end. + while (true) { + x++; - if (step >= options.stretch) { - step = 0; - - // Check if we already reached our final height. If yes, go downwards - if (isMirroring) { - y -= 1; - } else { - y++; + boolean isMirroring = y > options.height && options.mirror || reachedTop; + + if (isMirroring) { + reachedTop = true; + } + + // Break conditions + if (isMirroring && y <= -options.weight) { + break; + } + + if (!options.mirror && y >= options.height) { + break; + } + + if (noise != null) { + // In 20% of all cases the y-level shouldn't change. + if (noise.get(x, 0, 0) < options.noise.threshold) { + if (isMirroring) y--; + else y++; } + + System.out.println("Noise: " + noise.get(x, 0, 0) + " | X: " + x + " Y: " + y); + } else { + if (step >= options.stretch) { + step = 0; + + // Check if we already reached our final height. If yes, go downwards + if (isMirroring) { + y--; + } else { + y++; + } + } } for (int z = 0; z < options.width; z++) { - boolean isNextHeight = true; + boolean isNextHeight = false; - if (options.stretch > 1) { + if (noise != null) { if (isMirroring) { - isNextHeight = step == 0; + if (noise.get(x, 0, 0) < options.noise.threshold) { + isNextHeight = true; + } } else { - isNextHeight = step + 1 == options.stretch; + if (noise.get(x + 1, 0, 0) < options.noise.threshold) { + isNextHeight = true; + } } + } else { + 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) { + /*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; + int y2 = y + 2; while (true) { + break; 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)); @@ -93,18 +145,23 @@ public class Generations { break; } + System.out.println("Fence stuff " + y2 + "(" + mat.toString() + ")"); + + if (y2 <= -64) + break; + y2--; } } - } + }*/ boolean putStair = false; - // Decide if there should be mossy cobblestone or glass. If yes, then don't put a stair there. + // Decide if there should be mossy cobblestone or cracked stone. 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 if (rng.nextFloat() < options.chanceStoneBricks / 100f) { + block = Blocks.STONE_BRICKS; } else { putStair = true; } @@ -129,8 +186,8 @@ public class Generations { 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) { + //CustomStructures.LOGGER.info("Noise [" + x + ", " + y + ", " + z + "]: " + noise.get(x, y + options.weight, z)); + if (noise.get(x, y + options.weight, 0) < options.chanceWoodStair / 100f) { // Override stair as wood stair, copying the facing. stairState = Blocks.OAK_STAIRS.getDefaultState() @@ -146,7 +203,7 @@ public class Generations { 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) { @@ -163,6 +220,21 @@ public class Generations { .with(Properties.HORIZONTAL_FACING, facing)); } } + + // Minecart rails + if (minecartTrackPosition > 0 && z == minecartTrackPosition) { + System.array + if (minecartRedstoneTorchStep >= 4) { + placeQueue.put(at.add(x, y + options.weight - 2, z), Blocks.REDSTONE_TORCH.getDefaultState()); + placeQueue.put(at.add(x, y + options.weight, z), Blocks.POWERED_RAIL.getDefaultState()); + + minecartRedstoneTorchStep = 0; + } else { + placeQueue.put(at.add(x, y + options.weight, z), Blocks.RAIL.getDefaultState()); + } + + minecartRedstoneTorchStep++; + } } step++; diff --git a/src/main/java/de/nicolasklier/custom_structures/structures/StaircaseNoiseOptions.java b/src/main/java/de/nicolasklier/custom_structures/structures/StaircaseNoiseOptions.java index af8d471..3201264 100644 --- a/src/main/java/de/nicolasklier/custom_structures/structures/StaircaseNoiseOptions.java +++ b/src/main/java/de/nicolasklier/custom_structures/structures/StaircaseNoiseOptions.java @@ -6,6 +6,11 @@ public class StaircaseNoiseOptions { public float roughness = 0.1f; + /** + * Noise value that will change the y-level of the staircase. + */ + public float threshold = 0.7f; + public Random rng = new Random(); } diff --git a/src/main/java/de/nicolasklier/custom_structures/structures/StaircaseOptions.java b/src/main/java/de/nicolasklier/custom_structures/structures/StaircaseOptions.java index 3b79d15..390abd9 100644 --- a/src/main/java/de/nicolasklier/custom_structures/structures/StaircaseOptions.java +++ b/src/main/java/de/nicolasklier/custom_structures/structures/StaircaseOptions.java @@ -42,9 +42,9 @@ public class StaircaseOptions { public boolean mirror = false; /** - * Value between 0% and 100% being the chance to spawn glass. + * Value between 0% and 100% being the chance to spawn stone bricks. */ - public short chanceGlass = 10; + public short chanceStoneBricks = 10; /** * Value between 0% and 100% being the chance to spawn glowstone. @@ -71,4 +71,6 @@ public class StaircaseOptions { * To disable this feature set it to null. */ public StaircaseNoiseOptions noise = null; + + public boolean minecartTrack = false; } diff --git a/src/main/java/de/nicolasklier/custom_structures/utils/PerlinNoise.java b/src/main/java/de/nicolasklier/custom_structures/utils/PerlinNoise.java deleted file mode 100644 index 773b9aa..0000000 --- a/src/main/java/de/nicolasklier/custom_structures/utils/PerlinNoise.java +++ /dev/null @@ -1,110 +0,0 @@ -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; - } -} diff --git a/src/main/resources/customstructures.accesswidener b/src/main/resources/customstructures.accesswidener index c3813f0..31df623 100644 --- a/src/main/resources/customstructures.accesswidener +++ b/src/main/resources/customstructures.accesswidener @@ -1,3 +1,4 @@ accessWidener v1 named extendable class net/minecraft/world/gen/chunk/NoiseChunkGenerator -accessible class net/minecraft/world/gen/WorldPresets$Registrar \ No newline at end of file +accessible class net/minecraft/world/gen/WorldPresets$Registrar +accessible class net/minecraft/inventory/Inventory \ No newline at end of file