aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorolim88 <bobq4582@gmail.com>2025-01-22 05:56:35 +0000
committerGitHub <noreply@github.com>2025-01-22 13:56:35 +0800
commit080aa301dcb36bc5f32dd72afb41ea5e1d805d60 (patch)
tree7162124527d56584690416b6d4975ef165b75bd5 /src
parentade16c25fc4cae11bbbf8e9126a2d3d0ae42ea5e (diff)
downloadSkyblocker-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')
-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
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json16
-rw-r--r--src/main/resources/skyblocker.mixins.json1
8 files changed, 625 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;
+ }
+
+
+}
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index 058b321d..0a7dac78 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -887,6 +887,22 @@
"skyblocker.config.uiAndVisuals.showEquipmentInInventory": "Show Equipment in Inventory",
+ "skyblocker.config.uiAndVisuals.smoothAOTE": "Smooth AOTE",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.@Tooltip": "Smooths out teleporting with right click teleport abilities.",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.enableEtherTransmission": "Enable Ether Transmission",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.enableEtherTransmission.@Tooltip": "For: Etherwarp Conduit and Ether Merged.",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.enableInstantTransmission": "Enable Instant Transmission",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.enableInstantTransmission.@Tooltip": "For: AOTE and AOTV.",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.enableSinrecallTransmission": "Enable Sinrecall Transmission",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.enableSinrecallTransmission.@Tooltip": "For: Sinseeker Scythe.",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.enableWeirdTransmission": "Enable Weird Transmission",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.enableWeirdTransmission.@Tooltip": "For: Aspect of the Leech.",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.enableWitherImpact": "Enable Wither Impact",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.enableWitherImpact.@Tooltip": "For: Necron's Blade, Hyperion, Astraea, Scylla, and Valkyrie.",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.maximumAddedLag": "Maximum Added Lag (ms)",
+ "skyblocker.config.uiAndVisuals.smoothAOTE.maximumAddedLag.@Tooltip": "How long the animation is allowed to get behind the game in milliseconds. (If set to 0, smoothing will not have any lag, but stuttering will likely occur. Increase this value if you're experiencing flashing.)",
+
+
"skyblocker.config.uiAndVisuals.tabHud": "Fancy HUD and TAB",
"skyblocker.config.uiAndVisuals.tabHud.configScreen": "Open Config Screen",
"skyblocker.config.uiAndVisuals.tabHud.defaultPositioning": "Default Positioning Behavior",
diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json
index dc9d27cc..20ef3807 100644
--- a/src/main/resources/skyblocker.mixins.json
+++ b/src/main/resources/skyblocker.mixins.json
@@ -7,6 +7,7 @@
"BackgroundRendererMixin",
"BatEntityMixin",
"BossBarHudMixin",
+ "CameraMixin",
"ClientPlayerEntityMixin",
"ClientPlayNetworkHandlerMixin",
"ClientWorldMixin",