diff --git a/build.gradle.kts b/build.gradle.kts
index 6d8f5cf97..02cd106d7 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -46,6 +46,11 @@ repositories {
url = uri("https://maven.terraformersmc.com/")
}
+ maven {
+ name = "Ladysnake"
+ url = uri("https://maven.ladysnake.org/releases")
+ }
+
maven {
name = "Shedaniel"
url = uri("https://maven.shedaniel.me/")
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index 7411d06eb..6a41bb46b 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -23,7 +23,6 @@
-
diff --git a/gradle.properties b/gradle.properties
index b424cadd0..3a9f398dc 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -5,12 +5,12 @@ paradiseLostVersion=2.1.0-beta+1.19.2
minecraftVersion=1.19.2
yarnVersion=1.19.2+build.28
-loaderVersion=0.14.13
+loaderVersion=0.15.7
javaVersion=17
-fabricApiVersion=0.73.2+1.19.2
+fabricApiVersion=0.77.0+1.19.2
incubusCoreVersion=1.9.4
-customportalapiVersion=0.0.1-beta54-1.19
+customportalapiVersion=0.0.1-beta63.5-1.19.X
cardinalComponentsVersion=5.0.1
trinketsVersion=3.4.0
crowdinTranslateVersion=1.19.2
diff --git a/src/main/java/net/id/paradiselost/ParadiseLost.java b/src/main/java/net/id/paradiselost/ParadiseLost.java
index 3d0fb406d..4b620067f 100644
--- a/src/main/java/net/id/paradiselost/ParadiseLost.java
+++ b/src/main/java/net/id/paradiselost/ParadiseLost.java
@@ -26,6 +26,7 @@
import net.id.paradiselost.items.ParadiseLostItems;
import net.id.paradiselost.loot.ParadiseLostLootNumberProviderTypes;
import net.id.paradiselost.lore.ParadiseLostLore;
+import net.id.paradiselost.recipe.ParadiseLostRecipeTypes;
import net.id.paradiselost.registry.ParadiseLostRegistries;
import net.id.paradiselost.screen.ParadiseLostScreens;
import net.id.paradiselost.util.ParadiseLostSoundEvents;
@@ -89,6 +90,7 @@ public void onInitialize() {
ParadiseLostEntityTypes.init();
ParadiseLostItems.init();
ParadiseLostBlockEntityTypes.init();
+ ParadiseLostRecipeTypes.init();
ParadiseLostCommands.init();
ParadiseLostGameRules.init();
ParadiseLostLootNumberProviderTypes.init();
diff --git a/src/main/java/net/id/paradiselost/blocks/ParadiseLostBlocks.java b/src/main/java/net/id/paradiselost/blocks/ParadiseLostBlocks.java
index 51293977c..ce83c9c9c 100644
--- a/src/main/java/net/id/paradiselost/blocks/ParadiseLostBlocks.java
+++ b/src/main/java/net/id/paradiselost/blocks/ParadiseLostBlocks.java
@@ -5,10 +5,24 @@
import net.id.incubus_core.woodtypefactory.api.chest.ChestFactory;
import net.id.paradiselost.ParadiseLost;
import net.id.paradiselost.blocks.decorative.*;
-import net.id.paradiselost.blocks.mechanical.*;
-import net.id.paradiselost.blocks.natural.*;
-import net.id.paradiselost.blocks.natural.cloud.*;
-import net.id.paradiselost.blocks.natural.crop.*;
+import net.id.paradiselost.blocks.mechanical.CherineCampfireBlock;
+import net.id.paradiselost.blocks.mechanical.FoodBowlBlock;
+import net.id.paradiselost.blocks.mechanical.FourBiteCakeBlock;
+import net.id.paradiselost.blocks.mechanical.IncubatorBlock;
+import net.id.paradiselost.blocks.mechanical.TreeTapBlock;
+import net.id.paradiselost.blocks.natural.ParadiseLostGrassBlock;
+import net.id.paradiselost.blocks.natural.ParadiseLostSaplingBlock;
+import net.id.paradiselost.blocks.natural.ParadiseLostSnowyBlock;
+import net.id.paradiselost.blocks.natural.PoofBlock;
+import net.id.paradiselost.blocks.natural.SurtrumOreBlock;
+import net.id.paradiselost.blocks.natural.cloud.ParadiseLostCloudBlock;
+import net.id.paradiselost.blocks.natural.cloud.BlueParadiseLostCloudBlock;
+import net.id.paradiselost.blocks.natural.cloud.GoldenParadiseLostCloudBlock;
+import net.id.paradiselost.blocks.natural.cloud.PinkParadiseLostCloudBlock;
+import net.id.paradiselost.blocks.natural.crop.AmadrysCropBlock;
+import net.id.paradiselost.blocks.natural.crop.BlackcurrantBushBlock;
+import net.id.paradiselost.blocks.natural.crop.FlaxCropBlock;
+import net.id.paradiselost.blocks.natural.crop.SwedrootCropBlock;
import net.id.paradiselost.blocks.natural.plant.*;
import net.id.paradiselost.blocks.natural.tree.*;
import net.id.paradiselost.fluids.ParadiseLostFluids;
@@ -41,7 +55,7 @@
@SuppressWarnings("unused")
public class ParadiseLostBlocks {
- protected static Settings unbreakable(AbstractBlock.Settings settings) {
+ protected static Settings unbreakable(AbstractBlock.Settings settings) {
return settings.strength(-1f, 3600000f);
}
@@ -375,6 +389,7 @@ private static Settings cherineTorch() {
// Usables
public static final IncubatorBlock INCUBATOR = add("incubator", new IncubatorBlock(of(Material.WOOD, MapColor.DULL_RED).strength(2.5f).sounds(BlockSoundGroup.WOOD).nonOpaque()), cutoutMippedRenderLayer);
public static final FoodBowlBlock FOOD_BOWL = add("food_bowl", new FoodBowlBlock(of(Material.WOOD, MapColor.DULL_RED).strength(2.5f).sounds(BlockSoundGroup.WOOD).nonOpaque()), cutoutMippedRenderLayer);
+ public static final Block TREE_TAP = add("tree_tap", new TreeTapBlock(of(Material.WOOD, MapColor.OAK_TAN).strength(2.5f).sounds(BlockSoundGroup.WOOD).nonOpaque().ticksRandomly()), cutoutRenderLayer);
//dungeon
// public static final DungeonSwitchBlock DUNGEON_SWITCH = add("dungeonswitch", new DungeonSwitchBlock(of(Material.METAL, MapColor.BLUE).strength(-1.0F, 3600000.0F)));
@@ -395,7 +410,7 @@ private static V add(String id, V block, Action super V>...
/*
This is the same thing the add method above, but it doesn't wait to register or perform the actions.
- This is required because some of the block settings code uses ID caches, so without it some blocks
+ This is required because some block settings code uses ID caches, so without it some blocks
behave like air.
*/
@SafeVarargs
diff --git a/src/main/java/net/id/paradiselost/blocks/blockentity/ParadiseLostBlockEntityTypes.java b/src/main/java/net/id/paradiselost/blocks/blockentity/ParadiseLostBlockEntityTypes.java
index a126c706f..73c416c8c 100644
--- a/src/main/java/net/id/paradiselost/blocks/blockentity/ParadiseLostBlockEntityTypes.java
+++ b/src/main/java/net/id/paradiselost/blocks/blockentity/ParadiseLostBlockEntityTypes.java
@@ -12,12 +12,14 @@ public class ParadiseLostBlockEntityTypes {
public static final BlockEntityType FOOD_BOWL = create(FoodBowlBlockEntity::new, ParadiseLostBlocks.FOOD_BOWL).build();
public static final BlockEntityType INCUBATOR = create(IncubatorBlockEntity::new, ParadiseLostBlocks.INCUBATOR).build();
public static final BlockEntityType CHERINE_CAMPFIRE = create(CherineCampfireBlockEntity::new, ParadiseLostBlocks.CHERINE_CAMPFIRE).build();
+ public static final BlockEntityType TREE_TAP = create(TreeTapBlockEntity::new, ParadiseLostBlocks.TREE_TAP).build();
// public static final BlockEntityType DUNGEON_SWITCH = create(DungeonSwitchBlockEntity::new, ParadiseLostBlocks.DUNGEON_SWITCH).build();
public static void init() {
register("food_bowl", FOOD_BOWL);
register("incubator", INCUBATOR);
register("cherine_campfire", CHERINE_CAMPFIRE);
+ register("tree_tap", TREE_TAP);
// register("dungeonswitch", DUNGEON_SWITCH);
}
diff --git a/src/main/java/net/id/paradiselost/blocks/blockentity/TreeTapBlockEntity.java b/src/main/java/net/id/paradiselost/blocks/blockentity/TreeTapBlockEntity.java
new file mode 100644
index 000000000..29ee4f0f1
--- /dev/null
+++ b/src/main/java/net/id/paradiselost/blocks/blockentity/TreeTapBlockEntity.java
@@ -0,0 +1,107 @@
+package net.id.paradiselost.blocks.blockentity;
+
+import net.id.incubus_core.be.InventoryBlockEntity;
+import net.id.paradiselost.recipe.ParadiseLostRecipeTypes;
+import net.id.paradiselost.recipe.TreeTapRecipe;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.inventory.Inventories;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.network.Packet;
+import net.minecraft.network.listener.ClientPlayPacketListener;
+import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.state.property.Properties;
+import net.minecraft.util.Hand;
+import net.minecraft.util.ItemScatterer;
+import net.minecraft.util.collection.DefaultedList;
+import net.minecraft.util.math.BlockPos;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Optional;
+
+public class TreeTapBlockEntity extends BlockEntity implements InventoryBlockEntity {
+
+ private final DefaultedList inventory;
+
+ public TreeTapBlockEntity(BlockPos pos, BlockState state) {
+ super(ParadiseLostBlockEntityTypes.TREE_TAP, pos, state);
+ inventory = DefaultedList.ofSize(1, ItemStack.EMPTY);
+ }
+
+ public void handleUse(PlayerEntity player, Hand hand, ItemStack handStack) {
+ ItemStack stored = inventory.get(0);
+ inventory.set(0, handStack);
+ player.setStackInHand(hand, stored);
+ markDirty();
+ }
+
+ @Override
+ public @NotNull HopperStrategy getHopperStrategy() {
+ return HopperStrategy.IN_ANY_OUT_BOTTOM;
+ }
+
+ @Override
+ public DefaultedList getItems() {
+ return inventory;
+ }
+
+ @Override
+ public void readNbt(NbtCompound nbt) {
+ super.readNbt(nbt);
+
+ Inventories.readNbt(nbt, inventory);
+ }
+
+ @Override
+ public void writeNbt(NbtCompound nbt) {
+ super.writeNbt(nbt);
+ Inventories.writeNbt(nbt, inventory);
+ }
+
+ public BlockState getTappedState() {
+ return this.world.getBlockState(this.pos.offset(getCachedState().get(Properties.HORIZONTAL_FACING).getOpposite()));
+ }
+
+ public void tryCraft() {
+ ItemStack stack = getStack(0);
+ if (stack.isEmpty()) {
+ return;
+ }
+
+ Optional recipe = this.world.getRecipeManager().getFirstMatch(ParadiseLostRecipeTypes.TREE_TAP_RECIPE_TYPE, this, this.world);
+ if (recipe.isPresent()) {
+ ItemStack output = recipe.get().craft(this);
+ stack.decrement(1);
+
+ // TODO: play a sound?
+ if (stack.isEmpty()) {
+ this.inventory.set(0, output);
+ updateInClientWorld();
+ } else {
+ ItemScatterer.spawn(world, pos.getX(), pos.getY(), pos.getZ(), output);
+ }
+ }
+ }
+
+ @Override
+ public NbtCompound toInitialChunkDataNbt() {
+ NbtCompound nbtCompound = new NbtCompound();
+ this.writeNbt(nbtCompound);
+ return nbtCompound;
+ }
+
+ @Nullable
+ @Override
+ public Packet toUpdatePacket() {
+ return BlockEntityUpdateS2CPacket.create(this);
+ }
+
+ public void updateInClientWorld() {
+ ((ServerWorld) world).getChunkManager().markForUpdate(pos);
+ }
+
+}
diff --git a/src/main/java/net/id/paradiselost/blocks/mechanical/TreeTapBlock.java b/src/main/java/net/id/paradiselost/blocks/mechanical/TreeTapBlock.java
new file mode 100644
index 000000000..10b451530
--- /dev/null
+++ b/src/main/java/net/id/paradiselost/blocks/mechanical/TreeTapBlock.java
@@ -0,0 +1,141 @@
+package net.id.paradiselost.blocks.mechanical;
+
+import net.id.paradiselost.blocks.blockentity.TreeTapBlockEntity;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockRenderType;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.ShapeContext;
+import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.item.ItemPlacementContext;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.state.StateManager;
+import net.minecraft.state.property.DirectionProperty;
+import net.minecraft.state.property.Properties;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.BlockMirror;
+import net.minecraft.util.BlockRotation;
+import net.minecraft.util.Hand;
+import net.minecraft.util.ItemScatterer;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
+import net.minecraft.util.math.random.Random;
+import net.minecraft.util.shape.VoxelShape;
+import net.minecraft.world.BlockView;
+import net.minecraft.world.World;
+import net.minecraft.world.WorldView;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class TreeTapBlock extends ParadiseLostBlockWithEntity {
+
+ public static final DirectionProperty FACING = Properties.HORIZONTAL_FACING;
+ private static final VoxelShape SHAPE = Block.createCuboidShape(0, 0, 0, 16, 5, 16);
+
+ public TreeTapBlock(Settings settings) {
+ super(settings, true);
+ }
+
+ @Override
+ public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
+ if (!player.isSneaking() && world.getBlockEntity(pos) instanceof TreeTapBlockEntity treeTapBlockEntity) {
+ treeTapBlockEntity.handleUse(player, hand, player.getStackInHand(hand));
+ return ActionResult.success(world.isClient());
+ }
+ return super.onUse(state, world, pos, player, hand, hit);
+ }
+
+ @Override
+ public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
+ return SHAPE;
+ }
+
+ @Override
+ public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
+ Direction direction = state.get(FACING);
+ if (!direction.getAxis().isHorizontal()) {
+ return false;
+ }
+
+ BlockPos blockPos = pos.offset(direction.getOpposite());
+ BlockState blockState = world.getBlockState(blockPos);
+ return blockState.isSideSolidFullSquare(world, blockPos, direction);
+ }
+
+ @Override
+ public BlockRenderType getRenderType(BlockState state) {
+ return BlockRenderType.MODEL;
+ }
+
+ @Override
+ public BlockState getPlacementState(ItemPlacementContext ctx) {
+ Direction direction = ctx.getSide();
+ if (!direction.getAxis().isHorizontal()) {
+ return null;
+ }
+
+ return this.getDefaultState().with(FACING, direction);
+ }
+
+ @Override
+ protected void appendProperties(StateManager.Builder builder) {
+ builder.add(WATERLOGGED, POWERED, FACING);
+ }
+
+ @Override
+ public BlockState rotate(BlockState state, BlockRotation rotation) {
+ return state.with(FACING, rotation.rotate(state.get(FACING)));
+ }
+
+ @Override
+ public BlockState mirror(BlockState state, BlockMirror mirror) {
+ return state.rotate(mirror.getRotation(state.get(FACING)));
+ }
+
+ @Override
+ public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ super.randomTick(state, world, pos, random);
+
+ if (!world.isClient && world.getBlockEntity(pos) instanceof TreeTapBlockEntity treeTapBlockEntity) {
+ treeTapBlockEntity.tryCraft();
+ }
+ }
+
+ @Nullable
+ @Override
+ public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
+ return new TreeTapBlockEntity(pos, state);
+ }
+
+ @Override
+ public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
+ if (!state.isOf(newState.getBlock())) {
+ scatterContents(world, pos);
+ world.updateComparators(pos, this);
+ }
+ super.onStateReplaced(state, world, pos, newState, moved);
+ }
+
+ public static void scatterContents(World world, BlockPos pos) {
+ Block block = world.getBlockState(pos).getBlock();
+ BlockEntity blockEntity = world.getBlockEntity(pos);
+ if (blockEntity instanceof Inventory inventory) {
+ ItemScatterer.spawn(world, pos, inventory);
+ world.updateComparators(pos, block);
+ }
+ }
+
+ @Override
+ public boolean hasComparatorOutput(BlockState state) {
+ return true;
+ }
+
+ @Override
+ public int getComparatorOutput(BlockState state, @NotNull World world, BlockPos pos) {
+ return ScreenHandler.calculateComparatorOutput(world.getBlockEntity(pos));
+ }
+
+}
diff --git a/src/main/java/net/id/paradiselost/client/rendering/block/ParadiseLostBlockEntityRenderers.java b/src/main/java/net/id/paradiselost/client/rendering/block/ParadiseLostBlockEntityRenderers.java
index 3a4ec03e2..877be34e2 100644
--- a/src/main/java/net/id/paradiselost/client/rendering/block/ParadiseLostBlockEntityRenderers.java
+++ b/src/main/java/net/id/paradiselost/client/rendering/block/ParadiseLostBlockEntityRenderers.java
@@ -2,16 +2,17 @@
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
-import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry;
import net.id.paradiselost.blocks.blockentity.ParadiseLostBlockEntityTypes;
+import net.minecraft.client.render.block.entity.BlockEntityRendererFactories;
@Environment(EnvType.CLIENT)
public class ParadiseLostBlockEntityRenderers {
@Environment(EnvType.CLIENT)
public static void initClient() {
- BlockEntityRendererRegistry.register(ParadiseLostBlockEntityTypes.INCUBATOR, IncubatorBlockEntityRenderer::new);
- BlockEntityRendererRegistry.register(ParadiseLostBlockEntityTypes.CHERINE_CAMPFIRE, CherineCampfireBlockEntityRenderer::new);
-// BlockEntityRendererRegistry.register(ParadiseLostBlockEntityTypes.DUNGEON_SWITCH, DungeonSwitchBlockEntityRenderer::new);
+ BlockEntityRendererFactories.register(ParadiseLostBlockEntityTypes.INCUBATOR, IncubatorBlockEntityRenderer::new);
+ BlockEntityRendererFactories.register(ParadiseLostBlockEntityTypes.CHERINE_CAMPFIRE, CherineCampfireBlockEntityRenderer::new);
+ BlockEntityRendererFactories.register(ParadiseLostBlockEntityTypes.TREE_TAP, TreeTapBlockEntityRenderer::new);
+ // BlockEntityRendererFactories.register(ParadiseLostBlockEntityTypes.DUNGEON_SWITCH, DungeonSwitchBlockEntityRenderer::new);
}
}
diff --git a/src/main/java/net/id/paradiselost/client/rendering/block/TreeTapBlockEntityRenderer.java b/src/main/java/net/id/paradiselost/client/rendering/block/TreeTapBlockEntityRenderer.java
new file mode 100644
index 000000000..65d4fc178
--- /dev/null
+++ b/src/main/java/net/id/paradiselost/client/rendering/block/TreeTapBlockEntityRenderer.java
@@ -0,0 +1,29 @@
+package net.id.paradiselost.client.rendering.block;
+
+import net.fabricmc.api.*;
+import net.id.paradiselost.blocks.blockentity.*;
+import net.minecraft.client.*;
+import net.minecraft.client.render.*;
+import net.minecraft.client.render.block.entity.*;
+import net.minecraft.client.render.model.json.*;
+import net.minecraft.client.util.math.*;
+
+@Environment(EnvType.CLIENT)
+public class TreeTapBlockEntityRenderer implements BlockEntityRenderer {
+
+ public TreeTapBlockEntityRenderer(BlockEntityRendererFactory.Context ctx) {
+ }
+
+ @Override
+ public void render(TreeTapBlockEntity entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {
+ if (!entity.isEmpty()) {
+ matrices.push();
+ // todo: rotate along facing axis
+ matrices.translate(0.5, 0.4, 0.5);
+ matrices.scale(0.75F, 0.75F, 0.75F);
+ MinecraftClient.getInstance().getItemRenderer().renderItem(entity.getStack(0), ModelTransformation.Mode.FIXED, light, overlay, matrices, vertexConsumers, 0);
+ matrices.pop();
+ }
+ }
+
+}
diff --git a/src/main/java/net/id/paradiselost/items/ParadiseLostItems.java b/src/main/java/net/id/paradiselost/items/ParadiseLostItems.java
index 90579e92b..424ca41d5 100644
--- a/src/main/java/net/id/paradiselost/items/ParadiseLostItems.java
+++ b/src/main/java/net/id/paradiselost/items/ParadiseLostItems.java
@@ -175,6 +175,7 @@ private static Settings misc() {
public static final MoaEggItem MOA_EGG = add("moa_egg", new MoaEggItem(misc().maxCount(1)));
public static final BlockItem INCUBATOR = add("incubator", ParadiseLostBlocks.INCUBATOR, misc, fuel(300));
public static final BlockItem FOOD_BOWL = add("food_bowl", ParadiseLostBlocks.FOOD_BOWL, misc, fuel(300));
+ public static final BlockItem TREE_TAP = add("tree_tap", ParadiseLostBlocks.TREE_TAP, misc, fuel(300));
public static final AurelBucketItem AUREL_BUCKET = add("aurel_bucket", new AurelBucketItem(misc().maxCount(16)), fuel(200), emptyBucketBehavior);
diff --git a/src/main/java/net/id/paradiselost/recipe/ParadiseLostRecipeTypes.java b/src/main/java/net/id/paradiselost/recipe/ParadiseLostRecipeTypes.java
new file mode 100644
index 000000000..12f745324
--- /dev/null
+++ b/src/main/java/net/id/paradiselost/recipe/ParadiseLostRecipeTypes.java
@@ -0,0 +1,33 @@
+package net.id.paradiselost.recipe;
+
+import net.id.paradiselost.ParadiseLost;
+import net.minecraft.recipe.Recipe;
+import net.minecraft.recipe.RecipeSerializer;
+import net.minecraft.recipe.RecipeType;
+import net.minecraft.util.registry.Registry;
+
+public class ParadiseLostRecipeTypes {
+
+ public static final String TREE_TAP_RECIPE_ID = "tree_tap";
+ public static RecipeSerializer TREE_TAP_RECIPE_SERIALIZER;
+ public static RecipeType TREE_TAP_RECIPE_TYPE;
+
+ static , T extends Recipe>> S registerSerializer(String id, S serializer) {
+ return Registry.register(Registry.RECIPE_SERIALIZER, ParadiseLost.locate(id), serializer);
+ }
+
+ static > RecipeType registerRecipeType(String id) {
+ return Registry.register(Registry.RECIPE_TYPE, ParadiseLost.locate(id), new RecipeType() {
+ @Override
+ public String toString() {
+ return ParadiseLost.MOD_ID + ":" + id;
+ }
+ });
+ }
+
+ public static void init() {
+ TREE_TAP_RECIPE_SERIALIZER = registerSerializer(TREE_TAP_RECIPE_ID, new TreeTapRecipeSerializer(TreeTapRecipe::new));
+ TREE_TAP_RECIPE_TYPE = registerRecipeType(TREE_TAP_RECIPE_ID);
+ }
+
+}
diff --git a/src/main/java/net/id/paradiselost/recipe/TreeTapRecipe.java b/src/main/java/net/id/paradiselost/recipe/TreeTapRecipe.java
new file mode 100644
index 000000000..043da045a
--- /dev/null
+++ b/src/main/java/net/id/paradiselost/recipe/TreeTapRecipe.java
@@ -0,0 +1,74 @@
+package net.id.paradiselost.recipe;
+
+import net.id.paradiselost.blocks.blockentity.TreeTapBlockEntity;
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.recipe.Ingredient;
+import net.minecraft.recipe.Recipe;
+import net.minecraft.recipe.RecipeSerializer;
+import net.minecraft.recipe.RecipeType;
+import net.minecraft.util.Identifier;
+import net.minecraft.world.World;
+
+public class TreeTapRecipe implements Recipe {
+
+ protected final Identifier id;
+ protected final String group;
+
+ protected final Ingredient ingredient;
+ protected final ItemStack output;
+ protected final Block tappedBlock;
+
+ public TreeTapRecipe(Identifier id, String group, Ingredient ingredient, ItemStack output, Block tappedBlock) {
+ this.id = id;
+ this.group = group;
+ this.ingredient = ingredient;
+ this.output = output;
+ this.tappedBlock = tappedBlock;
+ }
+
+ @Override
+ public boolean matches(TreeTapBlockEntity inventory, World world) {
+ if (!ingredient.test(inventory.getStack(0))) {
+ return false;
+ }
+
+ return inventory.getTappedState().isOf(this.tappedBlock);
+ }
+
+ @Override
+ public ItemStack craft(TreeTapBlockEntity inventory) {
+ return output.copy();
+ }
+
+ @Override
+ public boolean fits(int width, int height) {
+ return true;
+ }
+
+ @Override
+ public ItemStack getOutput() {
+ return output;
+ }
+
+ @Override
+ public String getGroup() {
+ return group;
+ }
+
+ @Override
+ public Identifier getId() {
+ return id;
+ }
+
+ @Override
+ public RecipeSerializer> getSerializer() {
+ return ParadiseLostRecipeTypes.TREE_TAP_RECIPE_SERIALIZER;
+ }
+
+ @Override
+ public RecipeType> getType() {
+ return ParadiseLostRecipeTypes.TREE_TAP_RECIPE_TYPE;
+ }
+
+}
diff --git a/src/main/java/net/id/paradiselost/recipe/TreeTapRecipeSerializer.java b/src/main/java/net/id/paradiselost/recipe/TreeTapRecipeSerializer.java
new file mode 100644
index 000000000..c40fbe7e4
--- /dev/null
+++ b/src/main/java/net/id/paradiselost/recipe/TreeTapRecipeSerializer.java
@@ -0,0 +1,48 @@
+package net.id.paradiselost.recipe;
+
+import com.google.gson.JsonObject;
+import net.id.incubus_core.recipe.RecipeParser;
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.recipe.Ingredient;
+import net.minecraft.recipe.RecipeSerializer;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.JsonHelper;
+import net.minecraft.util.registry.Registry;
+
+public record TreeTapRecipeSerializer(TreeTapRecipeSerializer.RecipeFactory recipeFactory) implements RecipeSerializer {
+
+ public interface RecipeFactory {
+ TreeTapRecipe create(Identifier id, String group, Ingredient ingredient, ItemStack output, Block tappedBlock);
+ }
+
+ @Override
+ public TreeTapRecipe read(Identifier identifier, JsonObject jsonObject) {
+ String group = JsonHelper.getString(jsonObject, "group", "");
+ Ingredient ingredient = Ingredient.fromJson(JsonHelper.getObject(jsonObject, "ingredient"));
+ Block tappedBlock = Registry.BLOCK.get(Identifier.tryParse(JsonHelper.getString(jsonObject, "tapped_block")));
+ ItemStack output = RecipeParser.getItemStackWithNbtFromJson(JsonHelper.getObject(jsonObject, "result"));
+
+ return this.recipeFactory.create(identifier, group, ingredient, output, tappedBlock);
+ }
+
+ @Override
+ public void write(PacketByteBuf packetByteBuf, TreeTapRecipe recipe) {
+ packetByteBuf.writeString(recipe.group);
+ recipe.ingredient.write(packetByteBuf);
+ packetByteBuf.writeIdentifier(Registry.BLOCK.getId(recipe.tappedBlock));
+ packetByteBuf.writeItemStack(recipe.output);
+ }
+
+ @Override
+ public TreeTapRecipe read(Identifier identifier, PacketByteBuf packetByteBuf) {
+ String group = packetByteBuf.readString();
+ Ingredient ingredient = Ingredient.fromPacket(packetByteBuf);
+ Block tappedBlock = Registry.BLOCK.get(packetByteBuf.readIdentifier());
+ ItemStack output = packetByteBuf.readItemStack();
+
+ return this.recipeFactory.create(identifier, group, ingredient, output, tappedBlock);
+ }
+
+}
diff --git a/src/main/resources/assets/paradise_lost/blockstates/tree_tap.json b/src/main/resources/assets/paradise_lost/blockstates/tree_tap.json
new file mode 100644
index 000000000..0eb3bc66b
--- /dev/null
+++ b/src/main/resources/assets/paradise_lost/blockstates/tree_tap.json
@@ -0,0 +1,19 @@
+{
+ "variants": {
+ "facing=east": {
+ "model": "paradise_lost:block/tree_tap",
+ "y": 180
+ },
+ "facing=north": {
+ "model": "paradise_lost:block/tree_tap",
+ "y": 90
+ },
+ "facing=south": {
+ "model": "paradise_lost:block/tree_tap",
+ "y": 270
+ },
+ "facing=west": {
+ "model": "paradise_lost:block/tree_tap"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/assets/paradise_lost/lang/en_us.json b/src/main/resources/assets/paradise_lost/lang/en_us.json
index 96c9a75ba..982173c51 100644
--- a/src/main/resources/assets/paradise_lost/lang/en_us.json
+++ b/src/main/resources/assets/paradise_lost/lang/en_us.json
@@ -243,6 +243,7 @@
"block.paradise_lost.incubator": "Incubator",
"block.paradise_lost.food_bowl": "Feeding Trough",
+ "block.paradise_lost.tree_tap": "Tree Tap",
"block.paradise_lost.crystal_chest": "Crystal Chest",
"block.paradise_lost.golden_oak_chest": "Mother Aurel Chest",
diff --git a/src/main/resources/assets/paradise_lost/models/block/tree_tap.json b/src/main/resources/assets/paradise_lost/models/block/tree_tap.json
new file mode 100644
index 000000000..4652f39b9
--- /dev/null
+++ b/src/main/resources/assets/paradise_lost/models/block/tree_tap.json
@@ -0,0 +1,45 @@
+{
+ "credit": "Made with Blockbench",
+ "parent": "block/block",
+ "textures": {
+ "0": "paradise_lost:block/aurel_planks",
+ "particle": "paradise_lost:block/aurel_planks"
+ },
+ "elements": [
+ {
+ "from": [5, 0, 2],
+ "to": [16, 1, 14],
+ "faces": {
+ "north": {"uv": [0, 0, 11, 1], "texture": "#0"},
+ "east": {"uv": [0, 0, 12, 1], "texture": "#0"},
+ "south": {"uv": [0, 0, 11, 1], "texture": "#0"},
+ "west": {"uv": [0, 0, 12, 1], "texture": "#0"},
+ "up": {"uv": [0, 0, 11, 12], "texture": "#0"},
+ "down": {"uv": [0, 0, 11, 12], "texture": "#0"}
+ }
+ },
+ {
+ "from": [9, 13, 7],
+ "to": [16, 15, 9],
+ "faces": {
+ "north": {"uv": [0, 0, 7, 2], "texture": "#0"},
+ "east": {"uv": [0, 0, 2, 2], "texture": "#0"},
+ "south": {"uv": [0, 0, 7, 2], "texture": "#0"},
+ "west": {"uv": [0, 0, 2, 2], "texture": "#0"},
+ "up": {"uv": [0, 0, 7, 2], "texture": "#0"},
+ "down": {"uv": [0, 0, 7, 2], "texture": "#0"}
+ }
+ },
+ {
+ "from": [9, 12, 7],
+ "to": [11, 13, 9],
+ "faces": {
+ "north": {"uv": [0, 0, 2, 1], "texture": "#0"},
+ "east": {"uv": [0, 0, 2, 1], "texture": "#0"},
+ "south": {"uv": [0, 0, 2, 1], "texture": "#0"},
+ "west": {"uv": [0, 0, 2, 1], "texture": "#0"},
+ "down": {"uv": [0, 0, 2, 2], "texture": "#0"}
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/main/resources/assets/paradise_lost/models/item/tree_tap.json b/src/main/resources/assets/paradise_lost/models/item/tree_tap.json
new file mode 100644
index 000000000..8fd0579d8
--- /dev/null
+++ b/src/main/resources/assets/paradise_lost/models/item/tree_tap.json
@@ -0,0 +1,3 @@
+{
+ "parent": "paradise_lost:block/tree_tap"
+}
diff --git a/src/main/resources/data/paradise_lost/loot_tables/blocks/tree_tap.json b/src/main/resources/data/paradise_lost/loot_tables/blocks/tree_tap.json
new file mode 100644
index 000000000..5d560df11
--- /dev/null
+++ b/src/main/resources/data/paradise_lost/loot_tables/blocks/tree_tap.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "paradise_lost:tree_tap"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/main/resources/data/paradise_lost/recipes/tree_tap.json b/src/main/resources/data/paradise_lost/recipes/tree_tap.json
new file mode 100644
index 000000000..61b0199ea
--- /dev/null
+++ b/src/main/resources/data/paradise_lost/recipes/tree_tap.json
@@ -0,0 +1,19 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "SSS",
+ " S",
+ "PPP"
+ ],
+ "key": {
+ "P": {
+ "item": "paradise_lost:aurel_planks"
+ },
+ "S": {
+ "item": "minecraft:stick"
+ }
+ },
+ "result": {
+ "item": "paradise_lost:tree_tap"
+ }
+}
diff --git a/src/main/resources/data/paradise_lost/recipes/tree_tap/water.json b/src/main/resources/data/paradise_lost/recipes/tree_tap/water.json
new file mode 100644
index 000000000..469055eaf
--- /dev/null
+++ b/src/main/resources/data/paradise_lost/recipes/tree_tap/water.json
@@ -0,0 +1,11 @@
+{
+ "type": "paradise_lost:tree_tap",
+ "ingredient": {
+ "item": "minecraft:glass_bottle"
+ },
+ "tapped_block": "paradise_lost:aurel_log",
+ "result": {
+ "item": "minecraft:potion",
+ "nbt": "{Potion: \"minecraft:water\"}"
+ }
+}