diff --git a/FEATURES.md b/FEATURES.md index 998b546a7..98bae9e22 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -9,6 +9,7 @@ - **Slabs and stairs** - **Falling blocks** Baritone understands the costs of breaking blocks with falling blocks on top, and includes all of their break costs. Additionally, since it avoids breaking any blocks touching a liquid, it won't break the bottom of a gravel stack below a lava lake (anymore). - **Avoiding dangerous blocks** Obviously, it knows not to walk through fire or on magma, not to corner over lava (that deals some damage), not to break any blocks touching a liquid (it might drown), etc. +- **Parkour** Sprint jumping over 1, 2, or 3 block gaps # Pathing method Baritone uses a modified version of A*. @@ -41,8 +42,7 @@ Things it doesn't have yet See issues for more. Things it may not ever have, from most likely to least likely =( -- Parkour (jumping over gaps of any length) -- Boats - Pigs +- Boats - Horses (2x3 path instead of 1x2) - Elytra diff --git a/src/main/java/baritone/Settings.java b/src/main/java/baritone/Settings.java index 1eed43334..5bb575495 100644 --- a/src/main/java/baritone/Settings.java +++ b/src/main/java/baritone/Settings.java @@ -83,6 +83,11 @@ public class Settings { */ public Setting allowWalkOnBottomSlab = new Setting<>(true); + /** + * You know what it is + */ + public Setting allowParkour = new Setting<>(true); // disable in release because its sketchy af lol + /** * For example, if you have Mining Fatigue or Haste, adjust the costs of breaking blocks accordingly. */ diff --git a/src/main/java/baritone/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/pathing/calc/AStarPathFinder.java index 73442998f..893d9686d 100644 --- a/src/main/java/baritone/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/pathing/calc/AStarPathFinder.java @@ -233,7 +233,11 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { new MovementDiagonal(pos, EnumFacing.NORTH, EnumFacing.EAST), new MovementDiagonal(pos, EnumFacing.SOUTH, EnumFacing.WEST), new MovementDiagonal(pos, EnumFacing.SOUTH, EnumFacing.EAST), - new MovementPillar(pos, new BetterBlockPos(x, y + 1, z)) + new MovementPillar(pos, new BetterBlockPos(x, y + 1, z)), + MovementParkour.calculate(pos, EnumFacing.NORTH), + MovementParkour.calculate(pos, EnumFacing.SOUTH), + MovementParkour.calculate(pos, EnumFacing.EAST), + MovementParkour.calculate(pos, EnumFacing.WEST), }; } diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index 5eba39aba..76871934d 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -84,6 +84,10 @@ public abstract class Movement implements Helper, MovementHelper { return getCost(null); } + protected void override(double cost) { + this.cost = cost; + } + public double calculateCostWithoutCaching() { return calculateCost(new CalculationContext()); } diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index 6897d5a4d..7c9a8c36f 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -70,6 +70,9 @@ public interface MovementHelper extends ActionCosts, Helper { static boolean canWalkThrough(BlockPos pos, IBlockState state) { Block block = state.getBlock(); + if (block == Blocks.AIR) { + return true; + } if (block instanceof BlockFire || block instanceof BlockTripWire || block instanceof BlockWeb @@ -106,6 +109,37 @@ public interface MovementHelper extends ActionCosts, Helper { return block.isPassable(mc.world, pos); } + /** + * canWalkThrough but also won't impede movement at all. so not including doors or fence gates (we'd have to right click), + * not including water, and not including ladders or vines or cobwebs (they slow us down) + * + * @return + */ + static boolean fullyPassable(BlockPos pos) { + return fullyPassable(pos, BlockStateInterface.get(pos)); + } + + static boolean fullyPassable(BlockPos pos, IBlockState state) { + Block block = state.getBlock(); + if (block == Blocks.AIR) { + return true; + } + if (block == Blocks.FIRE + || block == Blocks.TRIPWIRE + || block == Blocks.WEB + || block == Blocks.VINE + || block == Blocks.LADDER + || block instanceof BlockDoor + || block instanceof BlockFenceGate + || block instanceof BlockSnow + || block instanceof BlockLiquid + || block instanceof BlockTrapDoor + || block instanceof BlockEndPortal) { + return false; + } + return block.isPassable(mc.world, pos); + } + static boolean isReplacable(BlockPos pos, 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 diff --git a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java new file mode 100644 index 000000000..3749e6ec2 --- /dev/null +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -0,0 +1,151 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Baritone. If not, see . + */ + +package baritone.pathing.movement.movements; + +import baritone.Baritone; +import baritone.pathing.movement.CalculationContext; +import baritone.pathing.movement.Movement; +import baritone.pathing.movement.MovementHelper; +import baritone.pathing.movement.MovementState; +import baritone.utils.BlockStateInterface; +import baritone.utils.InputOverrideHandler; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; + +public class MovementParkour extends Movement { + + final EnumFacing direction; + final int dist; + + private MovementParkour(BlockPos src, int dist, EnumFacing dir) { + super(src, src.offset(dir, dist), new BlockPos[]{}); + this.direction = dir; + this.dist = dist; + super.override(costFromJumpDistance(dist)); + } + + public static MovementParkour calculate(BlockPos src, EnumFacing dir) { + if (!Baritone.settings().allowParkour.get()) { + return null; + } + IBlockState standingOn = BlockStateInterface.get(src.down()); + if (standingOn.getBlock() == Blocks.VINE || standingOn.getBlock() == Blocks.LADDER || MovementHelper.isBottomSlab(standingOn)) { + return null; + } + BlockPos adjBlock = src.down().offset(dir); + IBlockState adj = BlockStateInterface.get(adjBlock); + if (MovementHelper.avoidWalkingInto(adj.getBlock())) { // magma sucks + return null; + } + if (MovementHelper.canWalkOn(adjBlock, adj)) { // don't parkour if we could just traverse (for now) + return null; + } + + if (!MovementHelper.fullyPassable(src.offset(dir))) { + return null; + } + if (!MovementHelper.fullyPassable(src.up().offset(dir))) { + return null; + } + for (int i = 2; i <= 4; i++) { + BlockPos dest = src.offset(dir, i); + if (!MovementHelper.fullyPassable(dest)) { + return null; + } + if (!MovementHelper.fullyPassable(dest.up())) { + return null; + } + if (MovementHelper.canWalkOn(dest.down())) { + return new MovementParkour(src, i, dir); + } + } + return null; + } + + private static double costFromJumpDistance(int dist) { + switch (dist) { + case 2: + return WALK_ONE_BLOCK_COST * 2; // IDK LOL + case 3: + return WALK_ONE_BLOCK_COST * 3; + case 4: + return SPRINT_ONE_BLOCK_COST * 3; + } + throw new IllegalStateException("LOL"); + } + + + @Override + protected double calculateCost(CalculationContext context) { + if (!MovementHelper.canWalkOn(dest.down())) { + return COST_INF; + } + if (MovementHelper.avoidWalkingInto(BlockStateInterface.get(src.down().offset(direction)).getBlock())) { + return COST_INF; + } + for (int i = 1; i <= 4; i++) { + BlockPos d = src.offset(direction, i); + if (!MovementHelper.fullyPassable(d)) { + return COST_INF; + } + if (!MovementHelper.fullyPassable(d.up())) { + return COST_INF; + } + if (d.equals(dest)) { + return costFromJumpDistance(i); + } + } + throw new IllegalStateException("invalid jump distance?"); + } + + @Override + public MovementState updateState(MovementState state) { + super.updateState(state); + switch (state.getStatus()) { + case WAITING: + state.setStatus(MovementState.MovementStatus.RUNNING); + case RUNNING: + break; + default: + return state; + } + if (dist >= 4) { + state.setInput(InputOverrideHandler.Input.SPRINT, true); + } + MovementHelper.moveTowards(state, dest); + if (playerFeet().equals(dest)) { + if (player().posY - playerFeet().getY() < 0.01) { + state.setStatus(MovementState.MovementStatus.SUCCESS); + } + } else if (!playerFeet().equals(src)) { + if (playerFeet().equals(src.offset(direction)) || player().posY - playerFeet().getY() > 0.0001) { + state.setInput(InputOverrideHandler.Input.JUMP, true); + } else { + state.setInput(InputOverrideHandler.Input.SPRINT, false); + if (playerFeet().equals(src.offset(direction, -1))) { + MovementHelper.moveTowards(state, src); + } else { + MovementHelper.moveTowards(state, src.offset(direction, -1)); + } + } + } + return state; + } +} \ No newline at end of file diff --git a/src/main/java/baritone/utils/pathing/BetterBlockPos.java b/src/main/java/baritone/utils/pathing/BetterBlockPos.java index ec8891f4d..6a05c109a 100644 --- a/src/main/java/baritone/utils/pathing/BetterBlockPos.java +++ b/src/main/java/baritone/utils/pathing/BetterBlockPos.java @@ -119,4 +119,10 @@ public final class BetterBlockPos extends BlockPos { Vec3i vec = dir.getDirectionVec(); return new BetterBlockPos(x + vec.getX(), y + vec.getY(), z + vec.getZ()); } + + @Override + public BlockPos offset(EnumFacing dir, int dist) { + Vec3i vec = dir.getDirectionVec(); + return new BetterBlockPos(x + vec.getX() * dist, y + vec.getY() * dist, z + vec.getZ() * dist); + } }