baritone/src/main/java/baritone/pathing/movement/Movement.java

277 lines
9.4 KiB
Java

/*
* 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 <https://www.gnu.org/licenses/>.
*/
package baritone.pathing.movement;
import baritone.api.IBaritone;
import baritone.api.pathing.movement.IMovement;
import baritone.api.pathing.movement.MovementStatus;
import baritone.api.utils.*;
import baritone.api.utils.input.Input;
import baritone.utils.BlockStateInterface;
import net.minecraft.block.BlockLiquid;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
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};
protected final IBaritone baritone;
protected final IPlayerContext ctx;
private MovementState currentState = new MovementState().setStatus(MovementStatus.PREPPING);
protected final BetterBlockPos src;
protected final BetterBlockPos dest;
/**
* The positions that need to be broken before this movement can ensue
*/
protected final BetterBlockPos[] positionsToBreak;
/**
* The position where we need to place a block before this movement can ensue
*/
protected final BetterBlockPos positionToPlace;
private Double cost;
public List<BlockPos> toBreakCached = null;
public List<BlockPos> toPlaceCached = null;
public List<BlockPos> toWalkIntoCached = null;
private Boolean calculatedWhileLoaded;
protected Movement(IBaritone baritone, BetterBlockPos src, BetterBlockPos dest, BetterBlockPos[] toBreak, BetterBlockPos toPlace) {
this.baritone = baritone;
this.ctx = baritone.getPlayerContext();
this.src = src;
this.dest = dest;
this.positionsToBreak = toBreak;
this.positionToPlace = toPlace;
}
protected Movement(IBaritone baritone, BetterBlockPos src, BetterBlockPos dest, BetterBlockPos[] toBreak) {
this(baritone, src, dest, toBreak, null);
}
@Override
public double getCost() {
if (cost == null) {
cost = calculateCost(new CalculationContext(baritone));
}
return cost;
}
public abstract double calculateCost(CalculationContext context);
@Override
public double recalculateCost() {
cost = null;
return getCost();
}
public void override(double cost) {
this.cost = cost;
}
@Override
public double calculateCostWithoutCaching() {
return calculateCost(new CalculationContext(baritone));
}
/**
* Handles the execution of the latest Movement
* State, and offers a Status to the calling class.
*
* @return Status
*/
@Override
public MovementStatus update() {
ctx.player().capabilities.isFlying = false;
currentState = updateState(currentState);
if (MovementHelper.isLiquid(ctx, ctx.playerFeet())) {
currentState.setInput(Input.JUMP, true);
}
if (ctx.player().isEntityInsideOpaqueBlock()) {
currentState.setInput(Input.CLICK_LEFT, true);
}
// If the movement target has to force the new rotations, or we aren't using silent move, then force the rotations
currentState.getTarget().getRotation().ifPresent(rotation ->
baritone.getLookBehavior().updateTarget(
rotation,
currentState.getTarget().hasToForceRotations()));
currentState.getInputStates().forEach((input, forced) -> {
baritone.getInputOverrideHandler().setInputForceState(input, forced);
});
currentState.getInputStates().clear();
// If the current status indicates a completed movement
if (currentState.getStatus().isComplete()) {
baritone.getInputOverrideHandler().clearAllKeys();
}
return currentState.getStatus();
}
protected boolean prepared(MovementState state) {
if (state.getStatus() == MovementStatus.WAITING) {
return true;
}
boolean somethingInTheWay = false;
for (BetterBlockPos blockPos : positionsToBreak) {
if (!MovementHelper.canWalkThrough(ctx, blockPos) && !(BlockStateInterface.getBlock(ctx, blockPos) instanceof BlockLiquid)) { // can't break liquid, so don't try
somethingInTheWay = true;
Optional<Rotation> reachable = RotationUtils.reachable(ctx.player(), blockPos, ctx.playerController().getBlockReachDistance());
if (reachable.isPresent()) {
Rotation rotTowardsBlock = reachable.get();
MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, blockPos));
state.setTarget(new MovementState.MovementTarget(rotTowardsBlock, true));
if (Objects.equals(ctx.getSelectedBlock().orElse(null), blockPos) || ctx.playerRotations().isReallyCloseTo(rotTowardsBlock)) {
state.setInput(Input.CLICK_LEFT, true);
}
return false;
}
//get rekt minecraft
//i'm doing it anyway
//i dont care if theres snow in the way!!!!!!!
//you dont own me!!!!
state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.player().getPositionEyes(1.0F),
VecUtils.getBlockPosCenter(blockPos)), true)
);
// don't check selectedblock on this one, this is a fallback when we can't see any face directly, it's intended to be breaking the "incorrect" block
state.setInput(Input.CLICK_LEFT, true);
return false;
}
}
if (somethingInTheWay) {
// There's a block or blocks that we can't walk through, but we have no target rotation to reach any
// So don't return true, actually set state to unreachable
state.setStatus(MovementStatus.UNREACHABLE);
return true;
}
return true;
}
@Override
public boolean safeToCancel() {
return safeToCancel(currentState);
}
protected boolean safeToCancel(MovementState currentState) {
return true;
}
@Override
public BetterBlockPos getSrc() {
return src;
}
@Override
public BetterBlockPos getDest() {
return dest;
}
@Override
public void reset() {
currentState = new MovementState().setStatus(MovementStatus.PREPPING);
}
/**
* Calculate latest movement state. Gets called once a tick.
*
* @param state The current state
* @return The new state
*/
public MovementState updateState(MovementState state) {
if (!prepared(state)) {
return state.setStatus(MovementStatus.PREPPING);
} else if (state.getStatus() == MovementStatus.PREPPING) {
state.setStatus(MovementStatus.WAITING);
}
if (state.getStatus() == MovementStatus.WAITING) {
state.setStatus(MovementStatus.RUNNING);
}
return state;
}
@Override
public BlockPos getDirection() {
return getDest().subtract(getSrc());
}
public void checkLoadedChunk(CalculationContext context) {
calculatedWhileLoaded = context.bsi.worldContainsLoadedChunk(dest.x, dest.z);
}
@Override
public boolean calculatedWhileLoaded() {
return calculatedWhileLoaded;
}
@Override
public void resetBlockCache() {
toBreakCached = null;
toPlaceCached = null;
toWalkIntoCached = null;
}
public List<BlockPos> toBreak(BlockStateInterface bsi) {
if (toBreakCached != null) {
return toBreakCached;
}
List<BlockPos> result = new ArrayList<>();
for (BetterBlockPos positionToBreak : positionsToBreak) {
if (!MovementHelper.canWalkThrough(bsi, positionToBreak.x, positionToBreak.y, positionToBreak.z)) {
result.add(positionToBreak);
}
}
toBreakCached = result;
return result;
}
public List<BlockPos> toPlace(BlockStateInterface bsi) {
if (toPlaceCached != null) {
return toPlaceCached;
}
List<BlockPos> result = new ArrayList<>();
if (positionToPlace != null && !MovementHelper.canWalkOn(bsi, positionToPlace.x, positionToPlace.y, positionToPlace.z)) {
result.add(positionToPlace);
}
toPlaceCached = result;
return result;
}
public List<BlockPos> toWalkInto(BlockStateInterface bsi) { // overridden by movementdiagonal
if (toWalkIntoCached == null) {
toWalkIntoCached = new ArrayList<>();
}
return toWalkIntoCached;
}
}