diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 1de894dc..0892f28d 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) ))); /** 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/Baritone.java b/src/main/java/baritone/Baritone.java index f7a20b0f..33916d59 100755 --- a/src/main/java/baritone/Baritone.java +++ b/src/main/java/baritone/Baritone.java @@ -80,6 +80,7 @@ public class Baritone implements IBaritone { private CustomGoalProcess customGoalProcess; private BuilderProcess builderProcess; private ExploreProcess exploreProcess; + private FarmProcess farmProcess; private PathingControlManager pathingControlManager; @@ -120,6 +121,7 @@ public class Baritone implements IBaritone { getToBlockProcess = new GetToBlockProcess(this); builderProcess = new BuilderProcess(this); exploreProcess = new ExploreProcess(this); + farmProcess = new FarmProcess(this); } this.worldProvider = new WorldProvider(); @@ -197,6 +199,10 @@ public class Baritone implements IBaritone { return this.mineProcess; } + public FarmProcess getFarmProcess() { + return this.farmProcess; + } + @Override public PathingBehavior getPathingBehavior() { return this.pathingBehavior; 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/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java index ce5f77e9..fbf57e6c 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -106,7 +106,9 @@ public class MovementParkour extends Movement { return; } } - if (MovementHelper.canWalkOn(context.bsi, x + xDiff * i, y - 1, z + zDiff * i)) { + IBlockState landingOn = context.bsi.get0(x + xDiff * i, y - 1, z + zDiff * i); + // farmland needs to be canwalkon otherwise farm can never work at all, but we want to specifically disallow ending a jumy on farmland haha + if (landingOn.getBlock() != Blocks.FARMLAND && MovementHelper.canWalkOn(context.bsi, x + xDiff * i, y - 1, z + zDiff * i, landingOn)) { res.x = x + xDiff * i; res.y = y; res.z = z + zDiff * i; 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 new file mode 100644 index 00000000..0e2fdaae --- /dev/null +++ b/src/main/java/baritone/process/FarmProcess.java @@ -0,0 +1,283 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.process; + +import baritone.Baritone; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.pathing.goals.GoalComposite; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +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.*; +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.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); + } + + @Override + public boolean isActive() { + return active; + } + + public void doit() { + 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) { + 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 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) { + openFarmland.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); + } + } + } + + baritone.getInputOverrideHandler().clearAllKeys(); + for (BlockPos pos : toBreak) { + Optional rot = RotationUtils.reachable(ctx, pos); + if (rot.isPresent() && isSafeToCancel) { + baritone.getLookBehavior().updateTarget(rot.get(), true); + 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 : 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.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 BuilderProcess.GoalBreak(pos)); + } + 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); + } + + @Override + public void onLostControl() { + active = false; + } + + @Override + public String displayName0() { + return "Farming"; + } +} diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java index f4261f07..4a819d74 100644 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java @@ -382,6 +382,11 @@ public class ExampleBaritoneControl extends Behavior implements Helper { logDirect("okay"); return true; } + if (msg.equals("farm")) { + baritone.getFarmProcess().doit(); + logDirect("farming"); + return true; + } if (msg.equals("echest")) { Optional> contents = baritone.getMemoryBehavior().echest(); if (contents.isPresent()) {