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.utils; 019 020import baritone.api.BaritoneAPI; 021import baritone.api.IBaritone; 022import net.minecraft.block.BlockFire; 023import net.minecraft.block.state.IBlockState; 024import net.minecraft.client.entity.EntityPlayerSP; 025import net.minecraft.entity.Entity; 026import net.minecraft.util.math.*; 027 028import java.util.Optional; 029 030/** 031 * @author Brady 032 * @since 9/25/2018 033 */ 034public final class RotationUtils { 035 036 /** 037 * Constant that a degree value is multiplied by to get the equivalent radian value 038 */ 039 public static final double DEG_TO_RAD = Math.PI / 180.0; 040 041 /** 042 * Constant that a radian value is multiplied by to get the equivalent degree value 043 */ 044 public static final double RAD_TO_DEG = 180.0 / Math.PI; 045 046 /** 047 * Offsets from the root block position to the center of each side. 048 */ 049 private static final Vec3d[] BLOCK_SIDE_MULTIPLIERS = new Vec3d[]{ 050 new Vec3d(0.5, 0, 0.5), // Down 051 new Vec3d(0.5, 1, 0.5), // Up 052 new Vec3d(0.5, 0.5, 0), // North 053 new Vec3d(0.5, 0.5, 1), // South 054 new Vec3d(0, 0.5, 0.5), // West 055 new Vec3d(1, 0.5, 0.5) // East 056 }; 057 058 private RotationUtils() {} 059 060 /** 061 * Calculates the rotation from BlockPos<sub>dest</sub> to BlockPos<sub>orig</sub> 062 * 063 * @param orig The origin position 064 * @param dest The destination position 065 * @return The rotation from the origin to the destination 066 */ 067 public static Rotation calcRotationFromCoords(BlockPos orig, BlockPos dest) { 068 return calcRotationFromVec3d(new Vec3d(orig), new Vec3d(dest)); 069 } 070 071 /** 072 * Wraps the target angles to a relative value from the current angles. This is done by 073 * subtracting the current from the target, normalizing it, and then adding the current 074 * angles back to it. 075 * 076 * @param current The current angles 077 * @param target The target angles 078 * @return The wrapped angles 079 */ 080 public static Rotation wrapAnglesToRelative(Rotation current, Rotation target) { 081 if (current.yawIsReallyClose(target)) { 082 return new Rotation(current.getYaw(), target.getPitch()); 083 } 084 return target.subtract(current).normalize().add(current); 085 } 086 087 /** 088 * Calculates the rotation from Vec<sub>dest</sub> to Vec<sub>orig</sub> and makes the 089 * return value relative to the specified current rotations. 090 * 091 * @param orig The origin position 092 * @param dest The destination position 093 * @param current The current rotations 094 * @return The rotation from the origin to the destination 095 * @see #wrapAnglesToRelative(Rotation, Rotation) 096 */ 097 public static Rotation calcRotationFromVec3d(Vec3d orig, Vec3d dest, Rotation current) { 098 return wrapAnglesToRelative(current, calcRotationFromVec3d(orig, dest)); 099 } 100 101 /** 102 * Calculates the rotation from Vec<sub>dest</sub> to Vec<sub>orig</sub> 103 * 104 * @param orig The origin position 105 * @param dest The destination position 106 * @return The rotation from the origin to the destination 107 */ 108 private static Rotation calcRotationFromVec3d(Vec3d orig, Vec3d dest) { 109 double[] delta = {orig.x - dest.x, orig.y - dest.y, orig.z - dest.z}; 110 double yaw = MathHelper.atan2(delta[0], -delta[2]); 111 double dist = Math.sqrt(delta[0] * delta[0] + delta[2] * delta[2]); 112 double pitch = MathHelper.atan2(delta[1], dist); 113 return new Rotation( 114 (float) (yaw * RAD_TO_DEG), 115 (float) (pitch * RAD_TO_DEG) 116 ); 117 } 118 119 /** 120 * Calculates the look vector for the specified yaw/pitch rotations. 121 * 122 * @param rotation The input rotation 123 * @return Look vector for the rotation 124 */ 125 public static Vec3d calcVec3dFromRotation(Rotation rotation) { 126 float f = MathHelper.cos(-rotation.getYaw() * (float) DEG_TO_RAD - (float) Math.PI); 127 float f1 = MathHelper.sin(-rotation.getYaw() * (float) DEG_TO_RAD - (float) Math.PI); 128 float f2 = -MathHelper.cos(-rotation.getPitch() * (float) DEG_TO_RAD); 129 float f3 = MathHelper.sin(-rotation.getPitch() * (float) DEG_TO_RAD); 130 return new Vec3d((double) (f1 * f2), (double) f3, (double) (f * f2)); 131 } 132 133 /** 134 * @param ctx Context for the viewing entity 135 * @param pos The target block position 136 * @return The optional rotation 137 * @see #reachable(EntityPlayerSP, BlockPos, double) 138 */ 139 public static Optional<Rotation> reachable(IPlayerContext ctx, BlockPos pos) { 140 return reachable(ctx.player(), pos, ctx.playerController().getBlockReachDistance()); 141 } 142 143 public static Optional<Rotation> reachable(IPlayerContext ctx, BlockPos pos, boolean wouldSneak) { 144 return reachable(ctx.player(), pos, ctx.playerController().getBlockReachDistance(), wouldSneak); 145 } 146 147 /** 148 * Determines if the specified entity is able to reach the center of any of the sides 149 * of the specified block. It first checks if the block center is reachable, and if so, 150 * that rotation will be returned. If not, it will return the first center of a given 151 * side that is reachable. The return type will be {@link Optional#empty()} if the entity is 152 * unable to reach any of the sides of the block. 153 * 154 * @param entity The viewing entity 155 * @param pos The target block position 156 * @param blockReachDistance The block reach distance of the entity 157 * @return The optional rotation 158 */ 159 public static Optional<Rotation> reachable(EntityPlayerSP entity, BlockPos pos, double blockReachDistance) { 160 return reachable(entity, pos, blockReachDistance, false); 161 } 162 163 public static Optional<Rotation> reachable(EntityPlayerSP entity, BlockPos pos, double blockReachDistance, boolean wouldSneak) { 164 IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(entity); 165 if (baritone.getPlayerContext().isLookingAt(pos)) { 166 /* 167 * why add 0.0001? 168 * to indicate that we actually have a desired pitch 169 * the way we indicate that the pitch can be whatever and we only care about the yaw 170 * is by setting the desired pitch to the current pitch 171 * setting the desired pitch to the current pitch + 0.0001 means that we do have a desired pitch, it's 172 * just what it currently is 173 * 174 * or if you're a normal person literally all this does it ensure that we don't nudge the pitch to a normal level 175 */ 176 Rotation hypothetical = new Rotation(entity.rotationYaw, entity.rotationPitch + 0.0001F); 177 if (wouldSneak) { 178 // the concern here is: what if we're looking at it now, but as soon as we start sneaking we no longer are 179 RayTraceResult result = RayTraceUtils.rayTraceTowards(entity, hypothetical, blockReachDistance, true); 180 if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK && result.getBlockPos().equals(pos)) { 181 return Optional.of(hypothetical); // yes, if we sneaked we would still be looking at the block 182 } 183 } else { 184 return Optional.of(hypothetical); 185 } 186 } 187 Optional<Rotation> possibleRotation = reachableCenter(entity, pos, blockReachDistance, wouldSneak); 188 //System.out.println("center: " + possibleRotation); 189 if (possibleRotation.isPresent()) { 190 return possibleRotation; 191 } 192 193 IBlockState state = entity.world.getBlockState(pos); 194 AxisAlignedBB aabb = state.getBoundingBox(entity.world, pos); 195 for (Vec3d sideOffset : BLOCK_SIDE_MULTIPLIERS) { 196 double xDiff = aabb.minX * sideOffset.x + aabb.maxX * (1 - sideOffset.x); 197 double yDiff = aabb.minY * sideOffset.y + aabb.maxY * (1 - sideOffset.y); 198 double zDiff = aabb.minZ * sideOffset.z + aabb.maxZ * (1 - sideOffset.z); 199 possibleRotation = reachableOffset(entity, pos, new Vec3d(pos).add(xDiff, yDiff, zDiff), blockReachDistance, wouldSneak); 200 if (possibleRotation.isPresent()) { 201 return possibleRotation; 202 } 203 } 204 return Optional.empty(); 205 } 206 207 /** 208 * Determines if the specified entity is able to reach the specified block with 209 * the given offsetted position. The return type will be {@link Optional#empty()} if 210 * the entity is unable to reach the block with the offset applied. 211 * 212 * @param entity The viewing entity 213 * @param pos The target block position 214 * @param offsetPos The position of the block with the offset applied. 215 * @param blockReachDistance The block reach distance of the entity 216 * @return The optional rotation 217 */ 218 public static Optional<Rotation> reachableOffset(Entity entity, BlockPos pos, Vec3d offsetPos, double blockReachDistance, boolean wouldSneak) { 219 Vec3d eyes = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(entity) : entity.getPositionEyes(1.0F); 220 Rotation rotation = calcRotationFromVec3d(eyes, offsetPos, new Rotation(entity.rotationYaw, entity.rotationPitch)); 221 RayTraceResult result = RayTraceUtils.rayTraceTowards(entity, rotation, blockReachDistance, wouldSneak); 222 //System.out.println(result); 223 if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK) { 224 if (result.getBlockPos().equals(pos)) { 225 return Optional.of(rotation); 226 } 227 if (entity.world.getBlockState(pos).getBlock() instanceof BlockFire && result.getBlockPos().equals(pos.down())) { 228 return Optional.of(rotation); 229 } 230 } 231 return Optional.empty(); 232 } 233 234 /** 235 * Determines if the specified entity is able to reach the specified block where it is 236 * looking at the direct center of it's hitbox. 237 * 238 * @param entity The viewing entity 239 * @param pos The target block position 240 * @param blockReachDistance The block reach distance of the entity 241 * @return The optional rotation 242 */ 243 public static Optional<Rotation> reachableCenter(Entity entity, BlockPos pos, double blockReachDistance, boolean wouldSneak) { 244 return reachableOffset(entity, pos, VecUtils.calculateBlockCenter(entity.world, pos), blockReachDistance, wouldSneak); 245 } 246}