diff --git a/src/main/java/ru/bclib/BCLib.java b/src/main/java/ru/bclib/BCLib.java index 52a92253..fc4704e9 100644 --- a/src/main/java/ru/bclib/BCLib.java +++ b/src/main/java/ru/bclib/BCLib.java @@ -4,8 +4,8 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; import ru.bclib.util.Logger; +import ru.bclib.world.surface.BCLSurfaceBuilders; import ru.bclib.api.BCLibTags; -import ru.bclib.api.SurfaceBuilders; public class BCLib implements ModInitializer { public static final String MOD_ID = "bclib"; @@ -13,7 +13,7 @@ public class BCLib implements ModInitializer { @Override public void onInitialize() { - SurfaceBuilders.register(); + BCLSurfaceBuilders.register(); BCLibTags.init(); } diff --git a/src/main/java/ru/bclib/api/BiomeAPI.java b/src/main/java/ru/bclib/api/BiomeAPI.java new file mode 100644 index 00000000..de61b50f --- /dev/null +++ b/src/main/java/ru/bclib/api/BiomeAPI.java @@ -0,0 +1,153 @@ +package ru.bclib.api; + +import java.util.HashMap; +import java.util.Random; + +import com.google.common.collect.Maps; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.impl.biome.InternalBiomeData; +import net.minecraft.client.Minecraft; +import net.minecraft.core.Registry; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biome.ClimateParameters; +import net.minecraft.world.level.biome.Biomes; +import ru.bclib.util.MHelper; +import ru.bclib.world.biomes.BCLBiome; + +public class BiomeAPI { + /** + * Empty biome used as default value if requested biome doesn't exist or linked. Shouldn't be registered anywhere to prevent bugs. + * Have {@code Biomes.THE_VOID} as the reference biome. + */ + public static final BCLBiome EMPTY_BIOME = new BCLBiome(Biomes.THE_VOID.location(), BuiltinRegistries.BIOME.get(Biomes.THE_VOID), 0, 0); + + private static final HashMap ID_MAP = Maps.newHashMap(); + private static final HashMap CLIENT = Maps.newHashMap(); + private static Registry biomeRegistry; + + /** + * Initialize registry if it was not initialized in world generation (when using mods/datapacks, that overrides the End generation) + * @param server - {@link MinecraftServer} + */ + public static void initRegistry(MinecraftServer server) { + if (biomeRegistry == null || biomeRegistry == BuiltinRegistries.BIOME) { + biomeRegistry = server.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY); + } + } + + public static void registerBiomeDirectly(BCLBiome biome) { + Registry.register(BuiltinRegistries.BIOME, biome.getID(), biome.getBiome()); + } + + /** + * Adds {@link BCLBiome} to FabricAPI biomes as the Nether biome (with random {@link ClimateParameters}). + * @param biome - {@link BCLBiome}. + */ + public static void addNetherBiomeToFabricApi(BCLBiome biome) { + ResourceKey key = BuiltinRegistries.BIOME.getResourceKey(biome.getBiome()).get(); + Random random = new Random(biome.getID().toString().hashCode()); + ClimateParameters parameters = new ClimateParameters( + MHelper.randRange(-2F, 2F, random), + MHelper.randRange(-2F, 2F, random), + MHelper.randRange(-2F, 2F, random), + MHelper.randRange(-2F, 2F, random), + MHelper.randRange(-2F, 2F, random) + ); + InternalBiomeData.addNetherBiome(key, parameters); + } + + /** + * Adds {@link BCLBiome} to FabricAPI biomes as an End land biome (generating on islands). + * @param biome - {@link BCLBiome}. + */ + public static void addEndLandBiomeToFabricApi(BCLBiome biome) { + float weight = biome.getGenChance(); + ResourceKey key = BuiltinRegistries.BIOME.getResourceKey(biome.getBiome()).get(); + InternalBiomeData.addEndBiomeReplacement(Biomes.END_HIGHLANDS, key, weight); + InternalBiomeData.addEndBiomeReplacement(Biomes.END_MIDLANDS, key, weight); + } + + /** + * Adds {@link BCLBiome} to FabricAPI biomes as an End void biome (generating between islands in the void). + * @param biome - {@link BCLBiome}. + */ + public static void addEndVoidBiomeToFabricApi(BCLBiome biome) { + float weight = biome.getGenChance(); + ResourceKey key = BuiltinRegistries.BIOME.getResourceKey(biome.getBiome()).get(); + InternalBiomeData.addEndBiomeReplacement(Biomes.SMALL_END_ISLANDS, key, weight); + } + + /** + * Get {@link BCLBiome} from {@link Biome} instance on server. Used to convert world biomes to BCLBiomes. + * @param biome - {@link Biome} from world. + * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. + */ + public static BCLBiome getFromBiome(Biome biome) { + return ID_MAP.getOrDefault(biomeRegistry.getKey(biome), EMPTY_BIOME); + } + + /** + * Get {@link BCLBiome} from biome on client. Used in fog rendering. + * @param biome - {@link Biome} from client world. + * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. + */ + @Environment(EnvType.CLIENT) + public static BCLBiome getRenderBiome(Biome biome) { + BCLBiome endBiome = CLIENT.get(biome); + if (endBiome == null) { + Minecraft minecraft = Minecraft.getInstance(); + ResourceLocation id = minecraft.level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).getKey(biome); + endBiome = id == null ? EMPTY_BIOME : ID_MAP.getOrDefault(id, EMPTY_BIOME); + CLIENT.put(biome, endBiome); + } + return endBiome; + } + + /** + * Get biome {@link ResourceLocation} from given {@link Biome}. + * @param biome - {@link Biome} from server world. + * @return biome {@link ResourceLocation}. + */ + public static ResourceLocation getBiomeID(Biome biome) { + ResourceLocation id = biomeRegistry.getKey(biome); + return id == null ? EMPTY_BIOME.getID() : id; + } + + /** + * Get {@link BCLBiome} from given {@link ResourceLocation}. + * @param biomeID - biome {@link ResourceLocation}. + * @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}. + */ + public static BCLBiome getBiome(ResourceLocation biomeID) { + return ID_MAP.getOrDefault(biomeID, EMPTY_BIOME); + } + + /** + * Get actual {@link Biome} from given {@link BCLBiome}. If it is null it will request it from current {@link Registry}. + * @param biome - {@link BCLBiome}. + * @return {@link Biome}. + */ + public static Biome getActualBiome(BCLBiome biome) { + Biome actual = biome.getActualBiome(); + if (actual == null) { + biome.updateActualBiomes(biomeRegistry); + actual = biome.getActualBiome(); + } + return actual; + } + + /** + * Check if biome with {@link ResourceLocation} exists in API registry. + * @param biomeID - biome {@link ResourceLocation}. + * @return + */ + public static boolean hasBiome(ResourceLocation biomeID) { + return ID_MAP.containsKey(biomeID); + } +} diff --git a/src/main/java/ru/bclib/api/BonemealAPI.java b/src/main/java/ru/bclib/api/BonemealAPI.java new file mode 100644 index 00000000..cc7180d9 --- /dev/null +++ b/src/main/java/ru/bclib/api/BonemealAPI.java @@ -0,0 +1,120 @@ +package ru.bclib.api; + +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import ru.bclib.util.WeightedList; + +public class BonemealAPI { + private static final Map>> WATER_GRASS_BIOMES = Maps.newHashMap(); + private static final Map>> LAND_GRASS_BIOMES = Maps.newHashMap(); + private static final Map> WATER_GRASS_TYPES = Maps.newHashMap(); + private static final Map> LAND_GRASS_TYPES = Maps.newHashMap(); + private static final Set SPREADABLE_BLOCKS = Sets.newHashSet(); + + public static void addSpreadableBlock(Block block) { + SPREADABLE_BLOCKS.add(block); + } + + public static boolean isSpreadable(Block block) { + return SPREADABLE_BLOCKS.contains(block); + } + + public static void addLandGrass(Block terrain, Block plant) { + addLandGrass(terrain, plant, 1F); + } + + public static void addLandGrass(ResourceLocation biome, Block terrain, Block plant) { + addLandGrass(biome, terrain, plant, 1F); + } + + public static void addLandGrass(Block terrain, Block plant, float chance) { + WeightedList list = LAND_GRASS_TYPES.get(terrain); + if (list == null) { + list = new WeightedList(); + LAND_GRASS_TYPES.put(terrain, list); + } + list.add(plant, chance); + } + + public static void addLandGrass(ResourceLocation biome, Block terrain, Block plant, float chance) { + Map> map = LAND_GRASS_BIOMES.get(biome); + if (map == null) { + map = Maps.newHashMap(); + LAND_GRASS_BIOMES.put(biome, map); + } + WeightedList list = map.get(terrain); + if (list == null) { + list = new WeightedList(); + map.put(terrain, list); + } + list.add(plant, chance); + } + + public static void addWaterGrass(Block terrain, Block plant) { + addWaterGrass(terrain, plant, 1F); + } + + public static void addWaterGrass(ResourceLocation biome, Block terrain, Block plant) { + addWaterGrass(biome, terrain, plant, 1F); + } + + public static void addWaterGrass(Block terrain, Block plant, float chance) { + WeightedList list = WATER_GRASS_TYPES.get(terrain); + if (list == null) { + list = new WeightedList(); + WATER_GRASS_TYPES.put(terrain, list); + } + list.add(plant, chance); + } + + public static void addWaterGrass(ResourceLocation biome, Block terrain, Block plant, float chance) { + Map> map = WATER_GRASS_BIOMES.get(biome); + if (map == null) { + map = Maps.newHashMap(); + WATER_GRASS_BIOMES.put(biome, map); + } + WeightedList list = map.get(terrain); + if (list == null) { + list = new WeightedList(); + map.put(terrain, list); + } + list.add(plant, chance); + } + + public static Block getLandGrass(ResourceLocation biomeID, Block terrain, Random random) { + Map> map = LAND_GRASS_BIOMES.get(biomeID); + WeightedList list = null; + if (map != null) { + list = map.get(terrain); + if (list == null) { + list = LAND_GRASS_TYPES.get(terrain); + } + } + else { + list = LAND_GRASS_TYPES.get(terrain); + } + return list == null ? null : list.get(random); + } + + public static Block getWaterGrass(ResourceLocation biomeID, Block terrain, Random random) { + Map> map = LAND_GRASS_BIOMES.get(biomeID); + WeightedList list = null; + if (map != null) { + list = map.get(terrain); + if (list == null) { + list = LAND_GRASS_TYPES.get(terrain); + } + } + else { + list = LAND_GRASS_TYPES.get(terrain); + } + return list == null ? null : list.get(random); + } +} diff --git a/src/main/java/ru/bclib/mixin/common/BoneMealItemMixin.java b/src/main/java/ru/bclib/mixin/common/BoneMealItemMixin.java new file mode 100644 index 00000000..01df4312 --- /dev/null +++ b/src/main/java/ru/bclib/mixin/common/BoneMealItemMixin.java @@ -0,0 +1,149 @@ +package ru.bclib.mixin.common; + +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.CallbackInfoReturnable; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.BoneMealItem; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.Biome.BiomeCategory; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import ru.bclib.api.BCLibTags; +import ru.bclib.api.BiomeAPI; +import ru.bclib.api.BonemealAPI; +import ru.bclib.util.BlocksHelper; +import ru.bclib.util.MHelper; + +@Mixin(BoneMealItem.class) +public class BoneMealItemMixin { + private static final MutableBlockPos bclib_BLOCK_POS = new MutableBlockPos(); + + @Inject(method = "useOn", at = @At("HEAD"), cancellable = true) + private void bclib_onUse(UseOnContext context, CallbackInfoReturnable info) { + Level world = context.getLevel(); + BlockPos blockPos = context.getClickedPos(); + if (!world.isClientSide) { + BlockPos offseted = blockPos.relative(context.getClickedFace()); + boolean endBiome = world.getBiome(offseted).getBiomeCategory() == BiomeCategory.THEEND; + + if (world.getBlockState(blockPos).is(BCLibTags.END_GROUND)) { + boolean consume = false; + if (world.getBlockState(blockPos).is(Blocks.END_STONE)) { + BlockState nylium = bclib_getNylium(world, blockPos); + if (nylium != null) { + BlocksHelper.setWithoutUpdate(world, blockPos, nylium); + consume = true; + } + } + else { + if (!world.getFluidState(offseted).isEmpty() && endBiome) { + if (world.getBlockState(offseted).getBlock().equals(Blocks.WATER)) { + consume = bclib_growWaterGrass(world, blockPos); + } + } + else { + consume = bclib_growLandGrass(world, blockPos); + } + } + if (consume) { + if (!context.getPlayer().isCreative()) { + context.getItemInHand().shrink(1); + } + world.levelEvent(2005, blockPos, 0); + info.setReturnValue(InteractionResult.SUCCESS); + info.cancel(); + } + } + else if (!world.getFluidState(offseted).isEmpty() && endBiome) { + if (world.getBlockState(offseted).getBlock().equals(Blocks.WATER)) { + info.setReturnValue(InteractionResult.FAIL); + info.cancel(); + } + } + } + } + + private boolean bclib_growLandGrass(Level world, BlockPos pos) { + int y1 = pos.getY() + 3; + int y2 = pos.getY() - 3; + boolean result = false; + for (int i = 0; i < 64; i++) { + int x = (int) (pos.getX() + world.random.nextGaussian() * 2); + int z = (int) (pos.getZ() + world.random.nextGaussian() * 2); + bclib_BLOCK_POS.setX(x); + bclib_BLOCK_POS.setZ(z); + for (int y = y1; y >= y2; y--) { + bclib_BLOCK_POS.setY(y); + BlockPos down = bclib_BLOCK_POS.below(); + if (world.isEmptyBlock(bclib_BLOCK_POS) && !world.isEmptyBlock(down)) { + BlockState grass = bclib_getLandGrassState(world, down); + if (grass != null) { + BlocksHelper.setWithoutUpdate(world, bclib_BLOCK_POS, grass); + result = true; + } + break; + } + } + } + return result; + } + + private boolean bclib_growWaterGrass(Level world, BlockPos pos) { + int y1 = pos.getY() + 3; + int y2 = pos.getY() - 3; + boolean result = false; + for (int i = 0; i < 64; i++) { + int x = (int) (pos.getX() + world.random.nextGaussian() * 2); + int z = (int) (pos.getZ() + world.random.nextGaussian() * 2); + bclib_BLOCK_POS.setX(x); + bclib_BLOCK_POS.setZ(z); + for (int y = y1; y >= y2; y--) { + bclib_BLOCK_POS.setY(y); + BlockPos down = bclib_BLOCK_POS.below(); + if (BlocksHelper.isFluid(world.getBlockState(bclib_BLOCK_POS)) && !BlocksHelper.isFluid(world.getBlockState(down))) { + BlockState grass = bclib_getWaterGrassState(world, down); + if (grass != null) { + BlocksHelper.setWithoutUpdate(world, bclib_BLOCK_POS, grass); + result = true; + } + break; + } + } + } + return result; + } + + private BlockState bclib_getLandGrassState(Level world, BlockPos pos) { + BlockState state = world.getBlockState(pos); + Block block = state.getBlock(); + block = BonemealAPI.getLandGrass(BiomeAPI.getBiomeID(world.getBiome(pos)), block, world.getRandom()); + return block == null ? null : block.defaultBlockState(); + } + + private BlockState bclib_getWaterGrassState(Level world, BlockPos pos) { + BlockState state = world.getBlockState(pos); + Block block = state.getBlock(); + block = BonemealAPI.getLandGrass(BiomeAPI.getBiomeID(world.getBiome(pos)), block, world.getRandom()); + return block == null ? null : block.defaultBlockState(); + } + + private BlockState bclib_getNylium(Level world, BlockPos pos) { + Vec3i[] offsets = MHelper.getOffsets(world.getRandom()); + for (Vec3i dir : offsets) { + BlockPos p = pos.offset(dir); + BlockState state = world.getBlockState(p); + if (BonemealAPI.isSpreadable(state.getBlock())) { + return state; + } + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/ru/bclib/util/BonemealUtil.java b/src/main/java/ru/bclib/util/BonemealUtil.java deleted file mode 100644 index 9a97255d..00000000 --- a/src/main/java/ru/bclib/util/BonemealUtil.java +++ /dev/null @@ -1,71 +0,0 @@ -package ru.bclib.util; - -import java.util.Map; -import java.util.Random; -import java.util.Set; - -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Block; - -public class BonemealUtil { - private static final Map>> GRASS_BIOMES = Maps.newHashMap(); - private static final Map> GRASS_TYPES = Maps.newHashMap(); - private static final Set SPREADABLE_BLOCKS = Sets.newHashSet(); - - public static void addSpreadableBlock(Block block) { - SPREADABLE_BLOCKS.add(block); - } - - public static boolean isSpreadable(Block block) { - return SPREADABLE_BLOCKS.contains(block); - } - - public static void addBonemealGrass(Block terrain, Block plant) { - addBonemealGrass(terrain, plant, 1F); - } - - public static void addBonemealGrass(Block terrain, Block plant, float chance) { - WeightedList list = GRASS_TYPES.get(terrain); - if (list == null) { - list = new WeightedList(); - GRASS_TYPES.put(terrain, list); - } - list.add(plant, chance); - } - - public static void addBonemealGrass(ResourceLocation biome, Block terrain, Block plant) { - addBonemealGrass(biome, terrain, plant, 1F); - } - - public static void addBonemealGrass(ResourceLocation biome, Block terrain, Block plant, float chance) { - Map> map = GRASS_BIOMES.get(biome); - if (map == null) { - map = Maps.newHashMap(); - GRASS_BIOMES.put(biome, map); - } - WeightedList list = map.get(terrain); - if (list == null) { - list = new WeightedList(); - map.put(terrain, list); - } - list.add(plant, chance); - } - - public static Block getGrass(ResourceLocation biomeID, Block terrain, Random random) { - Map> map = GRASS_BIOMES.get(biomeID); - WeightedList list = null; - if (map != null) { - list = map.get(terrain); - if (list == null) { - list = GRASS_TYPES.get(terrain); - } - } - else { - list = GRASS_TYPES.get(terrain); - } - return list == null ? null : list.get(random); - } -} diff --git a/src/main/java/ru/bclib/world/biomes/BCLBiome.java b/src/main/java/ru/bclib/world/biomes/BCLBiome.java index 65f9289b..6706f94d 100644 --- a/src/main/java/ru/bclib/world/biomes/BCLBiome.java +++ b/src/main/java/ru/bclib/world/biomes/BCLBiome.java @@ -35,7 +35,7 @@ public class BCLBiome { private BCLFeature structuresFeature; private Biome actualBiome; - public BCLBiome(BiomeDefinition definition) { + public BCLBiome(BCLBiomeDef definition) { this.mcID = definition.getID(); this.readStructureList(); if (structuresFeature != null) { diff --git a/src/main/java/ru/bclib/world/biomes/BiomeDefinition.java b/src/main/java/ru/bclib/world/biomes/BCLBiomeDef.java similarity index 76% rename from src/main/java/ru/bclib/world/biomes/BiomeDefinition.java rename to src/main/java/ru/bclib/world/biomes/BCLBiomeDef.java index 02cff669..7a810bef 100644 --- a/src/main/java/ru/bclib/world/biomes/BiomeDefinition.java +++ b/src/main/java/ru/bclib/world/biomes/BCLBiomeDef.java @@ -40,7 +40,7 @@ import ru.bclib.world.features.BCLFeature; import ru.bclib.world.structures.BCLStructureFeature; import ru.bclib.world.surface.DoubleBlockSurfaceBuilder; -public class BiomeDefinition { +public class BCLBiomeDef { private static final int DEF_FOLIAGE_OVERWORLD = Biomes.PLAINS.getFoliageColor(); private static final int DEF_FOLIAGE_NETHER =ColorUtil.color(117, 10, 10); private static final int DEF_FOLIAGE_END = ColorUtil.color(197, 210, 112); @@ -80,17 +80,17 @@ public class BiomeDefinition { * Custom biome definition. Can be extended with new parameters. * @param id - Biome {@link ResourceLocation} (identifier). */ - public BiomeDefinition(ResourceLocation id) { + public BCLBiomeDef(ResourceLocation id) { this.id = id; } /** * Create default definition for The Nether biome. * @param id - {@ResourceLocation}. - * @return {@link BiomeDefinition}. + * @return {@link BCLBiomeDef}. */ - public static BiomeDefinition netherBiome(ResourceLocation id) { - BiomeDefinition def = new BiomeDefinition(id); + public static BCLBiomeDef netherBiome(ResourceLocation id) { + BCLBiomeDef def = new BCLBiomeDef(id); def.foliageColor = DEF_FOLIAGE_NETHER; def.grassColor = DEF_FOLIAGE_NETHER; def.setCategory(BiomeCategory.NETHER); @@ -100,10 +100,10 @@ public class BiomeDefinition { /** * Create default definition for The End biome. * @param id - {@ResourceLocation}. - * @return {@link BiomeDefinition}. + * @return {@link BCLBiomeDef}. */ - public static BiomeDefinition endBiome(ResourceLocation id) { - BiomeDefinition def = new BiomeDefinition(id); + public static BCLBiomeDef endBiome(ResourceLocation id) { + BCLBiomeDef def = new BCLBiomeDef(id); def.foliageColor = DEF_FOLIAGE_END; def.grassColor = DEF_FOLIAGE_END; def.setCategory(BiomeCategory.THEEND); @@ -113,9 +113,9 @@ public class BiomeDefinition { /** * Used to load biome settings from config. * @param config - {@link IdConfig}. - * @return this {@link BiomeDefinition}. + * @return this {@link BCLBiomeDef}. */ - public BiomeDefinition loadConfigValues(IdConfig config) { + public BCLBiomeDef loadConfigValues(IdConfig config) { this.fogDensity = config.getFloat(id, "fog_density", this.fogDensity); this.genChance = config.getFloat(id, "generation_chance", this.genChance); this.edgeSize = config.getInt(id, "edge_size", this.edgeSize); @@ -125,19 +125,19 @@ public class BiomeDefinition { /** * Set category of the biome. * @param category - {@link BiomeCategory}. - * @return this {@link BiomeDefinition}. + * @return this {@link BCLBiomeDef}. */ - public BiomeDefinition setCategory(BiomeCategory category) { + public BCLBiomeDef setCategory(BiomeCategory category) { this.category = category; return this; } - public BiomeDefinition setPrecipitation(Precipitation precipitation) { + public BCLBiomeDef setPrecipitation(Precipitation precipitation) { this.precipitation = precipitation; return this; } - public BiomeDefinition setSurface(Block block) { + public BCLBiomeDef setSurface(Block block) { setSurface(SurfaceBuilder.DEFAULT.configured(new SurfaceBuilderBaseConfiguration( block.defaultBlockState(), Blocks.END_STONE.defaultBlockState(), @@ -146,47 +146,47 @@ public class BiomeDefinition { return this; } - public BiomeDefinition setSurface(Block block1, Block block2) { + public BCLBiomeDef setSurface(Block block1, Block block2) { setSurface(DoubleBlockSurfaceBuilder.register("bclib_" + id.getPath() + "_surface").setBlock1(block1).setBlock2(block2).configured()); return this; } - public BiomeDefinition setSurface(ConfiguredSurfaceBuilder builder) { + public BCLBiomeDef setSurface(ConfiguredSurfaceBuilder builder) { this.surface = builder; return this; } - public BiomeDefinition setParticles(ParticleOptions particle, float probability) { + public BCLBiomeDef setParticles(ParticleOptions particle, float probability) { this.particleConfig = new AmbientParticleSettings(particle, probability); return this; } - public BiomeDefinition setGenChance(float genChance) { + public BCLBiomeDef setGenChance(float genChance) { this.genChance = genChance; return this; } - public BiomeDefinition setDepth(float depth) { + public BCLBiomeDef setDepth(float depth) { this.depth = depth; return this; } - public BiomeDefinition setTemperature(float temperature) { + public BCLBiomeDef setTemperature(float temperature) { this.temperature = temperature; return this; } - public BiomeDefinition setDownfall(float downfall) { + public BCLBiomeDef setDownfall(float downfall) { this.downfall = downfall; return this; } - public BiomeDefinition setEdgeSize(int edgeSize) { + public BCLBiomeDef setEdgeSize(int edgeSize) { this.edgeSize = edgeSize; return this; } - public BiomeDefinition addMobSpawn(EntityType type, int weight, int minGroupSize, int maxGroupSize) { + public BCLBiomeDef addMobSpawn(EntityType type, int weight, int minGroupSize, int maxGroupSize) { ResourceLocation eID = Registry.ENTITY_TYPE.getKey(type); if (eID != Registry.ENTITY_TYPE.getDefaultKey()) { SpawnInfo info = new SpawnInfo(); @@ -199,22 +199,22 @@ public class BiomeDefinition { return this; } - public BiomeDefinition addMobSpawn(SpawnerData entry) { + public BCLBiomeDef addMobSpawn(SpawnerData entry) { spawns.add(entry); return this; } - public BiomeDefinition addStructureFeature(ConfiguredStructureFeature feature) { + public BCLBiomeDef addStructureFeature(ConfiguredStructureFeature feature) { structures.add(feature); return this; } - public BiomeDefinition addStructureFeature(BCLStructureFeature feature) { + public BCLBiomeDef addStructureFeature(BCLStructureFeature feature) { structures.add(feature.getFeatureConfigured()); return this; } - public BiomeDefinition addFeature(BCLFeature feature) { + public BCLBiomeDef addFeature(BCLFeature feature) { FeatureInfo info = new FeatureInfo(); info.featureStep = feature.getFeatureStep(); info.feature = feature.getFeatureConfigured(); @@ -222,7 +222,7 @@ public class BiomeDefinition { return this; } - public BiomeDefinition addFeature(Decoration featureStep, ConfiguredFeature feature) { + public BCLBiomeDef addFeature(Decoration featureStep, ConfiguredFeature feature) { FeatureInfo info = new FeatureInfo(); info.featureStep = featureStep; info.feature = feature; @@ -237,60 +237,60 @@ public class BiomeDefinition { return ColorUtil.color(r, g, b); } - public BiomeDefinition setFogColor(int r, int g, int b) { + public BCLBiomeDef setFogColor(int r, int g, int b) { this.fogColor = getColor(r, g, b); return this; } - public BiomeDefinition setFogDensity(float density) { + public BCLBiomeDef setFogDensity(float density) { this.fogDensity = density; return this; } - public BiomeDefinition setWaterColor(int r, int g, int b) { + public BCLBiomeDef setWaterColor(int r, int g, int b) { this.waterColor = getColor(r, g, b); return this; } - public BiomeDefinition setWaterFogColor(int r, int g, int b) { + public BCLBiomeDef setWaterFogColor(int r, int g, int b) { this.waterFogColor = getColor(r, g, b); return this; } - public BiomeDefinition setWaterAndFogColor(int r, int g, int b) { + public BCLBiomeDef setWaterAndFogColor(int r, int g, int b) { return setWaterColor(r, g, b).setWaterFogColor(r, g, b); } - public BiomeDefinition setFoliageColor(int r, int g, int b) { + public BCLBiomeDef setFoliageColor(int r, int g, int b) { this.foliageColor = getColor(r, g, b); return this; } - public BiomeDefinition setGrassColor(int r, int g, int b) { + public BCLBiomeDef setGrassColor(int r, int g, int b) { this.grassColor = getColor(r, g, b); return this; } - public BiomeDefinition setPlantsColor(int r, int g, int b) { + public BCLBiomeDef setPlantsColor(int r, int g, int b) { return this.setFoliageColor(r, g, b).setGrassColor(r, g, b); } - public BiomeDefinition setLoop(SoundEvent loop) { + public BCLBiomeDef setLoop(SoundEvent loop) { this.loop = loop; return this; } - public BiomeDefinition setMood(SoundEvent mood) { + public BCLBiomeDef setMood(SoundEvent mood) { this.mood = new AmbientMoodSettings(mood, 6000, 8, 2.0D); return this; } - public BiomeDefinition setAdditions(SoundEvent additions) { + public BCLBiomeDef setAdditions(SoundEvent additions) { this.additions = new AmbientAdditionsSettings(additions, 0.0111); return this; } - public BiomeDefinition setMusic(SoundEvent music) { + public BCLBiomeDef setMusic(SoundEvent music) { this.music = music; return this; } @@ -366,7 +366,7 @@ public class BiomeDefinition { return edgeSize; } - public BiomeDefinition addCarver(Carving carverStep, ConfiguredWorldCarver carver) { + public BCLBiomeDef addCarver(Carving carverStep, ConfiguredWorldCarver carver) { CarverInfo info = new CarverInfo(); info.carverStep = carverStep; info.carver = carver; diff --git a/src/main/java/ru/bclib/world/features/NBTStructureFeature.java b/src/main/java/ru/bclib/world/features/NBTStructureFeature.java new file mode 100644 index 00000000..7422e599 --- /dev/null +++ b/src/main/java/ru/bclib/world/features/NBTStructureFeature.java @@ -0,0 +1,213 @@ +package ru.bclib.world.features; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Random; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; +import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilderConfiguration; +import ru.bclib.api.BCLibTags; +import ru.bclib.api.BiomeAPI; +import ru.bclib.util.BlocksHelper; +import ru.bclib.world.processors.DestructionStructureProcessor; + +public abstract class NBTStructureFeature extends DefaultFeature { + protected static final DestructionStructureProcessor DESTRUCTION = new DestructionStructureProcessor(); + + protected abstract StructureTemplate getStructure(WorldGenLevel world, BlockPos pos, Random random); + + protected abstract boolean canSpawn(WorldGenLevel world, BlockPos pos, Random random); + + protected abstract Rotation getRotation(WorldGenLevel world, BlockPos pos, Random random); + + protected abstract Mirror getMirror(WorldGenLevel world, BlockPos pos, Random random); + + protected abstract int getYOffset(StructureTemplate structure, WorldGenLevel world, BlockPos pos, Random random); + + protected abstract TerrainMerge getTerrainMerge(WorldGenLevel world, BlockPos pos, Random random); + + protected abstract void addStructureData(StructurePlaceSettings data); + + protected BlockPos getGround(WorldGenLevel world, BlockPos center) { + Biome biome = world.getBiome(center); + ResourceLocation id = BiomeAPI.getBiomeID(biome); + if (id.getNamespace().contains("moutain") || id.getNamespace().contains("lake")) { + int y = getAverageY(world, center); + return new BlockPos(center.getX(), y, center.getZ()); + } + else { + int y = getAverageYWG(world, center); + return new BlockPos(center.getX(), y, center.getZ()); + } + } + + protected int getAverageY(WorldGenLevel world, BlockPos center) { + int y = getYOnSurface(world, center.getX(), center.getZ()); + y += getYOnSurface(world, center.getX() - 2, center.getZ() - 2); + y += getYOnSurface(world, center.getX() + 2, center.getZ() - 2); + y += getYOnSurface(world, center.getX() - 2, center.getZ() + 2); + y += getYOnSurface(world, center.getX() + 2, center.getZ() + 2); + return y / 5; + } + + protected int getAverageYWG(WorldGenLevel world, BlockPos center) { + int y = getYOnSurfaceWG(world, center.getX(), center.getZ()); + y += getYOnSurfaceWG(world, center.getX() - 2, center.getZ() - 2); + y += getYOnSurfaceWG(world, center.getX() + 2, center.getZ() - 2); + y += getYOnSurfaceWG(world, center.getX() - 2, center.getZ() + 2); + y += getYOnSurfaceWG(world, center.getX() + 2, center.getZ() + 2); + return y / 5; + } + + @Override + public boolean place(WorldGenLevel world, ChunkGenerator chunkGenerator, Random random, BlockPos center, NoneFeatureConfiguration featureConfig) { + center = new BlockPos(((center.getX() >> 4) << 4) | 8, 128, ((center.getZ() >> 4) << 4) | 8); + center = getGround(world, center); + + if (!canSpawn(world, center, random)) { + return false; + } + + int posY = center.getY() + 1; + StructureTemplate structure = getStructure(world, center, random); + Rotation rotation = getRotation(world, center, random); + Mirror mirror = getMirror(world, center, random); + BlockPos offset = StructureTemplate.transform(structure.getSize(), mirror, rotation, BlockPos.ZERO); + center = center.offset(0, getYOffset(structure, world, center, random) + 0.5, 0); + + BoundingBox bounds = makeBox(center); + StructurePlaceSettings placementData = new StructurePlaceSettings().setRotation(rotation).setMirror(mirror).setBoundingBox(bounds); + addStructureData(placementData); + center = center.offset(-offset.getX() * 0.5, 0, -offset.getZ() * 0.5); + structure.placeInWorldChunk(world, center, placementData, random); + + TerrainMerge merge = getTerrainMerge(world, center, random); + int x1 = center.getX(); + int z1 = center.getZ(); + int x2 = x1 + offset.getX(); + int z2 = z1 + offset.getZ(); + if (merge != TerrainMerge.NONE) { + MutableBlockPos mut = new MutableBlockPos(); + + if (x2 < x1) { + int a = x1; + x1 = x2; + x2 = a; + } + + if (z2 < z1) { + int a = z1; + z1 = z2; + z2 = a; + } + + int surfMax = posY - 1; + for (int x = x1; x <= x2; x++) { + mut.setX(x); + for (int z = z1; z <= z2; z++) { + mut.setZ(z); + mut.setY(surfMax); + BlockState state = world.getBlockState(mut); + if (!state.is(BCLibTags.GEN_TERRAIN) && state.isFaceSturdy(world, mut, Direction.DOWN)) { + for (int i = 0; i < 10; i++) { + mut.setY(mut.getY() - 1); + BlockState stateSt = world.getBlockState(mut); + if (!stateSt.is(BCLibTags.GEN_TERRAIN)) { + if (merge == TerrainMerge.SURFACE) { + SurfaceBuilderConfiguration config = world.getBiome(mut).getGenerationSettings().getSurfaceBuilderConfig(); + boolean isTop = mut.getY() == surfMax && state.getMaterial().isSolidBlocking(); + BlockState top = isTop ? config.getTopMaterial() : config.getUnderMaterial(); + BlocksHelper.setWithoutUpdate(world, mut, top); + } + else { + BlocksHelper.setWithoutUpdate(world, mut, state); + } + } + else { + if (stateSt.is(BCLibTags.END_GROUND) && state.getMaterial().isSolidBlocking()) { + if (merge == TerrainMerge.SURFACE) { + SurfaceBuilderConfiguration config = world.getBiome(mut).getGenerationSettings() + .getSurfaceBuilderConfig(); + BlocksHelper.setWithoutUpdate(world, mut, config.getUnderMaterial()); + } + else { + BlocksHelper.setWithoutUpdate(world, mut, state); + } + } + break; + } + } + } + } + } + } + //BlocksHelper.fixBlocks(world, new BlockPos(x1, center.getY(), z1), new BlockPos(x2, center.getY() + offset.getY(), z2)); + + return true; + } + + protected BoundingBox makeBox(BlockPos pos) { + int sx = ((pos.getX() >> 4) << 4) - 16; + int sz = ((pos.getZ() >> 4) << 4) - 16; + int ex = sx + 47; + int ez = sz + 47; + return BoundingBox.createProper(sx, 0, sz, ex, 255, ez); + } + + protected static StructureTemplate readStructure(ResourceLocation 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 StructureTemplate readStructureFromStream(InputStream stream) throws IOException { + CompoundTag nbttagcompound = NbtIo.readCompressed(stream); + + StructureTemplate template = new StructureTemplate(); + template.load(nbttagcompound); + + return template; + } + + public static enum TerrainMerge { + NONE, SURFACE, OBJECT; + + public static TerrainMerge getFromString(String type) { + if (type.equals("surface")) { + return SURFACE; + } + else if (type.equals("object")) { + return OBJECT; + } + else { + return NONE; + } + } + } +} diff --git a/src/main/java/ru/bclib/api/SurfaceBuilders.java b/src/main/java/ru/bclib/world/surface/BCLSurfaceBuilders.java similarity index 89% rename from src/main/java/ru/bclib/api/SurfaceBuilders.java rename to src/main/java/ru/bclib/world/surface/BCLSurfaceBuilders.java index cdbf6e36..641eeace 100644 --- a/src/main/java/ru/bclib/api/SurfaceBuilders.java +++ b/src/main/java/ru/bclib/world/surface/BCLSurfaceBuilders.java @@ -1,4 +1,4 @@ -package ru.bclib.api; +package ru.bclib.world.surface; import net.minecraft.core.Registry; import net.minecraft.world.level.block.Block; @@ -6,7 +6,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilder; import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilderBaseConfiguration; -public class SurfaceBuilders { +public class BCLSurfaceBuilders { public static SurfaceBuilder register(String name, SurfaceBuilder builder) { return Registry.register(Registry.SURFACE_BUILDER, name, builder); } diff --git a/src/main/resources/bclib.mixins.common.json b/src/main/resources/bclib.mixins.common.json index f88b1ac2..2d182b8c 100644 --- a/src/main/resources/bclib.mixins.common.json +++ b/src/main/resources/bclib.mixins.common.json @@ -4,6 +4,7 @@ "package": "ru.bclib.mixin.common", "compatibilityLevel": "JAVA_8", "mixins": [ + "BoneMealItemMixin", "TagLoaderMixin" ], "injectors": {