aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de')
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java55
-rw-r--r--src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java24
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/CameraMixin.java22
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java22
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/PingMeasurerMixin.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/SmoothAOTE.java483
6 files changed, 608 insertions, 0 deletions
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java
index 6d18c451..61540171 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java
@@ -398,6 +398,61 @@ public class UIAndVisualsCategory {
.build())
.build())
+ //Smooth AOTE
+ .group(OptionGroup.createBuilder()
+ .name(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.@Tooltip")))
+ .collapsed(true)
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.enableWeirdTransmission"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.enableWeirdTransmission.@Tooltip")))
+ .binding(defaults.uiAndVisuals.smoothAOTE.enableWeirdTransmission,
+ () -> config.uiAndVisuals.smoothAOTE.enableWeirdTransmission,
+ newValue -> config.uiAndVisuals.smoothAOTE.enableWeirdTransmission = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.enableInstantTransmission"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.enableInstantTransmission.@Tooltip")))
+ .binding(defaults.uiAndVisuals.smoothAOTE.enableInstantTransmission,
+ () -> config.uiAndVisuals.smoothAOTE.enableInstantTransmission,
+ newValue -> config.uiAndVisuals.smoothAOTE.enableInstantTransmission = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.enableEtherTransmission"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.enableEtherTransmission.@Tooltip")))
+ .binding(defaults.uiAndVisuals.smoothAOTE.enableEtherTransmission,
+ () -> config.uiAndVisuals.smoothAOTE.enableEtherTransmission,
+ newValue -> config.uiAndVisuals.smoothAOTE.enableEtherTransmission = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.enableSinrecallTransmission"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.enableSinrecallTransmission.@Tooltip")))
+ .binding(defaults.uiAndVisuals.smoothAOTE.enableSinrecallTransmission,
+ () -> config.uiAndVisuals.smoothAOTE.enableSinrecallTransmission,
+ newValue -> config.uiAndVisuals.smoothAOTE.enableSinrecallTransmission = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.enableWitherImpact"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.enableWitherImpact.@Tooltip")))
+ .binding(defaults.uiAndVisuals.smoothAOTE.enableWitherImpact,
+ () -> config.uiAndVisuals.smoothAOTE.enableWitherImpact,
+ newValue -> config.uiAndVisuals.smoothAOTE.enableWitherImpact = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Integer>createBuilder()
+ .name(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.maximumAddedLag"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.smoothAOTE.maximumAddedLag.@Tooltip")))
+ .binding(defaults.uiAndVisuals.smoothAOTE.maximumAddedLag,
+ () -> config.uiAndVisuals.smoothAOTE.maximumAddedLag,
+ newValue -> config.uiAndVisuals.smoothAOTE.maximumAddedLag = newValue)
+ .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(0, 500).step(1))
+ .build())
+ .build())
+
//Search overlay
.group(OptionGroup.createBuilder()
.name(Text.translatable("skyblocker.config.uiAndVisuals.searchOverlay"))
diff --git a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java
index 7a6b3f5d..ffe0dcce 100644
--- a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java
@@ -71,6 +71,9 @@ public class UIAndVisualsConfig {
public TeleportOverlay teleportOverlay = new TeleportOverlay();
@SerialEntry
+ public SmoothAOTE smoothAOTE = new SmoothAOTE();
+
+ @SerialEntry
public SearchOverlay searchOverlay = new SearchOverlay();
@SerialEntry
@@ -328,6 +331,27 @@ public class UIAndVisualsConfig {
public boolean enableWitherImpact = true;
}
+ public static class SmoothAOTE {
+
+ @SerialEntry
+ public boolean enableWeirdTransmission = false;
+
+ @SerialEntry
+ public boolean enableInstantTransmission = false;
+
+ @SerialEntry
+ public boolean enableEtherTransmission = false;
+
+ @SerialEntry
+ public boolean enableSinrecallTransmission = false;
+
+ @SerialEntry
+ public boolean enableWitherImpact = false;
+
+ @SerialEntry
+ public int maximumAddedLag = 100;
+ }
+
public static class SearchOverlay {
@SerialEntry
public boolean enableBazaar = true;
diff --git a/src/main/java/de/hysky/skyblocker/mixins/CameraMixin.java b/src/main/java/de/hysky/skyblocker/mixins/CameraMixin.java
new file mode 100644
index 00000000..78d9e547
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/mixins/CameraMixin.java
@@ -0,0 +1,22 @@
+package de.hysky.skyblocker.mixins;
+
+import com.llamalad7.mixinextras.injector.ModifyReturnValue;
+import de.hysky.skyblocker.skyblock.SmoothAOTE;
+import net.minecraft.client.render.Camera;
+import net.minecraft.util.math.Vec3d;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+
+@Mixin(Camera.class)
+public class CameraMixin {
+
+ @ModifyReturnValue(method = "getPos", at = @At("RETURN"))
+ private Vec3d skyblocker$onCameraUpdate(Vec3d original) {
+ Vec3d pos = SmoothAOTE.getInterpolatedPos();
+ if (pos != null) {
+ return pos;
+ }
+
+ return original;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java
index cd7c6ba3..75604f72 100644
--- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java
@@ -5,8 +5,10 @@ import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
import com.llamalad7.mixinextras.sugar.Local;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.config.configs.SlayersConfig;
+import de.hysky.skyblocker.config.configs.UIAndVisualsConfig;
import de.hysky.skyblocker.skyblock.CompactDamage;
import de.hysky.skyblocker.skyblock.FishingHelper;
+import de.hysky.skyblocker.skyblock.SmoothAOTE;
import de.hysky.skyblocker.skyblock.chocolatefactory.EggFinder;
import de.hysky.skyblocker.skyblock.crimson.dojo.DojoManager;
import de.hysky.skyblocker.skyblock.dungeon.DungeonScore;
@@ -40,6 +42,9 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+/**
+ * All mixins in this file should be arranged in the order of the methods they inject into.
+ */
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerMixin {
@Shadow
@@ -73,6 +78,12 @@ public abstract class ClientPlayNetworkHandlerMixin {
}
}
+ @Inject(method = "onPlayerPositionLook", at = @At("TAIL"))
+ private void onPlayerTeleported(PlayerPositionLookS2CPacket packet, CallbackInfo ci) {
+ //player has been teleported by the server tell the smooth AOTE this
+ SmoothAOTE.playerTeleported();
+ }
+
@ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V", ordinal = 0))
private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity) {
DungeonManager.onItemPickup(itemEntity);
@@ -150,4 +161,15 @@ public abstract class ClientPlayNetworkHandlerMixin {
EnderNodes.onParticle(packet);
WishingCompassSolver.onParticle(packet);
}
+
+ @ModifyExpressionValue(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/DebugHud;shouldShowPacketSizeAndPingCharts()Z"))
+ private boolean shouldShowPacketSizeAndPingCharts(boolean original) {
+ //make the f3+3 screen always send ping packets even when closed
+ //this is needed to make smooth AOTE work so check if its enabled
+ UIAndVisualsConfig.SmoothAOTE options = SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE;
+ if (Utils.isOnSkyblock() && !SmoothAOTE.teleportDisabled && (options.enableWeirdTransmission || options.enableEtherTransmission || options.enableInstantTransmission || options.enableSinrecallTransmission || options.enableWitherImpact)) {
+ return true;
+ }
+ return original;
+ }
}
diff --git a/src/main/java/de/hysky/skyblocker/mixins/PingMeasurerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/PingMeasurerMixin.java
index 32739686..b09abc64 100644
--- a/src/main/java/de/hysky/skyblocker/mixins/PingMeasurerMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixins/PingMeasurerMixin.java
@@ -1,5 +1,6 @@
package de.hysky.skyblocker.mixins;
+import de.hysky.skyblocker.skyblock.SmoothAOTE;
import de.hysky.skyblocker.skyblock.crimson.dojo.DojoManager;
import de.hysky.skyblocker.utils.Utils;
import net.minecraft.client.network.PingMeasurer;
@@ -15,6 +16,7 @@ public class PingMeasurerMixin {
if (Utils.isInCrimson()) {
DojoManager.onPingResult(ping);
}
+ SmoothAOTE.updatePing(ping);
return ping;
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/SmoothAOTE.java b/src/main/java/de/hysky/skyblocker/skyblock/SmoothAOTE.java
new file mode 100644
index 00000000..73def44c
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/SmoothAOTE.java
@@ -0,0 +1,483 @@
+package de.hysky.skyblocker.skyblock;
+
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.annotations.Init;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.dungeon.DungeonBoss;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
+import de.hysky.skyblocker.utils.ItemUtils;
+import de.hysky.skyblocker.utils.Location;
+import de.hysky.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.event.player.UseBlockCallback;
+import net.fabricmc.fabric.api.event.player.UseItemCallback;
+import net.minecraft.block.*;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.option.Perspective;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.state.property.Properties;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.Hand;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.util.shape.VoxelShape;
+import net.minecraft.world.World;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SmoothAOTE {
+
+ private static final MinecraftClient CLIENT = MinecraftClient.getInstance();
+
+ private static final Pattern MANA_LORE = Pattern.compile("Mana Cost: (\\d+)");
+ private static final long MAX_TELEPORT_TIME = 2500; //2.5 seconds
+
+ private static long startTime;
+ private static Vec3d startPos;
+ private static Vec3d cameraStartPos;
+ private static Vec3d teleportVector;
+ private static long lastPing;
+ private static long currentTeleportPing;
+ private static int teleportsAhead;
+ private static long lastTeleportTime;
+ public static boolean teleportDisabled;
+
+ @Init
+ public static void init() {
+ UseItemCallback.EVENT.register(SmoothAOTE::onItemInteract);
+ UseBlockCallback.EVENT.register(SmoothAOTE::onBlockInteract);
+ }
+
+ /**
+ * When a player receives a teleport packet finish a teleport
+ */
+ public static void playerTeleported() {
+ //the player has been teleported so 1 less teleport ahead
+ teleportsAhead = Math.max(0, teleportsAhead - 1);
+ //re-enable the animation if the player is teleported as this means they can teleport again. and reset timer for last teleport update
+ lastTeleportTime = System.currentTimeMillis();
+ teleportDisabled = false;
+
+ //if the server is in sync in number of teleports
+ if (teleportsAhead == 0) {
+ //see if the teleport has a small amount left to continue animating instead of jumping to the end
+ long timeLeft = (currentTeleportPing - (System.currentTimeMillis() - startTime));
+ if (timeLeft > 0 && timeLeft <= SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE.maximumAddedLag) {
+ return;
+ }
+ //reset when player has reached the end of the teleports
+ startPos = null;
+ teleportVector = null;
+
+ }
+ }
+
+ /**
+ * checks to see if a teleport device is using transmission tuner to increase the range
+ *
+ * @param customData the custom data of the teleport device
+ * @param baseRange the base range for the device without tuner
+ * @return the range with tuner
+ */
+ private static int extractTunedCustomData(NbtCompound customData, int baseRange) {
+ return customData != null && customData.contains("tuned_transmission") ? baseRange + customData.getInt("tuned_transmission") : baseRange;
+ }
+
+ /**
+ * When an item is right-clicked send off to calculate teleport with the clicked item
+ *
+ * @param playerEntity player
+ * @param world world
+ * @param hand held item
+ * @return pass
+ */
+ private static ActionResult onItemInteract(PlayerEntity playerEntity, World world, Hand hand) {
+ if (CLIENT.player == null) {
+ return null;
+ }
+ calculateTeleportUse(hand);
+ return ActionResult.PASS;
+ }
+
+ /**
+ * Allows shovel teleport items to be used when aiming at interactable blocks
+ *
+ * @param playerEntity player
+ * @param world world
+ * @param hand hand item
+ * @param blockHitResult target block
+ * @return always pass
+ */
+ private static ActionResult onBlockInteract(PlayerEntity playerEntity, World world, Hand hand, BlockHitResult blockHitResult) {
+ ItemStack itemStack = playerEntity.getStackInHand(hand);
+ if (isShovel(itemStack) && canShovelActOnBlock(world.getBlockState(blockHitResult.getBlockPos()).getBlock())) {
+ calculateTeleportUse(hand);
+ }
+ return ActionResult.PASS;
+ }
+
+ private static boolean isShovel(ItemStack itemStack) {
+ return itemStack.isOf(Items.WOODEN_SHOVEL) ||
+ itemStack.isOf(Items.STONE_SHOVEL) ||
+ itemStack.isOf(Items.IRON_SHOVEL) ||
+ itemStack.isOf(Items.GOLDEN_SHOVEL) ||
+ itemStack.isOf(Items.DIAMOND_SHOVEL);
+ }
+
+ /**
+ * Checks if the block is one that the shovel can turn into a path (e.g., grass or dirt)
+ *
+ * @param block block to check
+ * @return if block can be turned into path
+ */
+ private static boolean canShovelActOnBlock(Block block) {
+ return block == Blocks.GRASS_BLOCK ||
+ block == Blocks.DIRT ||
+ block == Blocks.COARSE_DIRT ||
+ block == Blocks.PODZOL;
+ }
+
+ /**
+ * Finds if a player uses a teleport and then saves the start position and time. then works out final position and saves that too
+ *
+ * @param hand what the player is holding
+ */
+
+ private static void calculateTeleportUse(Hand hand) {
+ //stop checking if player does not exist
+ if (CLIENT.player == null || CLIENT.world == null) {
+ return;
+ }
+ //get return item
+ ItemStack stack = CLIENT.player.getStackInHand(hand);
+
+ //make sure it's not disabled
+ if (teleportDisabled) {
+ return;
+ }
+
+ // make sure the camera is not in 3rd person
+ if (CLIENT.options.getPerspective() != Perspective.FIRST_PERSON) {
+ return;
+ }
+
+ //make sure the player is in an area teleporting is allowed not allowed in glacite mineshafts and floor 7 boss
+ if (!isAllowedLocation()) {
+ return;
+ }
+
+ //work out if the player is holding a teleporting item that is enabled and if so how far the item will take them
+ ItemStack heldItem = CLIENT.player.getMainHandStack();
+ String itemId = heldItem.getSkyblockId();
+ NbtCompound customData = ItemUtils.getCustomData(heldItem);
+
+ int distance;
+ switch (itemId) {
+ case "ASPECT_OF_THE_LEECH_1" -> {
+ if (SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE.enableWeirdTransmission) {
+ distance = 3;
+ break;
+ }
+ return;
+
+ }
+ case "ASPECT_OF_THE_LEECH_2" -> {
+ if (SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE.enableWeirdTransmission) {
+ distance = 4;
+ break;
+ }
+ return;
+ }
+ case "ASPECT_OF_THE_END", "ASPECT_OF_THE_VOID" -> {
+ if (CLIENT.options.sneakKey.isPressed() && customData.getInt("ethermerge") == 1) {
+ if (SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE.enableEtherTransmission) {
+ distance = extractTunedCustomData(customData, 57);
+ break;
+ }
+ } else if (SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE.enableInstantTransmission) {
+ distance = extractTunedCustomData(customData, 8);
+ break;
+ }
+ return;
+ }
+ case "ETHERWARP_CONDUIT" -> {
+ if (SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE.enableEtherTransmission) {
+ distance = extractTunedCustomData(customData, 57);
+ break;
+ }
+ return;
+ }
+ case "SINSEEKER_SCYTHE" -> {
+ if (SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE.enableSinrecallTransmission) {
+ distance = extractTunedCustomData(customData, 4);
+ break;
+ }
+ return;
+ }
+ case "NECRON_BLADE", "ASTRAEA", "HYPERION", "SCYLLA", "VALKYRIE" -> {
+ if (SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE.enableWitherImpact) {
+ distance = 10;
+ break;
+ }
+ return;
+ }
+ default -> {
+ return;
+ }
+ }
+ //make sure the player has enough mana to do the teleport
+ Matcher manaNeeded = ItemUtils.getLoreLineIfMatch(heldItem, MANA_LORE);
+ if (manaNeeded != null && manaNeeded.matches()) {
+ int manaCost = Integer.parseInt(manaNeeded.group(1));
+ int predictedMana = SkyblockerMod.getInstance().statusBarTracker.getMana().value() - teleportsAhead * manaCost;
+ if (predictedMana < manaCost) { // todo the players mana can lag behind as it is updated server side. client side mana calculations would help with this
+ return;
+ }
+ }
+
+ //work out start pos of warp and set start time. if there is an active warp going on make the end of that the start of the next one
+ if (teleportsAhead == 0 || startPos == null || teleportVector == null) {
+ //start of teleport sequence
+ startPos = CLIENT.player.getPos().add(0, 1.62, 0); // the eye poss should not be affected by crouching
+ cameraStartPos = CLIENT.player.getEyePos();
+ lastTeleportTime = System.currentTimeMillis();
+ // update the ping used for the teleport
+ currentTeleportPing = lastPing;
+ } else {
+ //add to the end of the teleport sequence
+ startPos = startPos.add(teleportVector);
+ //set the camera start pos to how far though the teleport the player is to make is smoother
+ cameraStartPos = getInterpolatedPos();
+ //update the ping used for this part of the teleport
+ currentTeleportPing = lastPing;
+ }
+
+ startTime = System.currentTimeMillis();
+
+ // calculate the vector the player will follow for the teleport
+ //get direction
+ float pitch = CLIENT.player.getPitch();
+ float yaw = CLIENT.player.getYaw();
+ Vec3d look = CLIENT.player.getRotationVector(pitch, yaw);
+
+ //find target location depending on how far the item they are using takes them
+ teleportVector = raycast(distance, look, startPos);
+ if (teleportVector == null) {
+ startPos = null;
+ return;
+ }
+
+ //compensate for hypixel round to center of block (to x.5 y.62 z.5)
+ Vec3d predictedEnd = startPos.add(teleportVector);
+ Vec3d offsetVec = new Vec3d(predictedEnd.x - roundToCenter(predictedEnd.x), predictedEnd.y - (Math.ceil(predictedEnd.y) + 0.62), predictedEnd.z - roundToCenter(predictedEnd.z));
+ teleportVector = teleportVector.subtract(offsetVec);
+ //add 1 to teleports ahead
+ teleportsAhead += 1;
+ }
+
+ /**
+ * Rounds a value to the nearest 0.5
+ *
+ * @param input number to round
+ * @return rounded number
+ */
+ private static double roundToCenter(double input) {
+ return Math.round(input - 0.5) + 0.5;
+ }
+
+ /**
+ * Works out if the players location lets them use teleportation or not
+ *
+ * @return if the player should be allowed to teleport
+ */
+ private static boolean isAllowedLocation() {
+ //check mines shafts
+ if (Utils.getMap().equals("Mineshaft")) {
+ return false;
+ } else if (Utils.getIslandArea().equals("⏣ Jungle Temple")) { //do not allow in jungle temple
+ return false;
+ } else if (Utils.getLocation() == Location.PRIVATE_ISLAND && !Utils.getIslandArea().equals("⏣ Your Island")) { //do not allow it when visiting
+ return false;
+ } else if (Utils.isInDungeons()) { //check places in dungeons where you can't teleport
+ if (DungeonManager.isInBoss() && DungeonManager.getBoss() == DungeonBoss.MAXOR) {
+ return false;
+ }
+ //make sure the player is in a room then check for disallowed rooms
+ if (!DungeonManager.isCurrentRoomMatched()) {
+ return true;
+ }
+ //does not work in boulder room
+ if (DungeonManager.getCurrentRoom().getName().equals("boxes-room")) {
+ return false;
+ }
+ //does not work in teleport maze room
+ if (DungeonManager.getCurrentRoom().getName().equals("teleport-pad-room")) {
+ return false;
+ }
+ //does not work in trap room
+ if (DungeonManager.getCurrentRoom().getName().startsWith("trap")) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Custom raycast for teleporting checks for blocks for each 1 block forward in teleport. (very similar to hypixels method)
+ *
+ * @param distance maximum distance
+ * @return teleport vector
+ */
+ private static Vec3d raycast(int distance, Vec3d direction, Vec3d startPos) {
+ if (CLIENT.world == null || direction == null || startPos == null) {
+ return null;
+ }
+
+ //based on which way the ray is going get the needed vector for checking diagonals
+ BlockPos xDiagonalOffset;
+ BlockPos zDiagonalOffset;
+ if (direction.getX() > 0) {
+ xDiagonalOffset = new BlockPos(-1, 0, 0);
+ } else {
+ xDiagonalOffset = new BlockPos(1, 0, 0);
+ }
+ if (direction.getZ() > 0) {
+ zDiagonalOffset = new BlockPos(0, 0, -1);
+ } else {
+ zDiagonalOffset = new BlockPos(0, 0, 1);
+ }
+
+ //initialise the closest floor value outside of possible values
+ int closeFloorY = 1000;
+
+ //loop though each block of a teleport checking each block if there are blocks in the way
+ for (double offset = 0; offset <= distance; offset++) {
+ Vec3d pos = startPos.add(direction.multiply(offset));
+ BlockPos checkPos = BlockPos.ofFloored(pos);
+
+ //check if there is a block at the check location
+ if (!canTeleportThrough(checkPos)) {
+ if (offset == 0) {
+ // no teleport can happen
+ return null;
+ }
+ return direction.multiply(offset - 1);
+ }
+
+ //check if the block at head height is free
+ if (!canTeleportThrough(checkPos.up())) {
+ if (offset == 0) {
+ //cancel the check if starting height is too low
+ Vec3d justAhead = startPos.add(direction.multiply(0.2));
+ if ((justAhead.getY() - Math.floor(justAhead.getY())) <= 0.495) {
+ continue;
+ }
+ // no teleport can happen
+ return null;
+ }
+ return direction.multiply(offset - 1);
+ }
+
+ //check the diagonals to make sure player is not going through diagonal wall (full height block in the way on both sides at either height)
+ if (offset != 0 && (isBlockFloor(checkPos.add(xDiagonalOffset)) || isBlockFloor(checkPos.up().add(xDiagonalOffset))) && (isBlockFloor(checkPos.add(zDiagonalOffset)) || isBlockFloor(checkPos.up().add(zDiagonalOffset)))) {
+ return direction.multiply(offset - 1);
+ }
+
+ //if the player is close to the floor (including diagonally) save Y and when player goes bellow this y finish teleport
+ if (offset != 0 && (isBlockFloor(checkPos.down()) || (isBlockFloor(checkPos.down().subtract(xDiagonalOffset)) && isBlockFloor(checkPos.down().subtract(zDiagonalOffset)))) && (pos.getY() - Math.floor(pos.getY())) < 0.31) {
+ closeFloorY = checkPos.getY() - 1;
+ }
+
+ //if the checking Y is same as closeY finish
+ if (closeFloorY == checkPos.getY()) {
+ return direction.multiply(offset - 1);
+ }
+ }
+
+ //return full distance if no collision found
+ return direction.multiply(distance);
+ }
+
+ /**
+ * Checks to see if a block is in the allowed list to teleport though
+ * Air, Buttons, carpets, crops, pots, mushrooms, nether wart, redstone, ladder, water, fire, lava, 3 or less snow layers
+ *
+ * @param blockPos block location
+ * @return if a block location can be teleported though
+ */
+ private static Boolean canTeleportThrough(BlockPos blockPos) {
+ if (CLIENT.world == null) {
+ return false;
+ }
+
+ BlockState blockState = CLIENT.world.getBlockState(blockPos);
+ if (blockState.isAir()) {
+ return true;
+ }
+ Block block = blockState.getBlock();
+ return block instanceof ButtonBlock || block instanceof CarpetBlock || block instanceof CropBlock || block instanceof FlowerPotBlock || block.equals(Blocks.BROWN_MUSHROOM) || block.equals(Blocks.RED_MUSHROOM) || block.equals(Blocks.NETHER_WART) || block.equals(Blocks.REDSTONE_WIRE) || block.equals(Blocks.LADDER) || block.equals(Blocks.FIRE) || (block.equals(Blocks.SNOW) && blockState.get(Properties.LAYERS) <= 3) || block.equals(Blocks.WATER) || block.equals(Blocks.LAVA);
+ }
+
+ /**
+ * Checks to see if a block goes to the top if so class it as a floor
+ *
+ * @param blockPos block location
+ * @return if it's a floor block
+ */
+ private static Boolean isBlockFloor(BlockPos blockPos) {
+ if (CLIENT.world == null) {
+ return false;
+ }
+
+ BlockState blockState = CLIENT.world.getBlockState(blockPos);
+ VoxelShape shape = blockState.getCollisionShape(CLIENT.world, blockPos);
+ if (shape.isEmpty()) {
+ return false;
+ }
+ return shape.getBoundingBox().maxY == 1;
+ }
+
+ /**
+ * works out where they player should be based on how far though the predicted teleport time.
+ *
+ * @return the camera position for the interpolated pos
+ */
+
+ public static Vec3d getInterpolatedPos() {
+ if (CLIENT.player == null || teleportVector == null || startPos == null || teleportDisabled) {
+ return null;
+ }
+ long gap = System.currentTimeMillis() - startTime;
+ //make sure the player is actually getting teleported if not disable teleporting until they are teleported again
+ if (System.currentTimeMillis() - lastTeleportTime > Math.min(Math.max(lastPing, currentTeleportPing) + SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE.maximumAddedLag, MAX_TELEPORT_TIME)) {
+ teleportDisabled = true;
+ startPos = null;
+ teleportVector = null;
+ teleportsAhead = 0;
+ return null;
+ }
+ long estimatedTeleportTime = Math.min(currentTeleportPing, MAX_TELEPORT_TIME);
+ double percentage = Math.clamp((double) (gap) / estimatedTeleportTime, 0, 1); // Sanity clamp
+
+ //if the animation is done and the player has finished the teleport server side finish the teleport
+ if (teleportsAhead == 0 && gap >= estimatedTeleportTime + SkyblockerConfigManager.get().uiAndVisuals.smoothAOTE.maximumAddedLag) {
+ //reset when player has reached the end of the teleports
+ startPos = null;
+ teleportVector = null;
+ return null;
+ }
+
+ return cameraStartPos.add(teleportVector.multiply(percentage));
+ }
+
+ public static void updatePing(long ping) {
+ lastPing = ping;
+ }
+
+
+}