diff --git a/src/main/java/net/wurstclient/hacks/AutoFarmHack.java b/src/main/java/net/wurstclient/hacks/AutoFarmHack.java index 6a5dac5e3b..d44d8757f8 100644 --- a/src/main/java/net/wurstclient/hacks/AutoFarmHack.java +++ b/src/main/java/net/wurstclient/hacks/AutoFarmHack.java @@ -13,7 +13,11 @@ import net.minecraft.block.*; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.enchantment.Enchantments; +import net.minecraft.item.AxeItem; import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket; import net.minecraft.registry.tag.BlockTags; @@ -27,6 +31,7 @@ import net.wurstclient.events.UpdateListener; import net.wurstclient.hack.Hack; import net.wurstclient.hacks.autofarm.AutoFarmRenderer; +import net.wurstclient.settings.BlockListSetting; import net.wurstclient.settings.CheckboxSetting; import net.wurstclient.settings.SliderSetting; import net.wurstclient.settings.SliderSetting.ValueDisplay; @@ -48,6 +53,26 @@ public final class AutoFarmHack extends Hack private final CheckboxSetting replant = new CheckboxSetting("Replant", true); + private final CheckboxSetting harvestFirst = new CheckboxSetting( + "Harvest first", "Harvest all crops first before replanting.", false); + + private final CheckboxSetting checkLOS = new CheckboxSetting( + "Check line of sight", + "Makes sure that you don't reach through walls when breaking or replanting.", + false); + + private final CheckboxSetting fortune = + new CheckboxSetting("Choose fortune tool", + "Chooses a fortune tool to harvest crops.", false); + + private final CheckboxSetting silkTouch = new CheckboxSetting( + "Choose silk touch tool", + "Chooses a silk touch tool to harvest melons. Axes will be prioritized.", + false); + + private final BlockListSetting excluded = new BlockListSetting( + "Excluded Crops", "List of crops that will not be harvested."); + private final HashMap seeds = new HashMap<>(); { seeds.put(Blocks.WHEAT, Items.WHEAT_SEEDS); @@ -60,6 +85,16 @@ public final class AutoFarmHack extends Hack seeds.put(Blocks.COCOA, Items.COCOA_BEANS); } + private final HashSet fortuneBlocks = new HashSet<>(); + { + fortuneBlocks.add(Blocks.WHEAT); + fortuneBlocks.add(Blocks.CARROTS); + fortuneBlocks.add(Blocks.POTATOES); + fortuneBlocks.add(Blocks.BEETROOTS); + fortuneBlocks.add(Blocks.NETHER_WART); + fortuneBlocks.add(Blocks.MELON); + } + private final HashMap plants = new HashMap<>(); private final ArrayDeque> prevBlocks = new ArrayDeque<>(); private BlockPos currentlyHarvesting; @@ -76,6 +111,11 @@ public AutoFarmHack() setCategory(Category.BLOCKS); addSetting(range); addSetting(replant); + addSetting(harvestFirst); + addSetting(checkLOS); + addSetting(fortune); + addSetting(silkTouch); + addSetting(excluded); } @Override @@ -141,11 +181,15 @@ public void onUpdate() getBlocksToReplant(eyesVec, eyesBlock, rangeSq, blockRange); } - // first, try to replant - boolean replanting = replant(blocksToReplant); + // replant and harvest + if(harvestFirst.isChecked()) + harvest(blocksToHarvest); + + boolean replanting = false; + if(currentlyHarvesting == null) + replanting = replant(blocksToReplant); - // if we can't replant, harvest instead - if(!replanting) + if(!harvestFirst.isChecked() && !replanting) harvest(blocksToHarvest); // upate busy state @@ -197,6 +241,10 @@ private boolean shouldBeHarvested(BlockPos pos) Block block = BlockUtils.getBlock(pos); BlockState state = BlockUtils.getState(pos); + if(Collections.binarySearch(excluded.getBlockNames(), + BlockUtils.getName(pos)) >= 0) + return false; + if(block instanceof CropBlock) return ((CropBlock)block).isMature(state); @@ -294,6 +342,9 @@ private boolean replant(List blocksToReplant) if(params == null || params.distanceSq() > range.getValueSq()) continue; + if(checkLOS.isChecked() && !params.lineOfSight()) + continue; + // face block WURST.getRotationFaker().faceVectorPacket(params.hitVec()); @@ -353,11 +404,61 @@ private void harvest(List blocksToHarvest) } for(BlockPos pos : blocksToHarvest) - if(BlockBreaker.breakOneBlock(pos)) + { + boolean findSilkTouch = silkTouch.isChecked() + && BlockUtils.getBlock(pos) == Blocks.MELON; + boolean findFortune = fortune.isChecked() + && fortuneBlocks.contains(BlockUtils.getBlock(pos)); + ItemStack held = MC.player.getMainHandStack(); + if(findSilkTouch) + { + if(EnchantmentHelper.getLevel(Enchantments.SILK_TOUCH, + held) == 0 || !(held.getItem() instanceof AxeItem)) + { + int slot = InventoryUtils + .indexOf(stack -> stack.getItem() instanceof AxeItem + && EnchantmentHelper + .getLevel(Enchantments.SILK_TOUCH, stack) > 0); + if(slot == -1) + slot = InventoryUtils.indexOf(stack -> EnchantmentHelper + .getLevel(Enchantments.SILK_TOUCH, stack) > 0); + InventoryUtils.selectItem(slot); + } + }else if(findFortune) + { + int[] slots = + InventoryUtils + .indicesOf( + stack -> EnchantmentHelper + .getLevel(Enchantments.SILK_TOUCH, stack) == 0 + && EnchantmentHelper + .getLevel(Enchantments.FORTUNE, stack) > 0, + 36, false); + + int selected = -1; + int level = EnchantmentHelper.getLevel(Enchantments.SILK_TOUCH, + held) > 0 ? 0 + : EnchantmentHelper.getLevel(Enchantments.FORTUNE, + held); + for(int slot : slots) + { + int curLevel = + EnchantmentHelper.getLevel(Enchantments.FORTUNE, + MC.player.getInventory().getStack(slot)); + if(curLevel > level) + { + selected = slot; + level = curLevel; + } + } + InventoryUtils.selectItem(selected); + } + if(BlockBreaker.breakOneBlock(pos, checkLOS.isChecked())) { currentlyHarvesting = pos; break; } + } if(currentlyHarvesting == null) MC.interactionManager.cancelBlockBreaking(); diff --git a/src/main/java/net/wurstclient/util/BlockBreaker.java b/src/main/java/net/wurstclient/util/BlockBreaker.java index cbcfcc89ff..9ecf473036 100644 --- a/src/main/java/net/wurstclient/util/BlockBreaker.java +++ b/src/main/java/net/wurstclient/util/BlockBreaker.java @@ -30,9 +30,14 @@ public enum BlockBreaker private static final MinecraftClient MC = WurstClient.MC; public static boolean breakOneBlock(BlockPos pos) + { + return breakOneBlock(pos, false); + } + + public static boolean breakOneBlock(BlockPos pos, boolean checkLOS) { BlockBreakingParams params = getBlockBreakingParams(pos); - if(params == null) + if(params == null || (checkLOS && !params.lineOfSight)) return false; // face block diff --git a/src/main/java/net/wurstclient/util/InventoryUtils.java b/src/main/java/net/wurstclient/util/InventoryUtils.java index 1f290c62cd..05d4bda5e0 100644 --- a/src/main/java/net/wurstclient/util/InventoryUtils.java +++ b/src/main/java/net/wurstclient/util/InventoryUtils.java @@ -82,6 +82,35 @@ public static int indexOf(Predicate predicate, int maxInvSlot, return slot; } + /** + * Searches the player's inventory from slot 0 to {@code maxInvSlot-1} for + * all items that matches the given predicate. + * + * @param predicate + * checks if an item is the one you want + * @param maxInvSlot + * the maximum slot to search (exclusive), usually 9 for the + * hotbar or 36 for the whole inventory + * @param includeOffhand + * also search the offhand (slot 40), even if maxInvSlot is lower + * @return + * all the slots the item was found on as an array + */ + public static int[] indicesOf(Predicate predicate, + int maxInvSlot, boolean includeOffhand) + { + PlayerInventory inventory = MC.player.getInventory(); + + // create a stream of all slots that we want to search + IntStream stream = IntStream.range(0, maxInvSlot); + if(includeOffhand) + stream = IntStream.concat(stream, IntStream.of(40)); + + // find the slots of the item we want + return stream.filter(i -> predicate.test(inventory.getStack(i))) + .toArray(); + } + public static boolean selectItem(Item item) { return selectItem(stack -> stack.isOf(item), 36, false);