2019-04-14 02:05:54 +00:00
|
|
|
/*
|
|
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package baritone.process;
|
|
|
|
|
|
|
|
import baritone.Baritone;
|
|
|
|
import baritone.api.pathing.goals.Goal;
|
|
|
|
import baritone.api.pathing.goals.GoalBlock;
|
|
|
|
import baritone.api.pathing.goals.GoalComposite;
|
2019-04-18 01:10:47 +00:00
|
|
|
import baritone.api.process.IFarmProcess;
|
2019-04-14 02:05:54 +00:00
|
|
|
import baritone.api.process.PathingCommand;
|
|
|
|
import baritone.api.process.PathingCommandType;
|
2019-07-14 18:13:51 +00:00
|
|
|
import baritone.api.utils.RayTraceUtils;
|
2019-04-14 02:05:54 +00:00
|
|
|
import baritone.api.utils.Rotation;
|
|
|
|
import baritone.api.utils.RotationUtils;
|
|
|
|
import baritone.api.utils.input.Input;
|
|
|
|
import baritone.cache.WorldScanner;
|
2019-04-16 00:12:20 +00:00
|
|
|
import baritone.pathing.movement.MovementHelper;
|
2019-04-14 02:05:54 +00:00
|
|
|
import baritone.utils.BaritoneProcessHelper;
|
2020-02-29 16:28:18 +00:00
|
|
|
import baritone.utils.NotificationHelper;
|
2019-04-16 00:12:20 +00:00
|
|
|
import net.minecraft.block.*;
|
2019-04-14 02:05:54 +00:00
|
|
|
import net.minecraft.block.state.IBlockState;
|
2019-04-16 00:12:20 +00:00
|
|
|
import net.minecraft.entity.Entity;
|
|
|
|
import net.minecraft.entity.item.EntityItem;
|
2019-04-14 02:05:54 +00:00
|
|
|
import net.minecraft.init.Blocks;
|
2019-04-16 00:12:20 +00:00
|
|
|
import net.minecraft.init.Items;
|
|
|
|
import net.minecraft.item.Item;
|
|
|
|
import net.minecraft.item.ItemStack;
|
2019-07-14 18:13:51 +00:00
|
|
|
import net.minecraft.util.EnumFacing;
|
2019-04-14 02:05:54 +00:00
|
|
|
import net.minecraft.util.math.BlockPos;
|
2019-07-14 18:13:51 +00:00
|
|
|
import net.minecraft.util.math.RayTraceResult;
|
2019-04-14 02:05:54 +00:00
|
|
|
import net.minecraft.util.math.Vec3d;
|
2019-04-16 00:12:20 +00:00
|
|
|
import net.minecraft.world.World;
|
2019-04-14 02:05:54 +00:00
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Optional;
|
2019-04-16 00:12:20 +00:00
|
|
|
import java.util.function.Predicate;
|
2019-04-14 02:05:54 +00:00
|
|
|
|
2019-05-20 05:53:09 +00:00
|
|
|
public final class FarmProcess extends BaritoneProcessHelper implements IFarmProcess {
|
2019-04-14 02:05:54 +00:00
|
|
|
|
|
|
|
private boolean active;
|
|
|
|
|
2019-07-11 19:46:24 +00:00
|
|
|
private List<BlockPos> locations;
|
|
|
|
private int tickCount;
|
|
|
|
|
2019-04-16 00:12:20 +00:00
|
|
|
private static final List<Item> FARMLAND_PLANTABLE = Arrays.asList(
|
|
|
|
Items.BEETROOT_SEEDS,
|
|
|
|
Items.MELON_SEEDS,
|
|
|
|
Items.WHEAT_SEEDS,
|
|
|
|
Items.PUMPKIN_SEEDS,
|
|
|
|
Items.POTATO,
|
|
|
|
Items.CARROT
|
|
|
|
);
|
|
|
|
|
|
|
|
private static final List<Item> PICKUP_DROPPED = Arrays.asList(
|
|
|
|
Items.BEETROOT_SEEDS,
|
2019-07-17 05:50:03 +00:00
|
|
|
Items.BEETROOT,
|
2019-04-16 00:12:20 +00:00
|
|
|
Items.MELON_SEEDS,
|
2019-05-01 19:15:31 +00:00
|
|
|
Items.MELON_SLICE,
|
2019-07-23 18:58:45 +00:00
|
|
|
Blocks.MELON.asItem(),
|
2019-04-16 00:12:20 +00:00
|
|
|
Items.WHEAT_SEEDS,
|
|
|
|
Items.WHEAT,
|
|
|
|
Items.PUMPKIN_SEEDS,
|
2019-07-23 18:58:45 +00:00
|
|
|
Blocks.PUMPKIN.asItem(),
|
2019-04-16 00:12:20 +00:00
|
|
|
Items.POTATO,
|
|
|
|
Items.CARROT,
|
|
|
|
Items.NETHER_WART,
|
2019-05-01 19:15:31 +00:00
|
|
|
Blocks.SUGAR_CANE.asItem(),
|
|
|
|
Blocks.CACTUS.asItem()
|
2019-04-16 00:12:20 +00:00
|
|
|
);
|
|
|
|
|
2019-04-14 02:05:54 +00:00
|
|
|
public FarmProcess(Baritone baritone) {
|
|
|
|
super(baritone);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isActive() {
|
|
|
|
return active;
|
|
|
|
}
|
|
|
|
|
2019-04-18 01:10:47 +00:00
|
|
|
@Override
|
|
|
|
public void farm() {
|
2019-04-14 02:05:54 +00:00
|
|
|
active = true;
|
2019-07-11 19:46:24 +00:00
|
|
|
locations = null;
|
2019-04-14 02:05:54 +00:00
|
|
|
}
|
|
|
|
|
2019-04-16 00:12:20 +00:00
|
|
|
private enum Harvest {
|
|
|
|
WHEAT((BlockCrops) Blocks.WHEAT),
|
|
|
|
CARROTS((BlockCrops) Blocks.CARROTS),
|
|
|
|
POTATOES((BlockCrops) Blocks.POTATOES),
|
|
|
|
BEETROOT((BlockCrops) Blocks.BEETROOTS),
|
|
|
|
PUMPKIN(Blocks.PUMPKIN, state -> true),
|
2019-05-01 19:15:31 +00:00
|
|
|
MELON(Blocks.MELON, state -> true),
|
|
|
|
NETHERWART(Blocks.NETHER_WART, state -> state.get(BlockNetherWart.AGE) >= 3),
|
|
|
|
SUGARCANE(Blocks.SUGAR_CANE, null) {
|
2019-04-16 00:12:20 +00:00
|
|
|
@Override
|
|
|
|
public boolean readyToHarvest(World world, BlockPos pos, IBlockState state) {
|
2019-08-29 08:36:31 +00:00
|
|
|
if (Baritone.settings().replantCrops.value) {
|
2019-08-28 18:15:41 +00:00
|
|
|
return world.getBlockState(pos.down()).getBlock() instanceof BlockReed;
|
2019-08-29 08:36:31 +00:00
|
|
|
}
|
2019-08-28 18:15:41 +00:00
|
|
|
return true;
|
2019-04-16 00:12:20 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
CACTUS(Blocks.CACTUS, null) {
|
|
|
|
@Override
|
|
|
|
public boolean readyToHarvest(World world, BlockPos pos, IBlockState state) {
|
2019-08-29 08:36:31 +00:00
|
|
|
if (Baritone.settings().replantCrops.value) {
|
2019-08-28 18:15:41 +00:00
|
|
|
return world.getBlockState(pos.down()).getBlock() instanceof BlockCactus;
|
2019-08-29 08:36:31 +00:00
|
|
|
}
|
2019-08-28 18:15:41 +00:00
|
|
|
return true;
|
2019-04-16 00:12:20 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
public final Block block;
|
|
|
|
public final Predicate<IBlockState> 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<IBlockState> 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;
|
|
|
|
}
|
|
|
|
|
2019-04-20 18:03:59 +00:00
|
|
|
private boolean isPlantable(ItemStack stack) {
|
|
|
|
return FARMLAND_PLANTABLE.contains(stack.getItem());
|
2019-04-16 00:12:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isBoneMeal(ItemStack stack) {
|
2019-05-01 19:15:31 +00:00
|
|
|
return !stack.isEmpty() && stack.getItem().equals(Items.BONE_MEAL);
|
2019-04-16 00:12:20 +00:00
|
|
|
}
|
|
|
|
|
2019-04-20 18:03:59 +00:00
|
|
|
private boolean isNetherWart(ItemStack stack) {
|
|
|
|
return !stack.isEmpty() && stack.getItem().equals(Items.NETHER_WART);
|
|
|
|
}
|
|
|
|
|
2019-04-14 02:05:54 +00:00
|
|
|
@Override
|
|
|
|
public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) {
|
2019-04-16 00:12:20 +00:00
|
|
|
ArrayList<Block> scan = new ArrayList<>();
|
|
|
|
for (Harvest harvest : Harvest.values()) {
|
|
|
|
scan.add(harvest.block);
|
|
|
|
}
|
2019-08-28 18:15:41 +00:00
|
|
|
if (Baritone.settings().replantCrops.value) {
|
|
|
|
scan.add(Blocks.FARMLAND);
|
2019-08-29 08:36:31 +00:00
|
|
|
if (Baritone.settings().replantNetherWart.value) {
|
|
|
|
scan.add(Blocks.SOUL_SAND);
|
|
|
|
}
|
2019-04-20 18:03:59 +00:00
|
|
|
}
|
2019-08-29 08:36:31 +00:00
|
|
|
|
2019-07-11 19:46:24 +00:00
|
|
|
if (Baritone.settings().mineGoalUpdateInterval.value != 0 && tickCount++ % Baritone.settings().mineGoalUpdateInterval.value == 0) {
|
|
|
|
Baritone.getExecutor().execute(() -> locations = WorldScanner.INSTANCE.scanChunkRadius(ctx, scan, 256, 10, 10));
|
|
|
|
}
|
|
|
|
if (locations == null) {
|
|
|
|
return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE);
|
|
|
|
}
|
2019-04-14 02:05:54 +00:00
|
|
|
List<BlockPos> toBreak = new ArrayList<>();
|
2019-04-16 00:12:20 +00:00
|
|
|
List<BlockPos> openFarmland = new ArrayList<>();
|
|
|
|
List<BlockPos> bonemealable = new ArrayList<>();
|
2019-04-20 18:03:59 +00:00
|
|
|
List<BlockPos> openSoulsand = new ArrayList<>();
|
2019-04-16 00:12:20 +00:00
|
|
|
for (BlockPos pos : locations) {
|
2019-04-14 02:05:54 +00:00
|
|
|
IBlockState state = ctx.world().getBlockState(pos);
|
2019-04-20 18:03:59 +00:00
|
|
|
boolean airAbove = ctx.world().getBlockState(pos.up()).getBlock() instanceof BlockAir;
|
2019-04-14 02:05:54 +00:00
|
|
|
if (state.getBlock() == Blocks.FARMLAND) {
|
2019-04-20 18:03:59 +00:00
|
|
|
if (airAbove) {
|
2019-04-16 00:12:20 +00:00
|
|
|
openFarmland.add(pos);
|
2019-04-14 02:05:54 +00:00
|
|
|
}
|
2019-04-16 00:12:20 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-04-20 18:03:59 +00:00
|
|
|
if (state.getBlock() == Blocks.SOUL_SAND) {
|
|
|
|
if (airAbove) {
|
|
|
|
openSoulsand.add(pos);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2019-04-16 00:12:20 +00:00
|
|
|
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);
|
2019-04-14 02:05:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
baritone.getInputOverrideHandler().clearAllKeys();
|
|
|
|
for (BlockPos pos : toBreak) {
|
|
|
|
Optional<Rotation> rot = RotationUtils.reachable(ctx, pos);
|
2019-04-16 00:12:20 +00:00
|
|
|
if (rot.isPresent() && isSafeToCancel) {
|
2019-04-14 02:05:54 +00:00
|
|
|
baritone.getLookBehavior().updateTarget(rot.get(), true);
|
2019-04-16 00:12:20 +00:00
|
|
|
MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos));
|
|
|
|
if (ctx.isLookingAt(pos)) {
|
2019-04-14 02:05:54 +00:00
|
|
|
baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true);
|
|
|
|
}
|
|
|
|
return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE);
|
|
|
|
}
|
|
|
|
}
|
2019-04-20 18:03:59 +00:00
|
|
|
ArrayList<BlockPos> both = new ArrayList<>(openFarmland);
|
|
|
|
both.addAll(openSoulsand);
|
|
|
|
for (BlockPos pos : both) {
|
|
|
|
boolean soulsand = openSoulsand.contains(pos);
|
2020-02-25 02:31:42 +00:00
|
|
|
Optional<Rotation> rot = RotationUtils.reachableOffset(ctx.player(), pos, new Vec3d(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5), ctx.playerController().getBlockReachDistance(), false);
|
2019-04-20 18:03:59 +00:00
|
|
|
if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, soulsand ? this::isNetherWart : this::isPlantable)) {
|
2019-07-14 18:13:51 +00:00
|
|
|
RayTraceResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), ctx.playerController().getBlockReachDistance());
|
2019-07-23 18:58:45 +00:00
|
|
|
if (result.type == RayTraceResult.Type.BLOCK && result.sideHit == EnumFacing.UP) {
|
2019-07-14 18:13:51 +00:00
|
|
|
baritone.getLookBehavior().updateTarget(rot.get(), true);
|
|
|
|
if (ctx.isLookingAt(pos)) {
|
|
|
|
baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true);
|
|
|
|
}
|
|
|
|
return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE);
|
2019-04-16 00:12:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (BlockPos pos : bonemealable) {
|
|
|
|
Optional<Rotation> rot = RotationUtils.reachable(ctx, pos);
|
2019-04-20 18:03:59 +00:00
|
|
|
if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, this::isBoneMeal)) {
|
2019-04-14 02:05:54 +00:00
|
|
|
baritone.getLookBehavior().updateTarget(rot.get(), true);
|
2019-04-16 00:12:20 +00:00
|
|
|
if (ctx.isLookingAt(pos)) {
|
2019-04-14 02:05:54 +00:00
|
|
|
baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true);
|
|
|
|
}
|
|
|
|
return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-16 00:12:20 +00:00
|
|
|
if (calcFailed) {
|
|
|
|
logDirect("Farm failed");
|
2020-02-29 16:28:18 +00:00
|
|
|
if (Baritone.settings().desktopNotifications.value && Baritone.settings().notificationOnFarmFail.value) {
|
|
|
|
NotificationHelper.notify("Farm failed", true);
|
|
|
|
}
|
2019-04-16 00:12:20 +00:00
|
|
|
onLostControl();
|
|
|
|
return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE);
|
|
|
|
}
|
|
|
|
|
2019-04-14 02:05:54 +00:00
|
|
|
List<Goal> goalz = new ArrayList<>();
|
|
|
|
for (BlockPos pos : toBreak) {
|
2019-04-16 00:12:20 +00:00
|
|
|
goalz.add(new BuilderProcess.GoalBreak(pos));
|
2019-04-14 02:05:54 +00:00
|
|
|
}
|
2019-04-20 18:03:59 +00:00
|
|
|
if (baritone.getInventoryBehavior().throwaway(false, this::isPlantable)) {
|
2019-04-16 00:12:20 +00:00
|
|
|
for (BlockPos pos : openFarmland) {
|
|
|
|
goalz.add(new GoalBlock(pos.up()));
|
|
|
|
}
|
|
|
|
}
|
2019-04-20 18:03:59 +00:00
|
|
|
if (baritone.getInventoryBehavior().throwaway(false, this::isNetherWart)) {
|
|
|
|
for (BlockPos pos : openSoulsand) {
|
|
|
|
goalz.add(new GoalBlock(pos.up()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (baritone.getInventoryBehavior().throwaway(false, this::isBoneMeal)) {
|
2019-04-16 00:12:20 +00:00
|
|
|
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())) {
|
2019-04-20 18:03:59 +00:00
|
|
|
// +0.1 because of farmland's 0.9375 dummy height lol
|
2019-04-16 00:12:20 +00:00
|
|
|
goalz.add(new GoalBlock(new BlockPos(entity.posX, entity.posY + 0.1, entity.posZ)));
|
|
|
|
}
|
|
|
|
}
|
2019-04-14 02:05:54 +00:00
|
|
|
}
|
|
|
|
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";
|
|
|
|
}
|
|
|
|
}
|