Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/main/kotlin/com/lambda/config/groups/Targeting.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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].
Expand Down
86 changes: 48 additions & 38 deletions src/main/kotlin/com/lambda/interaction/material/StackSelection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<ItemStack>? = null
var selector: (ItemStack) -> Boolean = EVERYTHING
var comparator: Comparator<ItemStack> = NO_COMPARE
var count: Int = DEFAULT_AMOUNT
var inShulkerBox: Boolean = false

Expand All @@ -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>): 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<ItemStack>): List<ItemStack> =
stacks.filter(::filterStack).let { filteredStacks ->
comparator?.run {
filteredStacks.sortedWith(this)
} ?: filteredStacks
}
stacks.filter(::filterStack).sortedWith(comparator)

fun filterSlots(slots: List<Slot>): List<Slot> =
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 <R : Comparable<R>> sortBy(selector: (ItemStack) -> R?): StackSelection = apply {
comparator = compareBy(selector)
}

@StackSelectionDsl
fun <R : Comparable<R>> sortByDescending(selector: (ItemStack) -> R?): StackSelection = apply {
comparator = compareByDescending(selector)
}

@StackSelectionDsl
fun <R : Comparable<R>> thenBy(selector: (ItemStack) -> R?): StackSelection = apply {
check(comparator != NO_COMPARE) { "No comparator specified" }
comparator = comparator.thenBy(selector)
}

@StackSelectionDsl
fun <R : Comparable<R>> thenByDescending(selector: (ItemStack) -> R?): StackSelection = apply {
check(comparator != NO_COMPARE) { "No comparator specified" }
comparator = comparator.thenByDescending(selector)
}

@StackSelectionDsl
fun sortWith(custom: Comparator<ItemStack>): 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.
Expand Down Expand Up @@ -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<ItemStack> = Comparator { _, _ -> 0 }

@StackSelectionDsl
fun Item.select() = selectStack { isItem(this@select) }
Expand All @@ -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<ItemStack>? = null,
block: StackSelection.() -> (ItemStack) -> Boolean,
sorter: Comparator<ItemStack> = NO_COMPARE,
block: StackSelection.() -> (ItemStack) -> Boolean = { EVERYTHING },
) = StackSelection().apply {
selector = block()
comparator = sorter
Expand Down
33 changes: 10 additions & 23 deletions src/main/kotlin/com/lambda/module/modules/combat/KillAura.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -101,20 +104,10 @@ object KillAura : Module(
listen<TickEvent.Pre> {
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
Expand All @@ -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) {
Expand Down
64 changes: 27 additions & 37 deletions src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,69 +17,59 @@

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
// - The client and the server entity attributes are not synced,
// - 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<EntityAttribute>) =
if (attributes.hasAttribute(attribute)) getAttributeBaseValue(attribute) else 0.0

fun AttributeModifiersComponent.filteredApply(base: Double, slot: EquipmentSlot, vararg attributes: RegistryEntry<EntityAttribute>): 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
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/com/lambda/util/math/Linear.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand All @@ -46,7 +47,7 @@ fun ClosedRange<Float>.step(step: Float) = object : FloatIterator() {
/**
* Returns a random number within the range.
*/
fun ClosedRange<Double>.random() = nextDouble(start, endInclusive)
fun ClosedRange<Double>.random(random: Random = Random) = start + (endInclusive - start) * random.nextDouble()

/**
* Converts a value from one range to a normalized value between 0 and 1.
Expand Down