From 27603549433a28fc3eb8818868372ac4cfb9197f Mon Sep 17 00:00:00 2001 From: Leijurv Date: Tue, 4 Dec 2018 18:12:04 -0800 Subject: [PATCH] cool vine and ladder descending, fixes #159 --- .../pathing/calc/AStarPathFinder.java | 2 +- .../java/baritone/pathing/calc/PathNode.java | 3 ++ .../movement/movements/MovementDescend.java | 50 +++++++++++++------ .../movement/movements/MovementFall.java | 48 ++++++++++++++++-- 4 files changed, 82 insertions(+), 21 deletions(-) diff --git a/src/main/java/baritone/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/pathing/calc/AStarPathFinder.java index 4b57abf1..bbc0738a 100644 --- a/src/main/java/baritone/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/pathing/calc/AStarPathFinder.java @@ -121,7 +121,7 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel if (actionCost >= ActionCosts.COST_INF) { continue; } - if (actionCost <= 0) { + if (actionCost <= 0 || Double.isNaN(actionCost)) { throw new IllegalStateException(moves + " calculated implausible cost " + actionCost); } if (moves.dynamicXZ && !worldBorder.entirelyContains(res.x, res.z)) { // see issue #218 diff --git a/src/main/java/baritone/pathing/calc/PathNode.java b/src/main/java/baritone/pathing/calc/PathNode.java index e12a2458..dc2dbf90 100644 --- a/src/main/java/baritone/pathing/calc/PathNode.java +++ b/src/main/java/baritone/pathing/calc/PathNode.java @@ -73,6 +73,9 @@ public final class PathNode { this.previous = null; this.cost = ActionCosts.COST_INF; this.estimatedCostToGoal = goal.heuristic(x, y, z); + if (Double.isNaN(estimatedCostToGoal)) { + throw new IllegalStateException(goal + " calculated implausible heuristic"); + } this.isOpen = false; this.x = x; this.y = y; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index 68ebbb66..fc61a1eb 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -115,66 +115,84 @@ public class MovementDescend extends Movement { res.cost = totalCost; } - public static void dynamicFallCost(CalculationContext context, int x, int y, int z, int destX, int destZ, double frontBreak, IBlockState below, MutableMoveResult res) { + public static boolean dynamicFallCost(CalculationContext context, int x, int y, int z, int destX, int destZ, double frontBreak, IBlockState below, MutableMoveResult res) { if (frontBreak != 0 && context.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; + return false; } if (!MovementHelper.canWalkThrough(context.bsi(), destX, y - 2, destZ, below) && below.getBlock() != Blocks.WATER) { - return; + return false; } + double costSoFar = 0; + int effectiveStartHeight = y; 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; + return false; } IBlockState ontoBlock = context.get(destX, newY, destZ); - double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[fallHeight] + frontBreak; - if (ontoBlock.getBlock() == Blocks.WATER && !MovementHelper.isFlowing(ontoBlock) && context.getBlock(destX, newY + 1, destZ) != Blocks.WATERLILY) { // TODO flowing check required here? + int unprotectedFallHeight = fallHeight - (y - effectiveStartHeight); // equal to fallHeight - y + effectiveFallHeight, which is equal to -newY + effectiveFallHeight, which is equal to effectiveFallHeight - newY + double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[unprotectedFallHeight] + frontBreak + costSoFar; + if (ontoBlock.getBlock() == Blocks.WATER && context.getBlock(destX, newY + 1, destZ) != Blocks.WATERLILY) { // lilypads are canWalkThrough, but we can't end a fall that should be broken by water if it's covered by a lilypad // however, don't return impossible in the lilypad scenario, because we could still jump right on it (water that's below a lilypad is canWalkOn so it works) if (context.assumeWalkOnWater()) { - return; // TODO fix + return false; // TODO fix + } + if (MovementHelper.isFlowing(ontoBlock)) { + return false; // TODO flowing check required here? + } + if (!MovementHelper.canWalkOn(context.bsi(), destX, newY - 1, destZ)) { + // we could punch right through the water into something else + return false; } // found a fall into water res.x = destX; res.y = newY; res.z = destZ; res.cost = tentativeCost;// TODO incorporate water swim up cost? - return; + return false; } if (ontoBlock.getBlock() == Blocks.FLOWING_WATER) { - return; + return false; + } + if (unprotectedFallHeight <= 11 && (ontoBlock.getBlock() == Blocks.VINE || ontoBlock.getBlock() == Blocks.LADDER)) { + // if fall height is greater than or equal to 11, we don't actually grab on to vines or ladders. the more you know + // this effectively "resets" our falling speed + costSoFar += FALL_N_BLOCKS_COST[unprotectedFallHeight - 1];// we fall until the top of this block (not including this block) + costSoFar += LADDER_DOWN_ONE_COST; + effectiveStartHeight = newY; + continue; } if (MovementHelper.canWalkThrough(context.bsi(), destX, newY, destZ, ontoBlock)) { continue; } if (!MovementHelper.canWalkOn(context.bsi(), destX, newY, destZ, ontoBlock)) { - return; + return false; } if (MovementHelper.isBottomSlab(ontoBlock)) { - return; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect + return false; // 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) { + if (context.hasWaterBucket() && unprotectedFallHeight <= context.maxFallHeightBucket() + 1) { res.x = destX; res.y = newY + 1;// this is the block we're falling onto, so dest is +1 res.z = destZ; res.cost = tentativeCost + context.placeBlockCost(); - return; + return true; } - if (fallHeight <= context.maxFallHeightNoWater() + 1) { + if (unprotectedFallHeight <= context.maxFallHeightNoWater() + 1) { // fallHeight = 4 means onto.up() is 3 blocks down, which is the max res.x = destX; res.y = newY + 1; res.z = destZ; res.cost = tentativeCost; - return; + return false; } else { - return; + return false; } } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementFall.java b/src/main/java/baritone/pathing/movement/movements/MovementFall.java index ef89919b..90b44c14 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementFall.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementFall.java @@ -17,7 +17,6 @@ package baritone.pathing.movement.movements; -import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; @@ -31,12 +30,19 @@ import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementState; import baritone.pathing.movement.MovementState.MovementTarget; import baritone.utils.pathing.MutableMoveResult; +import net.minecraft.block.BlockLadder; +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.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Vec3i; + +import java.util.Optional; public class MovementFall extends Movement { @@ -57,6 +63,12 @@ public class MovementFall extends Movement { return result.cost; } + private boolean willPlaceBucket() { + CalculationContext context = new CalculationContext(baritone); + MutableMoveResult result = new MutableMoveResult(); + return MovementDescend.dynamicFallCost(context, src.x, src.y, src.z, dest.x, dest.z, 0, context.get(dest.x, src.y - 2, dest.z), result); + } + @Override public MovementState updateState(MovementState state) { super.updateState(state); @@ -67,7 +79,7 @@ public class MovementFall extends Movement { BlockPos playerFeet = ctx.playerFeet(); Rotation toDest = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest)); Rotation targetRotation = null; - if (!MovementHelper.isWater(ctx, dest) && src.getY() - dest.getY() > Baritone.settings().maxFallHeightNoWater.get() && !playerFeet.equals(dest)) { + if (!MovementHelper.isWater(ctx, dest) && willPlaceBucket() && !playerFeet.equals(dest)) { if (!InventoryPlayer.isHotbar(ctx.player().inventory.getSlotFor(STACK_BUCKET_WATER)) || ctx.world().provider.isNether()) { return state.setStatus(MovementStatus.UNREACHABLE); } @@ -78,7 +90,7 @@ public class MovementFall extends Movement { targetRotation = new Rotation(toDest.getYaw(), 90.0F); RayTraceResult trace = ctx.objectMouseOver(); - if (trace != null && trace.typeOfHit == RayTraceResult.Type.BLOCK && ctx.player().rotationPitch > 89.0F) { + if (trace != null && trace.typeOfHit == RayTraceResult.Type.BLOCK && (trace.getBlockPos().equals(dest) || trace.getBlockPos().equals(dest.down()))) { state.setInput(Input.CLICK_RIGHT, true); } } @@ -107,12 +119,40 @@ public class MovementFall extends Movement { } } Vec3d destCenter = VecUtils.getBlockPosCenter(dest); // we are moving to the 0.5 center not the edge (like if we were falling on a ladder) - if (Math.abs(ctx.player().posX - destCenter.x) > 0.15 || Math.abs(ctx.player().posZ - destCenter.z) > 0.15) { + if (Math.abs(ctx.player().posX + ctx.player().motionX - destCenter.x) > 0.1 || Math.abs(ctx.player().posZ + ctx.player().motionZ - destCenter.z) > 0.1) { + if (!ctx.player().onGround && Math.abs(ctx.player().motionY) > 0.4) { + state.setInput(Input.SNEAK, true); + } state.setInput(Input.MOVE_FORWARD, true); } + Vec3i avoid = Optional.ofNullable(avoid()).map(EnumFacing::getDirectionVec).orElse(null); + if (avoid == null) { + avoid = src.subtract(dest); + } else { + double dist = Math.abs(avoid.getX() * (destCenter.x - avoid.getX() / 2.0 - ctx.player().posX)) + Math.abs(avoid.getZ() * (destCenter.z - avoid.getZ() / 2.0 - ctx.player().posZ)); + if (dist < 0.6) { + state.setInput(Input.MOVE_FORWARD, true); + } else { + state.setInput(Input.SNEAK, false); + } + } + if (targetRotation == null) { + Vec3d destCenterOffset = new Vec3d(destCenter.x + 0.125 * avoid.getX(), destCenter.y, destCenter.z + 0.125 * avoid.getZ()); + state.setTarget(new MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), destCenterOffset), false)); + } return state; } + private EnumFacing avoid() { + for (int i = 0; i < 15; i++) { + IBlockState state = ctx.world().getBlockState(ctx.playerFeet().down(i)); + if (state.getBlock() == Blocks.LADDER) { + return state.getValue(BlockLadder.FACING); + } + } + return null; + } + @Override public boolean safeToCancel(MovementState state) { // if we haven't started walking off the edge yet, or if we're in the process of breaking blocks before doing the fall