From 578275f4b052abb1331f483c529583aab1ea8893 Mon Sep 17 00:00:00 2001 From: Joseph Clack <28568841+Lordfirespeed@users.noreply.github.com> Date: Sun, 3 Sep 2023 00:53:41 +0100 Subject: [PATCH 1/4] add abstract classes that mirror implementation of existing classes --- .../common/block/AbstractStoveBlock.java | 245 ++++++++++++++++++ .../entity/AbstractStoveBlockEntity.java | 214 +++++++++++++++ 2 files changed, 459 insertions(+) create mode 100644 src/main/java/vectorwing/farmersdelight/common/block/AbstractStoveBlock.java create mode 100644 src/main/java/vectorwing/farmersdelight/common/block/entity/AbstractStoveBlockEntity.java 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..29facf46e --- /dev/null +++ b/src/main/java/vectorwing/farmersdelight/common/block/AbstractStoveBlock.java @@ -0,0 +1,245 @@ +package vectorwing.farmersdelight.common.block; + +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.Containers; +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.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.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.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.ToolActions; +import vectorwing.farmersdelight.common.block.entity.AbstractStoveBlockEntity; +import vectorwing.farmersdelight.common.registry.ModDamageTypes; +import vectorwing.farmersdelight.common.registry.ModSounds; +import vectorwing.farmersdelight.common.utility.MathUtils; + +import javax.annotation.Nullable; +import java.util.Optional; + +public abstract class AbstractStoveBlock extends BaseEntityBlock { + public static final BooleanProperty LIT = BlockStateProperties.LIT; + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + + public AbstractStoveBlock(Properties properties) { + super(properties); + this.registerDefaultState( + this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(LIT, false) + ); + } + + public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (state.getValue(LIT)) { + if (tryToExtinguish(state, level, pos, player, hand, hit)) return InteractionResult.sidedSuccess(level.isClientSide()); + } else { + if (tryToIgnite(state, level, pos, player, hand, hit)) return InteractionResult.sidedSuccess(level.isClientSide()); + } + + if (tryToPlaceFoodItem(state, level, pos, player, hand, hit)) return InteractionResult.sidedSuccess(level.isClientSide()); + return InteractionResult.PASS; + } + + protected boolean tryToIgnite(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + ItemStack heldStack = player.getItemInHand(hand); + Item heldItem = heldStack.getItem(); + + if (heldStack.canPerformAction(ToolActions.SHOVEL_DIG)) { + this.extinguish(player, state, level, pos); + heldStack.hurtAndBreak(1, player, (action) -> { + action.broadcastBreakEvent(hand); + }); + return true; + } + + if (heldItem == Items.WATER_BUCKET) { + if (!level.isClientSide()) { + level.playSound(null, pos, SoundEvents.GENERIC_EXTINGUISH_FIRE, SoundSource.BLOCKS, 1.0F, 1.0F); + } + + this.extinguish(player, state, level, pos); + + if (!player.getAbilities().instabuild) { + player.setItemInHand(hand, new ItemStack(Items.BUCKET)); + } + + return true; + } + + return false; + } + + protected boolean tryToExtinguish(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) { + level.playSound(player, pos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0F, MathUtils.RAND.nextFloat() * 0.4F + 0.8F); + this.ignite(state, level, pos); + heldStack.hurtAndBreak(1, player, (action) -> { + action.broadcastBreakEvent(hand); + }); + return true; + } + + 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); + } + this.ignite(state, level, pos); + if (!player.getAbilities().instabuild) { + heldStack.shrink(1); + } + return true; + } + + return false; + } + + protected boolean tryToPlaceFoodItem(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + BlockEntity blockEntity = level.getBlockEntity(pos); + if (!(blockEntity instanceof AbstractStoveBlockEntity stoveBlockEntity)) return false; + + ItemStack itemstack = player.getItemInHand(hand); + Optional recipe = stoveBlockEntity.getCookableRecipe(itemstack); + if (recipe.isEmpty()) return false; + + if (level.isClientSide()) return true; + + return stoveBlockEntity.placeFood( + player, + player.getAbilities().instabuild ? itemstack.copy() : itemstack, + recipe.get().getCookingTime() + ); + } + + public BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState() + .setValue(FACING, context.getHorizontalDirection().getOpposite()) + .setValue(LIT, true); + } + + public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) { + boolean isLit = level.getBlockState(pos).getValue(LIT); + if (isLit && !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); + } + + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { + if (state.is(newState.getBlock())) return; + + BlockEntity tileEntity = level.getBlockEntity(pos); + if (tileEntity instanceof AbstractStoveBlockEntity stoveBlockEntity) { + Containers.dropContents(level, pos, stoveBlockEntity.getItems()); + } + + super.onRemove(state, level, pos, newState, isMoving); + } + + public RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + public BlockState rotate(BlockState p_48722_, Rotation rotation) { + return p_48722_.setValue(FACING, rotation.rotate(p_48722_.getValue(FACING))); + } + + public BlockState mirror(BlockState state, Mirror mirror) { + return state.rotate(mirror.getRotation(state.getValue(FACING))); + } + + protected void createBlockStateDefinition(StateDefinition.Builder stateBuilder) { + super.createBlockStateDefinition(stateBuilder); + stateBuilder.add(FACING, LIT); + } + + @OnlyIn(Dist.CLIENT) + public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource randomSource) { + if (state.getValue(CampfireBlock.LIT)) { + double x = pos.getX() + 0.5; + double y = pos.getY(); + double z = pos.getZ() + 0.5; + if (randomSource.nextInt(10) == 0) { + level.playLocalSound(x, y, z, ModSounds.BLOCK_STOVE_CRACKLE.get(), SoundSource.BLOCKS, 1.0F, 1.0F, false); + } + + Direction direction = (Direction)state.getValue(HorizontalDirectionalBlock.FACING); + Direction.Axis direction$axis = direction.getAxis(); + double horizontalOffset = randomSource.nextDouble() * 0.6 - 0.3; + double xOffset = direction$axis == Direction.Axis.X ? (double)direction.getStepX() * 0.52 : horizontalOffset; + double yOffset = randomSource.nextDouble() * 6.0 / 16.0; + double zOffset = direction$axis == Direction.Axis.Z ? (double)direction.getStepZ() * 0.52 : horizontalOffset; + level.addParticle(ParticleTypes.SMOKE, x + xOffset, y + yOffset, z + zOffset, 0.0, 0.0, 0.0); + level.addParticle(ParticleTypes.FLAME, x + xOffset, y + yOffset, z + zOffset, 0.0, 0.0, 0.0); + } + + } + + public void extinguish(@Nullable Entity entity, BlockState state, Level level, BlockPos pos) { + level.setBlock(pos, state.setValue(LIT, false), 2); + double x = pos.getX() + 0.5; + double y = pos.getY(); + double z = pos.getZ() + 0.5; + level.playLocalSound(x, y, z, SoundEvents.FIRE_EXTINGUISH, SoundSource.BLOCKS, 0.5F, 2.6F, false); + + BlockEntity blockentity = level.getBlockEntity(pos); + if (blockentity instanceof AbstractStoveBlockEntity stoveBlockEntity) { + stoveBlockEntity.extinguish(); + } + + level.gameEvent(entity, GameEvent.BLOCK_CHANGE, pos); + } + + public void ignite(BlockState state, Level level, BlockPos pos) { + level.setBlock(pos, state.setValue(LIT, true), 11); + } + + @Nullable + protected static BlockEntityTicker createStoveTicker(Level level, BlockState state, BlockEntityType tickerBlockEntityType, BlockEntityType> thisBlockEntityType) { + if (level.isClientSide) return createTickerHelper( + tickerBlockEntityType, + thisBlockEntityType, + AbstractStoveBlockEntity::particleTick + ); + + return createTickerHelper( + tickerBlockEntityType, + thisBlockEntityType, + state.getValue(LIT) + ? AbstractStoveBlockEntity::cookTick + : AbstractStoveBlockEntity::cooldownTick + ); + } + + @Nullable + public BlockPathTypes getBlockPathType(BlockState state, BlockGetter world, BlockPos pos, @Nullable Mob entity) { + return state.getValue(LIT) ? BlockPathTypes.DAMAGE_FIRE : null; + } +} 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..62d364cbd --- /dev/null +++ b/src/main/java/vectorwing/farmersdelight/common/block/entity/AbstractStoveBlockEntity.java @@ -0,0 +1,214 @@ +package vectorwing.farmersdelight.common.block.entity; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.NonNullList; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.util.Mth; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.Containers; +import net.minecraft.world.SimpleContainer; +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.minecraft.world.phys.shapes.BooleanOp; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import vectorwing.farmersdelight.common.block.StoveBlock; + +import javax.annotation.Nullable; +import java.util.Optional; + +public abstract class AbstractStoveBlockEntity> extends BlockEntity { + private static final VoxelShape GRILLING_AREA = Block.box(3.0, 0.0, 3.0, 13.0, 1.0, 13.0); + private final NonNullList items; + private final int[] cookingProgress; + private final int[] cookingTime; + private final RecipeManager.CachedCheck recipeLookup; + private final Vec2[] itemRenderOffsets; + + protected AbstractStoveBlockEntity(BlockEntityType blockEntityType, int inventorySlotCount, RT recipeType, BlockPos pos, BlockState state, Vec2[] offsets) { + super(blockEntityType, pos, state); + + items = NonNullList.withSize(inventorySlotCount, ItemStack.EMPTY); + cookingProgress = new int[inventorySlotCount]; + cookingTime = new int[inventorySlotCount]; + recipeLookup = RecipeManager.createCheck(recipeType); + itemRenderOffsets = new Vec2[inventorySlotCount]; + System.arraycopy(offsets, 0, itemRenderOffsets, 0, Math.min(inventorySlotCount, offsets.length)); + } + + public NonNullList getItems() { + return this.items; + } + + public void load(CompoundTag nbtTag) { + super.load(nbtTag); + + this.items.clear(); + ContainerHelper.loadAllItems(nbtTag, this.items); + + if (nbtTag.contains("CookingTimes", 11)) { + int[] arrayCookingTimes = nbtTag.getIntArray("CookingTimes"); + System.arraycopy(arrayCookingTimes, 0, this.cookingProgress, 0, Math.min(this.cookingTime.length, arrayCookingTimes.length)); + } + + if (nbtTag.contains("CookingTotalTimes", 11)) { + int[] arrayCookingTimesTotal = nbtTag.getIntArray("CookingTotalTimes"); + System.arraycopy(arrayCookingTimesTotal, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, arrayCookingTimesTotal.length)); + } + } + + public void saveAdditional(CompoundTag nbtTag) { + super.saveAdditional(nbtTag); + ContainerHelper.saveAllItems(nbtTag, this.items, true); + nbtTag.putIntArray("CookingTimes", this.cookingProgress); + nbtTag.putIntArray("CookingTotalTimes", this.cookingTime); + } + + public CompoundTag getUpdateTag() { + CompoundTag nbtTag = super.getUpdateTag(); + ContainerHelper.saveAllItems(nbtTag, this.items, true); + return nbtTag; + } + + public ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + public static void particleTick(Level level, BlockPos pos, BlockState state, AbstractStoveBlockEntity stove) { + for(int i = 0; i < stove.items.size(); ++i) { + if (stove.items.get(i).isEmpty() || level.random.nextFloat() >= 0.2F) continue; + + 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 = pos.getX() + 0.5 - (direction.getStepX() * offset.x) + (direction.getClockWise().getStepX() * offset.x); + double y = pos.getY() + 1.0; + double z = pos.getZ() + 0.5 - (direction.getStepZ() * offset.y) + (direction.getClockWise().getStepZ() * offset.y); + + for(int k = 0; k < 3; ++k) { + level.addParticle(ParticleTypes.SMOKE, x, y, z, 0.0, 5.0E-4, 0.0); + } + } + } + + public static void cooldownTick(Level level, BlockPos pos, BlockState state, AbstractStoveBlockEntity stove) { + boolean didChange = false; + + for(int i = 0; i < stove.items.size(); ++i) { + if (stove.cookingProgress[i] <= 0) continue; + didChange = true; + stove.cookingProgress[i] = Mth.clamp(stove.cookingProgress[i] - 2, 0, stove.cookingTime[i]); + } + + if (!didChange) return; + setChanged(level, pos, state); + } + + public static void cookTick(Level level, BlockPos pos, BlockState state, AbstractStoveBlockEntity stove) { + if (level == null) return; + + boolean didChange = false; + + if (stove.isBlockedAbove()) { + stove.dropAllItems(level, pos, state); + return; + } + + for(int i = 0; i < stove.items.size(); ++i) { + ItemStack stoveStack = stove.items.get(i); + if (stoveStack.isEmpty()) continue; + + didChange = true; + + int var10002 = stove.cookingProgress[i]++; + if (stove.cookingProgress[i] < stove.cookingTime[i]) continue; + + Container inventoryWrapper = new SimpleContainer(stoveStack); + ItemStack result = stove.recipeLookup.getRecipeFor(inventoryWrapper, level).map((recipe) -> { + return recipe.assemble(inventoryWrapper, level.registryAccess()); + }).orElse(stoveStack); + if (!result.isItemEnabled(level.enabledFeatures())) continue; + + Containers.dropItemStack( + level, + stove.worldPosition.getX() + 0.5, + stove.worldPosition.getY() + 1.0, + stove.worldPosition.getZ() + 0.5, + result + ); + stove.items.set(i, ItemStack.EMPTY); + level.sendBlockUpdated(pos, state, state, 3); + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(state)); + } + + if (!didChange) return; + setChanged(level, pos, state); + } + + public Optional getCookableRecipe(ItemStack item) { + if (isBlockedAbove()) return Optional.empty(); + if (items.stream().noneMatch(ItemStack::isEmpty)) return Optional.empty(); + return this.recipeLookup.getRecipeFor(new SimpleContainer(item), this.level); + } + + public boolean placeFood(@Nullable Entity placer, ItemStack itemStackToPlace, int cookingTime) { + for (int i = 0; i < this.items.size(); ++i) { + ItemStack itemStack = this.items.get(i); + if (!itemStack.isEmpty()) continue; + + this.cookingTime[i] = cookingTime; + this.cookingProgress[i] = 0; + this.items.set(i, itemStackToPlace.split(1)); + this.level.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(placer, this.getBlockState())); + this.markUpdated(); + return true; + } + + return false; + } + + public boolean isBlockedAbove() { + if (this.level == null) return false; + + BlockState above = this.level.getBlockState(this.worldPosition.above()); + return Shapes.joinIsNotEmpty(GRILLING_AREA, above.getShape(this.level, this.worldPosition.above()), BooleanOp.AND); + } + + public void dropAllItems(Level level, BlockPos pos, BlockState state) { + if (level == null) return; + + Containers.dropContents(level, pos, items); + } + + public Vec2 getStoveItemOffset(int index) { + return itemRenderOffsets[index]; + } + + protected void markUpdated() { + this.setChanged(); + this.getLevel().sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3); + } + + public void onExtinguish() {} + + public void extinguish() { + if (level == null) return; + onExtinguish(); + markUpdated(); + } +} From 59d748dba31d7417dbf656891a00f64978ab4e38 Mon Sep 17 00:00:00 2001 From: Joseph Clack <28568841+Lordfirespeed@users.noreply.github.com> Date: Sun, 3 Sep 2023 00:55:01 +0100 Subject: [PATCH 2/4] make stove renderer support any extension of abstract stove block entity --- .../client/renderer/StoveRenderer.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/vectorwing/farmersdelight/client/renderer/StoveRenderer.java b/src/main/java/vectorwing/farmersdelight/client/renderer/StoveRenderer.java index 9f2a5f335..771c7fa4f 100644 --- a/src/main/java/vectorwing/farmersdelight/client/renderer/StoveRenderer.java +++ b/src/main/java/vectorwing/farmersdelight/client/renderer/StoveRenderer.java @@ -8,27 +8,27 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; 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 net.minecraftforge.items.ItemStackHandler; import vectorwing.farmersdelight.common.block.StoveBlock; -import vectorwing.farmersdelight.common.block.entity.StoveBlockEntity; +import vectorwing.farmersdelight.common.block.entity.AbstractStoveBlockEntity; -public class StoveRenderer implements BlockEntityRenderer +public class StoveRenderer> implements BlockEntityRenderer { public StoveRenderer(BlockEntityRendererProvider.Context context) { } @Override - public void render(StoveBlockEntity stoveEntity, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int combinedLightIn, int combinedOverlayIn) { + public void render(T stoveEntity, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int combinedLightIn, int combinedOverlayIn) { Direction direction = stoveEntity.getBlockState().getValue(StoveBlock.FACING).getOpposite(); - ItemStackHandler inventory = stoveEntity.getInventory(); + NonNullList inventory = stoveEntity.getItems(); int posLong = (int) stoveEntity.getBlockPos().asLong(); - for (int i = 0; i < inventory.getSlots(); ++i) { - ItemStack stoveStack = inventory.getStackInSlot(i); + for (int i = 0; i < inventory.size(); ++i) { + ItemStack stoveStack = inventory.get(i); if (!stoveStack.isEmpty()) { poseStack.pushPose(); From 05676fcc2ecf4a8490ce9aceb2880df211b35428 Mon Sep 17 00:00:00 2001 From: Joseph Clack <28568841+Lordfirespeed@users.noreply.github.com> Date: Sun, 3 Sep 2023 00:55:21 +0100 Subject: [PATCH 3/4] extend stove block from abstract stove block --- .../common/block/StoveBlock.java | 191 +----------------- 1 file changed, 6 insertions(+), 185 deletions(-) diff --git a/src/main/java/vectorwing/farmersdelight/common/block/StoveBlock.java b/src/main/java/vectorwing/farmersdelight/common/block/StoveBlock.java index 50625fc92..2e0cd9c2f 100644 --- a/src/main/java/vectorwing/farmersdelight/common/block/StoveBlock.java +++ b/src/main/java/vectorwing/farmersdelight/common/block/StoveBlock.java @@ -2,211 +2,32 @@ 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.*; -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; 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.pathfinder.BlockPathTypes; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.common.ToolActions; -import vectorwing.farmersdelight.common.block.entity.StoveBlockEntity; +import org.jetbrains.annotations.Nullable; import vectorwing.farmersdelight.common.registry.ModBlockEntityTypes; -import vectorwing.farmersdelight.common.registry.ModDamageTypes; -import vectorwing.farmersdelight.common.registry.ModSounds; -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 static final BooleanProperty LIT = BlockStateProperties.LIT; - public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; +public class StoveBlock extends AbstractStoveBlock { 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 (heldItem == Items.WATER_BUCKET) { - 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, new ItemStack(Items.BUCKET)); - } - 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; - } - } - - BlockEntity tileEntity = level.getBlockEntity(pos); - if (tileEntity instanceof StoveBlockEntity stoveEntity) { - int stoveSlot = stoveEntity.getNextEmptySlot(); - if (stoveSlot < 0 || stoveEntity.isStoveBlockedAbove()) { - 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)) { - return InteractionResult.SUCCESS; - } - return InteractionResult.CONSUME; - } - } - - return InteractionResult.PASS; - } - - @Override - public RenderShape getRenderShape(BlockState pState) { - 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) { - boolean isLit = level.getBlockState(pos).getValue(StoveBlock.LIT); - if (isLit && !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.getBlock() != newState.getBlock()) { - BlockEntity tileEntity = level.getBlockEntity(pos); - if (tileEntity instanceof StoveBlockEntity) { - ItemUtils.dropItems(level, pos, ((StoveBlockEntity) tileEntity).getInventory()); - } - - super.onRemove(state, level, pos, newState, isMoving); - } - } - - @Override - protected void createBlockStateDefinition(final StateDefinition.Builder builder) { - super.createBlockStateDefinition(builder); - builder.add(LIT, FACING); - } - - @OnlyIn(Dist.CLIENT) - @Override - public void animateTick(BlockState stateIn, Level level, BlockPos pos, RandomSource rand) { - if (stateIn.getValue(CampfireBlock.LIT)) { - double x = (double) pos.getX() + 0.5D; - double y = pos.getY(); - double z = (double) pos.getZ() + 0.5D; - if (rand.nextInt(10) == 0) { - level.playLocalSound(x, y, z, ModSounds.BLOCK_STOVE_CRACKLE.get(), SoundSource.BLOCKS, 1.0F, 1.0F, false); - } - - Direction direction = stateIn.getValue(HorizontalDirectionalBlock.FACING); - Direction.Axis direction$axis = direction.getAxis(); - double horizontalOffset = rand.nextDouble() * 0.6D - 0.3D; - double xOffset = direction$axis == Direction.Axis.X ? (double) direction.getStepX() * 0.52D : horizontalOffset; - double yOffset = rand.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); - } - - @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); - } + public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { return null; } - @Nullable - @Override - public BlockPathTypes getBlockPathType(BlockState state, BlockGetter world, BlockPos pos, @Nullable Mob entity) { - return state.getValue(LIT) ? BlockPathTypes.DAMAGE_FIRE : null; - } - + @javax.annotation.Nullable @Override - public BlockState rotate(BlockState pState, Rotation pRot) { - return pState.setValue(FACING, pRot.rotate(pState.getValue(FACING))); - } - - @Override - public BlockState mirror(BlockState pState, Mirror pMirror) { - return pState.rotate(pMirror.getRotation(pState.getValue(FACING))); + public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { + return createStoveTicker(level, state, blockEntityType, ModBlockEntityTypes.STOVE.get()); } } From 2266850ebd3e3facce17a82437069e4556ce0bb4 Mon Sep 17 00:00:00 2001 From: Joseph Clack <28568841+Lordfirespeed@users.noreply.github.com> Date: Sun, 3 Sep 2023 00:55:29 +0100 Subject: [PATCH 4/4] extend stove block entity from abstract stove block entity --- .../common/block/entity/StoveBlockEntity.java | 252 ++---------------- 1 file changed, 16 insertions(+), 236 deletions(-) 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 a3b7ec94a..4e8c9b239 100644 --- a/src/main/java/vectorwing/farmersdelight/common/block/entity/StoveBlockEntity.java +++ b/src/main/java/vectorwing/farmersdelight/common/block/entity/StoveBlockEntity.java @@ -1,249 +1,29 @@ package vectorwing.farmersdelight.common.block.entity; 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.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec2; -import net.minecraft.world.phys.shapes.BooleanOp; -import net.minecraft.world.phys.shapes.Shapes; -import net.minecraft.world.phys.shapes.VoxelShape; -import net.minecraftforge.items.ItemStackHandler; -import vectorwing.farmersdelight.common.block.StoveBlock; -import vectorwing.farmersdelight.common.mixin.accessor.RecipeManagerAccessor; import vectorwing.farmersdelight.common.registry.ModBlockEntityTypes; -import vectorwing.farmersdelight.common.utility.ItemUtils; - -import java.util.Optional; - -public class StoveBlockEntity extends SyncedBlockEntity -{ - private static final VoxelShape GRILLING_AREA = Block.box(3.0F, 0.0F, 3.0F, 13.0F, 1.0F, 13.0F); - private static final int INVENTORY_SLOT_COUNT = 6; - - private final ItemStackHandler inventory; - private final int[] cookingTimes; - private final int[] cookingTimesTotal; - private ResourceLocation[] lastRecipeIDs; +public class StoveBlockEntity extends AbstractStoveBlockEntity> { 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 (stove.isStoveBlockedAbove()) { - if (!ItemUtils.isInventoryEmpty(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().getResultItem(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(); - } - } - - public int getNextEmptySlot() { - for (int i = 0; i < inventory.getSlots(); ++i) { - ItemStack slotStack = inventory.getStackInSlot(i); - if (slotStack.isEmpty()) { - return i; - } - } - return -1; - } - - 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; - } - } - return false; - } - - public Optional getMatchingRecipe(Container recipeWrapper, int slot) { - if (level == null) return Optional.empty(); - - 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); - } - } - - return level.getRecipeManager().getRecipeFor(RecipeType.CAMPFIRE_COOKING, recipeWrapper, level); - } - - public ItemStackHandler getInventory() { - return this.inventory; - } - - public boolean isStoveBlockedAbove() { - if (level != null) { - BlockState above = level.getBlockState(worldPosition.above()); - return Shapes.joinIsNotEmpty(GRILLING_AREA, above.getShape(level, worldPosition.above()), BooleanOp.AND); - } - return false; - } - - 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), - }; - return OFFSETS[index]; - } - - private void addParticles() { - if (level == null) return; - - for (int i = 0; i < inventory.getSlots(); ++i) { - if (!inventory.getStackInSlot(i).isEmpty() && level.random.nextFloat() < 0.2F) { - Vec2 stoveItemVector = getStoveItemOffset(i); - Direction direction = getBlockState().getValue(StoveBlock.FACING); - int directionIndex = direction.get2DDataValue(); - Vec2 offset = directionIndex % 2 == 0 ? stoveItemVector : new Vec2(stoveItemVector.y, stoveItemVector.x); - - double x = ((double) worldPosition.getX() + 0.5D) - (direction.getStepX() * offset.x) + (direction.getClockWise().getStepX() * offset.x); - double y = (double) worldPosition.getY() + 1.0D; - double z = ((double) worldPosition.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); + super( + ModBlockEntityTypes.STOVE.get(), + 6, + RecipeType.CAMPFIRE_COOKING, + pos, + state, + new Vec2[]{ + new Vec2(0.3F, 0.2F), + new Vec2(0.0F, 0.2F), + new Vec2(-0.3F, 0.2F), + new Vec2(0.3F, -0.2F), + new Vec2(0.0F, -0.2F), + new Vec2(-0.3F, -0.2F) } - } - } - } - - @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; - } - }; + ); } } +