diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index b61656a9..fbf0baef 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -142,7 +142,8 @@ public final class Settings { public final Setting> acceptableThrowawayItems = new Setting<>(new ArrayList<>(Arrays.asList( Item.getItemFromBlock(Blocks.DIRT), Item.getItemFromBlock(Blocks.COBBLESTONE), - Item.getItemFromBlock(Blocks.NETHERRACK) + Item.getItemFromBlock(Blocks.NETHERRACK), + Item.getItemFromBlock(Blocks.STONE) ))); /** @@ -575,7 +576,7 @@ public final class Settings { /** * How far to move before repeating the build. -1 for the size of the build in that axis. 0 to disable */ - public final Setting buildRepeatDistance=new Setting<>(0); + public final Setting buildRepeatDistance = new Setting<>(0); /** * What direction te repeat the build in diff --git a/src/api/java/baritone/api/utils/IPlayerContext.java b/src/api/java/baritone/api/utils/IPlayerContext.java index 83040ab5..9acb2ad9 100644 --- a/src/api/java/baritone/api/utils/IPlayerContext.java +++ b/src/api/java/baritone/api/utils/IPlayerContext.java @@ -71,20 +71,26 @@ public interface IPlayerContext { * @return The position of the highlighted block */ default Optional getSelectedBlock() { - if (objectMouseOver() != null && objectMouseOver().typeOfHit == RayTraceResult.Type.BLOCK) { - return Optional.of(objectMouseOver().getBlockPos()); + RayTraceResult result = objectMouseOver(); + if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK) { + return Optional.of(result.getBlockPos()); } return Optional.empty(); } + default boolean isLookingAt(BlockPos pos) { + return getSelectedBlock().equals(Optional.of(pos)); + } + /** * Returns the entity that the crosshair is currently placed over. Updated once per tick. * * @return The entity */ default Optional getSelectedEntity() { - if (objectMouseOver() != null && objectMouseOver().typeOfHit == RayTraceResult.Type.ENTITY) { - return Optional.of(objectMouseOver().entityHit); + RayTraceResult result = objectMouseOver(); + if (result != null && result.typeOfHit == RayTraceResult.Type.ENTITY) { + return Optional.of(result.entityHit); } return Optional.empty(); } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementFall.java b/src/main/java/baritone/pathing/movement/movements/MovementFall.java index 93f60f5d..0a4c38c7 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementFall.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementFall.java @@ -39,7 +39,6 @@ import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3i; @@ -92,8 +91,7 @@ public class MovementFall extends Movement { targetRotation = new Rotation(toDest.getYaw(), 90.0F); - RayTraceResult trace = ctx.objectMouseOver(); - if (trace != null && trace.typeOfHit == RayTraceResult.Type.BLOCK && (trace.getBlockPos().equals(dest) || trace.getBlockPos().equals(dest.down()))) { + if (ctx.isLookingAt(dest) || ctx.isLookingAt(dest.down())) { state.setInput(Input.CLICK_RIGHT, true); } } diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index 6ddcda0f..3e767499 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -441,7 +441,10 @@ public class PathExecutor implements IPathExecutor, Helper { IMovement prev = path.movements().get(pathPosition - 1); if (prev instanceof MovementDescend && prev.getDirection().up().equals(current.getDirection().down())) { BlockPos center = current.getSrc().up(); - if (ctx.player().posY >= center.getY()) { // playerFeet adds 0.1251 to account for soul sand + // playerFeet adds 0.1251 to account for soul sand + // farmland is 0.9375 + // 0.07 is to account for farmland + if (ctx.player().posY >= center.getY() - 0.07) { behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); return true; } diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index 4af6c800..982e22ce 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -362,7 +362,7 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro // and is unable since it's unsneaked in the intermediary tick baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); } - if (Objects.equals(ctx.objectMouseOver().getBlockPos(), pos) || ctx.playerRotations().isReallyCloseTo(rot)) { + if (ctx.isLookingAt(pos) || ctx.playerRotations().isReallyCloseTo(rot)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); @@ -374,7 +374,7 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro baritone.getLookBehavior().updateTarget(rot, true); ctx.player().inventory.currentItem = toPlace.get().hotbarSelection; baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); - if ((Objects.equals(ctx.objectMouseOver().getBlockPos(), toPlace.get().placeAgainst) && ctx.objectMouseOver().sideHit.equals(toPlace.get().side)) || ctx.playerRotations().isReallyCloseTo(rot)) { + if ((ctx.isLookingAt(toPlace.get().placeAgainst) && ctx.objectMouseOver().sideHit.equals(toPlace.get().side)) || ctx.playerRotations().isReallyCloseTo(rot)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); diff --git a/src/main/java/baritone/process/FarmProcess.java b/src/main/java/baritone/process/FarmProcess.java index 9d27f953..0e2fdaae 100644 --- a/src/main/java/baritone/process/FarmProcess.java +++ b/src/main/java/baritone/process/FarmProcess.java @@ -27,24 +27,60 @@ import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; import baritone.api.utils.input.Input; import baritone.cache.WorldScanner; +import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; -import net.minecraft.block.BlockAir; -import net.minecraft.block.BlockCrops; +import net.minecraft.block.*; import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityItem; import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.item.EnumDyeColor; +import net.minecraft.item.Item; +import net.minecraft.item.ItemDye; +import net.minecraft.item.ItemStack; +import net.minecraft.util.NonNullList; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.function.Predicate; public class FarmProcess extends BaritoneProcessHelper { private boolean active; + private static final List FARMLAND_PLANTABLE = Arrays.asList( + Items.BEETROOT_SEEDS, + Items.MELON_SEEDS, + Items.WHEAT_SEEDS, + Items.PUMPKIN_SEEDS, + Items.POTATO, + Items.CARROT + ); + + private static final List PICKUP_DROPPED = Arrays.asList( + Items.BEETROOT_SEEDS, + Items.WHEAT, + Items.MELON_SEEDS, + Items.MELON, + Items.WHEAT_SEEDS, + Items.WHEAT, + Items.PUMPKIN_SEEDS, + Items.POTATO, + Items.CARROT, + Items.BEETROOT, + Item.getItemFromBlock(Blocks.PUMPKIN), + Item.getItemFromBlock(Blocks.MELON_BLOCK), + Items.NETHER_WART, + Items.REEDS, + Item.getItemFromBlock(Blocks.CACTUS) + ); + public FarmProcess(Baritone baritone) { super(baritone); } @@ -58,21 +94,115 @@ public class FarmProcess extends BaritoneProcessHelper { active = true; } + private enum Harvest { + WHEAT((BlockCrops) Blocks.WHEAT), + CARROTS((BlockCrops) Blocks.CARROTS), + POTATOES((BlockCrops) Blocks.POTATOES), + BEETROOT((BlockCrops) Blocks.BEETROOTS), + PUMPKIN(Blocks.PUMPKIN, state -> true), + MELON(Blocks.MELON_BLOCK, state -> true), + NETHERWART(Blocks.NETHER_WART, state -> state.getValue(BlockNetherWart.AGE) >= 3), + SUGARCANE(Blocks.REEDS, null) { + @Override + public boolean readyToHarvest(World world, BlockPos pos, IBlockState state) { + return world.getBlockState(pos.down()).getBlock() instanceof BlockReed; + } + }, + CACTUS(Blocks.CACTUS, null) { + @Override + public boolean readyToHarvest(World world, BlockPos pos, IBlockState state) { + return world.getBlockState(pos.down()).getBlock() instanceof BlockCactus; + } + }; + public final Block block; + public final Predicate readyToHarvest; + + Harvest(BlockCrops blockCrops) { + this(blockCrops, blockCrops::isMaxAge); + // max age is 7 for wheat, carrots, and potatoes, but 3 for beetroot + } + + Harvest(Block block, Predicate readyToHarvest) { + this.block = block; + this.readyToHarvest = readyToHarvest; + } + + public boolean readyToHarvest(World world, BlockPos pos, IBlockState state) { + return readyToHarvest.test(state); + } + } + + private boolean readyForHarvest(World world, BlockPos pos, IBlockState state) { + for (Harvest harvest : Harvest.values()) { + if (harvest.block == state.getBlock()) { + return harvest.readyToHarvest(world, pos, state); + } + } + return false; + } + + private boolean selectFarmlandPlantable(boolean doSelect) {//EnumDyeColor.WHITE == EnumDyeColor.byDyeDamage(stack.getMetadata()) + NonNullList invy = ctx.player().inventory.mainInventory; + for (int i = 0; i < 9; i++) { + if (FARMLAND_PLANTABLE.contains(invy.get(i).getItem())) { + if (doSelect) { + ctx.player().inventory.currentItem = i; + } + return true; + } + } + return false; + } + + private boolean selectBoneMeal(boolean doSelect) { + if (isBoneMeal(ctx.player().inventory.offHandInventory.get(0))) { + return true; + } + NonNullList invy = ctx.player().inventory.mainInventory; + for (int i = 0; i < 9; i++) { + if (isBoneMeal(invy.get(i))) { + if (doSelect) { + ctx.player().inventory.currentItem = i; + } + return true; + } + } + return false; + } + + private boolean isBoneMeal(ItemStack stack) { + return !stack.isEmpty() && stack.getItem() instanceof ItemDye && EnumDyeColor.byDyeDamage(stack.getMetadata()) == EnumDyeColor.WHITE; + } + @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { - List memes = WorldScanner.INSTANCE.scanChunkRadius(ctx, Arrays.asList(Blocks.FARMLAND, Blocks.WHEAT), 256, 10, 4); + ArrayList scan = new ArrayList<>(); + for (Harvest harvest : Harvest.values()) { + scan.add(harvest.block); + } + scan.add(Blocks.FARMLAND); + + List locations = WorldScanner.INSTANCE.scanChunkRadius(ctx, scan, 256, 10, 4); List toBreak = new ArrayList<>(); - List toRightClickOnTop = new ArrayList<>(); - for (BlockPos pos : memes) { + List openFarmland = new ArrayList<>(); + List bonemealable = new ArrayList<>(); + for (BlockPos pos : locations) { IBlockState state = ctx.world().getBlockState(pos); if (state.getBlock() == Blocks.FARMLAND) { if (ctx.world().getBlockState(pos.up()).getBlock() instanceof BlockAir) { - toRightClickOnTop.add(pos); + openFarmland.add(pos); } - } else { - if (state.getValue(BlockCrops.AGE) == 7) { - toBreak.add(pos); + continue; + } + if (readyForHarvest(ctx.world(), pos, state)) { + toBreak.add(pos); + continue; + } + if (state.getBlock() instanceof IGrowable) { + IGrowable ig = (IGrowable) state.getBlock(); + if (ig.canGrow(ctx.world(), pos, state, true) && ig.canUseBonemeal(ctx.world(), ctx.world().rand, pos, state)) { + bonemealable.add(pos); } } } @@ -80,31 +210,63 @@ public class FarmProcess extends BaritoneProcessHelper { baritone.getInputOverrideHandler().clearAllKeys(); for (BlockPos pos : toBreak) { Optional rot = RotationUtils.reachable(ctx, pos); - if (rot.isPresent()) { + if (rot.isPresent() && isSafeToCancel) { baritone.getLookBehavior().updateTarget(rot.get(), true); - if (ctx.objectMouseOver() != null && ctx.objectMouseOver().typeOfHit == RayTraceResult.Type.BLOCK && ctx.objectMouseOver().getBlockPos().equals(pos)) { + MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); + if (ctx.isLookingAt(pos)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } } - for (BlockPos pos : toRightClickOnTop) { - Optional rot = RotationUtils.reachableOffset(ctx.player(), pos, new Vec3d(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5), ctx.playerController().getBlockReachDistance()); - if (rot.isPresent()) { + for (BlockPos pos : openFarmland) { + Optional rot = RotationUtils.reachableOffset(ctx.player(), pos, new Vec3d(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5), ctx.playerController().getBlockReachDistance()); + if (rot.isPresent() && isSafeToCancel && selectFarmlandPlantable(true)) { baritone.getLookBehavior().updateTarget(rot.get(), true); - if (ctx.objectMouseOver() != null && ctx.objectMouseOver().typeOfHit == RayTraceResult.Type.BLOCK && ctx.objectMouseOver().getBlockPos().equals(pos)) { + if (ctx.isLookingAt(pos)) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + } + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + } + for (BlockPos pos : bonemealable) { + Optional rot = RotationUtils.reachable(ctx, pos); + if (rot.isPresent() && isSafeToCancel && selectBoneMeal(true)) { + baritone.getLookBehavior().updateTarget(rot.get(), true); + if (ctx.isLookingAt(pos)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } } + if (calcFailed) { + logDirect("Farm failed"); + onLostControl(); + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + List goalz = new ArrayList<>(); for (BlockPos pos : toBreak) { - goalz.add(new GoalBlock(pos)); + goalz.add(new BuilderProcess.GoalBreak(pos)); } - for (BlockPos pos : toRightClickOnTop) { - goalz.add(new GoalBlock(pos.up())); + if (selectFarmlandPlantable(false)) { + for (BlockPos pos : openFarmland) { + goalz.add(new GoalBlock(pos.up())); + } + } + if (selectBoneMeal(false)) { + for (BlockPos pos : bonemealable) { + goalz.add(new GoalBlock(pos)); + } + } + for (Entity entity : ctx.world().loadedEntityList) { + if (entity instanceof EntityItem && entity.onGround) { + EntityItem ei = (EntityItem) entity; + if (PICKUP_DROPPED.contains(ei.getItem().getItem())) { + goalz.add(new GoalBlock(new BlockPos(entity.posX, entity.posY + 0.1, entity.posZ))); + } + } } return new PathingCommand(new GoalComposite(goalz.toArray(new Goal[0])), PathingCommandType.SET_GOAL_AND_PATH); }