diff --git a/src/main/kotlin/org/kamiblue/client/module/modules/combat/AutoArmor.kt b/src/main/kotlin/org/kamiblue/client/module/modules/combat/AutoArmor.kt index ef53b69c..5c1a5bdf 100644 --- a/src/main/kotlin/org/kamiblue/client/module/modules/combat/AutoArmor.kt +++ b/src/main/kotlin/org/kamiblue/client/module/modules/combat/AutoArmor.kt @@ -21,13 +21,21 @@ internal object AutoArmor : Module( description = "Automatically equips armour", modulePriority = 500 ) { + private val delay = setting("Delay", 5, 1..10, 1) private val timer = TickTimer(TimeUnit.TICKS) private var lastTask = TaskState(true) + var isPaused = false + init { + onToggle { + isPaused = false + } + safeListener { + if (isPaused) return@safeListener if (!timer.tick(delay.value.toLong()) || !lastTask.done) return@safeListener if (!player.inventory.itemStack.isEmpty) { @@ -35,6 +43,7 @@ internal object AutoArmor : Module( else removeHoldingItem() return@safeListener } + // store slots and values of best armor pieces, initialize with currently equipped armor // Pair val bestArmors = Array(4) { -1 to getArmorValue(player.inventory.armorInventory[it]) } @@ -46,10 +55,14 @@ internal object AutoArmor : Module( if (item !is ItemArmor) continue val armorType = item.armorType.index - if (armorType == 2 && player.inventory.armorInventory[2].item == Items.ELYTRA) continue // Skip if item is chestplate and we have elytra equipped + + // Skip if item is chestplate and we have elytra equipped + if (armorType == 2 && player.inventory.armorInventory[2].item == Items.ELYTRA) continue val armorValue = getArmorValue(itemStack) - if (armorValue > bestArmors[armorType].second) bestArmors[armorType] = slot to armorValue + if (armorValue > bestArmors[armorType].second) { + bestArmors[armorType] = slot to armorValue + } } // equip better armor @@ -67,6 +80,7 @@ internal object AutoArmor : Module( for (i in 0 until itemStack.enchantmentTagList.tagCount()) { val id = itemStack.enchantmentTagList.getCompoundTagAt(i).getShort("id").toInt() val level = itemStack.enchantmentTagList.getCompoundTagAt(i).getShort("lvl").toInt() + if (id != 0) continue return 1f + 0.04f * level } @@ -87,6 +101,7 @@ internal object AutoArmor : Module( PlayerInventoryManager.ClickInfo(0, pair.first, type = ClickType.PICKUP) // Put the old one into the empty slot ) } + break // Don't move more than one at once } } diff --git a/src/main/kotlin/org/kamiblue/client/module/modules/combat/AutoMend.kt b/src/main/kotlin/org/kamiblue/client/module/modules/combat/AutoMend.kt index 7047d90e..5cc91610 100644 --- a/src/main/kotlin/org/kamiblue/client/module/modules/combat/AutoMend.kt +++ b/src/main/kotlin/org/kamiblue/client/module/modules/combat/AutoMend.kt @@ -4,6 +4,7 @@ import net.minecraft.enchantment.EnchantmentHelper import net.minecraft.entity.player.EntityPlayer import net.minecraft.init.Enchantments import net.minecraft.init.Items +import net.minecraft.inventory.ClickType import net.minecraft.util.EnumHand import net.minecraft.util.math.RayTraceResult import net.minecraftforge.fml.common.gameevent.TickEvent @@ -17,9 +18,9 @@ import org.kamiblue.client.module.Module import org.kamiblue.client.util.EntityUtils.isFakeOrSelf import org.kamiblue.client.util.TickTimer import org.kamiblue.client.util.TimeUnit +import org.kamiblue.client.util.items.clickSlot import org.kamiblue.client.util.items.swapToSlot import org.kamiblue.client.util.math.Vec2f -import org.kamiblue.client.util.text.MessageSendHelper import org.kamiblue.client.util.threads.runSafe import org.kamiblue.client.util.threads.safeListener import org.kamiblue.commons.utils.MathUtils.reverseNumber @@ -30,10 +31,14 @@ internal object AutoMend : Module( category = Category.COMBAT, description = "Automatically mends armour" ) { + private val autoThrow by setting("Auto Throw", true) private val throwDelay = setting("Throw Delay", 2, 0..5, 1, description = "Number of ticks between throws to allow absorption") private val autoSwitch by setting("Auto Switch", true) - private val autoDisable by setting("Auto Disable", false, { autoSwitch }) + private val autoDisableExp by setting("Auto Disable", false, { autoSwitch }, description = "Disable when you run out of XP bottles") + private val autoDisableComplete by setting("Disable on Complete", false) + private val takeOff by setting("Take Off", true) + private val pauseAutoArmor = setting("Pause AutoArmor", true, { takeOff }) private val cancelNearby by setting("Cancel Nearby", NearbyMode.OFF, description = "Don't mend when an enemy is nearby") private val pauseNearbyRadius by setting("Nearby Radius", 8, 1..8, 1, { cancelNearby != NearbyMode.OFF }) private val threshold by setting("Repair At", 75, 1..100, 1, description = "Percentage to start repairing any armor piece") @@ -62,6 +67,16 @@ internal object AutoMend : Module( onDisable { switchback() + if (AutoArmor.isPaused && pauseAutoArmor.value) { + AutoArmor.isPaused = false + } + } + + // TODO: Add proper module pausing system + pauseAutoArmor.listeners.add { + if (!pauseAutoArmor.value) { + AutoArmor.isPaused = false + } } listener { @@ -73,47 +88,89 @@ internal object AutoMend : Module( } safeListener { - if (isGuiOpened && !gui) return@safeListener + var isMending = false + + if (!isGuiOpened || gui) { + if (cancelNearby != NearbyMode.OFF && isNearbyPlayer()) { + if (cancelNearby == NearbyMode.DISABLE) { + disable() + } else { + if (!paused) + switchback() + paused = true + } + + return@safeListener + } - if (cancelNearby != NearbyMode.OFF && isNearbyPlayer()) { - if (cancelNearby == NearbyMode.DISABLE) { + paused = false + + // don't call twice in same tick so store in a var + val shouldMend = shouldMend(0) || shouldMend(1) || shouldMend(2) || shouldMend(3) + if (!shouldMend && autoDisableComplete) { disable() - } else { - if (!paused) - switchback() - paused = true } - return@safeListener - } - paused = false - if ((autoSwitch || autoThrow) // avoid checking if no actions are going to be done - && hasBlockUnder() - && (shouldMend(0) || shouldMend(1) || shouldMend(2) || shouldMend(3))) { - if (autoSwitch && player.heldItemMainhand.item !== Items.EXPERIENCE_BOTTLE) { - val xpSlot = findXpPots() + if ((autoSwitch || autoThrow) // avoid checking if no actions are going to be done + && hasBlockUnder() && shouldMend) { + if (autoSwitch && player.heldItemMainhand.item !== Items.EXPERIENCE_BOTTLE) { + val xpSlot = findXpPots() + + if (xpSlot == -1) { + if (autoDisableExp) { + disable() + } - if (xpSlot == -1) { - if (autoDisable) { - MessageSendHelper.sendWarningMessage("$chatName No XP in hotbar, disabling") - disable() + return@safeListener } - return@safeListener + + player.inventory.currentItem = xpSlot } - player.inventory.currentItem = xpSlot - } - if (autoThrow && player.heldItemMainhand.item === Items.EXPERIENCE_BOTTLE) { - sendPlayerPacket { - rotate(Vec2f(player.rotationYaw, 90.0f)) + + if (autoThrow && player.heldItemMainhand.item === Items.EXPERIENCE_BOTTLE) { + sendPlayerPacket { + rotate(Vec2f(player.rotationYaw, 90.0f)) + } + + isMending = true + + if (validServerSideRotation() && throwDelayTimer.tick(throwDelay.value.toLong())) { + playerController.processRightClick(player, world, EnumHand.MAIN_HAND) + } } - if (validServerSideRotation() && throwDelayTimer.tick(throwDelay.value.toLong())) { - playerController.processRightClick(player, world, EnumHand.MAIN_HAND) + } + } + + if (pauseAutoArmor.value) { + AutoArmor.isPaused = isMending + + if (takeOff && isMending) { + var minSlot = 9 + + for (i in 0..3) { + if (shouldMend(i) || !hasMending(i)) continue + val emptySlot = findEmptySlot(minSlot) + minSlot = emptySlot + 1 + + if (emptySlot == -1) break + clickSlot(player.inventoryContainer.windowId, 8 - i, 0, ClickType.PICKUP) + clickSlot(player.inventoryContainer.windowId, emptySlot, 0, ClickType.PICKUP) } } } } } + private fun SafeClientEvent.findEmptySlot(min: Int): Int { + for (i in min..36) { + if (player.inventory.getStackInSlot(i).isEmpty) { + return i + } + } + + return -1 + } + private fun SafeClientEvent.findXpPots(): Int { var slot = -1 for (i in 0..8) { @@ -125,10 +182,14 @@ internal object AutoMend : Module( return slot } + private fun SafeClientEvent.hasMending(slot: Int): Boolean { + val stack = player.inventory.armorInventory[slot] + return EnchantmentHelper.getEnchantmentLevel(Enchantments.MENDING, stack) > 0 + } + private fun SafeClientEvent.shouldMend(i: Int): Boolean { // (100 * damage / max damage) >= (100 - 70) val stack = player.inventory.armorInventory[i] - val hasMending = EnchantmentHelper.getEnchantmentLevel(Enchantments.MENDING, stack) > 0 - return hasMending && stack.isItemDamaged && 100 * stack.itemDamage / stack.maxDamage > reverseNumber(threshold, 1, 100) + return hasMending(i) && stack.isItemDamaged && 100 * stack.itemDamage / stack.maxDamage > reverseNumber(threshold, 1, 100) } private fun switchback() {