diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index c2d0326c..8b115e90 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -73,7 +73,7 @@ public final class LookBehavior extends Behavior implements ILookBehavior { float desiredPitch = this.target.getPitch(); ctx.player().rotationPitch = desiredPitch; if (desiredPitch == oldPitch) { - nudgeToLevel(); + //nudgeToLevel(); } this.target = null; } diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index 4599637a..4e7eb26f 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -34,7 +34,7 @@ import java.util.Optional; public abstract class Movement implements IMovement, MovementHelper { - protected static final EnumFacing[] HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST, EnumFacing.DOWN}; + public static final EnumFacing[] HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST, EnumFacing.DOWN}; protected final IBaritone baritone; protected final IPlayerContext ctx; diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index 9bfebcc2..437aa2c3 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -80,6 +80,9 @@ public interface MovementHelper extends ActionCosts, Helper { // be opened by just interacting. return block != Blocks.IRON_DOOR; } + if (block == Blocks.CARPET) { + return canWalkOn(bsi, x, y - 1, z); + } boolean snow = block instanceof BlockSnow; boolean trapdoor = block instanceof BlockTrapDoor; if (snow || trapdoor) { @@ -321,20 +324,22 @@ public interface MovementHelper extends ActionCosts, Helper { } static boolean canPlaceAgainst(BlockStateInterface bsi, int x, int y, int z) { - return canPlaceAgainst(bsi.get0(x, y, z)); + return canPlaceAgainst(bsi, x, y, z, bsi.get0(x, y, z)); } static boolean canPlaceAgainst(BlockStateInterface bsi, BlockPos pos) { - return canPlaceAgainst(bsi.get0(pos.getX(), pos.getY(), pos.getZ())); + return canPlaceAgainst(bsi, pos.getX(), pos.getY(), pos.getZ()); } static boolean canPlaceAgainst(IPlayerContext ctx, BlockPos pos) { return canPlaceAgainst(new BlockStateInterface(ctx), pos); } - static boolean canPlaceAgainst(IBlockState state) { - // TODO isBlockNormalCube isn't the best check for whether or not we can place a block against it. e.g. glass isn't normalCube but we can place against it - return state.isBlockNormalCube(); + static boolean canPlaceAgainst(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { + // can we look at the center of a side face of this block and likely be able to place? + // (thats how this check is used) + // therefore dont include weird things that we technically could place against (like carpet) but practically can't + return state.isBlockNormalCube() || state.isFullBlock() || state.getBlock() == Blocks.GLASS || state.getBlock() == Blocks.STAINED_GLASS; } static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, boolean includeFalling) { diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index cadb5416..5718ea27 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -25,14 +25,13 @@ import baritone.api.pathing.goals.GoalGetToBlock; import baritone.api.process.IBuilderProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; -import baritone.api.utils.BetterBlockPos; -import baritone.api.utils.ISchematic; -import baritone.api.utils.Rotation; -import baritone.api.utils.RotationUtils; +import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.pathing.movement.CalculationContext; +import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; +import baritone.utils.BlockStateInterface; import baritone.utils.PathingCommandContext; import baritone.utils.schematic.Schematic; import net.minecraft.block.state.IBlockState; @@ -44,8 +43,7 @@ import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.Tuple; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3i; +import net.minecraft.util.math.*; import java.io.File; import java.io.FileInputStream; @@ -65,6 +63,7 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro private String name; private ISchematic schematic; private Vec3i origin; + private int ticks; public boolean build(String schematicFile) { File file = new File(new File(Minecraft.getMinecraft().gameDir, "schematics"), schematicFile); @@ -145,6 +144,125 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro return Optional.empty(); } + public class Placement { + final int hotbarSelection; + final BlockPos placeAgainst; + final EnumFacing side; + final Rotation rot; + + public Placement(int hotbarSelection, BlockPos placeAgainst, EnumFacing side, Rotation rot) { + this.hotbarSelection = hotbarSelection; + this.placeAgainst = placeAgainst; + this.side = side; + this.rot = rot; + } + } + + public Optional searchForPlacables(BuilderCalculationContext bcc) { + BetterBlockPos center = ctx.playerFeet(); + for (int dx = -5; dx <= 5; dx++) { + for (int dy = -5; dy <= 1; dy++) { + for (int dz = -5; dz <= 5; dz++) { + int x = center.x + dx; + int y = center.y + dy; + int z = center.z + dz; + IBlockState desired = bcc.getSchematic(x, y, z); + if (desired == null) { + continue; // irrelevant + } + IBlockState curr = bcc.bsi.get0(x, y, z); + if (MovementHelper.isReplacable(x, y, z, curr, bcc.bsi) && !valid(curr, desired)) { + if (dy == 1 && bcc.bsi.get0(x, y + 1, z).getBlock() == Blocks.AIR) { + continue; + } + Optional opt = possibleToPlace(desired, x, y, z, bcc.bsi); + if (opt.isPresent()) { + return opt; + } + } + } + } + } + return Optional.empty(); + } + + public Optional possibleToPlace(IBlockState toPlace, int x, int y, int z, BlockStateInterface bsi) { + for (EnumFacing against : EnumFacing.values()) { + BetterBlockPos placeAgainstPos = new BetterBlockPos(x, y, z).offset(against); + IBlockState placeAgainstState = bsi.get0(placeAgainstPos); + if (MovementHelper.isReplacable(placeAgainstPos.x, placeAgainstPos.y, placeAgainstPos.z, placeAgainstState, bsi)) { + continue; + } + if (!ctx.world().mayPlace(toPlace.getBlock(), new BetterBlockPos(x, y, z), false, against, null)) { + continue; + } + AxisAlignedBB aabb = placeAgainstState.getBoundingBox(ctx.world(), placeAgainstPos); + for (Vec3d placementMultiplier : aabbSideMultipliers(against)) { + double placeX = placeAgainstPos.x + aabb.minX * placementMultiplier.x + aabb.maxX * (1 - placementMultiplier.x); + double placeY = placeAgainstPos.y + aabb.minY * placementMultiplier.y + aabb.maxY * (1 - placementMultiplier.y); + double placeZ = placeAgainstPos.z + aabb.minZ * placementMultiplier.z + aabb.maxZ * (1 - placementMultiplier.z); + Rotation rot = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(placeX, placeY, placeZ)); + RayTraceResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot, ctx.playerController().getBlockReachDistance()); + if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK && result.getBlockPos().equals(placeAgainstPos) && result.sideHit == against.getOpposite()) { + OptionalInt hotbar = hasAnyItemThatWouldPlace(toPlace, result, rot); + if (hotbar.isPresent()) { + return Optional.of(new Placement(hotbar.getAsInt(), placeAgainstPos, against.getOpposite(), rot)); + } + } + } + } + return Optional.empty(); + } + + + public OptionalInt hasAnyItemThatWouldPlace(IBlockState desired, RayTraceResult result, Rotation rot) { + for (int i = 0; i < 9; i++) { + ItemStack stack = ctx.player().inventory.mainInventory.get(i); + if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) { + continue; + } + float originalYaw = ctx.player().rotationYaw; + float originalPitch = ctx.player().rotationPitch; + // the state depends on the facing of the player sometimes + ctx.player().rotationYaw = rot.getYaw(); + ctx.player().rotationPitch = rot.getPitch(); + IBlockState wouldBePlaced = ((ItemBlock) stack.getItem()).getBlock().getStateForPlacement( + ctx.world(), + result.getBlockPos().offset(result.sideHit), + result.sideHit, + (float) result.hitVec.x - result.getBlockPos().getX(), // as in PlayerControllerMP + (float) result.hitVec.y - result.getBlockPos().getY(), + (float) result.hitVec.z - result.getBlockPos().getZ(), + stack.getItem().getMetadata(stack.getMetadata()), + ctx.player() + ); + ctx.player().rotationYaw = originalYaw; + ctx.player().rotationPitch = originalPitch; + if (valid(wouldBePlaced, desired)) { + return OptionalInt.of(i); + } + } + return OptionalInt.empty(); + } + + private static Vec3d[] aabbSideMultipliers(EnumFacing side) { + switch (side) { + case UP: + return new Vec3d[]{new Vec3d(0.5, 1, 0.5)}; + case DOWN: + return new Vec3d[]{new Vec3d(0.5, 0, 0.5)}; + case NORTH: + case SOUTH: + case EAST: + case WEST: + double x = side.getXOffset() == 0 ? 0.5 : (1 + side.getXOffset()) / 2D; + double z = side.getZOffset() == 0 ? 0.5 : (1 + side.getZOffset()) / 2D; + return new Vec3d[]{new Vec3d(x, 0.25, z), new Vec3d(x, 0.75, z)}; + default: // null + throw new NullPointerException(); + } + } + @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { // TODO somehow tell inventorybehavior what we'd like to have on the hotbar @@ -161,6 +279,11 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro onLostControl(); return null; } + if (baritone.getInputOverrideHandler().isInputForcedDown(Input.CLICK_LEFT)) { + ticks = 5; + } else { + ticks--; + } Optional> toBreak = toBreakNearPlayer(bcc); baritone.getInputOverrideHandler().clearAllKeys(); if (toBreak.isPresent() && isSafeToCancel && ctx.player().onGround) { @@ -170,11 +293,22 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro BetterBlockPos pos = toBreak.get().getFirst(); baritone.getLookBehavior().updateTarget(rot, true); MovementHelper.switchToBestToolFor(ctx, bcc.get(pos)); - if (Objects.equals(ctx.objectMouseOver().getBlockPos(), rot) || ctx.playerRotations().isReallyCloseTo(rot)) { + if (Objects.equals(ctx.objectMouseOver().getBlockPos(), pos) || ctx.playerRotations().isReallyCloseTo(rot)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } + Optional toPlace = searchForPlacables(bcc); + if (toPlace.isPresent() && isSafeToCancel && ctx.player().onGround && ticks <= 0) { + Rotation rot = toPlace.get().rot; + 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)) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + } + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } Goal goal = assemble(bcc); if (goal == null) { @@ -241,7 +375,7 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro List approxPlacable = placable(); List placable = incorrectPositions.stream().filter(pos -> bcc.bsi.get0(pos).getBlock() == Blocks.AIR && approxPlacable.contains(bcc.getSchematic(pos.x, pos.y, pos.z))).collect(Collectors.toList()); Goal[] toBreak = incorrectPositions.stream().filter(pos -> bcc.bsi.get0(pos).getBlock() != Blocks.AIR).map(GoalBreak::new).toArray(Goal[]::new); - Goal[] toPlace = placable.stream().filter(pos -> !placable.contains(pos.down()) && !placable.contains(pos.down(2))).map(GoalPlace::new).toArray(Goal[]::new); + Goal[] toPlace = placable.stream().filter(pos -> !placable.contains(pos.down()) && !placable.contains(pos.down(2))).map(pos -> placementgoal(pos, bcc)).toArray(Goal[]::new); if (toPlace.length != 0) { return new JankyGoalComposite(new GoalComposite(toPlace), new GoalComposite(toBreak)); @@ -290,6 +424,43 @@ public class BuilderProcess extends BaritoneProcessHelper implements IBuilderPro } } + public Goal placementgoal(BlockPos pos, BuilderCalculationContext bcc) { + boolean allowSameLevel = ctx.world().getBlockState(pos.up()).getBlock() != Blocks.AIR; + for (EnumFacing facing : Movement.HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP) { + if (MovementHelper.canPlaceAgainst(ctx, pos.offset(facing)) && ctx.world().mayPlace(bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ()).getBlock(), pos, false, facing, null)) { + return new GoalAdjacent(pos, allowSameLevel); + } + } + return new GoalPlace(pos); + } + + public static class GoalAdjacent extends GoalGetToBlock { + boolean allowSameLevel; + + public GoalAdjacent(BlockPos pos, boolean allowSameLevel) { + super(pos); + this.allowSameLevel = allowSameLevel; + } + + public boolean isInGoal(int x, int y, int z) { + if (x == this.x && y == this.y && z == this.z) { + return false; + } + if (!allowSameLevel && y == this.y - 1) { + return false; + } + if (y < this.y - 1) { + return false; + } + return super.isInGoal(x, y, z); + } + + public double heuristic(int x, int y, int z) { + // prioritize lower y coordinates + return this.y * 100 + super.heuristic(x, y, z); + } + } + public static class GoalPlace extends GoalBlock { public GoalPlace(BlockPos placeAt) { super(placeAt.up()); diff --git a/src/main/java/baritone/utils/InputOverrideHandler.java b/src/main/java/baritone/utils/InputOverrideHandler.java index 93fbe875..14fe46cc 100755 --- a/src/main/java/baritone/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/utils/InputOverrideHandler.java @@ -69,7 +69,9 @@ public final class InputOverrideHandler extends Behavior implements IInputOverri return false; } if (input == Input.CLICK_RIGHT) { - return isInputForcedDown(Input.CLICK_RIGHT); + boolean ret = isInputForcedDown(Input.CLICK_RIGHT); + System.out.println("click right ret: " + ret); + return ret; } return null; } @@ -122,7 +124,7 @@ public final class InputOverrideHandler extends Behavior implements IInputOverri } private boolean inControl() { - return baritone.getPathingBehavior().isPathing() || baritone != BaritoneAPI.getProvider().getPrimaryBaritone(); + return baritone.getPathingBehavior().isPathing() || baritone.getBuilderProcess().isActive() || baritone != BaritoneAPI.getProvider().getPrimaryBaritone(); } public BlockBreakHelper getBlockBreakHelper() {