001/*
002 * This file is part of Baritone.
003 *
004 * Baritone is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU Lesser General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * Baritone is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public License
015 * along with Baritone.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package baritone.api.pathing.goals;
019
020import baritone.api.BaritoneAPI;
021import baritone.api.utils.BetterBlockPos;
022import baritone.api.utils.SettingsUtil;
023import net.minecraft.util.Mth;
024import net.minecraft.world.phys.Vec3;
025
026/**
027 * Useful for long-range goals that don't have a specific Y level.
028 *
029 * @author leijurv
030 */
031public class GoalXZ implements Goal {
032
033    private static final double SQRT_2 = Math.sqrt(2);
034
035    /**
036     * The X block position of this goal
037     */
038    private final int x;
039
040    /**
041     * The Z block position of this goal
042     */
043    private final int z;
044
045    public GoalXZ(int x, int z) {
046        this.x = x;
047        this.z = z;
048    }
049
050    public GoalXZ(BetterBlockPos pos) {
051        this.x = pos.x;
052        this.z = pos.z;
053    }
054
055    @Override
056    public boolean isInGoal(int x, int y, int z) {
057        return x == this.x && z == this.z;
058    }
059
060    @Override
061    public double heuristic(int x, int y, int z) {//mostly copied from GoalBlock
062        int xDiff = x - this.x;
063        int zDiff = z - this.z;
064        return calculate(xDiff, zDiff);
065    }
066
067    @Override
068    public boolean equals(Object o) {
069        if (this == o) {
070            return true;
071        }
072        if (o == null || getClass() != o.getClass()) {
073            return false;
074        }
075
076        GoalXZ goal = (GoalXZ) o;
077        return x == goal.x && z == goal.z;
078    }
079
080    @Override
081    public int hashCode() {
082        int hash = 1791873246;
083        hash = hash * 222601791 + x;
084        hash = hash * -1331679453 + z;
085        return hash;
086    }
087
088    @Override
089    public String toString() {
090        return String.format(
091                "GoalXZ{x=%s,z=%s}",
092                SettingsUtil.maybeCensor(x),
093                SettingsUtil.maybeCensor(z)
094        );
095    }
096
097    public static double calculate(double xDiff, double zDiff) {
098        //This is a combination of pythagorean and manhattan distance
099        //It takes into account the fact that pathing can either walk diagonally or forwards
100
101        //It's not possible to walk forward 1 and right 2 in sqrt(5) time
102        //It's really 1+sqrt(2) because it'll walk forward 1 then diagonally 1
103        double x = Math.abs(xDiff);
104        double z = Math.abs(zDiff);
105        double straight;
106        double diagonal;
107        if (x < z) {
108            straight = z - x;
109            diagonal = x;
110        } else {
111            straight = x - z;
112            diagonal = z;
113        }
114        diagonal *= SQRT_2;
115        return (diagonal + straight) * BaritoneAPI.getSettings().costHeuristic.value; // big TODO tune
116    }
117
118    public static GoalXZ fromDirection(Vec3 origin, float yaw, double distance) {
119        float theta = (float) Math.toRadians(yaw);
120        double x = origin.x - Mth.sin(theta) * distance;
121        double z = origin.z + Mth.cos(theta) * distance;
122        return new GoalXZ(Mth.floor(x), Mth.floor(z));
123    }
124
125    public int getX() {
126        return x;
127    }
128
129    public int getZ() {
130        return z;
131    }
132}