|
| 1 | +package su.terrafirmagreg.core.mixins.common.gtceu; |
| 2 | + |
| 3 | +import org.spongepowered.asm.mixin.Mixin; |
| 4 | +import org.spongepowered.asm.mixin.Overwrite; |
| 5 | +import org.spongepowered.asm.mixin.injection.At; |
| 6 | +import org.spongepowered.asm.mixin.injection.Inject; |
| 7 | +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; |
| 8 | + |
| 9 | +import com.gregtechceu.gtceu.api.item.tool.ToolHelper; |
| 10 | +import com.gregtechceu.gtceu.common.item.tool.behavior.HarvestCropsBehavior; |
| 11 | + |
| 12 | +import net.dries007.tfc.common.TFCTags; |
| 13 | +import net.dries007.tfc.common.blocks.crop.DeadCropBlock; |
| 14 | +import net.minecraft.core.BlockPos; |
| 15 | +import net.minecraft.core.Direction; |
| 16 | +import net.minecraft.server.level.ServerLevel; |
| 17 | +import net.minecraft.world.InteractionHand; |
| 18 | +import net.minecraft.world.entity.player.Player; |
| 19 | +import net.minecraft.world.item.ItemStack; |
| 20 | +import net.minecraft.world.item.context.UseOnContext; |
| 21 | +import net.minecraft.world.level.Level; |
| 22 | +import net.minecraft.world.level.block.Block; |
| 23 | +import net.minecraft.world.level.block.CropBlock; |
| 24 | +import net.minecraft.world.level.block.LevelEvent; |
| 25 | +import net.minecraft.world.level.block.entity.BlockEntity; |
| 26 | +import net.minecraft.world.level.block.state.BlockState; |
| 27 | +import net.minecraft.world.level.material.FluidState; |
| 28 | +import net.minecraft.world.phys.BlockHitResult; |
| 29 | +import net.minecraft.world.phys.Vec3; |
| 30 | +import net.minecraftforge.common.Tags; |
| 31 | + |
| 32 | +@Mixin(value = HarvestCropsBehavior.class, remap = false) |
| 33 | +public abstract class HarvestCropsBehaviorMixin { |
| 34 | + |
| 35 | + /** Harvest routine used for the scythe |
| 36 | + * @author Ujhik |
| 37 | + * @reason To adapt GregTech harvest routine to TerraFirmaCraft crops so they reset to growth 0 correctly |
| 38 | + * And to customize harvest behavior for TerraFirmaGreg |
| 39 | + */ |
| 40 | + @Overwrite |
| 41 | + private static boolean harvestBlockRoutine(BlockPos pos, UseOnContext context) { |
| 42 | + Level world = context.getLevel(); |
| 43 | + ItemStack stack = context.getItemInHand(); |
| 44 | + Player player = context.getPlayer(); |
| 45 | + |
| 46 | + if (world.isClientSide()) |
| 47 | + return false; |
| 48 | + |
| 49 | + BlockState blockState = world.getBlockState(pos); |
| 50 | + boolean isDeadCrop = blockState.getBlock() instanceof DeadCropBlock; |
| 51 | + boolean isAliveCrop = blockState.getBlock() instanceof CropBlock; |
| 52 | + |
| 53 | + if (!isDeadCrop && !isAliveCrop) |
| 54 | + return false; |
| 55 | + |
| 56 | + if ((blockState.getBlock() instanceof CropBlock cropBlock) && !cropBlock.isMaxAge(blockState)) |
| 57 | + return false; |
| 58 | + |
| 59 | + // Getting the crop bottom block assuming crops are max 2 block tall |
| 60 | + BlockPos belowPos = pos.below(); |
| 61 | + BlockState belowState = world.getBlockState(belowPos); |
| 62 | + BlockPos targetPos; |
| 63 | + if (belowState.getBlock() == blockState.getBlock()) { |
| 64 | + targetPos = belowPos; |
| 65 | + blockState = belowState; |
| 66 | + } else { |
| 67 | + targetPos = pos; |
| 68 | + } |
| 69 | + |
| 70 | + // Not processing top blocks of plants |
| 71 | + belowPos = targetPos.below(); |
| 72 | + belowState = world.getBlockState(belowPos); |
| 73 | + boolean isTfcFarmland = belowState.is(TFCTags.Blocks.FARMLAND); |
| 74 | + if (!isTfcFarmland) |
| 75 | + return false; |
| 76 | + |
| 77 | + BlockEntity be = world.getBlockEntity(targetPos); |
| 78 | + var drops = Block.getDrops(blockState, (ServerLevel) world, targetPos, be, player, stack); |
| 79 | + |
| 80 | + ItemStack cropSeed = null; |
| 81 | + boolean removedSeed = false; |
| 82 | + for (ItemStack drop : drops) { |
| 83 | + // Accounting for the replanted seed |
| 84 | + if (!removedSeed && drop.is(Tags.Items.SEEDS)) { |
| 85 | + cropSeed = drop.copy(); |
| 86 | + cropSeed.setCount(1); |
| 87 | + drop.shrink(1); |
| 88 | + removedSeed = true; |
| 89 | + } |
| 90 | + |
| 91 | + Block.popResource(world, targetPos, drop); |
| 92 | + } |
| 93 | + |
| 94 | + // Replacing block to force multiblock crops to break without spawning drops |
| 95 | + FluidState fluidState = world.getFluidState(targetPos); |
| 96 | + world.setBlockAndUpdate(targetPos, fluidState.createLegacyBlock()); |
| 97 | + world.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, targetPos, Block.getId(blockState)); |
| 98 | + |
| 99 | + // Replanting in the next tick to ensure multiblock crop tops break |
| 100 | + if (cropSeed != null) { |
| 101 | + ItemStack finalCropSeed = cropSeed; |
| 102 | + UseOnContext plantCtx = new UseOnContext( |
| 103 | + world, |
| 104 | + player, |
| 105 | + InteractionHand.MAIN_HAND, |
| 106 | + cropSeed, |
| 107 | + new BlockHitResult( |
| 108 | + Vec3.atCenterOf(targetPos), |
| 109 | + Direction.UP, |
| 110 | + targetPos, |
| 111 | + false)); |
| 112 | + |
| 113 | + world.getServer().execute(() -> finalCropSeed.useOn(plantCtx)); |
| 114 | + } |
| 115 | + |
| 116 | + ToolHelper.damageItem(stack, player); |
| 117 | + |
| 118 | + return true; |
| 119 | + } |
| 120 | + |
| 121 | + /** Filters blocks used in harvest routine |
| 122 | + * @author Ujhik |
| 123 | + * @reason To add dead crop blocks to the filter |
| 124 | + */ |
| 125 | + @Inject(method = "isBlockCrops", at = @At("RETURN"), cancellable = true) |
| 126 | + private static void tfc$allowDeadCrops(UseOnContext context, CallbackInfoReturnable<Boolean> cir) { |
| 127 | + if (!cir.getReturnValue()) { |
| 128 | + Level level = context.getLevel(); |
| 129 | + BlockPos pos = context.getClickedPos(); |
| 130 | + |
| 131 | + if (level.getBlockState(pos.above()).isAir()) { |
| 132 | + Block block = level.getBlockState(pos).getBlock(); |
| 133 | + if (block instanceof DeadCropBlock) { |
| 134 | + cir.setReturnValue(true); |
| 135 | + } |
| 136 | + } |
| 137 | + } |
| 138 | + } |
| 139 | +} |
0 commit comments