diff --git a/.editorconfig b/.editorconfig index ba9de9338..2f34f8535 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,9 @@ # http://editorconfig.org root = true +[*.java] +indent_style = tab + [*.json] indent_style = space indent_size = 2 \ No newline at end of file diff --git a/src/main/java/vectorwing/farmersdelight/client/event/ClientSetupEvents.java b/src/main/java/vectorwing/farmersdelight/client/event/ClientSetupEvents.java index 4cc188567..4a9b207f6 100644 --- a/src/main/java/vectorwing/farmersdelight/client/event/ClientSetupEvents.java +++ b/src/main/java/vectorwing/farmersdelight/client/event/ClientSetupEvents.java @@ -16,6 +16,7 @@ import vectorwing.farmersdelight.client.particle.SteamParticle; import vectorwing.farmersdelight.client.recipebook.RecipeCategories; import vectorwing.farmersdelight.client.renderer.*; +import vectorwing.farmersdelight.common.block.entity.StoveBlockEntity; import vectorwing.farmersdelight.common.registry.ModBlockEntityTypes; import vectorwing.farmersdelight.common.registry.ModEntityTypes; import vectorwing.farmersdelight.common.registry.ModParticleTypes; @@ -40,7 +41,7 @@ public static void onEntityRendererRegister(EntityRenderersEvent.RegisterRendere @SubscribeEvent public static void onRegisterRenderers(EntityRenderersEvent.RegisterRenderers event) { - event.registerBlockEntityRenderer(ModBlockEntityTypes.STOVE.get(), StoveRenderer::new); + event.registerBlockEntityRenderer(ModBlockEntityTypes.STOVE.get(), DefaultStoveRenderer::new); event.registerBlockEntityRenderer(ModBlockEntityTypes.CUTTING_BOARD.get(), CuttingBoardRenderer::new); event.registerBlockEntityRenderer(ModBlockEntityTypes.CANVAS_SIGN.get(), CanvasSignRenderer::new); event.registerBlockEntityRenderer(ModBlockEntityTypes.HANGING_CANVAS_SIGN.get(), HangingCanvasSignRenderer::new); diff --git a/src/main/java/vectorwing/farmersdelight/client/renderer/DefaultStoveRenderer.java b/src/main/java/vectorwing/farmersdelight/client/renderer/DefaultStoveRenderer.java new file mode 100644 index 000000000..1e0920377 --- /dev/null +++ b/src/main/java/vectorwing/farmersdelight/client/renderer/DefaultStoveRenderer.java @@ -0,0 +1,62 @@ +package vectorwing.farmersdelight.client.renderer; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.core.Direction; +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec2; +import vectorwing.farmersdelight.common.block.StoveBlock; +import vectorwing.farmersdelight.common.block.entity.AbstractStoveBlockEntity; + +public class DefaultStoveRenderer implements BlockEntityRenderer +{ + private static final float SIZE = 0.375F; + private final ItemRenderer itemRenderer; + + public DefaultStoveRenderer(BlockEntityRendererProvider.Context context) { + this.itemRenderer = context.getItemRenderer(); + } + + @Override + public void render(T stove, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) { + Direction direction = stove.getBlockState().getValue(StoveBlock.FACING).getOpposite(); + + var items = stove.getItems(); + int posLong = (int) stove.getBlockPos().asLong(); + + for (int i = 0; i < items.getSlots(); ++i) { + ItemStack stoveStack = items.getStackInSlot(i); + if (stoveStack.isEmpty()) continue; + + poseStack.pushPose(); + + // Center item above the stove + poseStack.translate(0.5D, 1.02D, 0.5D); + + // Rotate item to face the stove's front side + float f = -direction.toYRot(); + poseStack.mulPose(Axis.YP.rotationDegrees(f)); + + // Rotate item flat on the stove. Use X and Y from now on + poseStack.mulPose(Axis.XP.rotationDegrees(90.0F)); + + // Neatly align items according to their index + Vec2 itemOffset = stove.getStoveItemOffset(i); + poseStack.translate(itemOffset.x, itemOffset.y, 0.0D); + + // Resize the items + poseStack.scale(SIZE, SIZE, SIZE); + + itemRenderer.renderStatic(stoveStack, ItemDisplayContext.FIXED, LevelRenderer.getLightColor(stove.getLevel(), stove.getBlockPos().above()), packedOverlay, poseStack, buffer, stove.getLevel(), posLong + i); + poseStack.popPose(); + } + } +} \ No newline at end of file diff --git a/src/main/java/vectorwing/farmersdelight/client/renderer/StoveRenderer.java b/src/main/java/vectorwing/farmersdelight/client/renderer/StoveRenderer.java deleted file mode 100644 index 760d848af..000000000 --- a/src/main/java/vectorwing/farmersdelight/client/renderer/StoveRenderer.java +++ /dev/null @@ -1,58 +0,0 @@ -package vectorwing.farmersdelight.client.renderer; - -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.math.Axis; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.LevelRenderer; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; -import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; -import net.minecraft.core.Direction; -import net.minecraft.world.item.ItemDisplayContext; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.phys.Vec2; -import net.minecraftforge.items.ItemStackHandler; -import vectorwing.farmersdelight.common.block.StoveBlock; -import vectorwing.farmersdelight.common.block.entity.StoveBlockEntity; - -public class StoveRenderer implements BlockEntityRenderer -{ - public StoveRenderer(BlockEntityRendererProvider.Context context) { - } - - @Override - public void render(StoveBlockEntity stove, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) { - Direction direction = stove.getBlockState().getValue(StoveBlock.FACING).getOpposite(); - - ItemStackHandler inventory = stove.getInventory(); - int posLong = (int) stove.getBlockPos().asLong(); - - for (int i = 0; i < inventory.getSlots(); ++i) { - ItemStack stoveStack = inventory.getStackInSlot(i); - if (!stoveStack.isEmpty()) { - poseStack.pushPose(); - - // Center item above the stove - poseStack.translate(0.5D, 1.02D, 0.5D); - - // Rotate item to face the stove's front side - float f = -direction.toYRot(); - poseStack.mulPose(Axis.YP.rotationDegrees(f)); - - // Rotate item flat on the stove. Use X and Y from now on - poseStack.mulPose(Axis.XP.rotationDegrees(90.0F)); - - // Neatly align items according to their index - Vec2 itemOffset = stove.getStoveItemOffset(i); - poseStack.translate(itemOffset.x, itemOffset.y, 0.0D); - - // Resize the items - poseStack.scale(0.375F, 0.375F, 0.375F); - - if (stove.getLevel() != null) - Minecraft.getInstance().getItemRenderer().renderStatic(stoveStack, ItemDisplayContext.FIXED, LevelRenderer.getLightColor(stove.getLevel(), stove.getBlockPos().above()), packedOverlay, poseStack, buffer, stove.getLevel(), posLong + i); - poseStack.popPose(); - } - } - } -} \ No newline at end of file diff --git a/src/main/java/vectorwing/farmersdelight/common/block/AbstractStoveBlock.java b/src/main/java/vectorwing/farmersdelight/common/block/AbstractStoveBlock.java new file mode 100644 index 000000000..9646e4a50 --- /dev/null +++ b/src/main/java/vectorwing/farmersdelight/common/block/AbstractStoveBlock.java @@ -0,0 +1,234 @@ +package vectorwing.farmersdelight.common.block; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.*; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.crafting.AbstractCookingRecipe; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.pathfinder.BlockPathTypes; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.BooleanOp; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraftforge.common.ToolActions; +import vectorwing.farmersdelight.common.block.entity.AbstractStoveBlockEntity; +import vectorwing.farmersdelight.common.registry.ModDamageTypes; +import vectorwing.farmersdelight.common.tag.CommonTags; +import vectorwing.farmersdelight.common.utility.ItemUtils; +import vectorwing.farmersdelight.common.utility.MathUtils; + +import javax.annotation.Nullable; +import java.util.Optional; + +@SuppressWarnings("deprecation") +public abstract class AbstractStoveBlock extends BaseEntityBlock { + public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; + public static final BooleanProperty LIT = BlockStateProperties.LIT; + + private static final VoxelShape GRILLING_AREA = Block.box(3.0F, 0.0F, 3.0F, 13.0F, 1.0F, 13.0F); + + public AbstractStoveBlock(BlockBehaviour.Properties properties) { + super(properties); + this.registerDefaultState( + this.stateDefinition.any() + .setValue(FACING, Direction.NORTH) + .setValue(LIT, false) + ); + } + + @Override + public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (state.getValue(LIT)) { + var extinguishResult = tryToExtinguish(state, level, pos, player, hand, hit); + if (extinguishResult != InteractionResult.PASS) return extinguishResult; + } else { + var igniteResult = tryToIgnite(state, level, pos, player, hand, hit); + if (igniteResult != InteractionResult.PASS) return igniteResult; + } + + return tryToPlaceFoodItem(state, level, pos, player, hand, hit); + } + + protected InteractionResult tryToIgnite(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + ItemStack heldStack = player.getItemInHand(hand); + Item heldItem = heldStack.getItem(); + + if (heldItem instanceof FlintAndSteelItem) { + if (!level.isClientSide()) { + level.playSound(null, pos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0F, MathUtils.RAND.nextFloat() * 0.4F + 0.8F); + } + ignite(player, level, pos, state); + heldStack.hurtAndBreak(1, player, action -> action.broadcastBreakEvent(hand)); + return InteractionResult.SUCCESS; + } + + if (heldItem instanceof FireChargeItem) { + if (!level.isClientSide()) { + level.playSound(null, pos, SoundEvents.FIRECHARGE_USE, SoundSource.BLOCKS, 1.0F, (MathUtils.RAND.nextFloat() - MathUtils.RAND.nextFloat()) * 0.2F + 1.0F); + } + ignite(player, level, pos, state); + if (!player.getAbilities().instabuild) { + heldStack.shrink(1); + } + return InteractionResult.SUCCESS; + } + + return InteractionResult.PASS; + } + + protected InteractionResult tryToExtinguish(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + ItemStack heldStack = player.getItemInHand(hand); + + if (heldStack.canPerformAction(ToolActions.SHOVEL_DIG)) { + if (!level.isClientSide()) { + level.levelEvent(null, LevelEvent.SOUND_EXTINGUISH_FIRE, pos, 0); + } + extinguish(player, level, pos, state); + heldStack.hurtAndBreak(1, player, (action) -> action.broadcastBreakEvent(hand)); + return InteractionResult.sidedSuccess(level.isClientSide()); + } + + if (heldStack.is(CommonTags.Items.BUCKETS_WATER)) { + if (!level.isClientSide()) { + level.playSound(null, pos, SoundEvents.GENERIC_EXTINGUISH_FIRE, SoundSource.BLOCKS, 1.0F, 1.0F); + } + extinguish(player, level, pos, state); + if (!player.getAbilities().instabuild) player.setItemInHand(hand, heldStack.getCraftingRemainingItem()); + return InteractionResult.sidedSuccess(level.isClientSide()); + } + + return InteractionResult.PASS; + } + + protected InteractionResult tryToPlaceFoodItem(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (isStoveTopCovered(level, pos, state)) return InteractionResult.PASS; + if (!(level.getBlockEntity(pos) instanceof AbstractStoveBlockEntity stoveEntity)) return InteractionResult.PASS; + + ItemStack heldStack = player.getItemInHand(hand); + Optional maybeRecipe = stoveEntity.getCookingRecipe(heldStack); + if (maybeRecipe.isEmpty()) return InteractionResult.PASS; + if (level.isClientSide) return InteractionResult.CONSUME; + boolean placeFoodSuccess = stoveEntity.placeFood(player, player.getAbilities().instabuild ? heldStack.copy() : heldStack, maybeRecipe.get().getCookingTime()); + if (!placeFoodSuccess) return InteractionResult.CONSUME; + level.playSound(null, pos, SoundEvents.LANTERN_PLACE, SoundSource.BLOCKS, 0.5F, 1.0F); + return InteractionResult.SUCCESS; + } + + public void ignite(@Nullable Entity entity, LevelAccessor level, BlockPos pos, BlockState state) { + if (level.getBlockEntity(pos) instanceof AbstractStoveBlockEntity stoveEntity) { + stoveEntity.ignite(); + } + if (level.isClientSide()) return; + + var newState = state.setValue(LIT, true); + level.setBlock(pos, newState, 11); + level.gameEvent(entity, GameEvent.BLOCK_CHANGE, pos); + } + + public void extinguish(@Nullable Entity entity, LevelAccessor level, BlockPos pos, BlockState state) { + if (level.getBlockEntity(pos) instanceof AbstractStoveBlockEntity stoveEntity) { + stoveEntity.extinguish(); + } + if (level.isClientSide()) return; + + var newState = state.setValue(LIT, false); + level.setBlock(pos, newState, 11); + level.gameEvent(entity, GameEvent.BLOCK_CHANGE, pos); + } + + @Override + public RenderShape getRenderShape(BlockState pState) { return RenderShape.MODEL; } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState() + .setValue(FACING, context.getHorizontalDirection().getOpposite()) + .setValue(LIT, true); + } + + @Override + public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) { + burnEntitySteppingOnStove(level, pos, state, entity); + super.stepOn(level, pos, state, entity); + } + + protected void burnEntitySteppingOnStove(Level level, BlockPos pos, BlockState state, Entity entity) { + if (!state.getValue(LIT)) return; + if (!entity.getBoundingBox().intersects(GRILLING_AREA.bounds().move(pos.above()))) return; + if (entity.isSteppingCarefully()) return; + if (entity.fireImmune()) return; + if (!(entity instanceof LivingEntity livingEntity)) return; + if (EnchantmentHelper.hasFrostWalker(livingEntity)) return; + entity.hurt(ModDamageTypes.getSimpleDamageSource(level, ModDamageTypes.STOVE_BURN), 1.0F); + } + + @Override + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { + if (state.is(newState.getBlock())) return; + if (level.getBlockEntity(pos) instanceof AbstractStoveBlockEntity stoveEntity) { + ItemUtils.dropItems(level, pos, stoveEntity.getItems()); + } + super.onRemove(state, level, pos, newState, isMoving); + } + + /** + * Checks if the state is a Stove, and if the grilling area is being obstructed by the block above. + */ + public static boolean isStoveTopCovered(Level level, BlockPos pos, BlockState stoveState) { + if (!(stoveState.getBlock() instanceof StoveBlock)) return false; + BlockPos abovePos = pos.above(); + BlockState aboveState = level.getBlockState(abovePos); + return Shapes.joinIsNotEmpty(GRILLING_AREA, aboveState.getShape(level, abovePos), BooleanOp.AND); + } + + @Override + protected void createBlockStateDefinition(final StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(FACING, LIT); + } + + @Nullable + protected static BlockEntityTicker createStoveTicker(Level level, BlockEntityType serverType, BlockEntityType clientType) { + if (level.isClientSide) return null; + return createTickerHelper(serverType, clientType, AbstractStoveBlockEntity::serverTick); + } + + @Nullable + @Override + public BlockPathTypes getBlockPathType(BlockState state, BlockGetter level, BlockPos pos, @Nullable Mob entity) { + return state.getValue(LIT) ? BlockPathTypes.DAMAGE_FIRE : null; + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return state.rotate(mirror.getRotation(state.getValue(FACING))); + } +} diff --git a/src/main/java/vectorwing/farmersdelight/common/block/StoveBlock.java b/src/main/java/vectorwing/farmersdelight/common/block/StoveBlock.java index 22a6f888e..a32e1f68c 100644 --- a/src/main/java/vectorwing/farmersdelight/common/block/StoveBlock.java +++ b/src/main/java/vectorwing/farmersdelight/common/block/StoveBlock.java @@ -3,24 +3,8 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.RandomSource; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.SimpleContainer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.FireChargeItem; -import net.minecraft.world.item.FlintAndSteelItem; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.item.crafting.CampfireCookingRecipe; -import net.minecraft.world.item.enchantment.EnchantmentHelper; -import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntity; @@ -28,204 +12,45 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; -import net.minecraft.world.level.block.state.properties.BooleanProperty; -import net.minecraft.world.level.block.state.properties.DirectionProperty; -import net.minecraft.world.level.pathfinder.BlockPathTypes; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.shapes.BooleanOp; -import net.minecraft.world.phys.shapes.Shapes; -import net.minecraft.world.phys.shapes.VoxelShape; -import net.minecraftforge.common.ToolActions; import vectorwing.farmersdelight.common.block.entity.StoveBlockEntity; import vectorwing.farmersdelight.common.registry.ModBlockEntityTypes; -import vectorwing.farmersdelight.common.registry.ModDamageTypes; import vectorwing.farmersdelight.common.registry.ModSounds; -import vectorwing.farmersdelight.common.tag.CommonTags; -import vectorwing.farmersdelight.common.utility.ItemUtils; -import vectorwing.farmersdelight.common.utility.MathUtils; import javax.annotation.Nullable; -import java.util.Optional; -@SuppressWarnings("deprecation") -public class StoveBlock extends BaseEntityBlock +public class StoveBlock extends AbstractStoveBlock { - public static final BooleanProperty LIT = BlockStateProperties.LIT; - public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; - - private static final VoxelShape GRILLING_AREA = Block.box(3.0F, 0.0F, 3.0F, 13.0F, 1.0F, 13.0F); - public StoveBlock(BlockBehaviour.Properties properties) { super(properties); - this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(LIT, false)); - } - - @Override - public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { - ItemStack heldStack = player.getItemInHand(hand); - Item heldItem = heldStack.getItem(); - - if (state.getValue(LIT)) { - if (heldStack.canPerformAction(ToolActions.SHOVEL_DIG)) { - extinguish(state, level, pos); - heldStack.hurtAndBreak(1, player, action -> action.broadcastBreakEvent(hand)); - return InteractionResult.SUCCESS; - } else if (heldStack.is(CommonTags.Items.BUCKETS_WATER)) { - if (!level.isClientSide()) { - level.playSound(null, pos, SoundEvents.GENERIC_EXTINGUISH_FIRE, SoundSource.BLOCKS, 1.0F, 1.0F); - } - extinguish(state, level, pos); - if (!player.isCreative()) { - player.setItemInHand(hand, heldStack.getCraftingRemainingItem()); - } - return InteractionResult.SUCCESS; - } - } else { - if (heldItem instanceof FlintAndSteelItem) { - level.playSound(player, pos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0F, MathUtils.RAND.nextFloat() * 0.4F + 0.8F); - level.setBlock(pos, state.setValue(BlockStateProperties.LIT, Boolean.TRUE), 11); - heldStack.hurtAndBreak(1, player, action -> action.broadcastBreakEvent(hand)); - return InteractionResult.SUCCESS; - } else if (heldItem instanceof FireChargeItem) { - level.playSound(null, pos, SoundEvents.FIRECHARGE_USE, SoundSource.BLOCKS, 1.0F, (MathUtils.RAND.nextFloat() - MathUtils.RAND.nextFloat()) * 0.2F + 1.0F); - level.setBlock(pos, state.setValue(BlockStateProperties.LIT, Boolean.TRUE), 11); - if (!player.isCreative()) { - heldStack.shrink(1); - } - return InteractionResult.SUCCESS; - } - } - - if (!isStoveTopCovered(level, pos, state) && level.getBlockEntity(pos) instanceof StoveBlockEntity stoveEntity) { - int stoveSlot = stoveEntity.getNextEmptySlot(); - if (stoveSlot < 0) { - return InteractionResult.PASS; - } - Optional recipe = stoveEntity.getMatchingRecipe(new SimpleContainer(heldStack), stoveSlot); - if (recipe.isPresent()) { - if (!level.isClientSide && stoveEntity.addItem(player.getAbilities().instabuild ? heldStack.copy() : heldStack, recipe.get(), stoveSlot)) { - level.playSound(null, pos, SoundEvents.LANTERN_PLACE, SoundSource.BLOCKS, 0.5F, 1.0F); - return InteractionResult.SUCCESS; - } - return InteractionResult.CONSUME; - } - } - - return InteractionResult.PASS; - } - - @Override - public RenderShape getRenderShape(BlockState state) { - return RenderShape.MODEL; - } - - public void extinguish(BlockState state, Level level, BlockPos pos) { - level.setBlock(pos, state.setValue(LIT, false), 2); - double x = (double) pos.getX() + 0.5D; - double y = pos.getY(); - double z = (double) pos.getZ() + 0.5D; - level.playLocalSound(x, y, z, SoundEvents.FIRE_EXTINGUISH, SoundSource.BLOCKS, 0.5F, 2.6F, false); - } - - @Override - public BlockState getStateForPlacement(BlockPlaceContext context) { - return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, true); - } - - @Override - public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) { - if (entity.getBoundingBox().intersects(GRILLING_AREA.bounds().move(pos.above()))) { - boolean isLit = level.getBlockState(pos).getValue(StoveBlock.LIT); - if (isLit && !entity.isSteppingCarefully() && !entity.fireImmune() && entity instanceof LivingEntity && !EnchantmentHelper.hasFrostWalker((LivingEntity) entity)) { - entity.hurt(ModDamageTypes.getSimpleDamageSource(level, ModDamageTypes.STOVE_BURN), 1.0F); - } - } - - super.stepOn(level, pos, state, entity); - } - - @Override - public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { - if (!state.is(newState.getBlock())) { - if (level.getBlockEntity(pos) instanceof StoveBlockEntity stove) { - ItemUtils.dropItems(level, pos, stove.getInventory()); - } - - super.onRemove(state, level, pos, newState, isMoving); - } - } - - /** - * Checks if the state is a Stove, and if the grilling area is being obstructed by the block above. - */ - public static boolean isStoveTopCovered(Level level, BlockPos pos, BlockState stoveState) { - if (stoveState.getBlock() instanceof StoveBlock) { - BlockPos abovePos = pos.above(); - BlockState aboveState = level.getBlockState(abovePos); - return Shapes.joinIsNotEmpty(GRILLING_AREA, aboveState.getShape(level, abovePos), BooleanOp.AND); - } - return false; - } - - @Override - protected void createBlockStateDefinition(final StateDefinition.Builder builder) { - super.createBlockStateDefinition(builder); - builder.add(LIT, FACING); } - @Override - public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { - if (state.getValue(CampfireBlock.LIT)) { - double x = (double) pos.getX() + 0.5D; - double y = pos.getY(); - double z = (double) pos.getZ() + 0.5D; - if (random.nextInt(10) == 0) { - level.playLocalSound(x, y, z, ModSounds.BLOCK_STOVE_CRACKLE.get(), SoundSource.BLOCKS, 1.0F, 1.0F, false); - } - - Direction direction = state.getValue(HorizontalDirectionalBlock.FACING); - Direction.Axis direction$axis = direction.getAxis(); - double horizontalOffset = random.nextDouble() * 0.6D - 0.3D; - double xOffset = direction$axis == Direction.Axis.X ? (double) direction.getStepX() * 0.52D : horizontalOffset; - double yOffset = random.nextDouble() * 6.0D / 16.0D; - double zOffset = direction$axis == Direction.Axis.Z ? (double) direction.getStepZ() * 0.52D : horizontalOffset; - level.addParticle(ParticleTypes.SMOKE, x + xOffset, y + yOffset, z + zOffset, 0.0D, 0.0D, 0.0D); - level.addParticle(ParticleTypes.FLAME, x + xOffset, y + yOffset, z + zOffset, 0.0D, 0.0D, 0.0D); - } - } - - @Nullable - @Override public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { - return ModBlockEntityTypes.STOVE.get().create(pos, state); + return new StoveBlockEntity(pos, state); } @Nullable - @Override public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { - if (state.getValue(LIT)) { - return createTickerHelper(blockEntityType, ModBlockEntityTypes.STOVE.get(), level.isClientSide - ? StoveBlockEntity::animationTick - : StoveBlockEntity::cookingTick); - } - return null; - } - - @Nullable - @Override - public BlockPathTypes getBlockPathType(BlockState state, BlockGetter level, BlockPos pos, @Nullable Mob entity) { - return state.getValue(LIT) ? BlockPathTypes.DAMAGE_FIRE : null; + if (level.isClientSide && state.getValue(LIT)) return createTickerHelper(blockEntityType, ModBlockEntityTypes.STOVE.get(), StoveBlockEntity::particleTick); + return createStoveTicker(level, blockEntityType, ModBlockEntityTypes.STOVE.get()); } @Override - public BlockState rotate(BlockState state, Rotation rotation) { - return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); - } + public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { + if (!state.getValue(CampfireBlock.LIT)) return; + double x = (double) pos.getX() + 0.5D; + double y = pos.getY(); + double z = (double) pos.getZ() + 0.5D; + if (random.nextInt(10) == 0) { + level.playLocalSound(x, y, z, ModSounds.BLOCK_STOVE_CRACKLE.get(), SoundSource.BLOCKS, 1.0F, 1.0F, false); + } - @Override - public BlockState mirror(BlockState state, Mirror mirror) { - return state.rotate(mirror.getRotation(state.getValue(FACING))); + Direction direction = state.getValue(HorizontalDirectionalBlock.FACING); + Direction.Axis direction$axis = direction.getAxis(); + double horizontalOffset = random.nextDouble() * 0.6D - 0.3D; + double xOffset = direction$axis == Direction.Axis.X ? (double) direction.getStepX() * 0.52D : horizontalOffset; + double yOffset = random.nextDouble() * 6.0D / 16.0D; + double zOffset = direction$axis == Direction.Axis.Z ? (double) direction.getStepZ() * 0.52D : horizontalOffset; + level.addParticle(ParticleTypes.SMOKE, x + xOffset, y + yOffset, z + zOffset, 0.0D, 0.0D, 0.0D); + level.addParticle(ParticleTypes.FLAME, x + xOffset, y + yOffset, z + zOffset, 0.0D, 0.0D, 0.0D); } } diff --git a/src/main/java/vectorwing/farmersdelight/common/block/entity/AbstractStoveBlockEntity.java b/src/main/java/vectorwing/farmersdelight/common/block/entity/AbstractStoveBlockEntity.java new file mode 100644 index 000000000..ad7136762 --- /dev/null +++ b/src/main/java/vectorwing/farmersdelight/common/block/entity/AbstractStoveBlockEntity.java @@ -0,0 +1,239 @@ +package vectorwing.farmersdelight.common.block.entity; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.util.Mth; +import net.minecraft.world.*; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.AbstractCookingRecipe; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.phys.Vec2; +import net.minecraftforge.items.ItemStackHandler; +import vectorwing.farmersdelight.common.block.AbstractStoveBlock; +import vectorwing.farmersdelight.common.utility.ItemUtils; + +import javax.annotation.Nullable; +import java.util.Optional; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public abstract class AbstractStoveBlockEntity extends BlockEntity implements Clearable { + private final ItemStackHandler items; + private final int[] cookingProgress; + private final int[] cookingTime; + private final RecipeManager.CachedCheck quickRecipeLookup; + + protected AbstractStoveBlockEntity( + BlockEntityType blockEntityType, + BlockPos blockPos, + BlockState blockState, + RecipeType recipeType + ) { + super(blockEntityType, blockPos, blockState); + + int inventorySlotCount = this.getInventorySlotCount(); + items = createHandler(inventorySlotCount); + cookingProgress = new int[inventorySlotCount]; + cookingTime = new int[inventorySlotCount]; + quickRecipeLookup = RecipeManager.createCheck(recipeType); + } + + protected abstract int getInventorySlotCount(); + + public abstract Vec2 getStoveItemOffset(int index); + + public ItemStackHandler getItems() { + return this.items; + } + + @Override + public void load(CompoundTag tag) { + super.load(tag); + + CompoundTag inventoryTag; + if (tag.contains("Inventory")) inventoryTag = tag.getCompound("Inventory"); + else inventoryTag = tag; + items.deserializeNBT(inventoryTag); + + if (tag.contains("CookingTimes", 11)) { + int[] arrayCookingTimes = tag.getIntArray("CookingTimes"); + System.arraycopy(arrayCookingTimes, 0, this.cookingProgress, 0, Math.min(this.cookingTime.length, arrayCookingTimes.length)); + } + + if (tag.contains("CookingTotalTimes", 11)) { + int[] arrayCookingTimesTotal = tag.getIntArray("CookingTotalTimes"); + System.arraycopy(arrayCookingTimesTotal, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, arrayCookingTimesTotal.length)); + } + } + + @Override + public void saveAdditional(CompoundTag tag) { + super.saveAdditional(tag); + tag.put("Inventory", items.serializeNBT()); + tag.putIntArray("CookingTimes", this.cookingProgress); + tag.putIntArray("CookingTotalTimes", this.cookingTime); + } + + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + @Override + public CompoundTag getUpdateTag() { + CompoundTag tag = super.getUpdateTag(); + tag.put("Inventory", items.serializeNBT()); + return tag; + } + + public static void serverTick(Level level, BlockPos pos, BlockState state, AbstractStoveBlockEntity stoveEntity) { + if (stoveEntity.isEmpty()) return; + if (stoveEntity.shouldDropItems()) { + stoveEntity.dropAllItems(); + stoveEntity.setChanged(); + return; + } + + if (state.getValue(AbstractStoveBlock.LIT)) { + stoveEntity.cookAndOutputItems(); + } else { + stoveEntity.coolItems(); + } + } + + private void cookAndOutputItems() { + assert this.level != null; + + boolean didChange = false; + for (int i = 0; i < items.getSlots(); ++i) { + ItemStack ingredient = this.items.getStackInSlot(i); + if (ingredient.isEmpty()) continue; + didChange = true; + + ++cookingProgress[i]; + if (cookingProgress[i] < cookingTime[i]) continue; + Container container = new SimpleContainer(ingredient); + ItemStack result = this.quickRecipeLookup.getRecipeFor(container, this.level).map( + (recipe) -> recipe.assemble(container, this.level.registryAccess()) + ).orElse(ingredient); + + if (!result.isItemEnabled(this.level.enabledFeatures())) continue; + Containers.dropItemStack( + this.level, + this.worldPosition.getX() + 0.5, + this.worldPosition.getY() + 1.0, + this.worldPosition.getZ() + 0.5, + result + ); + this.items.setStackInSlot(i, ItemStack.EMPTY); + var state = this.getBlockState(); + this.level.sendBlockUpdated(this.worldPosition, state, state, Block.UPDATE_ALL); + this.level.gameEvent(GameEvent.BLOCK_CHANGE, this.worldPosition, GameEvent.Context.of(state)); + } + if (didChange) this.setChanged(); + } + + private void coolItems() { + assert this.level != null; + + boolean didChange = false; + for (int i = 0; i < this.items.getSlots(); ++i) { + int thisItemCookingProgress = this.cookingProgress[i]; + if (thisItemCookingProgress <= 0) continue; + didChange = true; + this.cookingProgress[i] = Mth.clamp(thisItemCookingProgress - 2, 0, this.cookingTime[i]); + } + if (didChange) this.setChanged(); + } + + public Optional getCookingRecipe(ItemStack itemStack) { + assert this.level != null; + return this.quickRecipeLookup.getRecipeFor(new SimpleContainer(itemStack), this.level); + } + + public int getNextEmptySlot() { + return IntStream.range(0, this.items.getSlots()) + .filter((i) -> this.items.getStackInSlot(i).isEmpty()) + .findFirst() + .orElse(-1); + } + + public boolean placeFood(@Nullable Entity entity, ItemStack foodStackToPlace, int foodCookingTime) { + assert this.level != null; + + int emptySlotIndex = getNextEmptySlot(); + if (emptySlotIndex < 0) return false; + assert this.items.getStackInSlot(emptySlotIndex).isEmpty(); + + this.cookingTime[emptySlotIndex] = foodCookingTime; + this.cookingProgress[emptySlotIndex] = 0; + this.items.setStackInSlot(emptySlotIndex, foodStackToPlace.split(1)); + var state = this.getBlockState(); + this.level.sendBlockUpdated(this.worldPosition, state, state, Block.UPDATE_ALL); + this.level.gameEvent(GameEvent.BLOCK_CHANGE, this.worldPosition, GameEvent.Context.of(entity, state)); + this.setChanged(); + return true; + } + + public boolean shouldDropItems() { + if (this.level == null) return false; + return AbstractStoveBlock.isStoveTopCovered(this.level, this.worldPosition, this.getBlockState()); + } + + public Stream streamItems() { + return IntStream.range(0, this.items.getSlots()) + .mapToObj(this.items::getStackInSlot); + } + + public boolean isEmpty() { + return streamItems().allMatch(ItemStack::isEmpty); + } + + public boolean isFull() { + return streamItems().noneMatch(ItemStack::isEmpty); + } + + public void dropAllItems() { + if (this.level == null) return; + ItemUtils.dropItems(this.level, this.worldPosition, this.items); + var state = this.getBlockState(); + this.level.sendBlockUpdated(this.worldPosition, state, state, Block.UPDATE_ALL); + this.level.gameEvent(GameEvent.BLOCK_CHANGE, this.worldPosition, GameEvent.Context.of(state)); + } + + public void extinguish() { + if (this.level == null) return; + this.level.sendBlockUpdated(this.worldPosition, this.getBlockState(), this.getBlockState(), Block.UPDATE_ALL); + this.setChanged(); + // level.gameEvent is called in AbstractStoveBlock::extinguish, so it is not called here + } + + public void ignite() { + if (this.level == null) return; + this.level.sendBlockUpdated(this.worldPosition, this.getBlockState(), this.getBlockState(), Block.UPDATE_ALL); + this.setChanged(); + // level.gameEvent is called in AbstractStoveBLock::ignite, so it is not called here + } + + public void clearContent() { streamItems().forEach((stack) -> stack.setCount(0)); } + + private static ItemStackHandler createHandler(int slotCount) { + return new ItemStackHandler(slotCount) + { + @Override + public int getSlotLimit(int slot) { + return 1; + } + }; + } + +} diff --git a/src/main/java/vectorwing/farmersdelight/common/block/entity/StoveBlockEntity.java b/src/main/java/vectorwing/farmersdelight/common/block/entity/StoveBlockEntity.java index 2cfa1435f..d3c135cfe 100644 --- a/src/main/java/vectorwing/farmersdelight/common/block/entity/StoveBlockEntity.java +++ b/src/main/java/vectorwing/farmersdelight/common/block/entity/StoveBlockEntity.java @@ -3,213 +3,65 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.Mth; -import net.minecraft.world.Container; -import net.minecraft.world.SimpleContainer; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.CampfireCookingRecipe; -import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec2; -import net.minecraftforge.items.ItemStackHandler; -import vectorwing.farmersdelight.common.block.StoveBlock; -import vectorwing.farmersdelight.common.mixin.accessor.RecipeManagerAccessor; +import vectorwing.farmersdelight.common.block.AbstractStoveBlock; import vectorwing.farmersdelight.common.registry.ModBlockEntityTypes; -import vectorwing.farmersdelight.common.utility.ItemUtils; -import java.util.Optional; - -public class StoveBlockEntity extends SyncedBlockEntity +public class StoveBlockEntity extends AbstractStoveBlockEntity { - private static final int INVENTORY_SLOT_COUNT = 6; - - private final ItemStackHandler inventory; - private final int[] cookingTimes; - private final int[] cookingTimesTotal; - private ResourceLocation[] lastRecipeIDs; - public StoveBlockEntity(BlockPos pos, BlockState state) { - super(ModBlockEntityTypes.STOVE.get(), pos, state); - inventory = createHandler(); - cookingTimes = new int[INVENTORY_SLOT_COUNT]; - cookingTimesTotal = new int[INVENTORY_SLOT_COUNT]; - lastRecipeIDs = new ResourceLocation[INVENTORY_SLOT_COUNT]; - } - - @Override - public void load(CompoundTag compound) { - super.load(compound); - if (compound.contains("Inventory")) { - inventory.deserializeNBT(compound.getCompound("Inventory")); - } else { - inventory.deserializeNBT(compound); - } - if (compound.contains("CookingTimes", 11)) { - int[] arrayCookingTimes = compound.getIntArray("CookingTimes"); - System.arraycopy(arrayCookingTimes, 0, cookingTimes, 0, Math.min(cookingTimesTotal.length, arrayCookingTimes.length)); - } - - if (compound.contains("CookingTotalTimes", 11)) { - int[] arrayCookingTimesTotal = compound.getIntArray("CookingTotalTimes"); - System.arraycopy(arrayCookingTimesTotal, 0, cookingTimesTotal, 0, Math.min(cookingTimesTotal.length, arrayCookingTimesTotal.length)); - } - } - - @Override - public void saveAdditional(CompoundTag compound) { - writeItems(compound); - compound.putIntArray("CookingTimes", cookingTimes); - compound.putIntArray("CookingTotalTimes", cookingTimesTotal); - } - - private CompoundTag writeItems(CompoundTag compound) { - super.saveAdditional(compound); - compound.put("Inventory", inventory.serializeNBT()); - return compound; - } - - public static void cookingTick(Level level, BlockPos pos, BlockState state, StoveBlockEntity stove) { - boolean isStoveLit = state.getValue(StoveBlock.LIT); - - if (StoveBlock.isStoveTopCovered(level, pos, state)) { - if (ItemUtils.doesInventoryHaveItems(stove.inventory)) { - ItemUtils.dropItems(level, pos, stove.inventory); - stove.inventoryChanged(); - } - } else if (isStoveLit) { - stove.cookAndOutputItems(); - } else { - for (int i = 0; i < stove.inventory.getSlots(); ++i) { - if (stove.cookingTimes[i] > 0) { - stove.cookingTimes[i] = Mth.clamp(stove.cookingTimes[i] - 2, 0, stove.cookingTimesTotal[i]); - } - } - } - } - - public static void animationTick(Level level, BlockPos pos, BlockState state, StoveBlockEntity stove) { - for (int i = 0; i < stove.inventory.getSlots(); ++i) { - if (!stove.inventory.getStackInSlot(i).isEmpty() && level.random.nextFloat() < 0.2F) { - Vec2 stoveItemVector = stove.getStoveItemOffset(i); - Direction direction = state.getValue(StoveBlock.FACING); - int directionIndex = direction.get2DDataValue(); - Vec2 offset = directionIndex % 2 == 0 ? stoveItemVector : new Vec2(stoveItemVector.y, stoveItemVector.x); - - double x = ((double) pos.getX() + 0.5D) - (direction.getStepX() * offset.x) + (direction.getClockWise().getStepX() * offset.x); - double y = (double) pos.getY() + 1.0D; - double z = ((double) pos.getZ() + 0.5D) - (direction.getStepZ() * offset.y) + (direction.getClockWise().getStepZ() * offset.y); - - for (int k = 0; k < 3; ++k) { - level.addParticle(ParticleTypes.SMOKE, x, y, z, 0.0D, 5.0E-4D, 0.0D); - } - } - } - } - - private void cookAndOutputItems() { - if (level == null) return; - - boolean didInventoryChange = false; - for (int i = 0; i < inventory.getSlots(); ++i) { - ItemStack stoveStack = inventory.getStackInSlot(i); - if (!stoveStack.isEmpty()) { - ++cookingTimes[i]; - if (cookingTimes[i] >= cookingTimesTotal[i]) { - Container inventoryWrapper = new SimpleContainer(stoveStack); - Optional recipe = getMatchingRecipe(inventoryWrapper, i); - if (recipe.isPresent()) { - ItemStack resultStack = recipe.get().assemble(inventoryWrapper, level.registryAccess()); - if (!resultStack.isEmpty()) { - ItemUtils.spawnItemEntity(level, resultStack.copy(), - worldPosition.getX() + 0.5, worldPosition.getY() + 1.0, worldPosition.getZ() + 0.5, - level.random.nextGaussian() * (double) 0.01F, 0.1F, level.random.nextGaussian() * (double) 0.01F); - } - } - inventory.setStackInSlot(i, ItemStack.EMPTY); - didInventoryChange = true; - } - } - } - - if (didInventoryChange) { - inventoryChanged(); - } + super(ModBlockEntityTypes.STOVE.get(), pos, state, RecipeType.CAMPFIRE_COOKING); } - public int getNextEmptySlot() { - for (int i = 0; i < inventory.getSlots(); ++i) { - ItemStack slotStack = inventory.getStackInSlot(i); - if (slotStack.isEmpty()) { - return i; - } - } - return -1; + public static void particleTick(Level level, BlockPos pos, BlockState state, StoveBlockEntity stoveEntity) { + if (stoveEntity.isEmpty()) return; + stoveEntity.addSmokeParticles(); } - public boolean addItem(ItemStack itemStackIn, CampfireCookingRecipe recipe, int slot) { - if (0 <= slot && slot < inventory.getSlots()) { - ItemStack slotStack = inventory.getStackInSlot(slot); - if (slotStack.isEmpty()) { - cookingTimesTotal[slot] = recipe.getCookingTime(); - cookingTimes[slot] = 0; - inventory.setStackInSlot(slot, itemStackIn.split(1)); - lastRecipeIDs[slot] = recipe.getId(); - inventoryChanged(); - return true; + public void addSmokeParticles() { + assert this.level != null; + + var items = this.getItems(); + for (int i = 0; i < items.getSlots(); ++i) { + if (items.getStackInSlot(i).isEmpty()) continue; + if (level.random.nextFloat() >= 0.2F) continue; + Vec2 itemOffset = this.getStoveItemOffset(i); + Direction direction = this.getBlockState().getValue(AbstractStoveBlock.FACING); + if (direction.get2DDataValue() % 2 != 0) { + //noinspection SuspiciousNameCombination + itemOffset = new Vec2(itemOffset.y, itemOffset.x); } - } - return false; - } - public Optional getMatchingRecipe(Container recipeWrapper, int slot) { - if (level == null) return Optional.empty(); + double x = (worldPosition.getX() + 0.5D) - (direction.getStepX() * itemOffset.x) + (direction.getClockWise().getStepX() * itemOffset.x); + double y = worldPosition.getY() + 1.0D; + double z = (worldPosition.getZ() + 0.5D) - (direction.getStepZ() * itemOffset.y) + (direction.getClockWise().getStepZ() * itemOffset.y); - if (lastRecipeIDs[slot] != null) { - Recipe recipe = ((RecipeManagerAccessor) level.getRecipeManager()) - .getRecipeMap(RecipeType.CAMPFIRE_COOKING) - .get(lastRecipeIDs[slot]); - if (recipe instanceof CampfireCookingRecipe && recipe.matches(recipeWrapper, level)) { - return Optional.of((CampfireCookingRecipe) recipe); + for (int k = 0; k < 3; ++k) { + level.addParticle(ParticleTypes.SMOKE, x, y, z, 0.0D, 5.0E-4D, 0.0D); } } - - return level.getRecipeManager().getRecipeFor(RecipeType.CAMPFIRE_COOKING, recipeWrapper, level); } - public ItemStackHandler getInventory() { - return this.inventory; + @Override + protected int getInventorySlotCount() { + return 6; } + @Override public Vec2 getStoveItemOffset(int index) { final float X_OFFSET = 0.3F; final float Y_OFFSET = 0.2F; final Vec2[] OFFSETS = { - new Vec2(X_OFFSET, Y_OFFSET), - new Vec2(0.0F, Y_OFFSET), - new Vec2(-X_OFFSET, Y_OFFSET), - new Vec2(X_OFFSET, -Y_OFFSET), - new Vec2(0.0F, -Y_OFFSET), - new Vec2(-X_OFFSET, -Y_OFFSET), + new Vec2(X_OFFSET, Y_OFFSET), + new Vec2(0.0F, Y_OFFSET), + new Vec2(-X_OFFSET, Y_OFFSET), + new Vec2(X_OFFSET, -Y_OFFSET), + new Vec2(0.0F, -Y_OFFSET), + new Vec2(-X_OFFSET, -Y_OFFSET), }; return OFFSETS[index]; } - - @Override - public CompoundTag getUpdateTag() { - return writeItems(new CompoundTag()); - } - - private ItemStackHandler createHandler() { - return new ItemStackHandler(INVENTORY_SLOT_COUNT) - { - @Override - public int getSlotLimit(int slot) { - return 1; - } - }; - } }