diff --git a/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/src/main/kotlin/com/lambda/config/groups/Targeting.kt index c0981b97c..130164626 100644 --- a/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -60,33 +60,33 @@ abstract class Targeting( * The range within which entities can be targeted. This value is configurable and constrained * between 1.0 and [maxRange]. */ - override val targetingRange by owner.setting("Targeting Range", defaultRange, 1.0..maxRange, 0.05) { predicate() } + override val targetingRange by owner.setting("Targeting Range", defaultRange, 1.0..maxRange, 0.05) { predicate() }.group(baseGroup) /** * Whether players are included in the targeting scope. */ - override val players by owner.setting("Players", true) { predicate() } + override val players by owner.setting("Players", true) { predicate() }.group(baseGroup) /** * Whether friends are included in the targeting scope. * Requires [players] to be true. */ - override val friends by owner.setting("Friends", false) { predicate() && players } + override val friends by owner.setting("Friends", false) { predicate() && players }.group(baseGroup) /** * Whether mobs are included in the targeting scope. */ - private val mobs by owner.setting("Mobs", true) { predicate() } + private val mobs by owner.setting("Mobs", true) { predicate() }.group(baseGroup) /** * Whether hostile mobs are included in the targeting scope */ - private val hostilesSetting by owner.setting("Hostiles", true) { predicate() && mobs } + private val hostilesSetting by owner.setting("Hostiles", true) { predicate() && mobs }.group(baseGroup) /** * Whether passive animals are included in the targeting scope */ - private val animalsSetting by owner.setting("Animals", true) { predicate() && mobs } + private val animalsSetting by owner.setting("Animals", true) { predicate() && mobs }.group(baseGroup) /** * Indicates whether hostile entities are included in the targeting scope. @@ -101,12 +101,12 @@ abstract class Targeting( /** * Whether invisible entities are included in the targeting scope. */ - override val invisible by owner.setting("Invisible", true) { predicate() } + override val invisible by owner.setting("Invisible", true) { predicate() }.group(baseGroup) /** * Whether dead entities are included in the targeting scope. */ - override val dead by owner.setting("Dead", false) { predicate() } + override val dead by owner.setting("Dead", false) { predicate() }.group(baseGroup) /** * Validates whether a given entity is targetable by the player based on current settings. @@ -144,7 +144,7 @@ abstract class Targeting( /** * The field of view limit for targeting entities. Configurable between 5 and 180 degrees. */ - val fov by owner.setting("FOV Limit", 180, 5..180, 1) { predicate() }.group(baseGroup) + val fov by owner.setting("FOV Limit", 180, 5..180, 1) { predicate() && priority == Priority.FOV }.group(baseGroup) /** * The priority used to determine which entity is targeted. Configurable with default set to [Priority.DISTANCE]. diff --git a/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt b/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt index 6ec5289fd..9d6de926e 100644 --- a/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt +++ b/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt @@ -33,8 +33,8 @@ import kotlin.reflect.KClass * [StackSelection] is a class that holds a predicate for matching [ItemStack]s. */ class StackSelection { - var selector: (ItemStack) -> Boolean = { true } - var comparator: Comparator? = null + var selector: (ItemStack) -> Boolean = EVERYTHING + var comparator: Comparator = NO_COMPARE var count: Int = DEFAULT_AMOUNT var inShulkerBox: Boolean = false @@ -48,25 +48,54 @@ class StackSelection { val optimalStack: ItemStack? get() = itemStack ?: item?.let { ItemStack(it, count) } + /** + * Filters the given [stacks], sorts them with the [comparator] and returns the first value + */ + fun bestItemMatch(stacks: List): ItemStack? = stacks.minWithOrNull(comparator) + fun filterStack(stack: ItemStack) = if (inShulkerBox) stack.shulkerBoxContents.any { selector(it) } else selector(stack) - fun filterSlot(slot: Slot) = filterStack(slot.stack) + fun filterSlot(slot: Slot): Boolean = filterStack(slot.stack) fun filterStacks(stacks: List): List = - stacks.filter(::filterStack).let { filteredStacks -> - comparator?.run { - filteredStacks.sortedWith(this) - } ?: filteredStacks - } + stacks.filter(::filterStack).sortedWith(comparator) fun filterSlots(slots: List): List = - slots.filter(::filterSlot).let { filteredSlots -> - comparator?.run { - filteredSlots.sortedWith { slot, slot2 -> compare(slot.stack, slot2.stack) } - } ?: filteredSlots - } + slots.filter(::filterSlot).sortedWith { slot, slot2 -> comparator.compare(slot.stack, slot2.stack) } + + @StackSelectionDsl + fun > sortBy(selector: (ItemStack) -> R?): StackSelection = apply { + comparator = compareBy(selector) + } + + @StackSelectionDsl + fun > sortByDescending(selector: (ItemStack) -> R?): StackSelection = apply { + comparator = compareByDescending(selector) + } + + @StackSelectionDsl + fun > thenBy(selector: (ItemStack) -> R?): StackSelection = apply { + check(comparator != NO_COMPARE) { "No comparator specified" } + comparator = comparator.thenBy(selector) + } + + @StackSelectionDsl + fun > thenByDescending(selector: (ItemStack) -> R?): StackSelection = apply { + check(comparator != NO_COMPARE) { "No comparator specified" } + comparator = comparator.thenByDescending(selector) + } + + @StackSelectionDsl + fun sortWith(custom: Comparator): StackSelection = apply { + comparator = custom + } + + @StackSelectionDsl + fun reversed(): StackSelection = apply { + comparator = comparator.reversed() + } /** * returns a function that finds a shulker box to push matching items into. @@ -235,14 +264,12 @@ class StackSelection { annotation class StackSelectionDsl const val DEFAULT_AMOUNT = 1 - val FULL_SHULKERS: (ItemStack) -> Boolean = { stack -> - stack.shulkerBoxContents.none { it.isEmpty } - } - val EMPTY_SHULKERS: (ItemStack) -> Boolean = { stack -> - stack.shulkerBoxContents.all { it.isEmpty } - } + + val FULL_SHULKERS: (ItemStack) -> Boolean = { stack -> stack.shulkerBoxContents.none { it.isEmpty } } + val EMPTY_SHULKERS: (ItemStack) -> Boolean = { stack -> stack.shulkerBoxContents.all { it.isEmpty } } val EVERYTHING: (ItemStack) -> Boolean = { true } val NOTHING: (ItemStack) -> Boolean = { false } + val NO_COMPARE: Comparator = Comparator { _, _ -> 0 } @StackSelectionDsl fun Item.select() = selectStack { isItem(this@select) } @@ -261,29 +288,12 @@ class StackSelection { @StackSelectionDsl fun ((ItemStack) -> Boolean).select() = selectStack { this@select } - /** - * Builds a [StackSelection] with the given parameters. - * @param count The count of items to be selected. - * @param block The predicate to be used to select the items. - * @return A [StackSelection] with the given parameters. - */ - @StackSelectionDsl - fun selectStack( - count: Int = DEFAULT_AMOUNT, - inShulkerBox: Boolean = false, - block: StackSelection.() -> (ItemStack) -> Boolean, - ) = StackSelection().apply { - selector = block() - this.count = count - this.inShulkerBox = inShulkerBox - } - @StackSelectionDsl fun selectStack( count: Int = DEFAULT_AMOUNT, inShulkerBox: Boolean = false, - sorter: Comparator? = null, - block: StackSelection.() -> (ItemStack) -> Boolean, + sorter: Comparator = NO_COMPARE, + block: StackSelection.() -> (ItemStack) -> Boolean = { EVERYTHING }, ) = StackSelection().apply { selector = block() comparator = sorter diff --git a/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt b/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt index 541c8bfe1..edbca7442 100644 --- a/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt +++ b/src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt @@ -36,10 +36,13 @@ import com.lambda.task.RootTask.run import com.lambda.util.NamedEnum import com.lambda.util.item.ItemStackUtils.attackDamage import com.lambda.util.item.ItemStackUtils.attackSpeed +import com.lambda.util.item.ItemStackUtils.equal import com.lambda.util.math.random +import com.lambda.util.player.SlotUtils.hotbarAndStorage import com.lambda.util.world.raycast.InteractionMask import com.lambda.util.world.raycast.RayCastUtils.entityResult import net.minecraft.entity.LivingEntity +import net.minecraft.entity.attribute.EntityAttributes import net.minecraft.registry.tag.ItemTags import net.minecraft.util.Hand import net.minecraft.util.math.Vec3d @@ -52,7 +55,7 @@ object KillAura : Module( // Interact private val interactionSettings = InteractionSettings(this, Group.Interaction, InteractionMask.Entity) private val interactSettings = InteractSettings(this, listOf(Group.Interact)) - private val swap by setting("Swap", true, "Swap to the item with the highest damage") + private val swap by setting("Swap", true, "Swap to the item with the highest damage").group(Group.Interact) private val attackMode by setting("Attack Mode", AttackMode.Cooldown).group(Group.Interact) private val cooldownOffset by setting("Cooldown Offset", 0, -5..5, 1) { attackMode == AttackMode.Cooldown }.group(Group.Interact) private val hitDelay1 by setting("Hit Delay 1", 2.0, 0.0..20.0, 1.0) { attackMode == AttackMode.Delay }.group(Group.Interact) @@ -101,20 +104,10 @@ object KillAura : Module( listen { target?.let { entity -> if (swap) { - val selection = selectStack( - sorter = compareByDescending { attackDamage(stack = it) } - ) { isTag(ItemTags.SWORDS) } + val selection = selectStack().sortByDescending { player.attackDamage(stack = it) } - - if (!selection.selector(player.mainHandStack)) { - selection.transfer(MainHandContainer) - ?.finally { - // Wait until the rotation has a hit result on the entity - if (lookAtEntity(entity).requestBy(rotation).done) runAttack(entity) - }?.run() - - return@listen - } + if (!selection.bestItemMatch(player.hotbarAndStorage).equal(player.mainHandStack)) + selection.transfer(MainHandContainer)?.run() } // Wait until the rotation has a hit result on the entity @@ -129,18 +122,12 @@ object KillAura : Module( private fun SafeContext.runAttack(target: LivingEntity) { // Cooldown check when (attackMode) { - AttackMode.Cooldown -> { - if (player.lastAttackedTicks < 20 / player.attackSpeed() + cooldownOffset) return - } - - AttackMode.Delay -> { - if (System.currentTimeMillis() - lastAttackTime < hitDelay) return - } + AttackMode.Cooldown -> if (player.lastAttackedTicks < 1 / player.attackSpeed() * 20 + cooldownOffset) return + AttackMode.Delay -> if (System.currentTimeMillis() - lastAttackTime < hitDelay) return } // Rotation check - run { - if (!rotate) return@run + if (rotate) { val angle = RotationManager.activeRotation if (interactionSettings.strictRayCast) { diff --git a/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt b/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt index 89f42792c..902ee2459 100644 --- a/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt +++ b/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt @@ -17,13 +17,16 @@ package com.lambda.util.item -import com.lambda.context.SafeContext import com.lambda.util.collections.Cacheable.Companion.cacheable import net.minecraft.component.DataComponentTypes import net.minecraft.component.type.AttributeModifiersComponent +import net.minecraft.entity.EquipmentSlot import net.minecraft.entity.LivingEntity +import net.minecraft.entity.attribute.EntityAttribute +import net.minecraft.entity.attribute.EntityAttributeModifier import net.minecraft.entity.attribute.EntityAttributes import net.minecraft.item.ItemStack +import net.minecraft.registry.entry.RegistryEntry object ItemStackUtils { // FixMe: Change this fucking retarded stuff when mojang wake up from their coma and realize they fucked this shit up @@ -31,55 +34,42 @@ object ItemStackUtils { // - Enchantments do not change attributes, // - All enchantment utils are bound to the server - /** - * Returns the attack damage for the given [stack], the value is affected by potion effects and enchantments - */ - fun SafeContext.attackDamage(entity: LivingEntity = player, stack: ItemStack = entity.mainHandStack) = - entity.attackDamage(stack) + fun LivingEntity.attributeBaseValue(attribute: RegistryEntry) = + if (attributes.hasAttribute(attribute)) getAttributeBaseValue(attribute) else 0.0 + + fun AttributeModifiersComponent.filteredApply(base: Double, slot: EquipmentSlot, vararg attributes: RegistryEntry): Double = + modifiers.filter { attributes.contains(it.attribute) && it.slot.matches(slot) } + .foldRight(base) { entry, acc -> + val v = entry.modifier.value + + acc + when (entry.modifier.operation) { + EntityAttributeModifier.Operation.ADD_VALUE -> v + EntityAttributeModifier.Operation.ADD_MULTIPLIED_BASE -> v * base + EntityAttributeModifier.Operation.ADD_MULTIPLIED_TOTAL -> v * acc + } + } + /** * Returns the attack damage for the given [stack], the value is affected by potion effects and enchantments */ fun LivingEntity.attackDamage(stack: ItemStack = mainHandStack) = - (stack.getOrDefault(DataComponentTypes.ATTRIBUTE_MODIFIERS, AttributeModifiersComponent.DEFAULT) - .modifiers.find { it.attribute == EntityAttributes.ATTACK_DAMAGE }?.modifier?.value ?: 0.0) + - getAttributeValue(EntityAttributes.ATTACK_DAMAGE) + stack.getOrDefault(DataComponentTypes.ATTRIBUTE_MODIFIERS, AttributeModifiersComponent.DEFAULT) + .filteredApply(attributeBaseValue(EntityAttributes.ATTACK_DAMAGE), EquipmentSlot.MAINHAND, EntityAttributes.ATTACK_DAMAGE) /** * Returns the attack speed for the given [stack], the value is affected by potion effects * * The value represents the number of attacks-per-tick - */ - fun SafeContext.attackSpeed(entity: LivingEntity = player, stack: ItemStack = entity.mainHandStack) = - entity.attackSpeed(stack) - - /** - * Returns the attack speed for the given [stack], the value is affected by potion effects * - * The value represents the number of attacks-per-tick + * To get the number of ticks per attack do the following: + * ``` + * ticks = 1 / speed * 20 + * ``` */ fun LivingEntity.attackSpeed(stack: ItemStack = mainHandStack) = - (stack.getOrDefault(DataComponentTypes.ATTRIBUTE_MODIFIERS, AttributeModifiersComponent.DEFAULT) - .modifiers.find { it.attribute == EntityAttributes.ATTACK_SPEED }?.modifier?.value ?: 0.0) + - getAttributeValue(EntityAttributes.ATTACK_SPEED) - - /** - * Returns the mining speed for the given [stack], the value is affected by potion effects and enchantments - */ - fun SafeContext.miningSpeed(entity: LivingEntity = player, stack: ItemStack = entity.mainHandStack) = - entity.miningSpeed(stack) - - /** - * Returns the mining speed for the given [stack], the value is affected by potion effects and enchantments - */ - fun LivingEntity.miningSpeed(stack: ItemStack = mainHandStack) = - (stack.getOrDefault(DataComponentTypes.ATTRIBUTE_MODIFIERS, AttributeModifiersComponent.DEFAULT) - .modifiers.find { - it.attribute == EntityAttributes.MINING_EFFICIENCY || - it.attribute == EntityAttributes.SUBMERGED_MINING_SPEED - }?.modifier?.value ?: 0.0) + - if (isSubmergedInWater) getAttributeValue(EntityAttributes.SUBMERGED_MINING_SPEED) - else getAttributeValue(EntityAttributes.MINING_EFFICIENCY) + stack.getOrDefault(DataComponentTypes.ATTRIBUTE_MODIFIERS, AttributeModifiersComponent.DEFAULT) + .filteredApply(attributeBaseValue(EntityAttributes.ATTACK_SPEED), EquipmentSlot.MAINHAND, EntityAttributes.ATTACK_SPEED) val ItemStack.spaceLeft get() = maxCount - count val ItemStack.hasSpace get() = spaceLeft > 0 diff --git a/src/main/kotlin/com/lambda/util/math/Linear.kt b/src/main/kotlin/com/lambda/util/math/Linear.kt index 7f8e712eb..ca8f43953 100644 --- a/src/main/kotlin/com/lambda/util/math/Linear.kt +++ b/src/main/kotlin/com/lambda/util/math/Linear.kt @@ -23,6 +23,7 @@ import net.minecraft.util.math.Vec3d import java.awt.Color import kotlin.math.max import kotlin.math.min +import kotlin.random.Random import kotlin.random.Random.Default.nextDouble /** @@ -46,7 +47,7 @@ fun ClosedRange.step(step: Float) = object : FloatIterator() { /** * Returns a random number within the range. */ -fun ClosedRange.random() = nextDouble(start, endInclusive) +fun ClosedRange.random(random: Random = Random) = start + (endInclusive - start) * random.nextDouble() /** * Converts a value from one range to a normalized value between 0 and 1.