diff --git a/src/main/java/baritone/command/defaults/ElytraCommand.java b/src/main/java/baritone/command/defaults/ElytraCommand.java index eea7f3f33..3232ecb03 100644 --- a/src/main/java/baritone/command/defaults/ElytraCommand.java +++ b/src/main/java/baritone/command/defaults/ElytraCommand.java @@ -23,11 +23,8 @@ import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.pathing.goals.Goal; -import baritone.api.pathing.goals.GoalBlock; -import baritone.api.pathing.goals.GoalXZ; import baritone.api.process.ICustomGoalProcess; import baritone.api.process.IElytraProcess; -import net.minecraft.util.math.BlockPos; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/baritone/process/ElytraProcess.java b/src/main/java/baritone/process/ElytraProcess.java index c56ab9896..ff082fada 100644 --- a/src/main/java/baritone/process/ElytraProcess.java +++ b/src/main/java/baritone/process/ElytraProcess.java @@ -42,14 +42,21 @@ import baritone.process.elytra.NullElytraProcess; import baritone.utils.BaritoneProcessHelper; import baritone.utils.PathingCommandContext; import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; + import static baritone.api.pathing.movement.ActionCosts.COST_INF; public class ElytraProcess extends BaritoneProcessHelper implements IBaritoneProcess, IElytraProcess, AbstractGameEventListener { public State state; + private boolean goingToLandingSpot; private Goal goal; private LegacyElytraBehavior behavior; @@ -93,8 +100,8 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } - if (ctx.player().isElytraFlying()) { - final BetterBlockPos last = behavior.pathManager.path.getLast(); + if (ctx.player().isElytraFlying() && this.state != State.LANDING) { + final BetterBlockPos last = this.behavior.pathManager.path.getLast(); if (last != null && ctx.player().getDistanceSqToCenter(last) < (5 * 5)) { if (Baritone.settings().notificationOnPathComplete.value) { logNotification("Pathing complete", false); @@ -105,6 +112,14 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro ctx.world().sendQuittingDisconnectingPacket(); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } + if (!goingToLandingSpot) { + BlockPos landingSpot = findSafeLandingSpot(); + if (landingSpot != null) { + this.pathTo(landingSpot); + this.goingToLandingSpot = true; + return this.onTick(calcFailed, isSafeToCancel); + } + } this.state = State.LANDING; } } @@ -208,6 +223,7 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro @Override public void onLostControl() { this.goal = null; + this.goingToLandingSpot = false; this.state = State.START_FLYING; // TODO: null state? if (this.behavior != null) { this.behavior.destroy(); @@ -234,6 +250,7 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro @Override public void pathTo(BlockPos destination) { + this.onLostControl(); this.behavior = new LegacyElytraBehavior(this.baritone, this, destination); if (ctx.world() != null) { this.behavior.repackChunks(); @@ -253,7 +270,6 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro public enum State { LOCATE_JUMP("Finding spot to jump off"), - VALIDATE_PATH("Validating path"), PAUSE("Waiting for elytra path"), GET_TO_JUMP("Walking to takeoff"), START_FLYING("Begin flying"), @@ -329,4 +345,46 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro return COST_INF; } } + + private static boolean isInBounds(BlockPos pos) { + return pos.getY() >= 0 && pos.getY() < 128; + } + + private boolean isSafeLandingSpot(BlockPos pos) { + BlockPos.MutableBlockPos mut = new BlockPos.MutableBlockPos(pos); + while (mut.getY() >= 0) { + IBlockState state = ctx.world().getBlockState(mut); + if (state.getMaterial().isLiquid()) { // lava + return false; + } + if (state.getMaterial().blocksMovement() && state.getBlock() != Blocks.MAGMA) { + return true; + } + mut.setPos(mut.getX(), mut.getY() - 1, mut.getZ()); + } + return false; // void + } + + private BlockPos findSafeLandingSpot() { + final BlockPos start = new BlockPos(ctx.playerFeet()); + Queue queue = new LinkedList<>(); + Set visited = new HashSet<>(); + queue.add(start); + + while (!queue.isEmpty()) { + BlockPos pos = queue.poll(); + if (ctx.world().isBlockLoaded(pos) && isInBounds(pos) && ctx.world().getBlockState(pos).getBlock() == Blocks.AIR) { + if (isSafeLandingSpot(pos)) { + return pos; + } + if (visited.add(pos.north())) queue.add(pos.north()); + if (visited.add(pos.east())) queue.add(pos.east()); + if (visited.add(pos.south())) queue.add(pos.south()); + if (visited.add(pos.west())) queue.add(pos.west()); + if (visited.add(pos.up())) queue.add(pos.up()); + if (visited.add(pos.down())) queue.add(pos.down()); + } + } + return null; + } }