diff --git a/README.md b/README.md index c2c822002..f0e17d21f 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ [![License](https://img.shields.io/github/license/cabaletta/baritone.svg)](LICENSE) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/7150d8ccf6094057b1782aa7a8f92d7d)](https://www.codacy.com/app/leijurv/baritone?utm_source=github.com&utm_medium=referral&utm_content=cabaletta/baritone&utm_campaign=Badge_Grade) -A Minecraft pathfinder bot. This project is an updated version of [Minebot](https://github.com/leijurv/MineBot/), -the original version of the bot for Minecraft 1.8, rebuilt for 1.12.2. +A Minecraft pathfinder bot. This project is an updated version of [MineBot](https://github.com/leijurv/MineBot/), +the original version of the bot for Minecraft 1.8, rebuilt for 1.12.2. Baritone focuses on reliability and particularly performance (it's over 20x faster than MineBot at calculating paths). Features diff --git a/src/main/java/baritone/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/pathing/calc/AStarPathFinder.java index f5c3eacd6..410b5a3fe 100644 --- a/src/main/java/baritone/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/pathing/calc/AStarPathFinder.java @@ -24,16 +24,12 @@ import baritone.pathing.calc.openset.BinaryHeapOpenSet; import baritone.pathing.goals.Goal; import baritone.pathing.movement.ActionCosts; import baritone.pathing.movement.CalculationContext; -import baritone.pathing.movement.Movement; -import baritone.pathing.movement.MovementHelper; -import baritone.pathing.movement.movements.*; import baritone.pathing.path.IPath; import baritone.utils.BlockStateInterface; import baritone.utils.Helper; import baritone.utils.pathing.BetterBlockPos; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ChunkProviderClient; -import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import java.util.Collection; @@ -45,7 +41,7 @@ import java.util.Optional; * * @author leijurv */ -public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { +public final class AStarPathFinder extends AbstractNodeCostSearch implements Helper { private final Optional> favoredPositions; @@ -104,12 +100,14 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { logDebug("Took " + (System.nanoTime() / 1000000L - startTime) + "ms, " + numMovementsConsidered + " movements considered"); return Optional.of(new Path(startNode, currentNode, numNodes, goal)); } - Movement[] possibleMovements = getConnectedPositions(currentNodePos, calcContext);//movement that we could take that start at currentNodePos - for (Movement movementToGetToNeighbor : possibleMovements) { - if (movementToGetToNeighbor == null) { + for (Moves moves : Moves.values()) { + MoveResult res = moves.apply(calcContext, currentNodePos.x, currentNodePos.y, currentNodePos.z); + numMovementsConsidered++; + double actionCost = res.cost; + if (actionCost >= ActionCosts.COST_INF) { continue; } - BetterBlockPos dest = movementToGetToNeighbor.getDest(); + BetterBlockPos dest = new BetterBlockPos(res.destX, res.destY, res.destZ); int chunkX = currentNodePos.x >> 4; int chunkZ = currentNodePos.z >> 4; if (dest.x >> 4 != chunkX || dest.z >> 4 != chunkZ) { @@ -122,14 +120,11 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { } } } - // TODO cache cost - double actionCost = movementToGetToNeighbor.getCost(calcContext); - numMovementsConsidered++; if (actionCost >= ActionCosts.COST_INF) { continue; } if (actionCost <= 0) { - throw new IllegalStateException(movementToGetToNeighbor.getClass() + " " + movementToGetToNeighbor + " calculated implausible cost " + actionCost); + throw new IllegalStateException(moves + " calculated implausible cost " + actionCost); } if (favoring && favored.contains(dest)) { // see issue #18 @@ -139,7 +134,7 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { double tentativeCost = currentNode.cost + actionCost; if (tentativeCost < neighbor.cost) { if (tentativeCost < 0) { - throw new IllegalStateException(movementToGetToNeighbor.getClass() + " " + movementToGetToNeighbor + " overflowed into negative " + actionCost + " " + neighbor.cost + " " + tentativeCost); + throw new IllegalStateException(moves + " overflowed into negative " + actionCost + " " + neighbor.cost + " " + tentativeCost); } double improvementBy = neighbor.cost - tentativeCost; // there are floating point errors caused by random combinations of traverse and diagonal over a flat area @@ -150,7 +145,6 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { continue; } neighbor.previous = currentNode; - neighbor.previousMovement = movementToGetToNeighbor; neighbor.cost = tentativeCost; neighbor.combinedCost = tentativeCost + neighbor.estimatedCostToGoal; if (neighbor.isOpen) { @@ -203,45 +197,4 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { logDebug("No path found =("); return Optional.empty(); } - - - public static Movement[] getConnectedPositions(BetterBlockPos pos, CalculationContext calcContext) { - int x = pos.x; - int y = pos.y; - int z = pos.z; - BetterBlockPos east = new BetterBlockPos(x + 1, y, z); - BetterBlockPos west = new BetterBlockPos(x - 1, y, z); - BetterBlockPos south = new BetterBlockPos(x, y, z + 1); - BetterBlockPos north = new BetterBlockPos(x, y, z - 1); - return new Movement[]{ - new MovementDownward(pos, new BetterBlockPos(x, y - 1, z)), - - new MovementPillar(pos, new BetterBlockPos(x, y + 1, z)), - - new MovementTraverse(pos, east), - new MovementTraverse(pos, west), - new MovementTraverse(pos, north), - new MovementTraverse(pos, south), - - new MovementAscend(pos, new BetterBlockPos(x + 1, y + 1, z)), - new MovementAscend(pos, new BetterBlockPos(x - 1, y + 1, z)), - new MovementAscend(pos, new BetterBlockPos(x, y + 1, z + 1)), - new MovementAscend(pos, new BetterBlockPos(x, y + 1, z - 1)), - - MovementHelper.generateMovementFallOrDescend(pos, east, calcContext), - MovementHelper.generateMovementFallOrDescend(pos, west, calcContext), - MovementHelper.generateMovementFallOrDescend(pos, north, calcContext), - MovementHelper.generateMovementFallOrDescend(pos, south, calcContext), - - new MovementDiagonal(pos, EnumFacing.NORTH, EnumFacing.EAST), - new MovementDiagonal(pos, EnumFacing.NORTH, EnumFacing.WEST), - new MovementDiagonal(pos, EnumFacing.SOUTH, EnumFacing.EAST), - new MovementDiagonal(pos, EnumFacing.SOUTH, EnumFacing.WEST), - - MovementParkour.generate(pos, EnumFacing.EAST, calcContext), - MovementParkour.generate(pos, EnumFacing.WEST, calcContext), - MovementParkour.generate(pos, EnumFacing.NORTH, calcContext), - MovementParkour.generate(pos, EnumFacing.SOUTH, calcContext), - }; - } } diff --git a/src/main/java/baritone/pathing/calc/MoveResult.java b/src/main/java/baritone/pathing/calc/MoveResult.java new file mode 100644 index 000000000..e9c6caa9e --- /dev/null +++ b/src/main/java/baritone/pathing/calc/MoveResult.java @@ -0,0 +1,32 @@ +/* + * 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.pathing.calc; + +public final class MoveResult { + public final int destX; + public final int destY; + public final int destZ; + public final double cost; + + public MoveResult(int x, int y, int z, double cost) { + this.destX = x; + this.destY = y; + this.destZ = z; + this.cost = cost; + } +} diff --git a/src/main/java/baritone/pathing/calc/Moves.java b/src/main/java/baritone/pathing/calc/Moves.java new file mode 100644 index 000000000..dd9496104 --- /dev/null +++ b/src/main/java/baritone/pathing/calc/Moves.java @@ -0,0 +1,274 @@ +/* + * 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.pathing.calc; + +import baritone.pathing.movement.CalculationContext; +import baritone.pathing.movement.Movement; +import baritone.pathing.movement.movements.*; +import baritone.utils.pathing.BetterBlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.Tuple; + +public enum Moves { + DOWNWARD() { + @Override + protected Movement apply0(BetterBlockPos src) { // TODO specific return types + return new MovementDownward(src, src.down()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y - 1, z, MovementDownward.cost(context, x, y, z)); + } + }, + + PILLAR() { + @Override + protected Movement apply0(BetterBlockPos src) { // TODO specific return types + return new MovementPillar(src, src.up()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y + 1, z, MovementPillar.cost(context, x, y, z)); + } + }, + + TRAVERSE_NORTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementTraverse(src, src.north()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y, z - 1, MovementTraverse.cost(context, x, y, z, x, z - 1)); + } + }, + + TRAVERSE_SOUTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementTraverse(src, src.south()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y, z + 1, MovementTraverse.cost(context, x, y, z, x, z + 1)); + } + }, + + TRAVERSE_EAST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementTraverse(src, src.east()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x + 1, y, z, MovementTraverse.cost(context, x, y, z, x + 1, z)); + } + }, + + TRAVERSE_WEST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementTraverse(src, src.west()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x - 1, y, z, MovementTraverse.cost(context, x, y, z, x - 1, z)); + } + }, + + ASCEND_NORTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementAscend(src, new BetterBlockPos(src.x, src.y + 1, src.z - 1)); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y + 1, z - 1, MovementAscend.cost(context, x, y, z, x, z - 1)); + } + }, + + ASCEND_SOUTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementAscend(src, new BetterBlockPos(src.x, src.y + 1, src.z + 1)); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y + 1, z + 1, MovementAscend.cost(context, x, y, z, x, z + 1)); + } + }, + + ASCEND_EAST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementAscend(src, new BetterBlockPos(src.x + 1, src.y + 1, src.z)); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y + 1, z + 1, MovementAscend.cost(context, x, y, z, x + 1, z)); + } + }, + + ASCEND_WEST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementAscend(src, new BetterBlockPos(src.x - 1, src.y + 1, src.z)); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y + 1, z - 1, MovementAscend.cost(context, x, y, z, x - 1, z)); + } + }, + + DESCEND_EAST() { + @Override + protected Movement apply0(BetterBlockPos src) { + MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); + if (res.destY == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } else { + return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + Tuple res = MovementDescend.cost(context, x, y, z, x + 1, z); + return new MoveResult(x + 1, res.getFirst(), z, res.getSecond()); + } + }, + + DESCEND_WEST() { + @Override + protected Movement apply0(BetterBlockPos src) { + MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); + if (res.destY == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } else { + return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + Tuple res = MovementDescend.cost(context, x, y, z, x - 1, z); + return new MoveResult(x - 1, res.getFirst(), z, res.getSecond()); + } + }, + + DESCEND_NORTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); + if (res.destY == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } else { + return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + Tuple res = MovementDescend.cost(context, x, y, z, x, z - 1); + return new MoveResult(x, res.getFirst(), z - 1, res.getSecond()); + } + }, + + DESCEND_SOUTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); + if (res.destY == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } else { + return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + Tuple res = MovementDescend.cost(context, x, y, z, x, z + 1); + return new MoveResult(x, res.getFirst(), z + 1, res.getSecond()); + } + }, + + DIAGONAL_NORTHEAST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementDiagonal(src, EnumFacing.NORTH, EnumFacing.EAST); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x + 1, y, z - 1, MovementDiagonal.cost(context, x, y, z, x + 1, z - 1)); + } + }, + + DIAGONAL_NORTHWEST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementDiagonal(src, EnumFacing.NORTH, EnumFacing.WEST); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x - 1, y, z - 1, MovementDiagonal.cost(context, x, y, z, x - 1, z - 1)); + } + }, + + DIAGONAL_SOUTHEAST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementDiagonal(src, EnumFacing.SOUTH, EnumFacing.EAST); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x + 1, y, z + 1, MovementDiagonal.cost(context, x, y, z, x + 1, z + 1)); + } + }, + + DIAGONAL_SOUTHWEST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementDiagonal(src, EnumFacing.SOUTH, EnumFacing.WEST); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x - 1, y, z + 1, MovementDiagonal.cost(context, x, y, z, x - 1, z + 1)); + } + }, + // TODO parkour + ; + + protected abstract Movement apply0(BetterBlockPos src); + + + public abstract MoveResult apply(CalculationContext context, int x, int y, int z); +} diff --git a/src/main/java/baritone/pathing/calc/Path.java b/src/main/java/baritone/pathing/calc/Path.java index e94f2fb3e..50dd49d76 100644 --- a/src/main/java/baritone/pathing/calc/Path.java +++ b/src/main/java/baritone/pathing/calc/Path.java @@ -89,7 +89,7 @@ class Path implements IPath { LinkedList tempMovements = new LinkedList<>(); // Instead, do it into a linked list, then convert at the end while (!current.equals(start)) { tempPath.addFirst(current.pos); - tempMovements.addFirst(current.previousMovement); + tempMovements.addFirst(runBackwards(current.previous.pos, current.pos)); current = current.previous; } tempPath.addFirst(start.pos); @@ -100,6 +100,16 @@ class Path implements IPath { movements.addAll(tempMovements); } + private static Movement runBackwards(BetterBlockPos src, BetterBlockPos dest) { // TODO this is horrifying + for (Moves moves : Moves.values()) { + Movement move = moves.apply0(src); + if (move.getDest().equals(dest)) { + return move; + } + } + throw new IllegalStateException("Movement became impossible during calculation " + src + " " + dest + " " + dest.subtract(src)); + } + /** * Performs a series of checks to ensure that the assembly of the path went as expected. */ diff --git a/src/main/java/baritone/pathing/calc/PathNode.java b/src/main/java/baritone/pathing/calc/PathNode.java index 8f9895d85..50283cf98 100644 --- a/src/main/java/baritone/pathing/calc/PathNode.java +++ b/src/main/java/baritone/pathing/calc/PathNode.java @@ -19,7 +19,6 @@ package baritone.pathing.calc; import baritone.pathing.goals.Goal; import baritone.pathing.movement.ActionCosts; -import baritone.pathing.movement.Movement; import baritone.utils.pathing.BetterBlockPos; /** @@ -62,12 +61,6 @@ public final class PathNode { */ PathNode previous; - /** - * In the graph search, what previous movement (edge) was taken to get to here - * Mutable and changed by PathFinder - */ - Movement previousMovement; - /** * Is this a member of the open set in A*? (only used during pathfinding) * Instead of doing a costly member check in the open set, cache membership in each node individually too. @@ -85,7 +78,6 @@ public final class PathNode { this.cost = ActionCosts.COST_INF; this.goal = goal; this.estimatedCostToGoal = goal.heuristic(pos); - this.previousMovement = null; this.isOpen = false; } diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index 8c3ad4f4c..b1be71fda 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -47,11 +47,12 @@ import java.util.Optional; */ public interface MovementHelper extends ActionCosts, Helper { - static boolean avoidBreaking(BlockPos pos, IBlockState state) { + static boolean avoidBreaking(BetterBlockPos pos, IBlockState state) { + return avoidBreaking(pos.x, pos.y, pos.z, state); + } + + static boolean avoidBreaking(int x, int y, int z, IBlockState state) { Block b = state.getBlock(); - int x = pos.getX(); - int y = pos.getY(); - int z = pos.getZ(); return b == Blocks.ICE // ice becomes water, and water can mess up the path || b instanceof BlockSilverfish // obvious reasons // call BlockStateInterface.get directly with x,y,z. no need to make 5 new BlockPos for no reason @@ -165,6 +166,10 @@ public interface MovementHelper extends ActionCosts, Helper { } static boolean isReplacable(BlockPos pos, IBlockState state) { + return isReplacable(pos.getX(), pos.getY(), pos.getZ(), state); + } + + static boolean isReplacable(int x, int y, int z, IBlockState state) { // for MovementTraverse and MovementAscend // block double plant defaults to true when the block doesn't match, so don't need to check that case // all other overrides just return true or false @@ -175,13 +180,19 @@ public interface MovementHelper extends ActionCosts, Helper { * return ((Integer)worldIn.getBlockState(pos).getValue(LAYERS)).intValue() == 1; * } */ - if (state.getBlock() instanceof BlockSnow) { + Block block = state.getBlock(); + if (block instanceof BlockSnow) { // as before, default to true (mostly because it would otherwise make long distance pathing through snowy biomes impossible) - if (mc.world.getChunk(pos) instanceof EmptyChunk) { + if (mc.world.getChunk(x >> 4, z >> 4) instanceof EmptyChunk) { return true; } + return state.getValue(BlockSnow.LAYERS) == 1; } - return state.getBlock().isReplaceable(mc.world, pos); + if (block instanceof BlockDoublePlant) { + BlockDoublePlant.EnumPlantType kek = state.getValue(BlockDoublePlant.VARIANT); + return kek == BlockDoublePlant.EnumPlantType.FERN || kek == BlockDoublePlant.EnumPlantType.GRASS; + } + return state.getBlock().isReplaceable(null, null); } static boolean isDoorPassable(BlockPos doorPos, BlockPos playerPos) { @@ -314,24 +325,39 @@ public interface MovementHelper extends ActionCosts, Helper { return canWalkOn(x, y, z, BlockStateInterface.get(x, y, z)); } + static boolean canPlaceAgainst(int x, int y, int z) { + return canPlaceAgainst(BlockStateInterface.get(x, y, z)); + } + static boolean canPlaceAgainst(BlockPos pos) { - IBlockState state = BlockStateInterface.get(pos); + return canPlaceAgainst(BlockStateInterface.get(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 double getMiningDurationTicks(CalculationContext context, BetterBlockPos position, boolean includeFalling) { IBlockState state = BlockStateInterface.get(position); - return getMiningDurationTicks(context, position, state, includeFalling); + return getMiningDurationTicks(context, position.x, position.y, position.z, state, includeFalling); } static double getMiningDurationTicks(CalculationContext context, BetterBlockPos position, IBlockState state, boolean includeFalling) { + return getMiningDurationTicks(context, position.x, position.y, position.z, state, includeFalling); + } + + static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, boolean includeFalling) { + return getMiningDurationTicks(context, x, y, z, BlockStateInterface.get(x, y, z), includeFalling); + } + + static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, IBlockState state, boolean includeFalling) { Block block = state.getBlock(); - if (!canWalkThrough(position, state)) { + if (!canWalkThrough(x, y, z, state)) { if (!context.allowBreak()) { return COST_INF; } - if (avoidBreaking(position, state)) { + if (avoidBreaking(x, y, z, state)) { return COST_INF; } double m = Blocks.CRAFTING_TABLE.equals(block) ? 10 : 1; // TODO see if this is still necessary. it's from MineBot when we wanted to penalize breaking its crafting table @@ -342,10 +368,9 @@ public interface MovementHelper extends ActionCosts, Helper { double result = m / strVsBlock; if (includeFalling) { - BetterBlockPos up = position.up(); - IBlockState above = BlockStateInterface.get(up); + IBlockState above = BlockStateInterface.get(x, y + 1, z); if (above.getBlock() instanceof BlockFalling) { - result += getMiningDurationTicks(context, up, above, true); + result += getMiningDurationTicks(context, x, y + 1, z, above, true); } } return result; @@ -437,15 +462,7 @@ public interface MovementHelper extends ActionCosts, Helper { } static Movement generateMovementFallOrDescend(BetterBlockPos pos, BetterBlockPos dest, CalculationContext calcContext) { - // A - //SA - // A - // B - // C - // D - //if S is where you start, B needs to be air for a movementfall - //A is plausibly breakable by either descend or fall - //C, D, etc determine the length of the fall + int x = dest.x; int y = dest.y; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index 9d3398a21..6d313c488 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -28,7 +28,6 @@ import baritone.utils.BlockStateInterface; import baritone.utils.InputOverrideHandler; import baritone.utils.Utils; import baritone.utils.pathing.BetterBlockPos; -import net.minecraft.block.Block; import net.minecraft.block.BlockFalling; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; @@ -55,46 +54,54 @@ public class MovementAscend extends Movement { @Override protected double calculateCost(CalculationContext context) { - IBlockState srcDown = BlockStateInterface.get(src.down()); + return cost(context, src.x, src.y, src.z, dest.x, dest.z); + } + + public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { + IBlockState srcDown = BlockStateInterface.get(x, y - 1, z); if (srcDown.getBlock() == Blocks.LADDER || srcDown.getBlock() == Blocks.VINE) { return COST_INF; } // we can jump from soul sand, but not from a bottom slab boolean jumpingFromBottomSlab = MovementHelper.isBottomSlab(srcDown); - IBlockState toPlace = BlockStateInterface.get(positionToPlace); + IBlockState toPlace = BlockStateInterface.get(destX, y, destZ); boolean jumpingToBottomSlab = MovementHelper.isBottomSlab(toPlace); if (jumpingFromBottomSlab && !jumpingToBottomSlab) { return COST_INF;// the only thing we can ascend onto from a bottom slab is another bottom slab } - if (!MovementHelper.canWalkOn(positionToPlace, toPlace)) { + boolean hasToPlace = false; + if (!MovementHelper.canWalkOn(destX, y, z, toPlace)) { if (!context.hasThrowaway()) { return COST_INF; } - if (toPlace.getBlock() != Blocks.AIR && !BlockStateInterface.isWater(toPlace.getBlock()) && !MovementHelper.isReplacable(positionToPlace, toPlace)) { + if (toPlace.getBlock() != Blocks.AIR && !BlockStateInterface.isWater(toPlace.getBlock()) && !MovementHelper.isReplacable(destX, y, destZ, toPlace)) { return COST_INF; } // TODO: add ability to place against .down() as well as the cardinal directions // useful for when you are starting a staircase without anything to place against // Counterpoint to the above TODO ^ you should move then pillar instead of ascend for (int i = 0; i < 4; i++) { - BlockPos against1 = positionToPlace.offset(HORIZONTALS[i]); - if (against1.equals(src)) { + int againstX = destX + HORIZONTALS[i].getXOffset(); + int againstZ = destZ + HORIZONTALS[i].getZOffset(); + if (againstX == x && againstZ == z) { continue; } - if (MovementHelper.canPlaceAgainst(against1)) { - return JUMP_ONE_BLOCK_COST + WALK_ONE_BLOCK_COST + context.placeBlockCost() + getTotalHardnessOfBlocksToBreak(context); + if (MovementHelper.canPlaceAgainst(againstX, y, againstZ)) { + hasToPlace = true; + break; } } - return COST_INF; + if (!hasToPlace) { // didn't find a valid place =( + return COST_INF; + } } - if (BlockStateInterface.get(src.up(3)).getBlock() instanceof BlockFalling) {//it would fall on us and possibly suffocate us + IBlockState srcUp2 = null; + if (BlockStateInterface.get(x, y + 3, z).getBlock() instanceof BlockFalling) {//it would fall on us and possibly suffocate us // HOWEVER, we assume that we're standing in the start position // that means that src and src.up(1) are both air // maybe they aren't now, but they will be by the time this starts - Block srcUp = BlockStateInterface.get(src.up(1)).getBlock(); - Block srcUp2 = BlockStateInterface.get(src.up(2)).getBlock(); - if (!(srcUp instanceof BlockFalling) || !(srcUp2 instanceof BlockFalling)) { + if (!(BlockStateInterface.getBlock(x, y + 1, z) instanceof BlockFalling) || !((srcUp2 = BlockStateInterface.get(x, y + 2, z)).getBlock() instanceof BlockFalling)) { // if both of those are BlockFalling, that means that by standing on src // (the presupposition of this Movement) // we have necessarily already cleared the entire BlockFalling stack @@ -109,15 +116,41 @@ public class MovementAscend extends Movement { // it's possible srcUp is AIR from the start, and srcUp2 is falling // and in that scenario, when we arrive and break srcUp2, that lets srcUp3 fall on us and suffocate us } - double walk = WALK_ONE_BLOCK_COST; - if (jumpingToBottomSlab && !jumpingFromBottomSlab) { - return walk + getTotalHardnessOfBlocksToBreak(context); // we don't hit space we just walk into the slab + double walk; + if (jumpingToBottomSlab) { + if (jumpingFromBottomSlab) { + walk = Math.max(JUMP_ONE_BLOCK_COST, WALK_ONE_BLOCK_COST); // we hit space immediately on entering this action + } else { + walk = WALK_ONE_BLOCK_COST; // we don't hit space we just walk into the slab + } + } else { + if (toPlace.getBlock() == Blocks.SOUL_SAND) { + walk = WALK_ONE_OVER_SOUL_SAND_COST; + } else { + walk = WALK_ONE_BLOCK_COST; + } } - if (!jumpingToBottomSlab && toPlace.getBlock().equals(Blocks.SOUL_SAND)) { - walk *= WALK_ONE_OVER_SOUL_SAND_COST / WALK_ONE_BLOCK_COST; + + // cracks knuckles + + double totalCost = 0; + totalCost += walk; + if (hasToPlace) { + totalCost += context.placeBlockCost(); } - // we hit space immediately on entering this action - return Math.max(JUMP_ONE_BLOCK_COST, walk) + getTotalHardnessOfBlocksToBreak(context); + if (srcUp2 == null) { + srcUp2 = BlockStateInterface.get(x, y + 2, z); + } + totalCost += MovementHelper.getMiningDurationTicks(context, x, y + 2, z, srcUp2, false); // TODO MAKE ABSOLUTELY SURE we don't need includeFalling here, from the falling check above + if (totalCost >= COST_INF) { + return COST_INF; + } + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, false); + if (totalCost >= COST_INF) { + return COST_INF; + } + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 2, destZ, true); + return totalCost; } @Override diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index 85f7f1107..275344e4a 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -17,6 +17,7 @@ package baritone.pathing.movement.movements; +import baritone.Baritone; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; @@ -26,11 +27,16 @@ import baritone.utils.BlockStateInterface; import baritone.utils.InputOverrideHandler; import baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.Block; +import net.minecraft.block.BlockFalling; +import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; +import net.minecraft.util.Tuple; import net.minecraft.util.math.BlockPos; public class MovementDescend extends Movement { + private static final Tuple IMPOSSIBLE = new Tuple<>(0, COST_INF); + private int numTicks = 0; public MovementDescend(BetterBlockPos start, BetterBlockPos end) { @@ -45,24 +51,103 @@ public class MovementDescend extends Movement { @Override protected double calculateCost(CalculationContext context) { - Block fromDown = BlockStateInterface.get(src.down()).getBlock(); + Tuple result = cost(context, src.x, src.y, src.z, dest.x, dest.z); + if (result.getFirst() != dest.y) { + return COST_INF; // doesn't apply to us, this position is a fall not a descend + } + return result.getSecond(); + } + + public static Tuple cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { + Block fromDown = BlockStateInterface.get(x, y - 1, z).getBlock(); if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { - return COST_INF; + return IMPOSSIBLE; } - if (!MovementHelper.canWalkOn(positionToPlace)) { - return COST_INF; + + double totalCost = 0; + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y - 1, destZ, false); + if (totalCost >= COST_INF) { + return IMPOSSIBLE; } - Block tmp1 = BlockStateInterface.get(dest).getBlock(); + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y, destZ, false); + if (totalCost >= COST_INF) { + return IMPOSSIBLE; + } + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, true); // only the top block in the 3 we need to mine needs to consider the falling blocks above + if (totalCost >= COST_INF) { + return IMPOSSIBLE; + } + + // A + //SA + // A + // B + // C + // D + //if S is where you start, B needs to be air for a movementfall + //A is plausibly breakable by either descend or fall + //C, D, etc determine the length of the fall + if (!MovementHelper.canWalkOn(destX, y - 2, destZ)) { + return dynamicFallCost(context, x, y, z, destX, destZ, totalCost); + } + + Block tmp1 = BlockStateInterface.get(destX, y - 1, destZ).getBlock(); if (tmp1 == Blocks.LADDER || tmp1 == Blocks.VINE) { - return COST_INF; + return IMPOSSIBLE; } + // we walk half the block plus 0.3 to get to the edge, then we walk the other 0.2 while simultaneously falling (math.max because of how it's in parallel) double walk = WALK_OFF_BLOCK_COST; if (fromDown == Blocks.SOUL_SAND) { // use this ratio to apply the soul sand speed penalty to our 0.8 block distance walk = WALK_ONE_OVER_SOUL_SAND_COST; } - return walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST) + getTotalHardnessOfBlocksToBreak(context); + totalCost += walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST); + return new Tuple<>(y - 1, totalCost); + } + + public static Tuple dynamicFallCost(CalculationContext context, int x, int y, int z, int destX, int destZ, double frontBreak) { + if (frontBreak != 0 && BlockStateInterface.get(destX, y + 2, destZ).getBlock() instanceof BlockFalling) { + // if frontBreak is 0 we can actually get through this without updating the falling block and making it actually fall + // but if frontBreak is nonzero, we're breaking blocks in front, so don't let anything fall through this column, + // and potentially replace the water we're going to fall into + return IMPOSSIBLE; + } + for (int fallHeight = 3; true; fallHeight++) { + int newY = y - fallHeight; + if (newY < 0) { + // when pathing in the end, where you could plausibly fall into the void + // this check prevents it from getting the block at y=-1 and crashing + return IMPOSSIBLE; + } + IBlockState ontoBlock = BlockStateInterface.get(destX, newY, destZ); + double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[fallHeight] + frontBreak; + if (ontoBlock.getBlock() == Blocks.WATER) { // TODO flowing check required here? + if (Baritone.settings().assumeWalkOnWater.get()) { + return IMPOSSIBLE; // TODO fix + } + // found a fall into water + return new Tuple<>(newY, tentativeCost); // TODO incorporate water swim up cost? + } + if (MovementHelper.canWalkThrough(destX, newY, destZ, ontoBlock)) { + continue; + } + if (!MovementHelper.canWalkOn(destX, newY, destZ, ontoBlock)) { + return IMPOSSIBLE; + } + if (MovementHelper.isBottomSlab(ontoBlock)) { + return IMPOSSIBLE; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect + } + if (context.hasWaterBucket() && fallHeight <= context.maxFallHeightBucket() + 1) { + return new Tuple<>(newY + 1, tentativeCost + context.placeBlockCost()); // this is the block we're falling onto, so dest is +1 + } + if (fallHeight <= context.maxFallHeightNoWater() + 1) { + // fallHeight = 4 means onto.up() is 3 blocks down, which is the max + return new Tuple<>(newY + 1, tentativeCost); + } else { + return IMPOSSIBLE; + } + } } @Override diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java index d092b66ea..5a5b736a6 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java @@ -53,40 +53,43 @@ public class MovementDiagonal extends Movement { @Override protected double calculateCost(CalculationContext context) { - Block fromDown = BlockStateInterface.get(src.down()).getBlock(); + return cost(context, src.x, src.y, src.z, dest.x, dest.z); + } + + public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { + Block fromDown = BlockStateInterface.get(x, y - 1, z).getBlock(); if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { return COST_INF; } - if (!MovementHelper.canWalkThrough(positionsToBreak[4]) || !MovementHelper.canWalkThrough(positionsToBreak[5])) { + if (!MovementHelper.canWalkThrough(destX, y, destZ) || !MovementHelper.canWalkThrough(destX, y + 1, destZ)) { return COST_INF; } - BetterBlockPos destDown = dest.down(); - IBlockState destWalkOn = BlockStateInterface.get(destDown); - if (!MovementHelper.canWalkOn(destDown, destWalkOn)) { + IBlockState destWalkOn = BlockStateInterface.get(destX, y - 1, destZ); + if (!MovementHelper.canWalkOn(destX, y - 1, destZ, destWalkOn)) { return COST_INF; } double multiplier = WALK_ONE_BLOCK_COST; // For either possible soul sand, that affects half of our walking - if (destWalkOn.getBlock().equals(Blocks.SOUL_SAND)) { + if (destWalkOn.getBlock() == Blocks.SOUL_SAND) { multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } if (fromDown == Blocks.SOUL_SAND) { multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } - Block cuttingOver1 = BlockStateInterface.get(positionsToBreak[2].down()).getBlock(); + Block cuttingOver1 = BlockStateInterface.get(x, y - 1, destZ).getBlock(); if (cuttingOver1 instanceof BlockMagma || BlockStateInterface.isLava(cuttingOver1)) { return COST_INF; } - Block cuttingOver2 = BlockStateInterface.get(positionsToBreak[4].down()).getBlock(); + Block cuttingOver2 = BlockStateInterface.get(destX, y - 1, z).getBlock(); if (cuttingOver2 instanceof BlockMagma || BlockStateInterface.isLava(cuttingOver2)) { return COST_INF; } - IBlockState pb0 = BlockStateInterface.get(positionsToBreak[0]); - IBlockState pb1 = BlockStateInterface.get(positionsToBreak[1]); - IBlockState pb2 = BlockStateInterface.get(positionsToBreak[2]); - IBlockState pb3 = BlockStateInterface.get(positionsToBreak[3]); - double optionA = MovementHelper.getMiningDurationTicks(context, positionsToBreak[0], pb0, false) + MovementHelper.getMiningDurationTicks(context, positionsToBreak[1], pb1, true); - double optionB = MovementHelper.getMiningDurationTicks(context, positionsToBreak[2], pb2, false) + MovementHelper.getMiningDurationTicks(context, positionsToBreak[3], pb3, true); + IBlockState pb0 = BlockStateInterface.get(x, y, destZ); + IBlockState pb1 = BlockStateInterface.get(x, y + 1, destZ); + IBlockState pb2 = BlockStateInterface.get(destX, y, z); + IBlockState pb3 = BlockStateInterface.get(destX, y + 1, z); + double optionA = MovementHelper.getMiningDurationTicks(context, x, y, destZ, pb0, false) + MovementHelper.getMiningDurationTicks(context, x, y + 1, destZ, pb1, true); + double optionB = MovementHelper.getMiningDurationTicks(context, destX, y, z, pb2, false) + MovementHelper.getMiningDurationTicks(context, destX, y + 1, z, pb3, true); if (optionA != 0 && optionB != 0) { return COST_INF; } @@ -100,7 +103,7 @@ public class MovementDiagonal extends Movement { return COST_INF; } } - if (BlockStateInterface.isWater(src) || BlockStateInterface.isWater(dest)) { + if (BlockStateInterface.isWater(BlockStateInterface.getBlock(x, y, z)) || BlockStateInterface.isWater(BlockStateInterface.getBlock(destX, y, destZ))) { // Ignore previous multiplier // Whatever we were walking on (possibly soul sand) doesn't matter as we're actually floating on water // Not even touching the blocks below diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java index b265ddd71..148dc3b38 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java @@ -43,17 +43,21 @@ public class MovementDownward extends Movement { @Override protected double calculateCost(CalculationContext context) { - if (!MovementHelper.canWalkOn(dest.down())) { + return cost(context, src.x, src.y, src.z); + } + + public static double cost(CalculationContext context, int x, int y, int z) { + if (!MovementHelper.canWalkOn(x, y - 2, z)) { return COST_INF; } - IBlockState d = BlockStateInterface.get(dest); + IBlockState d = BlockStateInterface.get(x, y - 1, z); Block td = d.getBlock(); boolean ladder = td == Blocks.LADDER || td == Blocks.VINE; if (ladder) { return LADDER_DOWN_ONE_COST; } else { // we're standing on it, while it might be block falling, it'll be air by the time we get here in the movement - return FALL_N_BLOCKS_COST[1] + MovementHelper.getMiningDurationTicks(context, dest, d, false); + return FALL_N_BLOCKS_COST[1] + MovementHelper.getMiningDurationTicks(context, x, y - 1, z, d, false); } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementFall.java b/src/main/java/baritone/pathing/movement/movements/MovementFall.java index a6a9e9f6f..8ff115911 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementFall.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementFall.java @@ -26,13 +26,10 @@ import baritone.pathing.movement.MovementState.MovementStatus; import baritone.pathing.movement.MovementState.MovementTarget; import baritone.utils.*; import baritone.utils.pathing.BetterBlockPos; -import net.minecraft.block.Block; -import net.minecraft.block.BlockFalling; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.InventoryPlayer; -import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; +import net.minecraft.util.Tuple; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; @@ -48,58 +45,11 @@ public class MovementFall extends Movement { @Override protected double calculateCost(CalculationContext context) { - Block fromDown = BlockStateInterface.get(src.down()).getBlock(); - if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { - return COST_INF; + Tuple result = MovementDescend.cost(context, src.x, src.y, src.z, dest.x, dest.z); + if (result.getFirst() != dest.y) { + return COST_INF; // doesn't apply to us, this position is a descend not a fall } - IBlockState fallOnto = BlockStateInterface.get(dest.down()); - if (!MovementHelper.canWalkOn(dest.down(), fallOnto)) { - return COST_INF; - } - if (MovementHelper.isBottomSlab(fallOnto)) { - return COST_INF; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect - } - double placeBucketCost = 0.0; - boolean destIsWater = BlockStateInterface.isWater(dest); - if (!destIsWater && src.getY() - dest.getY() > context.maxFallHeightNoWater()) { - if (!context.hasWaterBucket()) { - return COST_INF; - } - if (src.getY() - dest.getY() > context.maxFallHeightBucket()) { - return COST_INF; - } - placeBucketCost = context.placeBlockCost(); - } - double frontThree = 0; - for (int i = 0; i < 3; i++) { - frontThree += MovementHelper.getMiningDurationTicks(context, positionsToBreak[i], false); - // don't include falling because we will check falling right after this, and if it's there it's COST_INF - if (frontThree >= COST_INF) { - return COST_INF; - } - } - if (BlockStateInterface.get(positionsToBreak[0].up()).getBlock() instanceof BlockFalling) { - return COST_INF; - } - for (int i = 3; i < positionsToBreak.length; i++) { - // TODO is this the right check here? - // MiningDurationTicks is all right, but shouldn't it be canWalkThrough instead? - // Lilypads (i think?) are 0 ticks to mine, but they definitely cause fall damage - // Same thing for falling through water... we can't actually do that - // And falling through signs is possible, but they do have a mining duration, right? - if (MovementHelper.getMiningDurationTicks(context, positionsToBreak[i], false) > 0) { - //can't break while falling - - if (i != positionsToBreak.length - 1 || !destIsWater) { - // if we're checking the very last block to mine - // and it's water (so this is a water fall) - // don't consider the cost of "mining" it - // (if assumeWalkOnWater is true, water isn't canWalkThrough) - return COST_INF; - } - } - } - return WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[positionsToBreak.length - 1] + placeBucketCost + frontThree; + return result.getSecond(); } @Override diff --git a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java index 15e7d52c0..bf77855d3 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java @@ -48,9 +48,13 @@ public class MovementPillar extends Movement { @Override protected double calculateCost(CalculationContext context) { - Block fromDown = BlockStateInterface.get(src).getBlock(); + return cost(context, src.x, src.y, src.z); + } + + public static double cost(CalculationContext context, int x, int y, int z) { + Block fromDown = BlockStateInterface.get(x, y, z).getBlock(); boolean ladder = fromDown instanceof BlockLadder || fromDown instanceof BlockVine; - IBlockState fromDownDown = BlockStateInterface.get(src.down()); + IBlockState fromDownDown = BlockStateInterface.get(x, y - 1, z); if (!ladder) { if (fromDownDown.getBlock() instanceof BlockLadder || fromDownDown.getBlock() instanceof BlockVine) { return COST_INF; @@ -65,24 +69,23 @@ public class MovementPillar extends Movement { return COST_INF; } if (fromDown instanceof BlockVine) { - if (getAgainst(src) == null) { + if (!hasAgainst(x, y, z)) { return COST_INF; } } - BetterBlockPos toBreakPos = src.up(2); - IBlockState toBreak = BlockStateInterface.get(toBreakPos); + IBlockState toBreak = BlockStateInterface.get(x, y + 2, z); Block toBreakBlock = toBreak.getBlock(); if (toBreakBlock instanceof BlockFenceGate) { return COST_INF; } Block srcUp = null; if (BlockStateInterface.isWater(toBreakBlock) && BlockStateInterface.isWater(fromDown)) { - srcUp = BlockStateInterface.get(dest).getBlock(); + srcUp = BlockStateInterface.get(x, y + 1, z).getBlock(); if (BlockStateInterface.isWater(srcUp)) { return LADDER_UP_ONE_COST; } } - double hardness = MovementHelper.getMiningDurationTicks(context, toBreakPos, toBreak, true); + double hardness = MovementHelper.getMiningDurationTicks(context, x, y + 2, z, toBreak, true); if (hardness >= COST_INF) { return COST_INF; } @@ -90,12 +93,11 @@ public class MovementPillar extends Movement { if (toBreakBlock instanceof BlockLadder || toBreakBlock instanceof BlockVine) { hardness = 0; // we won't actually need to break the ladder / vine because we're going to use it } else { - BlockPos chkPos = src.up(3); - IBlockState check = BlockStateInterface.get(chkPos); + IBlockState check = BlockStateInterface.get(x, y + 3, z); if (check.getBlock() instanceof BlockFalling) { // see MovementAscend's identical check for breaking a falling block above our head if (srcUp == null) { - srcUp = BlockStateInterface.get(dest).getBlock(); + srcUp = BlockStateInterface.get(x, y + 1, z).getBlock(); } if (!(toBreakBlock instanceof BlockFalling) || !(srcUp instanceof BlockFalling)) { return COST_INF; @@ -120,6 +122,13 @@ public class MovementPillar extends Movement { } } + public static boolean hasAgainst(int x, int y, int z) { + return BlockStateInterface.get(x + 1, y, z).isBlockNormalCube() || + BlockStateInterface.get(x - 1, y, z).isBlockNormalCube() || + BlockStateInterface.get(x, y, z + 1).isBlockNormalCube() || + BlockStateInterface.get(x, y, z - 1).isBlockNormalCube(); + } + public static BlockPos getAgainst(BlockPos vine) { if (BlockStateInterface.get(vine.north()).isBlockNormalCube()) { return vine.north(); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index a2d643772..232a8376e 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -57,11 +57,15 @@ public class MovementTraverse extends Movement { @Override protected double calculateCost(CalculationContext context) { - IBlockState pb0 = BlockStateInterface.get(positionsToBreak[0]); - IBlockState pb1 = BlockStateInterface.get(positionsToBreak[1]); - IBlockState destOn = BlockStateInterface.get(positionToPlace); - Block srcDown = BlockStateInterface.getBlock(src.down()); - if (MovementHelper.canWalkOn(positionToPlace, destOn)) {//this is a walk, not a bridge + return cost(context, src.x, src.y, src.z, dest.x, dest.z); + } + + public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { + IBlockState pb0 = BlockStateInterface.get(destX, y + 1, destZ); + IBlockState pb1 = BlockStateInterface.get(destX, y, destZ); + IBlockState destOn = BlockStateInterface.get(destX, y - 1, destZ); + Block srcDown = BlockStateInterface.getBlock(x, y - 1, z); + if (MovementHelper.canWalkOn(destX, y - 1, destZ, destOn)) {//this is a walk, not a bridge double WC = WALK_ONE_BLOCK_COST; if (BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock())) { WC = WALK_ONE_IN_WATER_COST; @@ -73,11 +77,11 @@ public class MovementTraverse extends Movement { WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } } - double hardness1 = MovementHelper.getMiningDurationTicks(context, positionsToBreak[0], pb0, true); + double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb0, true); if (hardness1 >= COST_INF) { return COST_INF; } - double hardness2 = MovementHelper.getMiningDurationTicks(context, positionsToBreak[1], pb1, false); + double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb1, false); if (hardness1 == 0 && hardness2 == 0) { if (WC == WALK_ONE_BLOCK_COST && context.canSprint()) { // If there's nothing in the way, and this isn't water or soul sand, and we aren't sneak placing @@ -95,7 +99,7 @@ public class MovementTraverse extends Movement { if (srcDown == Blocks.LADDER || srcDown == Blocks.VINE) { return COST_INF; } - if (destOn.getBlock().equals(Blocks.AIR) || MovementHelper.isReplacable(positionToPlace, destOn)) { + if (destOn.getBlock().equals(Blocks.AIR) || MovementHelper.isReplacable(destX, y - 1, destZ, destOn)) { boolean throughWater = BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock()); if (BlockStateInterface.isWater(destOn.getBlock()) && throughWater) { return COST_INF; @@ -103,15 +107,21 @@ public class MovementTraverse extends Movement { if (!context.hasThrowaway()) { return COST_INF; } + double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb0, false); + if (hardness1 >= COST_INF) { + return COST_INF; + } + double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb1, true); + double WC = throughWater ? WALK_ONE_IN_WATER_COST : WALK_ONE_BLOCK_COST; for (int i = 0; i < 4; i++) { - BlockPos against1 = dest.offset(HORIZONTALS[i]); - if (against1.equals(src)) { + int againstX = destX + HORIZONTALS[i].getXOffset(); + int againstZ = destZ + HORIZONTALS[i].getZOffset(); + if (againstX == x && againstZ == z) { continue; } - against1 = against1.down(); - if (MovementHelper.canPlaceAgainst(against1)) { - return WC + context.placeBlockCost() + getTotalHardnessOfBlocksToBreak(context); + if (MovementHelper.canPlaceAgainst(againstX, y - 1, againstZ)) { + return WC + context.placeBlockCost() + hardness1 + hardness2; } } if (srcDown == Blocks.SOUL_SAND || (srcDown instanceof BlockSlab && !((BlockSlab) srcDown).isDouble())) { @@ -121,7 +131,7 @@ public class MovementTraverse extends Movement { return COST_INF; // this is obviously impossible } WC = WC * SNEAK_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST;//since we are placing, we are sneaking - return WC + context.placeBlockCost() + getTotalHardnessOfBlocksToBreak(context); + return WC + context.placeBlockCost() + hardness1 + hardness2; } return COST_INF; // Out.log("Can't walk on " + Baritone.get(positionsToPlace[0]).getBlock()); diff --git a/src/main/java/baritone/utils/BlockStateInterface.java b/src/main/java/baritone/utils/BlockStateInterface.java index 6e0c7297b..ff55c7d89 100644 --- a/src/main/java/baritone/utils/BlockStateInterface.java +++ b/src/main/java/baritone/utils/BlockStateInterface.java @@ -98,6 +98,9 @@ public class BlockStateInterface implements Helper { return get(pos).getBlock(); } + public static Block getBlock(int x, int y, int z) { + return get(x, y, z).getBlock(); + } /** * Returns whether or not the specified block is diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java index 6cd2655aa..19a33f32f 100644 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java @@ -27,14 +27,9 @@ import baritone.behavior.PathingBehavior; import baritone.cache.ChunkPacker; import baritone.cache.Waypoint; import baritone.cache.WorldProvider; -import baritone.pathing.calc.AStarPathFinder; import baritone.pathing.calc.AbstractNodeCostSearch; import baritone.pathing.goals.*; -import baritone.pathing.movement.ActionCosts; -import baritone.pathing.movement.CalculationContext; -import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; -import baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.Block; import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.entity.Entity; @@ -433,7 +428,8 @@ public class ExampleBaritoneControl extends Behavior implements Helper { event.cancel(); return; } - if (msg.equals("costs")) { + // TODO + /*if (msg.equals("costs")) { Movement[] movements = AStarPathFinder.getConnectedPositions(new BetterBlockPos(playerFeet()), new CalculationContext()); List moves = new ArrayList<>(Arrays.asList(movements)); while (moves.contains(null)) { @@ -451,6 +447,6 @@ public class ExampleBaritoneControl extends Behavior implements Helper { } event.cancel(); return; - } + }*/ } } diff --git a/src/main/java/baritone/utils/pathing/BetterBlockPos.java b/src/main/java/baritone/utils/pathing/BetterBlockPos.java index e46b27dc7..09727b846 100644 --- a/src/main/java/baritone/utils/pathing/BetterBlockPos.java +++ b/src/main/java/baritone/utils/pathing/BetterBlockPos.java @@ -134,4 +134,24 @@ public final class BetterBlockPos extends BlockPos { Vec3i vec = dir.getDirectionVec(); return new BetterBlockPos(x + vec.getX() * dist, y + vec.getY() * dist, z + vec.getZ() * dist); } + + @Override + public BetterBlockPos north() { + return new BetterBlockPos(x, y, z - 1); + } + + @Override + public BetterBlockPos south() { + return new BetterBlockPos(x, y, z + 1); + } + + @Override + public BetterBlockPos east() { + return new BetterBlockPos(x + 1, y, z); + } + + @Override + public BetterBlockPos west() { + return new BetterBlockPos(x - 1, y, z); + } }