From f80ec74a7cd498216163b65c851d7e7ef3363137 Mon Sep 17 00:00:00 2001 From: paulevsGitch Date: Thu, 29 Oct 2020 22:30:51 +0300 Subject: [PATCH] NBT structure WIP --- .../java/ru/betterend/registry/EndBiomes.java | 16 +-- .../ru/betterend/registry/EndFeatures.java | 4 + .../world/features/DefaultFeature.java | 8 ++ .../betterend/world/features/EndFeature.java | 5 + .../world/features/EteranlPortalFeature.java | 46 ++++++++ .../world/features/NBTStructureFeature.java | 98 ++++++++++++++++++ .../structures/portal/eternal_portal.nbt | Bin 0 -> 9357 bytes 7 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 src/main/java/ru/betterend/world/features/EteranlPortalFeature.java create mode 100644 src/main/java/ru/betterend/world/features/NBTStructureFeature.java create mode 100644 src/main/resources/data/betterend/structures/portal/eternal_portal.nbt diff --git a/src/main/java/ru/betterend/registry/EndBiomes.java b/src/main/java/ru/betterend/registry/EndBiomes.java index 72ad1329..5434af5a 100644 --- a/src/main/java/ru/betterend/registry/EndBiomes.java +++ b/src/main/java/ru/betterend/registry/EndBiomes.java @@ -85,14 +85,6 @@ public class EndBiomes { CLIENT.clear(); } - private static EndBiome registerBiome(RegistryKey key, BiomeType type, float genChance) { - return registerBiome(BuiltinRegistries.BIOME.get(key), type, genChance); - } - - private static EndBiome registerSubBiome(RegistryKey key, EndBiome parent, float genChance) { - return registerSubBiome(BuiltinRegistries.BIOME.get(key), parent, genChance); - } - /** * Registers new {@link EndBiome} and adds it to picker, can be used to add existing mod biomes into the End. * @param biome - {@link Biome} instance @@ -173,6 +165,14 @@ public class EndBiomes { return biome; } + private static EndBiome registerBiome(RegistryKey key, BiomeType type, float genChance) { + return registerBiome(BuiltinRegistries.BIOME.get(key), type, genChance); + } + + private static EndBiome registerSubBiome(RegistryKey key, EndBiome parent, float genChance) { + return registerSubBiome(BuiltinRegistries.BIOME.get(key), parent, genChance); + } + private static void addToPicker(EndBiome biome, BiomeType type) { if (type == BiomeType.LAND) LAND_BIOMES.addBiome(biome); diff --git a/src/main/java/ru/betterend/registry/EndFeatures.java b/src/main/java/ru/betterend/registry/EndFeatures.java index f2385ac3..db56bd86 100644 --- a/src/main/java/ru/betterend/registry/EndFeatures.java +++ b/src/main/java/ru/betterend/registry/EndFeatures.java @@ -20,6 +20,7 @@ import ru.betterend.world.features.EndLilyFeature; import ru.betterend.world.features.EndLotusFeature; import ru.betterend.world.features.EndLotusLeafFeature; import ru.betterend.world.features.EndPortalFeature; +import ru.betterend.world.features.EteranlPortalFeature; import ru.betterend.world.features.LacugroveFeature; import ru.betterend.world.features.MossyGlowshroomFeature; import ru.betterend.world.features.PythadendronBushFeature; @@ -70,6 +71,8 @@ public class EndFeatures { public static final EndPortalFeature END_PORTAL = new EndPortalFeature(new DefaultEndPortalFeature(), (RunedFlavolite) EndBlocks.FLAVOLITE_RUNED); public static final EndPortalFeature END_PORTAL_ETERNAL = new EndPortalFeature(new DefaultEndPortalFeature(), (RunedFlavolite) EndBlocks.FLAVOLITE_RUNED_ETERNAL); + public static final EndFeature ETERNAL_PORTAL = EndFeature.makeChancedFeature("eternal_portal", new EteranlPortalFeature(), 500); + public static void registerBiomeFeatures(Identifier id, Biome biome, List>>> features) { if (id.getNamespace().equals("minecraft")) { String path = id.getPath(); @@ -89,6 +92,7 @@ public class EndFeatures { addFeature(ENDER_ORE, features); addFeature(ROUND_CAVE_RARE, features); addFeature(CAVE_GRASS, features); + addFeature(ETERNAL_PORTAL, features); } private static void addFeature(EndFeature feature, List>>> features) { diff --git a/src/main/java/ru/betterend/world/features/DefaultFeature.java b/src/main/java/ru/betterend/world/features/DefaultFeature.java index 239e6c4f..adf0ff64 100644 --- a/src/main/java/ru/betterend/world/features/DefaultFeature.java +++ b/src/main/java/ru/betterend/world/features/DefaultFeature.java @@ -17,6 +17,14 @@ public abstract class DefaultFeature extends Feature { super(DefaultFeatureConfig.CODEC); } + public static int getYOnSurface(StructureWorldAccess world, int x, int z) { + return world.getTopY(Type.WORLD_SURFACE, x, z); + } + + public static int getYOnSurfaceWG(StructureWorldAccess world, int x, int z) { + return world.getTopY(Type.WORLD_SURFACE_WG, x, z); + } + public static BlockPos getPosOnSurface(StructureWorldAccess world, BlockPos pos) { return world.getTopPosition(Type.WORLD_SURFACE, pos); } diff --git a/src/main/java/ru/betterend/world/features/EndFeature.java b/src/main/java/ru/betterend/world/features/EndFeature.java index 672814d7..e170e8c7 100644 --- a/src/main/java/ru/betterend/world/features/EndFeature.java +++ b/src/main/java/ru/betterend/world/features/EndFeature.java @@ -97,6 +97,11 @@ public class EndFeature { return new EndFeature(name, feature, GenerationStep.Feature.LOCAL_MODIFICATIONS, configured); } + public static EndFeature makeChancedFeature(String name, Feature feature, int chance) { + ConfiguredFeature configured = feature.configure(FeatureConfig.DEFAULT).decorate(Decorator.CHANCE.configure(new ChanceDecoratorConfig(chance))); + return new EndFeature(name, feature, GenerationStep.Feature.SURFACE_STRUCTURES, configured); + } + public Feature getFeature() { return feature; } diff --git a/src/main/java/ru/betterend/world/features/EteranlPortalFeature.java b/src/main/java/ru/betterend/world/features/EteranlPortalFeature.java new file mode 100644 index 00000000..c9127144 --- /dev/null +++ b/src/main/java/ru/betterend/world/features/EteranlPortalFeature.java @@ -0,0 +1,46 @@ +package ru.betterend.world.features; + +import java.util.Random; + +import net.minecraft.structure.Structure; +import net.minecraft.util.BlockMirror; +import net.minecraft.util.BlockRotation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.StructureWorldAccess; +import ru.betterend.BetterEnd; +import ru.betterend.registry.EndTags; + +public class EteranlPortalFeature extends NBTStructureFeature { + private static final Structure PORTAL = readStructure(BetterEnd.makeID("portal/eternal_portal")); + + @Override + protected Structure getStructure() { + return PORTAL; + } + + @Override + protected boolean canSpawn(StructureWorldAccess world, BlockPos pos) { + if (world.getBlockState(pos.down()).isIn(EndTags.END_GROUND)) { + return world.getBlockState(pos.north(2)).getMaterial().isReplaceable() + && world.getBlockState(pos.south(2)).getMaterial().isReplaceable() + && world.getBlockState(pos.east(2)).getMaterial().isReplaceable() + && world.getBlockState(pos.west(2)).getMaterial().isReplaceable(); + } + return false; + } + + @Override + protected BlockRotation getRotation(StructureWorldAccess world, BlockPos pos, Random random) { + return BlockRotation.random(random); + } + + @Override + protected BlockMirror getMirror(StructureWorldAccess world, BlockPos pos, Random random) { + return BlockMirror.values()[random.nextInt(3)]; + } + + @Override + protected int getYOffset(Structure structure, StructureWorldAccess world, BlockPos pos, Random random) { + return -3; + } +} diff --git a/src/main/java/ru/betterend/world/features/NBTStructureFeature.java b/src/main/java/ru/betterend/world/features/NBTStructureFeature.java new file mode 100644 index 00000000..c39b452d --- /dev/null +++ b/src/main/java/ru/betterend/world/features/NBTStructureFeature.java @@ -0,0 +1,98 @@ +package ru.betterend.world.features; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Random; + +import net.minecraft.block.Blocks; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.MinecraftServer; +import net.minecraft.structure.Structure; +import net.minecraft.structure.StructurePlacementData; +import net.minecraft.util.BlockMirror; +import net.minecraft.util.BlockRotation; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockBox; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.StructureWorldAccess; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.gen.feature.DefaultFeatureConfig; +import ru.betterend.util.BlocksHelper; + +public abstract class NBTStructureFeature extends DefaultFeature { + protected abstract Structure getStructure(); + + protected abstract boolean canSpawn(StructureWorldAccess world, BlockPos pos); + + protected abstract BlockRotation getRotation(StructureWorldAccess world, BlockPos pos, Random random); + + protected abstract BlockMirror getMirror(StructureWorldAccess world, BlockPos pos, Random random); + + protected abstract int getYOffset(Structure structure, StructureWorldAccess world, BlockPos pos, Random random); + + protected BlockPos getGround(StructureWorldAccess world, BlockPos center) { + return getPosOnSurfaceWG(world, center); + } + + @Override + public boolean generate(StructureWorldAccess world, ChunkGenerator chunkGenerator, Random random, BlockPos center, DefaultFeatureConfig featureConfig) { + BlockPos a = center; + center = new BlockPos(((center.getX() >> 4) << 4) | 8, 128, ((center.getZ() >> 4) << 4) | 8); + center = getGround(world, center); + + if (!canSpawn(world, center)) { + return false; + } + + Structure structure = getStructure(); + BlockRotation rotation = getRotation(world, center, random); + BlockMirror mirror = getMirror(world, center, random); + BlockPos size = structure.getSize().rotate(rotation); + double px = size.getX(); + double pz = size.getZ(); + if (mirror == BlockMirror.FRONT_BACK) + px = -px; + if (mirror == BlockMirror.LEFT_RIGHT) + pz = -pz; + + center = center.add(-px * 0.5, 0, -pz * 0.5); + //center.subtract(structure.getRotatedSize(rotation)); + int offset = getYOffset(structure, world, center, random); + center = center.add(0, offset, 0); + StructurePlacementData placementData = new StructurePlacementData() + .setRotation(rotation) + .setMirror(mirror) + .setPosition(center) + .setBoundingBox(BlockBox.create(center.getX() - 8 - 15, 0, center.getZ() - 8 - 15, center.getX() + 8 + 15, 128, center.getZ() + 8 + 15)); + structure.place(world, center, placementData, random); + BlocksHelper.setWithoutUpdate(world, new BlockPos(a.getX(), center.getY() + 10, a.getZ()), Blocks.DIAMOND_BLOCK); + BlocksHelper.setWithoutUpdate(world, center, Blocks.GOLD_BLOCK); + + return true; + } + + protected static Structure readStructure(Identifier resource) { + String ns = resource.getNamespace(); + String nm = resource.getPath(); + + try { + InputStream inputstream = MinecraftServer.class.getResourceAsStream("/data/" + ns + "/structures/" + nm + ".nbt"); + return readStructureFromStream(inputstream); + } + catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + private static Structure readStructureFromStream(InputStream stream) throws IOException { + CompoundTag nbttagcompound = NbtIo.readCompressed(stream); + + Structure template = new Structure(); + template.fromTag(nbttagcompound); + + return template; + } +} diff --git a/src/main/resources/data/betterend/structures/portal/eternal_portal.nbt b/src/main/resources/data/betterend/structures/portal/eternal_portal.nbt new file mode 100644 index 0000000000000000000000000000000000000000..2e3acc9acc755b8b55a8dfc7adcf1ac4b4d38cd4 GIT binary patch literal 9357 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