diff options
| author | olim88 <bobq4582@gmail.com> | 2025-01-22 05:56:35 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-22 13:56:35 +0800 |
| commit | 080aa301dcb36bc5f32dd72afb41ea5e1d805d60 (patch) | |
| tree | 7162124527d56584690416b6d4975ef165b75bd5 /src/main/java | |
| parent | ade16c25fc4cae11bbbf8e9126a2d3d0ae42ea5e (diff) | |
| download | Skyblocker-080aa301dcb36bc5f32dd72afb41ea5e1d805d60.tar.gz Skyblocker-080aa301dcb36bc5f32dd72afb41ea5e1d805d60.tar.bz2 Skyblocker-080aa301dcb36bc5f32dd72afb41ea5e1d805d60.zip | |
Smooth AOTE (#963)
* init commit
basics working just needs lots of polish
* add setting and base teleport time on ping
now constantly monitors ping and makes the teleport try to last as long as last ping packet recived
* add settings for each wepon type and work with each of them
* add raycast
add raycast so predicted camera can not go though walls.
could use improvement this is not how it is done exactly on hypixels side
* cheak the players mana before assuming they can teleport
* remove un needed setting
and update settings
* add java docs and 3rd person check
* clean
* fix 3rd person check
* apply suggested changes
* do not allow when invalid location
* fix etherwarp logic using wrong tp
* add tooltips to options
* fix : click too fast cause the animation break
adds counter for how many teleports ahead the player is to the server so receiving a teleport for an old use will not reset the camera
* fix mixin
* add more disabled locations and fix for not coded disabled locations
adds other suggested locations to disable but also a timer for if the go to long without the server teleporting them assuming that its disabled and not working until they are teleported again
* improve raycast to be more like hypxiels
still not the same but closer
* add separate cameraStartPos to try and smooth combined animations
* fix extra code
* fix not working when clicking dirt with shovel
* more clean
* fix the init
* fix multiple teleports when looking at a block
there is still debug stuff in this commit
* hopefully improve raycast and add allowed blocks
still debug stuff in
* do bad client side mana calculation
still debug
* only don't check head pos on first block
still debug
* improve head height test
still debug
* add close floor check
still debug
* add can teleport though fire
still debug
* add checking for diagonals
still in debug
* add new is floor check for the close floor check
still debug
* update allowed blocks and improve diagonal collision
still debug
* diagonals only work if its floor block
still debug
* java docs
* remove debug
* can teleport though pots
* Update SmoothAOTE.java
* Apply suggestions from code review
* apply suggested changes
* add check to see if smoothing is enabled
* fix rebase
* make the setting off by default
* fix some glitchyness with changing ping
by not updating the teleport duration while teleporting. this was causing the camera to jump if your ping changed mid teleport
* add option to smooth teleport more
give the user the option to let the annimation lag behind the game so it does not jump to the end
* Clean up ClientPlayNetworkHandlerMixin
* Fix camera flashing when animation finishes before server teleports player
---------
Co-authored-by: viciscat <51047087+viciscat@users.noreply.github.com>
Co-authored-by: Kevin <92656833+kevinthegreat1@users.noreply.github.com>
Diffstat (limited to 'src/main/java')
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; + } + + +} |
