Files
baritone/src/main/java/baritone/pathing/movement/Movement.java
2018-11-05 20:37:54 -08:00

280 lines
9.1 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.Baritone;
import baritone.api.pathing.movement.IMovement;
import baritone.api.pathing.movement.MovementStatus;
import baritone.api.utils.BetterBlockPos;
import baritone.api.utils.Rotation;
import baritone.api.utils.RotationUtils;
import baritone.api.utils.VecUtils;
import baritone.utils.BlockStateInterface;
import baritone.utils.Helper;
import baritone.utils.InputOverrideHandler;
import net.minecraft.block.BlockLiquid;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.chunk.EmptyChunk;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static baritone.utils.InputOverrideHandler.Input;
public abstract class Movement implements IMovement, Helper, MovementHelper {
protected static final EnumFacing[] HORIZONTALS = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST};
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 boolean didBreakLastTick;
private Double cost;
public List<BlockPos> toBreakCached = null;
public List<BlockPos> toPlaceCached = null;
public List<BlockPos> toWalkIntoCached = null;
private Boolean calculatedWhileLoaded;
protected Movement(BetterBlockPos src, BetterBlockPos dest, BetterBlockPos[] toBreak, BetterBlockPos toPlace) {
this.src = src;
this.dest = dest;
this.positionsToBreak = toBreak;
this.positionToPlace = toPlace;
}
protected Movement(BetterBlockPos src, BetterBlockPos dest, BetterBlockPos[] toBreak) {
this(src, dest, toBreak, null);
}
@Override
public double getCost() {
if (cost == null) {
cost = calculateCost(new CalculationContext());
}
return cost;
}
protected abstract double calculateCost(CalculationContext context);
@Override
public double recalculateCost() {
cost = null;
return getCost();
}
protected void override(double cost) {
this.cost = cost;
}
@Override
public double calculateCostWithoutCaching() {
return calculateCost(new CalculationContext());
}
/**
* Handles the execution of the latest Movement
* State, and offers a Status to the calling class.
*
* @return Status
*/
@Override
public MovementStatus update() {
player().capabilities.isFlying = false;
currentState = updateState(currentState);
if (MovementHelper.isLiquid(playerFeet())) {
currentState.setInput(Input.JUMP, true);
}
if (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.INSTANCE.getLookBehavior().updateTarget(
rotation,
currentState.getTarget().hasToForceRotations()));
// TODO: calculate movement inputs from latestState.getGoal().position
// latestState.getTarget().position.ifPresent(null); NULL CONSUMER REALLY SHOULDN'T BE THE FINAL THING YOU SHOULD REALLY REPLACE THIS WITH ALMOST ACTUALLY ANYTHING ELSE JUST PLEASE DON'T LEAVE IT AS IT IS THANK YOU KANYE
currentState.getInputStates().forEach((input, forced) -> {
Baritone.INSTANCE.getInputOverrideHandler().setInputForceState(input, forced);
});
currentState.getInputStates().replaceAll((input, forced) -> false);
// If the current status indicates a completed movement
if (currentState.getStatus().isComplete()) {
Baritone.INSTANCE.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(blockPos) && !(BlockStateInterface.getBlock(blockPos) instanceof BlockLiquid)) { // can't break liquid, so don't try
somethingInTheWay = true;
Optional<Rotation> reachable = RotationUtils.reachable(player(), blockPos);
if (reachable.isPresent()) {
MovementHelper.switchToBestToolFor(BlockStateInterface.get(blockPos));
state.setTarget(new MovementState.MovementTarget(reachable.get(), true)).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(player().getPositionEyes(1.0F),
VecUtils.getBlockPosCenter(blockPos)), true)
).setInput(InputOverrideHandler.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.
*
* @return
*/
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() {
calculatedWhileLoaded = !(world().getChunk(getDest()) instanceof EmptyChunk);
}
@Override
public boolean calculatedWhileLoaded() {
return calculatedWhileLoaded;
}
@Override
public void resetBlockCache() {
toBreakCached = null;
toPlaceCached = null;
toWalkIntoCached = null;
}
@Override
public List<BlockPos> toBreak() {
if (toBreakCached != null) {
return toBreakCached;
}
List<BlockPos> result = new ArrayList<>();
for (BetterBlockPos positionToBreak : positionsToBreak) {
if (!MovementHelper.canWalkThrough(positionToBreak)) {
result.add(positionToBreak);
}
}
toBreakCached = result;
return result;
}
@Override
public List<BlockPos> toPlace() {
if (toPlaceCached != null) {
return toPlaceCached;
}
List<BlockPos> result = new ArrayList<>();
if (positionToPlace != null && !MovementHelper.canWalkOn(positionToPlace)) {
result.add(positionToPlace);
}
toPlaceCached = result;
return result;
}
@Override
public List<BlockPos> toWalkInto() { // overridden by movementdiagonal
if (toWalkIntoCached == null) {
toWalkIntoCached = new ArrayList<>();
}
return toWalkIntoCached;
}
}