diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/WildBackport.java b/common/src/main/java/com/cursedcauldron/wildbackport/WildBackport.java
index 911a69d..c06cf21 100644
--- a/common/src/main/java/com/cursedcauldron/wildbackport/WildBackport.java
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/WildBackport.java
@@ -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();
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/CommonSetup.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/CommonSetup.java
index 262aa81..9bbda02 100644
--- a/common/src/main/java/com/cursedcauldron/wildbackport/common/CommonSetup.java
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/CommonSetup.java
@@ -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();
}
}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/blocks/MangroveTreeGrower.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/blocks/MangroveTreeGrower.java
index e95c803..0744c8e 100644
--- a/common/src/main/java/com/cursedcauldron/wildbackport/common/blocks/MangroveTreeGrower.java
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/blocks/MangroveTreeGrower.java
@@ -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;
}
}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/RootPlacerType.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/RootPlacerType.java
new file mode 100644
index 0000000..54ddf71
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/RootPlacerType.java
@@ -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
{
+ public static final CoreRegistry> PLACERS = CoreRegistry.create(WBRegistries.ROOT_PLACER_TYPES.getSecond(), WildBackport.MOD_ID);
+
+ public static final Supplier> MANGROVE_ROOT_PLACER = PLACERS.register("mangrove_root_placer", () -> new RootPlacerType<>(MangroveRootPlacer.CODEC));
+
+
+ private final Codec codec;
+
+ private RootPlacerType(Codec
codec) {
+ this.codec = codec;
+ }
+
+ public Codec
codec() {
+ return this.codec;
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBFeatures.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBFeatures.java
new file mode 100644
index 0000000..7ffde0c
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBFeatures.java
@@ -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> FEATURES = CoreRegistry.create(Registry.FEATURE, WildBackport.MOD_ID);
+
+ public static final Supplier> TREE = FEATURES.register("tree", () -> new RootedTreeFeature(RootedTreeConfig.CODEC));
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBRegistries.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBRegistries.java
new file mode 100644
index 0000000..f16552b
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBRegistries.java
@@ -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>>, Registry>> ROOT_PLACER_TYPES = create("worldgen/root_placer_type", registry -> RootPlacerType.MANGROVE_ROOT_PLACER.get());
+
+ private static Pair>, Registry> create(String key, Registry.RegistryBootstrap bootstrap) {
+ ResourceKey> resource = ResourceKey.createRegistryKey(new ResourceLocation(WildBackport.MOD_ID, key));
+ Registry registry = RegistryAccessor.callRegisterSimple(resource, bootstrap);
+ return Pair.of(resource, registry);
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBTreeDecorators.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBTreeDecorators.java
new file mode 100644
index 0000000..bf432dd
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBTreeDecorators.java
@@ -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> DECORATORS = CoreRegistry.create(Registry.TREE_DECORATOR_TYPES, WildBackport.MOD_ID);
+
+ public static final Supplier> WEIGHTED_LEAVE_VINE = DECORATORS.register("leave_vine", () -> TreeDecoratorTypeAccessor.createTreeDecoratorType(WeightedLeaveVineDecorator.CODEC));
+ public static final Supplier> ATTACHED_TO_LEAVES = DECORATORS.register("attached_to_leaves", () -> TreeDecoratorTypeAccessor.createTreeDecoratorType(AttachedToLeavesDecorator.CODEC));
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBTrunkPlacers.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBTrunkPlacers.java
new file mode 100644
index 0000000..2369d31
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBTrunkPlacers.java
@@ -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> PLACERS = CoreRegistry.create(Registry.TRUNK_PLACER_TYPES, WildBackport.MOD_ID);
+
+ public static final Supplier> UPWARDS_BRANCHING_TRUNK = PLACERS.register("upward_branching_trunk", () -> TrunkPlacerTypeAccessor.createTrunkPlacerType(UpwardBranchingTrunk.CODEC));
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBWorldGeneration.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBWorldGeneration.java
new file mode 100644
index 0000000..7cce869
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/registry/worldgen/WBWorldGeneration.java
@@ -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> 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> 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 MANGROVE_CHECKED = place("mangrove_checked", MANGROVE, PlacementUtils.filteredByBlockSurvival(WBBlocks.MANGROVE_PROPAGULE.get()));
+ public static final Holder TALL_MANGROVE_CHECKED = place("tall_mangrove_checked", TALL_MANGROVE, PlacementUtils.filteredByBlockSurvival(WBBlocks.MANGROVE_PROPAGULE.get()));
+
+ public static final Holder> 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 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 > Holder> config(String key, F feature, FC configuration) {
+ return BuiltinRegistries.registerExact(BuiltinRegistries.CONFIGURED_FEATURE, WildBackport.MOD_ID + ":" + key, new ConfiguredFeature<>(feature, configuration));
+ }
+
+ public static Holder 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))));
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/tag/WBBlockTags.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/tag/WBBlockTags.java
index bb1becd..b4d7f0f 100644
--- a/common/src/main/java/com/cursedcauldron/wildbackport/common/tag/WBBlockTags.java
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/tag/WBBlockTags.java
@@ -9,8 +9,14 @@ import net.minecraft.world.level.block.Block;
public class WBBlockTags {
public static final TagRegistry TAGS = TagRegistry.create(Registry.BLOCK, WildBackport.MOD_ID);
- public static final TagKey FROG_PREFER_JUMP_TO = TAGS.create("frog_prefer_jump_to");
- public static final TagKey FROGS_SPAWNABLE_ON = TAGS.create("frogs_spawnable_on");
- public static final TagKey SCULK_REPLACEABLE = TAGS.create("sculk_replaceable");
- public static final TagKey SCULK_REPLACEABLE_WORLD_GEN = TAGS.create("sculk_replaceable_world_gen");
+ // Mangrove Swamp
+
+ public static final TagKey MANGROVE_LOGS_CAN_GROW_THROUGH = TAGS.create("mangrove_logs_can_grow_through");
+ public static final TagKey MANGROVE_ROOTS_CAN_GROW_THROUGH = TAGS.create("mangrove_roots_can_grow_through");
+ public static final TagKey FROG_PREFER_JUMP_TO = TAGS.create("frog_prefer_jump_to");
+ public static final TagKey FROGS_SPAWNABLE_ON = TAGS.create("frogs_spawnable_on");
+
+ // Deep Dark
+ public static final TagKey SCULK_REPLACEABLE = TAGS.create("sculk_replaceable");
+ public static final TagKey SCULK_REPLACEABLE_WORLD_GEN = TAGS.create("sculk_replaceable_world_gen");
}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/AttachedToLeavesDecorator.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/AttachedToLeavesDecorator.java
new file mode 100644
index 0000000..3fd8d83
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/AttachedToLeavesDecorator.java
@@ -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 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 directions;
+
+ public AttachedToLeavesDecorator(float probability, int exclusionRadiusXZ, int exclusionRadiusY, BlockStateProvider blockProvider, int requiredEmptyBlocks, List 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 replacer, Random random, List trunkPositions, List foliagePositions) {
+ HashSet 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();
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/LayerRootDecorator.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/LayerRootDecorator.java
new file mode 100644
index 0000000..eeee00c
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/LayerRootDecorator.java
@@ -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 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);
+ });
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/MangroveRootPlacement.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/MangroveRootPlacement.java
new file mode 100644
index 0000000..bf58e4e
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/MangroveRootPlacement.java
@@ -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 canGrowThrough, HolderSet muddyRootsIn, BlockStateProvider muddyRootsProvider, int maxRootWidth, int maxRootLength, float randomSkewChance) {
+ public static final Codec 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);
+ });
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/WeightedLeaveVineDecorator.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/WeightedLeaveVineDecorator.java
new file mode 100644
index 0000000..3b2a443
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/decorator/WeightedLeaveVineDecorator.java
@@ -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 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 replacer, Random random, List trunkPositions, List 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 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();
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/features/RootedTreeConfig.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/features/RootedTreeConfig.java
new file mode 100644
index 0000000..5127c76
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/features/RootedTreeConfig.java
@@ -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 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;
+
+ protected RootedTreeConfig(BlockStateProvider trunkProvider, TrunkPlacer trunkPlacer, BlockStateProvider foliageProvider, FoliagePlacer foliagePlacer, Optional rootPlacer, BlockStateProvider dirtProvider, FeatureSize minimumSize, List 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;
+ private BlockStateProvider dirtProvider;
+ private final FeatureSize minimumSize;
+ private List decorators = ImmutableList.of();
+ private boolean ignoreVines;
+ private boolean forceDirt;
+
+ public Builder(BlockStateProvider trunkProvider, TrunkPlacer trunkPlacer, BlockStateProvider foliageProvider, FoliagePlacer foliagePlacer, Optional 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 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/features/RootedTreeFeature.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/features/RootedTreeFeature.java
new file mode 100644
index 0000000..fa56b5a
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/features/RootedTreeFeature.java
@@ -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 {
+ public RootedTreeFeature(Codec codec) {
+ super(codec);
+ }
+
+ private boolean generate(WorldGenLevel level, Random random, BlockPos pos, BiConsumer rootPlacerReplacer, BiConsumer trunkPlacerReplacer, BiConsumer 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 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 context) {
+ WorldGenLevel level = context.level();
+ Random random = context.random();
+ BlockPos pos = context.origin();
+ RootedTreeConfig config = context.config();
+ HashSet rootPos = Sets.newHashSet();
+ HashSet trunkPos = Sets.newHashSet();
+ HashSet foliagePos = Sets.newHashSet();
+ HashSet decoratorPos = Sets.newHashSet();
+ BiConsumer rootReplacer = (position, state) -> {
+ rootPos.add(position.immutable());
+ level.setBlock(position, state, 19);
+ };
+ BiConsumer trunkReplacer = (position, state) -> {
+ trunkPos.add(position.immutable());
+ level.setBlock(position, state, 19);
+ };
+ BiConsumer foliageReplacer = (position, state) -> {
+ foliagePos.add(position.immutable());
+ level.setBlock(position, state, 19);
+ };
+ BiConsumer 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 rootPositions = Lists.newArrayList(rootPos);
+ ArrayList trunkPositions = Lists.newArrayList(trunkPos);
+ ArrayList 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 trunkPositions, Set decoratorPositions) {
+ ArrayList> 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 trunkPos = positions.get(tries - 1);
+ Set 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> 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 trunkPos = positions.get(tries - 1);
+// Set 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;
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/placers/MangroveRootPlacer.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/placers/MangroveRootPlacer.java
new file mode 100644
index 0000000..6420101
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/placers/MangroveRootPlacer.java
@@ -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 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 aboveRootPlacement, MangroveRootPlacement mangroveRootPlacement) {
+ super(trunkOffsetY, rootProvider, aboveRootPlacement);
+ this.mangroveRootPlacement = mangroveRootPlacement;
+ }
+
+ @Override
+ public boolean generate(LevelSimulatedReader level, BiConsumer replacer, Random random, BlockPos pos, BlockPos origin, RootedTreeConfig config) {
+ List 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 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 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 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 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();
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/placers/RootPlacer.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/placers/RootPlacer.java
new file mode 100644
index 0000000..c36e52d
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/placers/RootPlacer.java
@@ -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 CODEC = WBRegistries.ROOT_PLACER_TYPES.getSecond().byNameCodec().dispatch(RootPlacer::getType, RootPlacerType::codec);
+ protected final IntProvider trunkOffsetY;
+ protected final BlockStateProvider rootProvider;
+ protected final Optional aboveRootPlacement;
+
+ protected static Products.P3, IntProvider, BlockStateProvider, Optional> codec(RecordCodecBuilder.Instance 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 aboveRootPlacement) {
+ this.trunkOffsetY = trunkOffsetY;
+ this.rootProvider = rootProvider;
+ this.aboveRootPlacement = aboveRootPlacement;
+ }
+
+ protected abstract RootPlacerType> getType();
+
+ public abstract boolean generate(LevelSimulatedReader level, BiConsumer 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 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));
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/placers/UpwardBranchingTrunk.java b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/placers/UpwardBranchingTrunk.java
new file mode 100644
index 0000000..e8b7ee0
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/common/worldgen/placers/UpwardBranchingTrunk.java
@@ -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 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 canGrowThrough;
+
+ public UpwardBranchingTrunk(int baseHeight, int firstRandomHeight, int secondRandomHeight, IntProvider extraBranchSteps, float placeBranchPerLogProbability, IntProvider extraBranchLength, HolderSet canGrowThrough) {
+ super(baseHeight, firstRandomHeight, secondRandomHeight);
+ this.extraBranchSteps = extraBranchSteps;
+ this.placeBranchPerLogProbability = placeBranchPerLogProbability;
+ this.extraBranchLength = extraBranchLength;
+ this.canGrowThrough = canGrowThrough;
+ }
+
+ public List placeTrunk(LevelSimulatedReader level, BiConsumer replacer, Random random, int height, BlockPos startPos, TreeConfiguration config) {
+ List 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 replacer, Random random, int height, TreeConfiguration config, List 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();
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/api/Environment.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/api/Environment.java
new file mode 100644
index 0000000..ac5553f
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/api/Environment.java
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/RegistryAccessor.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/RegistryAccessor.java
new file mode 100644
index 0000000..79c58e9
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/RegistryAccessor.java
@@ -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 Registry callRegisterSimple(ResourceKey extends Registry> resourceKey, Registry.RegistryBootstrap registryBootstrap) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/TreeDecoratorTypeAccessor.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/TreeDecoratorTypeAccessor.java
new file mode 100644
index 0000000..7e12e90
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/TreeDecoratorTypeAccessor.java
@@ -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("")
+ static TreeDecoratorType
createTreeDecoratorType(Codec
codec) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/TreeFeatureAccessor.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/TreeFeatureAccessor.java
new file mode 100644
index 0000000..aabca95
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/TreeFeatureAccessor.java
@@ -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();
+ }
+}
diff --git a/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/TrunkPlacerTypeAccessor.java b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/TrunkPlacerTypeAccessor.java
new file mode 100644
index 0000000..e2c6a67
--- /dev/null
+++ b/common/src/main/java/com/cursedcauldron/wildbackport/core/mixin/access/TrunkPlacerTypeAccessor.java
@@ -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("")
+ static TrunkPlacerType
createTrunkPlacerType(Codec
codec) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/leaves.json b/common/src/main/resources/data/minecraft/tags/blocks/leaves.json
new file mode 100644
index 0000000..e00969d
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/leaves.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mangrove_leaves"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/logs_that_burn.json b/common/src/main/resources/data/minecraft/tags/blocks/logs_that_burn.json
new file mode 100644
index 0000000..8771410
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/logs_that_burn.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "#wildbackport:mangrove_logs"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/mineable/axe.json b/common/src/main/resources/data/minecraft/tags/blocks/mineable/axe.json
new file mode 100644
index 0000000..9d71f29
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/mineable/axe.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mangrove_roots"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json b/common/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json
new file mode 100644
index 0000000..a3c7989
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json
@@ -0,0 +1,10 @@
+{
+ "replace": false,
+ "values": [
+ "minecraft:sculk_sensor",
+ "wildbackport:sculk",
+ "wildbackport:sculk_catalyst",
+ "wildbackport:sculk_vein",
+ "wildbackport:sculk_shrieker"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json b/common/src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json
new file mode 100644
index 0000000..6c29af2
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json
@@ -0,0 +1,9 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mud_bricks",
+ "wildbackport:mud_brick_stairs",
+ "wildbackport:mud_brick_slab",
+ "wildbackport:packed_mud"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/mineable/shovel.json b/common/src/main/resources/data/minecraft/tags/blocks/mineable/shovel.json
new file mode 100644
index 0000000..353ed7e
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/mineable/shovel.json
@@ -0,0 +1,7 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mud",
+ "wildbackport:muddy_mangrove_roots"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/planks.json b/common/src/main/resources/data/minecraft/tags/blocks/planks.json
new file mode 100644
index 0000000..c62d7d6
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/planks.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mangrove_planks"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/walls.json b/common/src/main/resources/data/minecraft/tags/blocks/walls.json
new file mode 100644
index 0000000..af2cfaa
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/walls.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mud_brick_wall"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/wooden_doors.json b/common/src/main/resources/data/minecraft/tags/blocks/wooden_doors.json
new file mode 100644
index 0000000..fbe3af3
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/wooden_doors.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mangrove_door"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/wooden_fences.json b/common/src/main/resources/data/minecraft/tags/blocks/wooden_fences.json
new file mode 100644
index 0000000..be90b5c
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/wooden_fences.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mangrove_fence"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/wooden_slabs.json b/common/src/main/resources/data/minecraft/tags/blocks/wooden_slabs.json
new file mode 100644
index 0000000..2715336
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/wooden_slabs.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mangrove_slab"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/minecraft/tags/blocks/wooden_stairs.json b/common/src/main/resources/data/minecraft/tags/blocks/wooden_stairs.json
new file mode 100644
index 0000000..3d20251
--- /dev/null
+++ b/common/src/main/resources/data/minecraft/tags/blocks/wooden_stairs.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mangrove_stairs"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/wildbackport/tags/blocks/mangrove_logs.json b/common/src/main/resources/data/wildbackport/tags/blocks/mangrove_logs.json
new file mode 100644
index 0000000..5357192
--- /dev/null
+++ b/common/src/main/resources/data/wildbackport/tags/blocks/mangrove_logs.json
@@ -0,0 +1,9 @@
+{
+ "replace": false,
+ "values": [
+ "wildbackport:mangrove_log",
+ "wildbackport:mangrove_wood",
+ "wildbackport:stripped_mangrove_log",
+ "wildbackport:stripped_mangrove_wood"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/wildbackport/tags/blocks/mangrove_logs_can_grow_through.json b/common/src/main/resources/data/wildbackport/tags/blocks/mangrove_logs_can_grow_through.json
new file mode 100644
index 0000000..d588a67
--- /dev/null
+++ b/common/src/main/resources/data/wildbackport/tags/blocks/mangrove_logs_can_grow_through.json
@@ -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"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/data/wildbackport/tags/blocks/mangrove_roots_can_grow_through.json b/common/src/main/resources/data/wildbackport/tags/blocks/mangrove_roots_can_grow_through.json
new file mode 100644
index 0000000..d380be2
--- /dev/null
+++ b/common/src/main/resources/data/wildbackport/tags/blocks/mangrove_roots_can_grow_through.json
@@ -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"
+ ]
+}
\ No newline at end of file
diff --git a/common/src/main/resources/wildbackport-common.mixins.json b/common/src/main/resources/wildbackport-common.mixins.json
index fcee79c..e05c625 100644
--- a/common/src/main/resources/wildbackport-common.mixins.json
+++ b/common/src/main/resources/wildbackport-common.mixins.json
@@ -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",
diff --git a/common/src/main/resources/wildbackport.accesswidener b/common/src/main/resources/wildbackport.accesswidener
index 7e49688..42dc488 100644
--- a/common/src/main/resources/wildbackport.accesswidener
+++ b/common/src/main/resources/wildbackport.accesswidener
@@ -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 (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
\ No newline at end of file
diff --git a/fabric/src/main/java/com/cursedcauldron/wildbackport/core/api/fabric/EnvironmentImpl.java b/fabric/src/main/java/com/cursedcauldron/wildbackport/core/api/fabric/EnvironmentImpl.java
new file mode 100644
index 0000000..c491b50
--- /dev/null
+++ b/fabric/src/main/java/com/cursedcauldron/wildbackport/core/api/fabric/EnvironmentImpl.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/fabric/src/main/resources/wildbackport.accesswidener b/fabric/src/main/resources/wildbackport.accesswidener
index 7e49688..42dc488 100644
--- a/fabric/src/main/resources/wildbackport.accesswidener
+++ b/fabric/src/main/resources/wildbackport.accesswidener
@@ -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 (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
\ No newline at end of file
diff --git a/forge/src/main/java/com/cursedcauldron/wildbackport/core/api/forge/EnvironmentImpl.java b/forge/src/main/java/com/cursedcauldron/wildbackport/core/api/forge/EnvironmentImpl.java
new file mode 100644
index 0000000..08f3c2b
--- /dev/null
+++ b/forge/src/main/java/com/cursedcauldron/wildbackport/core/api/forge/EnvironmentImpl.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/forge/src/main/java/com/cursedcauldron/wildbackport/forge/WildBackportForge.java b/forge/src/main/java/com/cursedcauldron/wildbackport/forge/WildBackportForge.java
index 0d6ea8b..37a146d 100644
--- a/forge/src/main/java/com/cursedcauldron/wildbackport/forge/WildBackportForge.java
+++ b/forge/src/main/java/com/cursedcauldron/wildbackport/forge/WildBackportForge.java
@@ -17,7 +17,7 @@ public class WildBackportForge {
public WildBackportForge() {
IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();
EventBuses.registerModEventBus(WildBackport.MOD_ID, bus);
- bus.addListener(event -> CommonSetup.onPostClient());
+ bus.addListener(event -> CommonSetup.onPostCommon());
bus.addListener(event -> ClientSetup.onPostClient());
WildBackport.bootstrap();