Mangrove Trees!

- Todo: fix the moss not generating on roots
This commit is contained in:
ItsBlackGear 2022-07-16 03:46:02 -04:00
parent 484129642c
commit b698b03a9a
45 changed files with 1267 additions and 13 deletions

View file

@ -9,6 +9,10 @@ import com.cursedcauldron.wildbackport.common.registry.WBEnchantments;
import com.cursedcauldron.wildbackport.common.registry.WBGameEvents;
import com.cursedcauldron.wildbackport.common.registry.WBItems;
import com.cursedcauldron.wildbackport.common.registry.WBPositionSources;
import com.cursedcauldron.wildbackport.common.registry.worldgen.WBFeatures;
import com.cursedcauldron.wildbackport.common.registry.worldgen.RootPlacerType;
import com.cursedcauldron.wildbackport.common.registry.worldgen.WBTreeDecorators;
import com.cursedcauldron.wildbackport.common.registry.worldgen.WBTrunkPlacers;
import com.cursedcauldron.wildbackport.common.registry.entity.WBActivities;
import com.cursedcauldron.wildbackport.common.registry.entity.WBEntities;
import com.cursedcauldron.wildbackport.common.registry.entity.WBMemoryModules;
@ -36,12 +40,16 @@ public class WildBackport {
WBEnchantments.ENCHANTMENTS.register();
WBEntities.ENTITIES.register();
WBGameEvents.EVENTS.register();
WBFeatures.FEATURES.register();
WBItems.ITEMS.register();
WBMemoryModules.MEMORIES.register();
WBParticleTypes.PARTICLES.register();
WBPositionSources.SOURCES.register();
RootPlacerType.PLACERS.register();
WBSensorTypes.SENSORS.register();
WBSoundEvents.SOUNDS.register();
WBTreeDecorators.DECORATORS.register();
WBTrunkPlacers.PLACERS.register();
// Tags
WBBiomeTags.TAGS.bootstrap();

View file

@ -6,6 +6,8 @@ import com.cursedcauldron.wildbackport.common.entities.Tadpole;
import com.cursedcauldron.wildbackport.common.entities.Warden;
import com.cursedcauldron.wildbackport.common.events.StructureEvent;
import com.cursedcauldron.wildbackport.common.registry.entity.WBEntities;
import com.cursedcauldron.wildbackport.common.registry.worldgen.WBWorldGeneration;
import com.cursedcauldron.wildbackport.core.api.Environment;
import com.cursedcauldron.wildbackport.core.api.MobRegistry;
public class CommonSetup {
@ -18,13 +20,15 @@ public class CommonSetup {
MobRegistry.registerAttributes(WBEntities.FROG, Frog::createAttributes);
MobRegistry.registerAttributes(WBEntities.TADPOLE, Tadpole::createAttributes);
MobRegistry.registerAttributes(WBEntities.WARDEN, Warden::createAttributes);
StructureEvent.bootstrap();
// StructureEvent.bootstrap();
if (Environment.isFabric()) WBWorldGeneration.bootstrap();
}
/**
* Runs features post bootstrap
*/
public static void onPostClient() {
StructureEvent.bootstrap();
public static void onPostCommon() {
// StructureEvent.bootstrap();
if (Environment.isForge()) WBWorldGeneration.bootstrap();
}
}

View file

@ -1,5 +1,6 @@
package com.cursedcauldron.wildbackport.common.blocks;
import com.cursedcauldron.wildbackport.common.registry.worldgen.WBWorldGeneration;
import net.minecraft.core.Holder;
import net.minecraft.world.level.block.grower.AbstractTreeGrower;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
@ -8,14 +9,14 @@ import org.jetbrains.annotations.Nullable;
import java.util.Random;
public class MangroveTreeGrower extends AbstractTreeGrower {
private final float tallMangroveChance;
private final float tallChance;
public MangroveTreeGrower(float tallMangroveChance) {
this.tallMangroveChance = tallMangroveChance;
public MangroveTreeGrower(float tallChance) {
this.tallChance = tallChance;
}
@Override @Nullable
protected Holder<? extends ConfiguredFeature<?, ?>> getConfiguredFeature(Random random, boolean bl) {
return null;
protected Holder<? extends ConfiguredFeature<?, ?>> getConfiguredFeature(Random random, boolean bees) {
return random.nextFloat() < this.tallChance ? WBWorldGeneration.TALL_MANGROVE : WBWorldGeneration.MANGROVE;
}
}

View file

@ -0,0 +1,26 @@
package com.cursedcauldron.wildbackport.common.registry.worldgen;
import com.cursedcauldron.wildbackport.WildBackport;
import com.cursedcauldron.wildbackport.common.worldgen.placers.MangroveRootPlacer;
import com.cursedcauldron.wildbackport.common.worldgen.placers.RootPlacer;
import com.cursedcauldron.wildbackport.core.api.CoreRegistry;
import com.mojang.serialization.Codec;
import java.util.function.Supplier;
public class RootPlacerType<P extends RootPlacer> {
public static final CoreRegistry<RootPlacerType<?>> PLACERS = CoreRegistry.create(WBRegistries.ROOT_PLACER_TYPES.getSecond(), WildBackport.MOD_ID);
public static final Supplier<RootPlacerType<MangroveRootPlacer>> MANGROVE_ROOT_PLACER = PLACERS.register("mangrove_root_placer", () -> new RootPlacerType<>(MangroveRootPlacer.CODEC));
private final Codec<P> codec;
private RootPlacerType(Codec<P> codec) {
this.codec = codec;
}
public Codec<P> codec() {
return this.codec;
}
}

View file

@ -0,0 +1,18 @@
package com.cursedcauldron.wildbackport.common.registry.worldgen;
import com.cursedcauldron.wildbackport.WildBackport;
import com.cursedcauldron.wildbackport.common.worldgen.features.RootedTreeConfig;
import com.cursedcauldron.wildbackport.common.worldgen.features.RootedTreeFeature;
import com.cursedcauldron.wildbackport.core.api.CoreRegistry;
import net.minecraft.core.Registry;
import net.minecraft.world.level.levelgen.feature.Feature;
import java.util.function.Supplier;
//<>
public class WBFeatures {
public static final CoreRegistry<Feature<?>> FEATURES = CoreRegistry.create(Registry.FEATURE, WildBackport.MOD_ID);
public static final Supplier<Feature<RootedTreeConfig>> TREE = FEATURES.register("tree", () -> new RootedTreeFeature(RootedTreeConfig.CODEC));
}

View file

@ -0,0 +1,20 @@
package com.cursedcauldron.wildbackport.common.registry.worldgen;
import com.cursedcauldron.wildbackport.WildBackport;
import com.cursedcauldron.wildbackport.core.mixin.access.RegistryAccessor;
import com.mojang.datafixers.util.Pair;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
//<>
public class WBRegistries {
public static final Pair<ResourceKey<Registry<RootPlacerType<?>>>, Registry<RootPlacerType<?>>> ROOT_PLACER_TYPES = create("worldgen/root_placer_type", registry -> RootPlacerType.MANGROVE_ROOT_PLACER.get());
private static <T> Pair<ResourceKey<Registry<T>>, Registry<T>> create(String key, Registry.RegistryBootstrap<T> bootstrap) {
ResourceKey<Registry<T>> resource = ResourceKey.createRegistryKey(new ResourceLocation(WildBackport.MOD_ID, key));
Registry<T> registry = RegistryAccessor.callRegisterSimple(resource, bootstrap);
return Pair.of(resource, registry);
}
}

View file

@ -0,0 +1,23 @@
package com.cursedcauldron.wildbackport.common.registry.worldgen;
import com.cursedcauldron.wildbackport.WildBackport;
import com.cursedcauldron.wildbackport.common.worldgen.decorator.AttachedToLeavesDecorator;
import com.cursedcauldron.wildbackport.common.worldgen.decorator.WeightedLeaveVineDecorator;
import com.cursedcauldron.wildbackport.common.worldgen.placers.UpwardBranchingTrunk;
import com.cursedcauldron.wildbackport.core.api.CoreRegistry;
import com.cursedcauldron.wildbackport.core.mixin.access.TreeDecoratorTypeAccessor;
import com.cursedcauldron.wildbackport.core.mixin.access.TrunkPlacerTypeAccessor;
import net.minecraft.core.Registry;
import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecoratorType;
import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacerType;
import java.util.function.Supplier;
//<>
public class WBTreeDecorators {
public static final CoreRegistry<TreeDecoratorType<?>> DECORATORS = CoreRegistry.create(Registry.TREE_DECORATOR_TYPES, WildBackport.MOD_ID);
public static final Supplier<TreeDecoratorType<WeightedLeaveVineDecorator>> WEIGHTED_LEAVE_VINE = DECORATORS.register("leave_vine", () -> TreeDecoratorTypeAccessor.createTreeDecoratorType(WeightedLeaveVineDecorator.CODEC));
public static final Supplier<TreeDecoratorType<AttachedToLeavesDecorator>> ATTACHED_TO_LEAVES = DECORATORS.register("attached_to_leaves", () -> TreeDecoratorTypeAccessor.createTreeDecoratorType(AttachedToLeavesDecorator.CODEC));
}

View file

@ -0,0 +1,18 @@
package com.cursedcauldron.wildbackport.common.registry.worldgen;
import com.cursedcauldron.wildbackport.WildBackport;
import com.cursedcauldron.wildbackport.common.worldgen.placers.UpwardBranchingTrunk;
import com.cursedcauldron.wildbackport.core.api.CoreRegistry;
import com.cursedcauldron.wildbackport.core.mixin.access.TrunkPlacerTypeAccessor;
import net.minecraft.core.Registry;
import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacerType;
import java.util.function.Supplier;
//<>
public class WBTrunkPlacers {
public static final CoreRegistry<TrunkPlacerType<?>> PLACERS = CoreRegistry.create(Registry.TRUNK_PLACER_TYPES, WildBackport.MOD_ID);
public static final Supplier<TrunkPlacerType<UpwardBranchingTrunk>> UPWARDS_BRANCHING_TRUNK = PLACERS.register("upward_branching_trunk", () -> TrunkPlacerTypeAccessor.createTrunkPlacerType(UpwardBranchingTrunk.CODEC));
}

View file

@ -0,0 +1,98 @@
package com.cursedcauldron.wildbackport.common.registry.worldgen;
import com.cursedcauldron.wildbackport.WildBackport;
import com.cursedcauldron.wildbackport.common.blocks.MangrovePropaguleBlock;
import com.cursedcauldron.wildbackport.common.registry.WBBlocks;
import com.cursedcauldron.wildbackport.common.tag.WBBlockTags;
import com.cursedcauldron.wildbackport.common.worldgen.decorator.AttachedToLeavesDecorator;
import com.cursedcauldron.wildbackport.common.worldgen.decorator.LayerRootDecorator;
import com.cursedcauldron.wildbackport.common.worldgen.decorator.MangroveRootPlacement;
import com.cursedcauldron.wildbackport.common.worldgen.decorator.WeightedLeaveVineDecorator;
import com.cursedcauldron.wildbackport.common.worldgen.features.RootedTreeConfig;
import com.cursedcauldron.wildbackport.common.worldgen.placers.MangroveRootPlacer;
import com.cursedcauldron.wildbackport.common.worldgen.placers.UpwardBranchingTrunk;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.core.Registry;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.data.worldgen.placement.PlacementUtils;
import net.minecraft.util.valueproviders.ConstantInt;
import net.minecraft.util.valueproviders.UniformInt;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.WeightedPlacedFeature;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.world.level.levelgen.feature.configurations.RandomFeatureConfiguration;
import net.minecraft.world.level.levelgen.feature.featuresize.TwoLayersFeatureSize;
import net.minecraft.world.level.levelgen.feature.foliageplacers.RandomSpreadFoliagePlacer;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
import net.minecraft.world.level.levelgen.feature.stateproviders.RandomizedIntStateProvider;
import net.minecraft.world.level.levelgen.feature.treedecorators.BeehiveDecorator;
import net.minecraft.world.level.levelgen.placement.BiomeFilter;
import net.minecraft.world.level.levelgen.placement.BlockPredicateFilter;
import net.minecraft.world.level.levelgen.placement.CountPlacement;
import net.minecraft.world.level.levelgen.placement.InSquarePlacement;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraft.world.level.levelgen.placement.PlacementModifier;
import net.minecraft.world.level.levelgen.placement.SurfaceWaterDepthFilter;
import java.util.List;
import java.util.Optional;
//<>
public class WBWorldGeneration {
public static void bootstrap() {}
// Mangrove Swamp
public static final Holder<ConfiguredFeature<RootedTreeConfig, ?>> MANGROVE = config("mangrove", WBFeatures.TREE.get(), new RootedTreeConfig.Builder(
BlockStateProvider.simple(WBBlocks.MANGROVE_LOG.get()),
new UpwardBranchingTrunk(2, 1, 4, UniformInt.of(1, 4), 0.5F, UniformInt.of(0, 1), Registry.BLOCK.getOrCreateTag(WBBlockTags.MANGROVE_LOGS_CAN_GROW_THROUGH)),
BlockStateProvider.simple(WBBlocks.MANGROVE_LEAVES.get()),
new RandomSpreadFoliagePlacer(ConstantInt.of(3), ConstantInt.of(0), ConstantInt.of(2), 70),
Optional.of(new MangroveRootPlacer(UniformInt.of(1, 3), BlockStateProvider.simple(WBBlocks.MANGROVE_ROOTS.get()), Optional.of(new LayerRootDecorator(BlockStateProvider.simple(Blocks.MOSS_CARPET), 0.5F)), new MangroveRootPlacement(Registry.BLOCK.getOrCreateTag(WBBlockTags.MANGROVE_ROOTS_CAN_GROW_THROUGH), HolderSet.direct(Block::builtInRegistryHolder, WBBlocks.MUD.get(), WBBlocks.MUDDY_MANGROVE_ROOTS.get()), BlockStateProvider.simple(WBBlocks.MUDDY_MANGROVE_ROOTS.get()), 8, 15, 0.2F))),
new TwoLayersFeatureSize(2, 0, 2)
).decorators(List.of(
new WeightedLeaveVineDecorator(0.125F),
new AttachedToLeavesDecorator(0.14F, 1, 0, new RandomizedIntStateProvider(BlockStateProvider.simple(WBBlocks.MANGROVE_PROPAGULE.get().defaultBlockState().setValue(MangrovePropaguleBlock.HANGING, true)), MangrovePropaguleBlock.AGE, UniformInt.of(0, 4)), 2, List.of(Direction.DOWN)),
new BeehiveDecorator(0.01F)
)).ignoreVines().build());
public static final Holder<ConfiguredFeature<RootedTreeConfig, ?>> TALL_MANGROVE = config("tall_mangrove", WBFeatures.TREE.get(), new RootedTreeConfig.Builder(
BlockStateProvider.simple(WBBlocks.MANGROVE_LOG.get()),
new UpwardBranchingTrunk(4, 1, 9, UniformInt.of(1, 6), 0.5F, UniformInt.of(0, 1), Registry.BLOCK.getOrCreateTag(WBBlockTags.MANGROVE_LOGS_CAN_GROW_THROUGH)),
BlockStateProvider.simple(WBBlocks.MANGROVE_LEAVES.get()),
new RandomSpreadFoliagePlacer(ConstantInt.of(3), ConstantInt.of(0), ConstantInt.of(2), 70),
Optional.of(new MangroveRootPlacer(UniformInt.of(3, 7), BlockStateProvider.simple(WBBlocks.MANGROVE_ROOTS.get()), Optional.of(new LayerRootDecorator(BlockStateProvider.simple(Blocks.MOSS_CARPET), 0.5F)), new MangroveRootPlacement(Registry.BLOCK.getOrCreateTag(WBBlockTags.MANGROVE_ROOTS_CAN_GROW_THROUGH), HolderSet.direct(Block::builtInRegistryHolder, WBBlocks.MUD.get(), WBBlocks.MUDDY_MANGROVE_ROOTS.get()), BlockStateProvider.simple(WBBlocks.MUDDY_MANGROVE_ROOTS.get()), 8, 15, 0.2F))),
new TwoLayersFeatureSize(3, 0, 2)
).decorators(List.of(
new WeightedLeaveVineDecorator(0.125F),
new AttachedToLeavesDecorator(0.14F, 1, 0, new RandomizedIntStateProvider(BlockStateProvider.simple(WBBlocks.MANGROVE_PROPAGULE.get().defaultBlockState().setValue(MangrovePropaguleBlock.HANGING, true)), MangrovePropaguleBlock.AGE, UniformInt.of(0, 4)), 2, List.of(Direction.DOWN)),
new BeehiveDecorator(0.01F)
)).ignoreVines().build());
public static final Holder<PlacedFeature> MANGROVE_CHECKED = place("mangrove_checked", MANGROVE, PlacementUtils.filteredByBlockSurvival(WBBlocks.MANGROVE_PROPAGULE.get()));
public static final Holder<PlacedFeature> TALL_MANGROVE_CHECKED = place("tall_mangrove_checked", TALL_MANGROVE, PlacementUtils.filteredByBlockSurvival(WBBlocks.MANGROVE_PROPAGULE.get()));
public static final Holder<ConfiguredFeature<RandomFeatureConfiguration, ?>> MANGROVE_VEGETATION = config("mangrove_vegetation", Feature.RANDOM_SELECTOR, new RandomFeatureConfiguration(List.of(new WeightedPlacedFeature(TALL_MANGROVE_CHECKED, 0.85F)), MANGROVE_CHECKED));
public static final Holder<PlacedFeature> TREES_MANGROVE = place("trees_mangrove", MANGROVE_VEGETATION, CountPlacement.of(25), InSquarePlacement.spread(), SurfaceWaterDepthFilter.forMaxDepth(5), PlacementUtils.HEIGHTMAP_OCEAN_FLOOR, BiomeFilter.biome(), BlockPredicateFilter.forPredicate(BlockPredicate.wouldSurvive(WBBlocks.MANGROVE_PROPAGULE.get().defaultBlockState(), BlockPos.ZERO)));
// Deep Dark
// Registry
public static <FC extends FeatureConfiguration, F extends Feature<FC>> Holder<ConfiguredFeature<FC, ?>> config(String key, F feature, FC configuration) {
return BuiltinRegistries.registerExact(BuiltinRegistries.CONFIGURED_FEATURE, WildBackport.MOD_ID + ":" + key, new ConfiguredFeature<>(feature, configuration));
}
public static Holder<PlacedFeature> place(String key, Holder<? extends ConfiguredFeature<?, ?>> feature, PlacementModifier... placements) {
return BuiltinRegistries.registerExact(BuiltinRegistries.PLACED_FEATURE, WildBackport.MOD_ID + ":" + key, new PlacedFeature(Holder.hackyErase(feature), List.copyOf(List.of(placements))));
}
}

View file

@ -9,8 +9,14 @@ import net.minecraft.world.level.block.Block;
public class WBBlockTags {
public static final TagRegistry<Block> TAGS = TagRegistry.create(Registry.BLOCK, WildBackport.MOD_ID);
public static final TagKey<Block> FROG_PREFER_JUMP_TO = TAGS.create("frog_prefer_jump_to");
public static final TagKey<Block> FROGS_SPAWNABLE_ON = TAGS.create("frogs_spawnable_on");
public static final TagKey<Block> SCULK_REPLACEABLE = TAGS.create("sculk_replaceable");
public static final TagKey<Block> SCULK_REPLACEABLE_WORLD_GEN = TAGS.create("sculk_replaceable_world_gen");
// Mangrove Swamp
public static final TagKey<Block> MANGROVE_LOGS_CAN_GROW_THROUGH = TAGS.create("mangrove_logs_can_grow_through");
public static final TagKey<Block> MANGROVE_ROOTS_CAN_GROW_THROUGH = TAGS.create("mangrove_roots_can_grow_through");
public static final TagKey<Block> FROG_PREFER_JUMP_TO = TAGS.create("frog_prefer_jump_to");
public static final TagKey<Block> FROGS_SPAWNABLE_ON = TAGS.create("frogs_spawnable_on");
// Deep Dark
public static final TagKey<Block> SCULK_REPLACEABLE = TAGS.create("sculk_replaceable");
public static final TagKey<Block> SCULK_REPLACEABLE_WORLD_GEN = TAGS.create("sculk_replaceable_world_gen");
}

View file

@ -0,0 +1,85 @@
package com.cursedcauldron.wildbackport.common.worldgen.decorator;
import com.cursedcauldron.wildbackport.common.registry.worldgen.WBTreeDecorators;
import com.cursedcauldron.wildbackport.common.utils.ModUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecorator;
import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecoratorType;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.function.BiConsumer;
public class AttachedToLeavesDecorator extends TreeDecorator {
public static final Codec<AttachedToLeavesDecorator> CODEC = RecordCodecBuilder.create(instance -> {
return instance.group(Codec.floatRange(0.0F, 1.0F).fieldOf("probability").forGetter(decorator -> {
return decorator.probability;
}), Codec.intRange(0, 16).fieldOf("exclusion_radius_xz").forGetter(decorator -> {
return decorator.exclusionRadiusXZ;
}), Codec.intRange(0, 16).fieldOf("exclusion_radius_y").forGetter(decorator -> {
return decorator.exclusionRadiusY;
}), BlockStateProvider.CODEC.fieldOf("block_provider").forGetter(decorator -> {
return decorator.blockProvider;
}), Codec.intRange(1, 16).fieldOf("required_empty_blocks").forGetter(decorator -> {
return decorator.requiredEmptyBlocks;
}), Direction.CODEC.listOf().fieldOf("directions").forGetter(decorator -> {
return decorator.directions;
})).apply(instance, AttachedToLeavesDecorator::new);
});
protected final float probability;
protected final int exclusionRadiusXZ;
protected final int exclusionRadiusY;
protected final BlockStateProvider blockProvider;
protected final int requiredEmptyBlocks;
protected final List<Direction> directions;
public AttachedToLeavesDecorator(float probability, int exclusionRadiusXZ, int exclusionRadiusY, BlockStateProvider blockProvider, int requiredEmptyBlocks, List<Direction> directions) {
this.probability = probability;
this.exclusionRadiusXZ = exclusionRadiusXZ;
this.exclusionRadiusY = exclusionRadiusY;
this.blockProvider = blockProvider;
this.requiredEmptyBlocks = requiredEmptyBlocks;
this.directions = directions;
}
@Override
public void place(LevelSimulatedReader level, BiConsumer<BlockPos, BlockState> replacer, Random random, List<BlockPos> trunkPositions, List<BlockPos> foliagePositions) {
HashSet<BlockPos> positions = new HashSet<>();
for (BlockPos pos : ModUtils.copyShuffled(new ObjectArrayList<>(foliagePositions), random)) {
Direction direction = Util.getRandom(this.directions, random);
BlockPos offset = pos.relative(direction);
if (!positions.contains(offset) && random.nextFloat() < this.probability && this.meetsRequiredEmptyBlocks(level, pos, direction)) {
BlockPos minRadius = offset.offset(-this.exclusionRadiusXZ, -this.exclusionRadiusY, -this.exclusionRadiusXZ);
BlockPos maxRadius = offset.offset(this.exclusionRadiusXZ, this.exclusionRadiusY, this.exclusionRadiusXZ);
for (BlockPos position : BlockPos.betweenClosed(minRadius, maxRadius)) {
positions.add(position.immutable());
}
replacer.accept(offset, this.blockProvider.getState(random, offset));
}
}
}
private boolean meetsRequiredEmptyBlocks(LevelSimulatedReader level, BlockPos pos, Direction direction) {
for (int i = 1; i <= this.requiredEmptyBlocks; i++) {
if (!Feature.isAir(level, pos.relative(direction, i))) return false;
}
return true;
}
@Override
protected TreeDecoratorType<?> type() {
return WBTreeDecorators.ATTACHED_TO_LEAVES.get();
}
}

View file

@ -0,0 +1,15 @@
package com.cursedcauldron.wildbackport.common.worldgen.decorator;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
public record LayerRootDecorator(BlockStateProvider aboveRootProvider, float aboveRootPlacementChance) {
public static final Codec<LayerRootDecorator> CODEC = RecordCodecBuilder.create(instance -> {
return instance.group(BlockStateProvider.CODEC.fieldOf("above_root_provider").forGetter(decorator -> {
return decorator.aboveRootProvider;
}), Codec.floatRange(0.0F, 1.0F).fieldOf("above_root_placement_chance").forGetter(decorator -> {
return decorator.aboveRootPlacementChance;
})).apply(instance, LayerRootDecorator::new);
});
}

View file

@ -0,0 +1,27 @@
package com.cursedcauldron.wildbackport.common.worldgen.decorator;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.HolderSet;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryCodecs;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
public record MangroveRootPlacement(HolderSet<Block> canGrowThrough, HolderSet<Block> muddyRootsIn, BlockStateProvider muddyRootsProvider, int maxRootWidth, int maxRootLength, float randomSkewChance) {
public static final Codec<MangroveRootPlacement> CODEC = RecordCodecBuilder.create(instance -> {
return instance.group(RegistryCodecs.homogeneousList(Registry.BLOCK_REGISTRY).fieldOf("can_grow_through").forGetter(placer -> {
return placer.canGrowThrough;
}), RegistryCodecs.homogeneousList(Registry.BLOCK_REGISTRY).fieldOf("muddy_roots_in").forGetter(placer -> {
return placer.muddyRootsIn;
}), BlockStateProvider.CODEC.fieldOf("muddy_roots_provider").forGetter(placer -> {
return placer.muddyRootsProvider;
}), Codec.intRange(1, 12).fieldOf("max_root_width").forGetter(placer -> {
return placer.maxRootWidth;
}), Codec.intRange(1, 64).fieldOf("max_root_length").forGetter(placer -> {
return placer.maxRootLength;
}), Codec.floatRange(0.0F, 1.0F).fieldOf("random_skew_chance").forGetter(placer -> {
return placer.randomSkewChance;
})).apply(instance, MangroveRootPlacement::new);
});
}

View file

@ -0,0 +1,55 @@
package com.cursedcauldron.wildbackport.common.worldgen.decorator;
import com.cursedcauldron.wildbackport.common.registry.worldgen.WBTreeDecorators;
import com.mojang.serialization.Codec;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.world.level.block.VineBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.treedecorators.LeaveVineDecorator;
import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecorator;
import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecoratorType;
import java.util.List;
import java.util.Random;
import java.util.function.BiConsumer;
//<>
public class WeightedLeaveVineDecorator extends TreeDecorator {
public static final Codec<WeightedLeaveVineDecorator> CODEC = Codec.floatRange(0.0F, 1.0F).fieldOf("probability").xmap(WeightedLeaveVineDecorator::new, decorator -> {
return decorator.probability;
}).codec();
private final float probability;
public WeightedLeaveVineDecorator(float probability) {
this.probability = probability;
}
@Override
public void place(LevelSimulatedReader level, BiConsumer<BlockPos, BlockState> replacer, Random random, List<BlockPos> trunkPositions, List<BlockPos> foliagePositions) {
foliagePositions.forEach(pos -> {
if (random.nextFloat() < this.probability && Feature.isAir(level, pos.west())) addHangingVine(level, pos.west(), VineBlock.EAST, replacer);
if (random.nextFloat() < this.probability && Feature.isAir(level, pos.east())) addHangingVine(level, pos.east(), VineBlock.WEST, replacer);
if (random.nextFloat() < this.probability && Feature.isAir(level, pos.north())) addHangingVine(level, pos.north(), VineBlock.SOUTH, replacer);
if (random.nextFloat() < this.probability && Feature.isAir(level, pos.south())) addHangingVine(level, pos.south(), VineBlock.NORTH, replacer);
});
}
private static void addHangingVine(LevelSimulatedReader level, BlockPos pos, BooleanProperty property, BiConsumer<BlockPos, BlockState> replacer) {
LeaveVineDecorator.placeVine(replacer, pos, property);
pos = pos.below();
for (int i = 4; Feature.isAir(level, pos) && i > 0; --i) {
LeaveVineDecorator.placeVine(replacer, pos, property);
pos = pos.below();
}
}
@Override
protected TreeDecoratorType<?> type() {
return WBTreeDecorators.WEIGHTED_LEAVE_VINE.get();
}
}

View file

@ -0,0 +1,101 @@
package com.cursedcauldron.wildbackport.common.worldgen.features;
import com.cursedcauldron.wildbackport.common.worldgen.placers.RootPlacer;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration;
import net.minecraft.world.level.levelgen.feature.featuresize.FeatureSize;
import net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacer;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecorator;
import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer;
import java.util.List;
import java.util.Optional;
//<>
public class RootedTreeConfig extends TreeConfiguration {
public static final Codec<RootedTreeConfig> CODEC = RecordCodecBuilder.create(instance -> {
return instance.group(BlockStateProvider.CODEC.fieldOf("trunk_provider").forGetter(config -> {
return config.trunkProvider;
}), TrunkPlacer.CODEC.fieldOf("trunk_placer").forGetter(config -> {
return config.trunkPlacer;
}), BlockStateProvider.CODEC.fieldOf("foliage_provider").forGetter(config -> {
return config.foliageProvider;
}), FoliagePlacer.CODEC.fieldOf("foliage_placer").forGetter(config -> {
return config.foliagePlacer;
}), RootPlacer.CODEC.optionalFieldOf("root_placer").forGetter(config -> {
return config.rootPlacer;
}), BlockStateProvider.CODEC.fieldOf("dirt_provider").forGetter(config -> {
return config.dirtProvider;
}), FeatureSize.CODEC.fieldOf("minimum_size").forGetter(config -> {
return config.minimumSize;
}), TreeDecorator.CODEC.listOf().fieldOf("decorators").forGetter(config -> {
return config.decorators;
}), Codec.BOOL.fieldOf("ignore_vines").orElse(false).forGetter(config -> {
return config.ignoreVines;
}), Codec.BOOL.fieldOf("force_dirt").orElse(false).forGetter(config -> {
return config.forceDirt;
})).apply(instance, RootedTreeConfig::new);
});
public final Optional<RootPlacer> rootPlacer;
protected RootedTreeConfig(BlockStateProvider trunkProvider, TrunkPlacer trunkPlacer, BlockStateProvider foliageProvider, FoliagePlacer foliagePlacer, Optional<RootPlacer> rootPlacer, BlockStateProvider dirtProvider, FeatureSize minimumSize, List<TreeDecorator> decorators, boolean ignoreVines, boolean forceDirt) {
super(trunkProvider, trunkPlacer, foliageProvider, foliagePlacer, dirtProvider, minimumSize, decorators, ignoreVines, forceDirt);
this.rootPlacer = rootPlacer;
}
public static class Builder {
public final BlockStateProvider trunkProvider;
private final TrunkPlacer trunkPlacer;
public final BlockStateProvider foliageProvider;
private final FoliagePlacer foliagePlacer;
private final Optional<RootPlacer> rootPlacer;
private BlockStateProvider dirtProvider;
private final FeatureSize minimumSize;
private List<TreeDecorator> decorators = ImmutableList.of();
private boolean ignoreVines;
private boolean forceDirt;
public Builder(BlockStateProvider trunkProvider, TrunkPlacer trunkPlacer, BlockStateProvider foliageProvider, FoliagePlacer foliagePlacer, Optional<RootPlacer> rootPlacer, FeatureSize minimumSize) {
this.trunkProvider = trunkProvider;
this.trunkPlacer = trunkPlacer;
this.foliageProvider = foliageProvider;
this.dirtProvider = BlockStateProvider.simple(Blocks.DIRT);
this.foliagePlacer = foliagePlacer;
this.rootPlacer = rootPlacer;
this.minimumSize = minimumSize;
}
public Builder(BlockStateProvider trunkProvider, TrunkPlacer trunkPlacer, BlockStateProvider foliageProvider, FoliagePlacer foliagePlacer, FeatureSize minimumSize) {
this(trunkProvider, trunkPlacer, foliageProvider, foliagePlacer, Optional.empty(), minimumSize);
}
public Builder dirtProvider(BlockStateProvider dirtProvider) {
this.dirtProvider = dirtProvider;
return this;
}
public Builder decorators(List<TreeDecorator> decorators) {
this.decorators = decorators;
return this;
}
public Builder ignoreVines() {
this.ignoreVines = true;
return this;
}
public Builder forceDirt() {
this.forceDirt = true;
return this;
}
public RootedTreeConfig build() {
return new RootedTreeConfig(this.trunkProvider, this.trunkPlacer, this.foliageProvider, this.foliagePlacer, this.rootPlacer, this.dirtProvider, this.minimumSize, this.decorators, this.ignoreVines, this.forceDirt);
}
}
}

View file

@ -0,0 +1,222 @@
package com.cursedcauldron.wildbackport.common.worldgen.features;
import com.cursedcauldron.wildbackport.common.worldgen.placers.UpwardBranchingTrunk;
import com.cursedcauldron.wildbackport.core.mixin.access.TreeFeatureAccessor;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.mojang.serialization.Codec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.TreeFeature;
import net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacer;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape;
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.OptionalInt;
import java.util.Random;
import java.util.Set;
import java.util.function.BiConsumer;
//<>
public class RootedTreeFeature extends Feature<RootedTreeConfig> {
public RootedTreeFeature(Codec<RootedTreeConfig> codec) {
super(codec);
}
private boolean generate(WorldGenLevel level, Random random, BlockPos pos, BiConsumer<BlockPos, BlockState> rootPlacerReplacer, BiConsumer<BlockPos, BlockState> trunkPlacerReplacer, BiConsumer<BlockPos, BlockState> foliagePlacerReplacer, RootedTreeConfig config) {
int treeHeight = config.trunkPlacer.getTreeHeight(random);
int foliageHeight = config.foliagePlacer.foliageHeight(random, treeHeight, config);
int trunkLength = treeHeight - foliageHeight;
int foliageRadius = config.foliagePlacer.foliageRadius(random, trunkLength);
BlockPos origin = config.rootPlacer.map(rootPlacer -> rootPlacer.trunkOffset(pos, random)).orElse(pos);
int minHeight = Math.min(pos.getY(), origin.getY());
int maxHeight = Math.max(pos.getY(), origin.getY()) + treeHeight + 1;
if (minHeight >= level.getMinBuildHeight() + 1 && maxHeight <= level.getMaxBuildHeight()) {
OptionalInt clippedHeight = config.minimumSize.minClippedHeight();
int topPosition = this.getTopPosition(level, treeHeight, origin, config);
if (topPosition >= treeHeight || clippedHeight.isPresent() && topPosition >= clippedHeight.getAsInt()) {
if (config.rootPlacer.isPresent() && !config.rootPlacer.get().generate(level, rootPlacerReplacer, random, pos, origin, config)) {
return false;
} else {
List<FoliagePlacer.FoliageAttachment> foliage = config.trunkPlacer.placeTrunk(level, trunkPlacerReplacer, random, topPosition, origin, config);
foliage.forEach(node -> config.foliagePlacer.createFoliage(level, foliagePlacerReplacer, random, config, topPosition, node, foliageHeight, foliageRadius));
return true;
}
} else {
return false;
}
} else {
return false;
}
}
@SuppressWarnings("ConstantConditions")
private int getTopPosition(LevelSimulatedReader level, int height, BlockPos pos, RootedTreeConfig config) {
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
for (int y = 0; y <= height + 1; ++y) {
int size = config.minimumSize.getSizeAtHeight(height, y);
for (int x = -size; x <= size; ++x) {
for (int z = -size; z <= size; ++z) {
mutable.setWithOffset(pos, x, y, z);
boolean isValid = (TreeFeature.validTreePos(level, pos) || level.isStateAtPosition(pos, state -> state.is(BlockTags.LOGS))) || (config.trunkPlacer instanceof UpwardBranchingTrunk placer && level.isStateAtPosition(pos, state -> state.is(placer.canGrowThrough)));
if (!isValid || !config.ignoreVines && TreeFeatureAccessor.isVine(level, mutable)) return y - 2;
}
}
}
return height;
}
@Override
public boolean place(FeaturePlaceContext<RootedTreeConfig> context) {
WorldGenLevel level = context.level();
Random random = context.random();
BlockPos pos = context.origin();
RootedTreeConfig config = context.config();
HashSet<BlockPos> rootPos = Sets.newHashSet();
HashSet<BlockPos> trunkPos = Sets.newHashSet();
HashSet<BlockPos> foliagePos = Sets.newHashSet();
HashSet<BlockPos> decoratorPos = Sets.newHashSet();
BiConsumer<BlockPos, BlockState> rootReplacer = (position, state) -> {
rootPos.add(position.immutable());
level.setBlock(position, state, 19);
};
BiConsumer<BlockPos, BlockState> trunkReplacer = (position, state) -> {
trunkPos.add(position.immutable());
level.setBlock(position, state, 19);
};
BiConsumer<BlockPos, BlockState> foliageReplacer = (position, state) -> {
foliagePos.add(position.immutable());
level.setBlock(position, state, 19);
};
BiConsumer<BlockPos, BlockState> decoratorReplacer = (position, state) -> {
decoratorPos.add(position.immutable());
level.setBlock(position, state, 19);
};
boolean generate = this.generate(level, random, pos, rootReplacer, trunkReplacer, foliageReplacer, config);
if (!generate || trunkPos.isEmpty() && foliagePos.isEmpty()) return false;
if (!config.decorators.isEmpty()) {
ArrayList<BlockPos> rootPositions = Lists.newArrayList(rootPos);
ArrayList<BlockPos> trunkPositions = Lists.newArrayList(trunkPos);
ArrayList<BlockPos> foliagePositions = Lists.newArrayList(foliagePos);
trunkPositions.sort(Comparator.comparingInt(Vec3i::getY));
foliagePositions.sort(Comparator.comparingInt(Vec3i::getY));
rootPositions.sort(Comparator.comparingInt(Vec3i::getY));
config.decorators.forEach(treeDecorator -> treeDecorator.place(level, decoratorReplacer, random, trunkPositions, foliagePositions));
}
return BoundingBox.encapsulatingPositions(Iterables.concat(trunkPos, foliagePos, decoratorPos)).map(box -> {
DiscreteVoxelShape shape = RootedTreeFeature.placeLogsAndLeaves(level, box, trunkPos, decoratorPos);
StructureTemplate.updateShapeAtEdge(level, Block.UPDATE_ALL, shape, box.minX(), box.minY(), box.minZ());
return true;
}).orElse(false);
}
private static DiscreteVoxelShape placeLogsAndLeaves(LevelAccessor level, BoundingBox box, Set<BlockPos> trunkPositions, Set<BlockPos> decoratorPositions) {
ArrayList<Set<BlockPos>> positions = Lists.newArrayList();
BitSetDiscreteVoxelShape shape = new BitSetDiscreteVoxelShape(box.getXSpan(), box.getYSpan(), box.getZSpan());
for (int tries = 0; tries < 6; ++tries) positions.add(Sets.newHashSet());
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
for (BlockPos pos : Lists.newArrayList(decoratorPositions)) if (box.isInside(pos)) shape.fill(pos.getX() - box.minX(), pos.getY() - box.minY(), pos.getZ() - box.minZ());
for (BlockPos pos : Lists.newArrayList(trunkPositions)) {
if (box.isInside(pos)) shape.fill(pos.getX() - box.minX(), pos.getY() - box.minY(), pos.getZ() - box.minZ());
for (Direction direction : Direction.values()) {
BlockState blockState;
mutable.setWithOffset(pos, direction);
if (trunkPositions.contains(mutable) || !(blockState = level.getBlockState(mutable)).hasProperty(BlockStateProperties.DISTANCE)) continue;
positions.get(0).add(mutable.immutable());
TreeFeatureAccessor.setBlockKnownShape(level, mutable, blockState.setValue(BlockStateProperties.DISTANCE, 1));
if (!box.isInside(mutable)) continue;
shape.fill(mutable.getX() - box.minX(), mutable.getY() - box.minY(), mutable.getZ() - box.minZ());
}
}
for (int tries = 1; tries < 6; ++tries) {
Set<BlockPos> trunkPos = positions.get(tries - 1);
Set<BlockPos> foliagePos = positions.get(tries);
for (BlockPos pos : trunkPos) {
if (box.isInside(pos)) shape.fill(pos.getX() - box.minX(), pos.getY() - box.minY(), pos.getZ() - box.minZ());
for (Direction direction : Direction.values()) {
mutable.setWithOffset(pos, direction);
BlockState state = level.getBlockState(mutable);
if (trunkPos.contains(mutable) || foliagePos.contains(mutable) || !state.hasProperty(BlockStateProperties.DISTANCE) || (state.getValue(BlockStateProperties.DISTANCE)) <= tries + 1) continue;
BlockState foliage = state.setValue(BlockStateProperties.DISTANCE, tries + 1);
TreeFeatureAccessor.setBlockKnownShape(level, mutable, foliage);
if (box.isInside(mutable)) (shape).fill(mutable.getX() - box.minX(), mutable.getY() - box.minY(), mutable.getZ() - box.minZ());
foliagePos.add(mutable.immutable());
}
}
}
return shape;
// ArrayList<Set<BlockPos>> positions = Lists.newArrayList();
// BitSetDiscreteVoxelShape shape = new BitSetDiscreteVoxelShape(box.getXSpan(), box.getYSpan(), box.getZSpan());
//
// for (int tries = 0; tries < 6; ++tries) positions.add(Sets.newHashSet());
//
// BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
// for (BlockPos pos : Lists.newArrayList(decoratorPositions)) if (box.isInside(pos)) shape.fill(pos.getX() - box.minX(), pos.getY() - box.minY(), pos.getZ() - box.minZ());
//
// for (BlockPos pos : Lists.newArrayList(trunkPositions)) {
// if (box.isInside(pos)) shape.fill(pos.getX() - box.minX(), pos.getY() - box.minY(), pos.getZ() - box.minZ());
//
// for (Direction direction : Direction.values()) {
// mutable.setWithOffset(pos, direction);
// if (trunkPositions.contains(mutable)) {
// BlockState state = level.getBlockState(mutable);
// if (state.hasProperty(BlockStateProperties.DISTANCE)) {
// positions.get(0).add(mutable.immutable());
// TreeFeatureAccessor.setBlockKnownShape(level, mutable, state.setValue(BlockStateProperties.DISTANCE, 1));
// if (box.isInside(mutable)) shape.fill(mutable.getX() - box.minX(), mutable.getY() - box.minY(), mutable.getZ() - box.minZ());
// }
// }
// }
// }
//
// for (int tries = 1; tries < 6; ++tries) {
// Set<BlockPos> trunkPos = positions.get(tries - 1);
// Set<BlockPos> foliagePos = positions.get(tries);
// for (BlockPos pos : trunkPos) {
// if (box.isInside(pos)) shape.fill(pos.getX() - box.minX(), pos.getY() - box.minY(), pos.getZ() - box.minZ());
//
// for (Direction direction : Direction.values()) {
// mutable.setWithOffset(pos, direction);
// if (!trunkPos.contains(mutable) && !foliagePos.contains(mutable)) {
// BlockState state = level.getBlockState(mutable);
// if (state.hasProperty(BlockStateProperties.DISTANCE) && state.getValue(BlockStateProperties.DISTANCE) > tries + 1) {
// BlockState foliage = state.setValue(BlockStateProperties.DISTANCE, tries + 1);
// TreeFeatureAccessor.setBlockKnownShape(level, mutable, foliage);
// if (box.isInside(mutable)) (shape).fill(mutable.getX() - box.minX(), mutable.getY() - box.minY(), mutable.getZ() - box.minZ());
// foliagePos.add(mutable.immutable());
// }
// }
// }
// }
// }
// return shape;
}
}

View file

@ -0,0 +1,114 @@
package com.cursedcauldron.wildbackport.common.worldgen.placers;
import com.cursedcauldron.wildbackport.common.registry.worldgen.RootPlacerType;
import com.cursedcauldron.wildbackport.common.worldgen.decorator.LayerRootDecorator;
import com.cursedcauldron.wildbackport.common.worldgen.decorator.MangroveRootPlacement;
import com.cursedcauldron.wildbackport.common.worldgen.features.RootedTreeConfig;
import com.google.common.collect.Lists;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.valueproviders.IntProvider;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.function.BiConsumer;
public class MangroveRootPlacer extends RootPlacer {
public static final Codec<MangroveRootPlacer> CODEC = RecordCodecBuilder.create(instance -> {
return codec(instance).and(MangroveRootPlacement.CODEC.fieldOf("mangrove_root_placement").forGetter(placer -> {
return placer.mangroveRootPlacement;
})).apply(instance, MangroveRootPlacer::new);
});
private final MangroveRootPlacement mangroveRootPlacement;
public MangroveRootPlacer(IntProvider trunkOffsetY, BlockStateProvider rootProvider, Optional<LayerRootDecorator> aboveRootPlacement, MangroveRootPlacement mangroveRootPlacement) {
super(trunkOffsetY, rootProvider, aboveRootPlacement);
this.mangroveRootPlacement = mangroveRootPlacement;
}
@Override
public boolean generate(LevelSimulatedReader level, BiConsumer<BlockPos, BlockState> replacer, Random random, BlockPos pos, BlockPos origin, RootedTreeConfig config) {
List<BlockPos> positions = Lists.newArrayList();
BlockPos.MutableBlockPos mutable = pos.mutable();
while(mutable.getY() < origin.getY()) {
if (!this.canGrowThrough(level, mutable)) return false;
mutable.move(Direction.UP);
}
positions.add(origin.below());
for(Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos position = origin.relative(direction);
List<BlockPos> offshootPositions = Lists.newArrayList();
if (!this.canGrow(level, random, position, direction, origin, offshootPositions, 0)) return false;
positions.addAll(offshootPositions);
positions.add(origin.relative(direction));
}
for(BlockPos position : positions) this.placeRoots(level, replacer, random, position, config);
return true;
}
private boolean canGrow(LevelSimulatedReader level, Random random, BlockPos pos, Direction direction, BlockPos origin, List<BlockPos> offshootPositions, int rootLength) {
int length = this.mangroveRootPlacement.maxRootLength();
if (rootLength != length && offshootPositions.size() <= length) {
for(BlockPos position : this.potentialRootPositions(pos, direction, random, origin)) {
if (this.canGrowThrough(level, position)) {
offshootPositions.add(position);
if (!this.canGrow(level, random, position, direction, origin, offshootPositions, rootLength + 1)) return false;
}
}
return true;
} else {
return false;
}
}
protected List<BlockPos> potentialRootPositions(BlockPos pos, Direction direction, Random random, BlockPos origin) {
BlockPos below = pos.below();
BlockPos offset = pos.relative(direction);
int distance = pos.distManhattan(origin);
int rootWidth = this.mangroveRootPlacement.maxRootWidth();
float chance = this.mangroveRootPlacement.randomSkewChance();
if (distance > rootWidth - 3 && distance <= rootWidth) {
return random.nextFloat() < chance ? List.of(below, offset.below()) : List.of(below);
} else if (distance > rootWidth) {
return List.of(below);
} else if (random.nextFloat() < chance) {
return List.of(below);
} else {
return random.nextBoolean() ? List.of(offset) : List.of(below);
}
}
@Override
protected boolean canGrowThrough(LevelSimulatedReader level, BlockPos pos) {
return super.canGrowThrough(level, pos) || level.isStateAtPosition(pos, state -> state.is(this.mangroveRootPlacement.canGrowThrough()));
}
@Override
protected void placeRoots(LevelSimulatedReader level, BiConsumer<BlockPos, BlockState> replacer, Random random, BlockPos pos, RootedTreeConfig config) {
if (level.isStateAtPosition(pos, state -> state.is(this.mangroveRootPlacement.muddyRootsIn()))) {
BlockState state = this.mangroveRootPlacement.muddyRootsProvider().getState(random, pos);
replacer.accept(pos, this.applyWaterlogging(level, pos, state));
} else {
super.placeRoots(level, replacer, random, pos, config);
}
}
@Override
protected RootPlacerType<?> getType() {
return RootPlacerType.MANGROVE_ROOT_PLACER.get();
}
}

View file

@ -0,0 +1,72 @@
package com.cursedcauldron.wildbackport.common.worldgen.placers;
import com.cursedcauldron.wildbackport.common.registry.worldgen.RootPlacerType;
import com.cursedcauldron.wildbackport.common.registry.worldgen.WBRegistries;
import com.cursedcauldron.wildbackport.common.worldgen.decorator.LayerRootDecorator;
import com.cursedcauldron.wildbackport.common.worldgen.features.RootedTreeConfig;
import com.mojang.datafixers.Products;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.valueproviders.IntProvider;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.levelgen.feature.TreeFeature;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
import java.util.Optional;
import java.util.Random;
import java.util.function.BiConsumer;
public abstract class RootPlacer {
public static final Codec<RootPlacer> CODEC = WBRegistries.ROOT_PLACER_TYPES.getSecond().byNameCodec().dispatch(RootPlacer::getType, RootPlacerType::codec);
protected final IntProvider trunkOffsetY;
protected final BlockStateProvider rootProvider;
protected final Optional<LayerRootDecorator> aboveRootPlacement;
protected static <P extends RootPlacer> Products.P3<RecordCodecBuilder.Mu<P>, IntProvider, BlockStateProvider, Optional<LayerRootDecorator>> codec(RecordCodecBuilder.Instance<P> instance) {
return instance.group(IntProvider.CODEC.fieldOf("trunk_offset_y").forGetter(placer -> {
return placer.trunkOffsetY;
}), BlockStateProvider.CODEC.fieldOf("root_provider").forGetter(placer -> {
return placer.rootProvider;
}), LayerRootDecorator.CODEC.optionalFieldOf("above_root_placement").forGetter(placer -> {
return placer.aboveRootPlacement;
}));
}
protected RootPlacer(IntProvider trunkOffsetY, BlockStateProvider rootProvider, Optional<LayerRootDecorator> aboveRootPlacement) {
this.trunkOffsetY = trunkOffsetY;
this.rootProvider = rootProvider;
this.aboveRootPlacement = aboveRootPlacement;
}
protected abstract RootPlacerType<?> getType();
public abstract boolean generate(LevelSimulatedReader level, BiConsumer<BlockPos, BlockState> replacer, Random random, BlockPos pos, BlockPos origin, RootedTreeConfig config);
protected boolean canGrowThrough(LevelSimulatedReader level, BlockPos pos) {
return TreeFeature.validTreePos(level, pos);
}
protected void placeRoots(LevelSimulatedReader level, BiConsumer<BlockPos, BlockState> replacer, Random random, BlockPos pos, RootedTreeConfig config) {
if (this.canGrowThrough(level, pos)) {
replacer.accept(pos, this.applyWaterlogging(level, pos, this.rootProvider.getState(random, pos)));
if (this.aboveRootPlacement.isPresent()) {
LayerRootDecorator decorator = this.aboveRootPlacement.get();
BlockPos aboveRoot = pos.above();
if (random.nextFloat() < decorator.aboveRootPlacementChance() && level.isStateAtPosition(pos, BlockBehaviour.BlockStateBase::isAir)) replacer.accept(aboveRoot, this.applyWaterlogging(level, aboveRoot, decorator.aboveRootProvider().getState(random, aboveRoot)));
}
}
}
protected BlockState applyWaterlogging(LevelSimulatedReader level, BlockPos pos, BlockState state) {
return state.hasProperty(BlockStateProperties.WATERLOGGED) ? state.setValue(BlockStateProperties.WATERLOGGED, level.isFluidAtPosition(pos, fluid -> fluid.is(FluidTags.WATER))) : state;
}
public BlockPos trunkOffset(BlockPos pos, Random random) {
return pos.above(this.trunkOffsetY.sample(random));
}
}

View file

@ -0,0 +1,102 @@
package com.cursedcauldron.wildbackport.common.worldgen.placers;
import com.cursedcauldron.wildbackport.common.registry.worldgen.WBTrunkPlacers;
import com.google.common.collect.Lists;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderSet;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryCodecs;
import net.minecraft.util.valueproviders.IntProvider;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration;
import net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacer;
import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer;
import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacerType;
import java.util.List;
import java.util.Random;
import java.util.function.BiConsumer;
//<>
public class UpwardBranchingTrunk extends TrunkPlacer {
public static final Codec<UpwardBranchingTrunk> CODEC = RecordCodecBuilder.create(instance -> {
return trunkPlacerParts(instance).and(instance.group(IntProvider.POSITIVE_CODEC.fieldOf("extra_branch_steps").forGetter(placer -> {
return placer.extraBranchSteps;
}), Codec.floatRange(0.0F, 1.0F).fieldOf("place_branch_per_log_probability").forGetter(placer -> {
return placer.placeBranchPerLogProbability;
}), IntProvider.NON_NEGATIVE_CODEC.fieldOf("extra_branch_length").forGetter(placer -> {
return placer.extraBranchLength;
}), RegistryCodecs.homogeneousList(Registry.BLOCK_REGISTRY).fieldOf("can_grow_through").forGetter(placer -> {
return placer.canGrowThrough;
}))).apply(instance, UpwardBranchingTrunk::new);
});
private final IntProvider extraBranchSteps;
private final float placeBranchPerLogProbability;
private final IntProvider extraBranchLength;
public final HolderSet<Block> canGrowThrough;
public UpwardBranchingTrunk(int baseHeight, int firstRandomHeight, int secondRandomHeight, IntProvider extraBranchSteps, float placeBranchPerLogProbability, IntProvider extraBranchLength, HolderSet<Block> canGrowThrough) {
super(baseHeight, firstRandomHeight, secondRandomHeight);
this.extraBranchSteps = extraBranchSteps;
this.placeBranchPerLogProbability = placeBranchPerLogProbability;
this.extraBranchLength = extraBranchLength;
this.canGrowThrough = canGrowThrough;
}
public List<FoliagePlacer.FoliageAttachment> placeTrunk(LevelSimulatedReader level, BiConsumer<BlockPos, BlockState> replacer, Random random, int height, BlockPos startPos, TreeConfiguration config) {
List<FoliagePlacer.FoliageAttachment> attachments = Lists.newArrayList();
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
for(int i = 0; i < height; ++i) {
int yOffset = startPos.getY() + i;
if (placeLog(level, replacer, random, mutable.set(startPos.getX(), yOffset, startPos.getZ()), config) && i < height - 1 && random.nextFloat() < this.placeBranchPerLogProbability) {
Direction direction = Direction.Plane.HORIZONTAL.getRandomDirection(random);
int offset = this.extraBranchLength.sample(random);
int length = Math.max(0, offset - this.extraBranchLength.sample(random) - 1);
int steps = this.extraBranchSteps.sample(random);
this.placeBranch(level, replacer, random, height, config, attachments, mutable, yOffset, direction, length, steps);
}
if (i == height - 1) attachments.add(new FoliagePlacer.FoliageAttachment(mutable.set(startPos.getX(), yOffset + 1, startPos.getZ()), 0, false));
}
return attachments;
}
private void placeBranch(LevelSimulatedReader level, BiConsumer<BlockPos, BlockState> replacer, Random random, int height, TreeConfiguration config, List<FoliagePlacer.FoliageAttachment> attachments, BlockPos.MutableBlockPos mutable, int yOffset, Direction direction, int length, int steps) {
int y = yOffset + length;
int x = mutable.getX();
int z = mutable.getZ();
for(int l = length; l < height && steps > 0; --steps) {
if (l >= 1) {
int offset = yOffset + l;
x += direction.getStepX();
z += direction.getStepZ();
y = offset;
if (placeLog(level, replacer, random, mutable.set(x, offset, z), config)) y = offset + 1;
attachments.add(new FoliagePlacer.FoliageAttachment(mutable.immutable(), 0, false));
}
++l;
}
if (y - yOffset > 1) {
BlockPos pos = new BlockPos(x, y, z);
attachments.add(new FoliagePlacer.FoliageAttachment(pos, 0, false));
attachments.add(new FoliagePlacer.FoliageAttachment(pos.below(2), 0, false));
}
}
@Override
protected TrunkPlacerType<?> type() {
return WBTrunkPlacers.UPWARDS_BRANCHING_TRUNK.get();
}
}

View file

@ -0,0 +1,23 @@
package com.cursedcauldron.wildbackport.core.api;
import dev.architectury.injectables.annotations.ExpectPlatform;
public class Environment {
public static boolean isForge() {
return getPlatform() == Platform.FORGE;
}
public static boolean isFabric() {
return getPlatform() == Platform.FABRIC;
}
@ExpectPlatform
public static Platform getPlatform() {
throw new AssertionError();
}
public enum Platform {
FORGE,
FABRIC
}
}

View file

@ -0,0 +1,14 @@
package com.cursedcauldron.wildbackport.core.mixin.access;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(Registry.class)
public interface RegistryAccessor {
@Invoker
static <T> Registry<T> callRegisterSimple(ResourceKey<? extends Registry<T>> resourceKey, Registry.RegistryBootstrap<T> registryBootstrap) {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,15 @@
package com.cursedcauldron.wildbackport.core.mixin.access;
import com.mojang.serialization.Codec;
import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecorator;
import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecoratorType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(TreeDecoratorType.class)
public interface TreeDecoratorTypeAccessor {
@Invoker("<init>")
static <P extends TreeDecorator> TreeDecoratorType<P> createTreeDecoratorType(Codec<P> codec) {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,32 @@
package com.cursedcauldron.wildbackport.core.mixin.access;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.world.level.LevelWriter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.TreeFeature;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(TreeFeature.class)
public interface TreeFeatureAccessor {
@Invoker("isVine")
static boolean isVine(LevelSimulatedReader levelSimulatedReader, BlockPos blockPos) {
throw new UnsupportedOperationException();
}
@Invoker("isBlockWater")
static boolean isBlockWater(LevelSimulatedReader levelSimulatedReader, BlockPos blockPos) {
throw new UnsupportedOperationException();
}
@Invoker("isReplaceablePlant")
static boolean isReplaceablePlant(LevelSimulatedReader levelSimulatedReader, BlockPos blockPos) {
throw new UnsupportedOperationException();
}
@Invoker("setBlockKnownShape")
static void setBlockKnownShape(LevelWriter levelWriter, BlockPos blockPos, BlockState blockState) {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,15 @@
package com.cursedcauldron.wildbackport.core.mixin.access;
import com.mojang.serialization.Codec;
import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer;
import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacerType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(TrunkPlacerType.class)
public interface TrunkPlacerTypeAccessor {
@Invoker("<init>")
static <P extends TrunkPlacer> TrunkPlacerType<P> createTrunkPlacerType(Codec<P> codec) {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"wildbackport:mangrove_leaves"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"#wildbackport:mangrove_logs"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"wildbackport:mangrove_roots"
]
}

View file

@ -0,0 +1,10 @@
{
"replace": false,
"values": [
"minecraft:sculk_sensor",
"wildbackport:sculk",
"wildbackport:sculk_catalyst",
"wildbackport:sculk_vein",
"wildbackport:sculk_shrieker"
]
}

View file

@ -0,0 +1,9 @@
{
"replace": false,
"values": [
"wildbackport:mud_bricks",
"wildbackport:mud_brick_stairs",
"wildbackport:mud_brick_slab",
"wildbackport:packed_mud"
]
}

View file

@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"wildbackport:mud",
"wildbackport:muddy_mangrove_roots"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"wildbackport:mangrove_planks"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"wildbackport:mud_brick_wall"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"wildbackport:mangrove_door"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"wildbackport:mangrove_fence"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"wildbackport:mangrove_slab"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"wildbackport:mangrove_stairs"
]
}

View file

@ -0,0 +1,9 @@
{
"replace": false,
"values": [
"wildbackport:mangrove_log",
"wildbackport:mangrove_wood",
"wildbackport:stripped_mangrove_log",
"wildbackport:stripped_mangrove_wood"
]
}

View file

@ -0,0 +1,13 @@
{
"replace": false,
"values": [
"wildbackport:mud",
"wildbackport:muddy_mangrove_roots",
"wildbackport:mangrove_roots",
"wildbackport:mangrove_leaves",
"wildbackport:mangrove_log",
"wildbackport:mangrove_propagule",
"minecraft:moss_carpet",
"minecraft:vine"
]
}

View file

@ -0,0 +1,12 @@
{
"replace": false,
"values": [
"wildbackport:mud",
"wildbackport:muddy_mangrove_roots",
"wildbackport:mangrove_roots",
"minecraft:moss_carpet",
"minecraft:vine",
"wildbackport:mangrove_propagule",
"minecraft:snow"
]
}

View file

@ -12,12 +12,16 @@
"access.ModelPartAccessor",
"access.PressurePlateBlockAccessor",
"access.RecordItemAccessor",
"access.RegistryAccessor",
"access.SensorTypeAccessor",
"access.SheetsAccessor",
"access.SimpleParticleTypeAccessor",
"access.StairBlockAccessor",
"access.StructureTemplatePoolAccessor",
"access.TrapDoorBlockAccessor",
"access.TreeDecoratorTypeAccessor",
"access.TreeFeatureAccessor",
"access.TrunkPlacerTypeAccessor",
"access.WalkNodeEvaluatorAccessor",
"access.WoodButtonBlockAccessor",
"access.WoodTypeAccessor",

View file

@ -4,5 +4,7 @@ transitive-accessible class net/minecraft/world/level/block/entity/BlockEntityTy
transitive-accessible class net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement$Placer
transitive-accessible class net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement$PieceState
transitive-accessible class net/minecraft/core/Registry$RegistryBootstrap
transitive-accessible method net/minecraft/client/particle/HugeExplosionParticle <init> (Lnet/minecraft/client/multiplayer/ClientLevel;DDDDLnet/minecraft/client/particle/SpriteSet;)V
transitive-accessible method net/minecraft/world/level/block/MultifaceBlock hasFace (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;)Z

View file

@ -0,0 +1,9 @@
package com.cursedcauldron.wildbackport.core.api.fabric;
import com.cursedcauldron.wildbackport.core.api.Environment;
public class EnvironmentImpl {
public static Environment.Platform getPlatform() {
return Environment.Platform.FABRIC;
}
}

View file

@ -4,5 +4,7 @@ transitive-accessible class net/minecraft/world/level/block/entity/BlockEntityTy
transitive-accessible class net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement$Placer
transitive-accessible class net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement$PieceState
transitive-accessible class net/minecraft/core/Registry$RegistryBootstrap
transitive-accessible method net/minecraft/client/particle/HugeExplosionParticle <init> (Lnet/minecraft/client/multiplayer/ClientLevel;DDDDLnet/minecraft/client/particle/SpriteSet;)V
transitive-accessible method net/minecraft/world/level/block/MultifaceBlock hasFace (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;)Z

View file

@ -0,0 +1,9 @@
package com.cursedcauldron.wildbackport.core.api.forge;
import com.cursedcauldron.wildbackport.core.api.Environment;
public class EnvironmentImpl {
public static Environment.Platform getPlatform() {
return Environment.Platform.FORGE;
}
}

View file

@ -17,7 +17,7 @@ public class WildBackportForge {
public WildBackportForge() {
IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();
EventBuses.registerModEventBus(WildBackport.MOD_ID, bus);
bus.<FMLCommonSetupEvent>addListener(event -> CommonSetup.onPostClient());
bus.<FMLCommonSetupEvent>addListener(event -> CommonSetup.onPostCommon());
bus.<FMLClientSetupEvent>addListener(event -> ClientSetup.onPostClient());
WildBackport.bootstrap();