Skip to content

Commit

Permalink
feat(nextgen): SmartEat (#1766)
Browse files Browse the repository at this point in the history
Co-authored-by: Elliott <[email protected]>
Co-authored-by: superblaubeere27 <[email protected]>
  • Loading branch information
3 people authored Mar 9, 2024
1 parent cfa338a commit 3e3ff43
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,23 @@
import net.ccbluex.liquidbounce.event.events.AttackEvent;
import net.ccbluex.liquidbounce.event.events.BlockBreakingProgressEvent;
import net.ccbluex.liquidbounce.event.events.CancelBlockBreakingEvent;
import net.ccbluex.liquidbounce.event.events.PlayerInteractedItem;
import net.ccbluex.liquidbounce.event.events.GameModeChangeEvent;
import net.ccbluex.liquidbounce.features.module.modules.combat.ModuleAutoBow;
import net.ccbluex.liquidbounce.features.module.modules.combat.ModuleAutoClicker;
import net.ccbluex.liquidbounce.features.module.modules.combat.ModuleSmartEat;
import net.ccbluex.liquidbounce.features.module.modules.player.ModuleReach;
import net.ccbluex.liquidbounce.utils.aiming.Rotation;
import net.ccbluex.liquidbounce.utils.aiming.RotationManager;
import net.ccbluex.liquidbounce.utils.client.SilentHotbar;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
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.Direction;
import net.minecraft.world.GameMode;
Expand Down Expand Up @@ -123,6 +129,12 @@ private void hookFixRotation(Args args) {
args.set(4, rotation.getPitch());
}

@Inject(method = "interactItem", at = @At("RETURN"))
private void hookItemInteract(PlayerEntity player, Hand hand, CallbackInfoReturnable<ActionResult> cir) {
final PlayerInteractedItem cancelEvent = new PlayerInteractedItem(player, hand, cir.getReturnValue());
EventManager.INSTANCE.callEvent(cancelEvent);
}

@Inject(method = "stopUsingItem", at = @At("HEAD"))
private void stopUsingItem(PlayerEntity player, CallbackInfo callbackInfo) {
ModuleAutoBow.onStopUsingItem();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ val ALL_EVENT_CLASSES: Array<KClass<out Event>> = arrayOf(
PlayerJumpEvent::class,
PlayerAfterJumpEvent::class,
PlayerUseMultiplier::class,
PlayerInteractedItem::class,
PlayerVelocityStrafe::class,
PlayerStrideEvent::class,
PlayerSafeWalkEvent::class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import net.ccbluex.liquidbounce.event.EventState
import net.ccbluex.liquidbounce.utils.client.Nameable
import net.ccbluex.liquidbounce.web.socket.protocol.event.WebSocketEvent
import net.minecraft.entity.MovementType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.math.Vec3d

// Entity events bound to client-user entity
Expand Down Expand Up @@ -66,6 +69,9 @@ class PlayerAfterJumpEvent : Event()
@Nameable("playerUseMultiplier")
class PlayerUseMultiplier(var forward: Float, var sideways: Float) : Event()

@Nameable("playerInteractedItem")
class PlayerInteractedItem(val player: PlayerEntity, val hand: Hand, val actionResult: ActionResult) : Event()

@Nameable("playerStrafe")
class PlayerVelocityStrafe(val movementInput: Vec3d, val speed: Float, val yaw: Float, var velocity: Vec3d) : Event()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ import net.ccbluex.liquidbounce.features.module.modules.movement.step.ModuleStep
import net.ccbluex.liquidbounce.features.module.modules.movement.terrainspeed.ModuleTerrainSpeed
import net.ccbluex.liquidbounce.features.module.modules.player.*
import net.ccbluex.liquidbounce.features.module.modules.player.autoplay.ModuleAutoPlay
import net.ccbluex.liquidbounce.features.module.modules.player.chestStealer.ModuleChestStealer
import net.ccbluex.liquidbounce.features.module.modules.player.invcleaner.ModuleInventoryCleaner
import net.ccbluex.liquidbounce.features.module.modules.player.nofall.ModuleNoFall
import net.ccbluex.liquidbounce.features.module.modules.player.invcleaner.ModuleInventoryCleaner
import net.ccbluex.liquidbounce.features.module.modules.render.*
import net.ccbluex.liquidbounce.features.module.modules.render.murdermystery.ModuleMurderMystery
import net.ccbluex.liquidbounce.features.module.modules.render.nametags.ModuleNametags
Expand Down Expand Up @@ -197,7 +196,6 @@ object ModuleManager : Listenable, Iterable<Module> by modules {
ModuleAutoTotem,
ModuleAutoWalk,
ModuleBlink,
ModuleChestStealer,
ModuleEagle,
ModuleFastUse,
ModuleInventoryCleaner,
Expand All @@ -207,6 +205,7 @@ object ModuleManager : Listenable, Iterable<Module> by modules {
ModuleRegen,
ModuleZoot,
ModuleAutoPlay,
ModuleSmartEat,

// Render
ModuleAnimations,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*
* This file is part of LiquidBounce (https://github.com/CCBlueX/LiquidBounce)
*
* Copyright (c) 2015 - 2023 CCBlueX
*
* LiquidBounce is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LiquidBounce is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LiquidBounce. If not, see <https://www.gnu.org/licenses/>.
*/
package net.ccbluex.liquidbounce.features.module.modules.combat

import net.ccbluex.liquidbounce.config.ToggleableConfigurable
import net.ccbluex.liquidbounce.event.events.OverlayRenderEvent
import net.ccbluex.liquidbounce.event.events.PlayerInteractedItem
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.event.repeatable
import net.ccbluex.liquidbounce.features.module.Category
import net.ccbluex.liquidbounce.features.module.Module
import net.ccbluex.liquidbounce.features.module.modules.player.invcleaner.HotbarItemSlot
import net.ccbluex.liquidbounce.render.renderEnvironmentForGUI
import net.ccbluex.liquidbounce.utils.client.SilentHotbar
import net.ccbluex.liquidbounce.utils.item.Hotbar
import net.ccbluex.liquidbounce.utils.sorting.ComparatorChain
import net.ccbluex.liquidbounce.utils.sorting.compareByCondition
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.option.KeyBinding
import net.minecraft.entity.effect.StatusEffects
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.potion.PotionUtil
import net.minecraft.util.ActionResult
import net.minecraft.util.Identifier
import net.minecraft.util.UseAction
import kotlin.math.absoluteValue
import kotlin.math.max

/**
* SmartEat module
*
* Makes it easier to eat
*/

object ModuleSmartEat : Module("SmartEat", Category.PLAYER) {
private val HOTBAR_OFFHAND_LEFT_TEXTURE = Identifier("hud/hotbar_offhand_left")

private val swapBackDelay by int("SwapBackDelay", 5, 1..20)

private val preferGappleHealth by float("PreferGappleHealthThreshold", 9f, 0f..20f)
private val preferNotchAppleHealth by float("PreferNotchAppleHealthThreshold", 2f, 0f..20f)
private val preferHealthPotHealth by float("PreferHealthPotHealthThreshold", 12f, 0f..20f)

private object Estimator {
fun findBestFood(): HotbarItemSlot? {
val comparator = ComparatorChain<Pair<HotbarItemSlot, FoodEstimationData>>(
// If there is an indication for a special item, we should use it. Items with lower health threshold
// are preferred since their usage is probably more urgent.
compareByDescending { it.second.healthThreshold },
compareBy { it.second.restoredHunger },
// Use the closest slot
compareByDescending { (it.first.hotbarSlot - SilentHotbar.serversideSlot).absoluteValue },
// Just for stabilization reasons
compareBy { SilentHotbar.serversideSlot }
)

return Hotbar.slots
.mapNotNull { slot -> getFoodEstimationData(slot.itemStack)?.let { slot to it } }
.maxWithOrNull(comparator)?.first
}

private class FoodEstimationData(val restoredHunger: Int = 0, val healthThreshold: Int = 20)

private fun getFoodEstimationData(itemStack: ItemStack): FoodEstimationData? {
val item = itemStack.item

val prefersGapples = player.health <= preferGappleHealth
val prefersNotchApple = player.health <= preferNotchAppleHealth
val prefersHealthPot = player.health <= preferHealthPotHealth

return when {
prefersGapples && item == Items.POTION -> {
val hasHealthEffect =
PotionUtil.getPotionEffects(itemStack).any {
it.effectType == StatusEffects.INSTANT_HEALTH
}

if (hasHealthEffect)
FoodEstimationData(healthThreshold = preferHealthPotHealth.toInt())
else
null
}
prefersHealthPot && item == Items.GOLDEN_APPLE -> {
FoodEstimationData(
healthThreshold = preferHealthPotHealth.toInt(),
restoredHunger = item.foodComponent!!.hunger
)
}
prefersNotchApple && item == Items.ENCHANTED_GOLDEN_APPLE -> {
FoodEstimationData(
healthThreshold = preferNotchAppleHealth.toInt(),
restoredHunger = item.foodComponent!!.hunger
)
}
item.foodComponent != null -> FoodEstimationData(restoredHunger = item.foodComponent!!.hunger)
else -> null
}
}
}

private object SilentOffhand : ToggleableConfigurable(this, "SilentOffhand", true) {
private object RenderSlot : ToggleableConfigurable(this, "RenderSlot", true) {
private val offset by int("Offset", 26, 0..40)
val renderHandler = handler<OverlayRenderEvent> {
renderEnvironmentForGUI {
// MC-Rendering code for off-hand

val currentFood = Estimator.findBestFood() ?: return@renderEnvironmentForGUI
val dc = DrawContext(mc, mc.bufferBuilders.entityVertexConsumers)
val scaledWidth = dc.scaledWindowWidth
val scaledHeight = dc.scaledWindowHeight
val i: Int = scaledWidth / 2
val x = i - 91 - 26 - offset
val y = scaledHeight - 16 - 3
dc.drawItemInSlot(mc.textRenderer, currentFood.itemStack, x, y)
dc.drawItem(currentFood.itemStack, x, y)
dc.drawGuiTexture(
HOTBAR_OFFHAND_LEFT_TEXTURE, i - 91 - 29 - offset,
scaledHeight - 23, 29, 24
)
}
}
}

val InteractionHandler = handler<PlayerInteractedItem> { event ->
if (!enabled)
return@handler
if (event.actionResult != ActionResult.PASS)
return@handler

val currentFood = Estimator.findBestFood() ?: return@handler

val alwaysEdible = currentFood.itemStack.item.foodComponent?.isAlwaysEdible == false

if (!player.canConsume(false) && alwaysEdible) {
return@handler
}

SilentHotbar.selectSlotSilently(
this@SilentOffhand,
currentFood.hotbarSlot,
swapBackDelay.coerceAtLeast(5)
)
}

val tickHandler = repeatable {
val useAction = player.activeItem.useAction

if (useAction != UseAction.EAT && useAction != UseAction.DRINK)
return@repeatable
if (!SilentHotbar.isSlotModified(this@SilentOffhand))
return@repeatable

// if we are already eating, we want to keep the silent slot
SilentHotbar.selectSlotSilently(this@SilentOffhand, SilentHotbar.serversideSlot, swapBackDelay)
}

init {
tree(RenderSlot)
}
}

private object AutoEat : ToggleableConfigurable(this, "AutoEat", true) {
private val minHunger by int("MinHunger", 15, 0..20)

private val tickHandler = repeatable {

if (player.hungerManager.foodLevel < minHunger) {
waitUntil {
eat()
player.hungerManager.foodLevel > minHunger
}

KeyBinding.setKeyPressed(mc.options.useKey.boundKey, false)
}
}

fun eat() {
val currentBestFood = Estimator.findBestFood() ?: return

SilentHotbar.selectSlotSilently(AutoEat, currentBestFood.hotbarSlot, swapBackDelay)

KeyBinding.setKeyPressed(mc.options.useKey.boundKey, true)
}
}


init {
tree(SilentOffhand)
tree(AutoEat)
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ internal object NoFallMLG : Choice("MLG") {

val options = BlockPlacementTargetFindingOptions(
listOf(Vec3i(0, 0, 0)),
player.inventory.getStack(itemForMLG!!),
itemForMLG!!.itemStack,
CenterTargetPositionFactory,
BlockPlacementTargetFindingOptions.PRIORITIZE_LEAST_BLOCK_DISTANCE,
player.pos
Expand Down Expand Up @@ -106,7 +106,7 @@ internal object NoFallMLG : Choice("MLG") {
}

val item = itemForMLG ?: return@repeatable
SilentHotbar.selectSlotSilently(this, item, 1)
SilentHotbar.selectSlotSilently(this, item.hotbarSlotForServer, 1)

doPlacement(rayTraceResult)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ internal object NoFallRettungsplatform : Choice("Rettungsplatform") {
return@repeatable
}

useHotbarSlotOrOffhand(HotbarItemSlot(itemToPlatform))
useHotbarSlotOrOffhand(itemToPlatform)

// Wait 5 seconds
waitTicks(20 * 5)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ object ModuleIgnite : Module("Ignite", Category.WORLD) {

val options = BlockPlacementTargetFindingOptions(
listOf(Vec3i(0, 0, 0)),
player.inventory.getStack(slot),
slot.itemStack,
CenterTargetPositionFactory,
BlockPlacementTargetFindingOptions.PRIORITIZE_LEAST_BLOCK_DISTANCE,
player.pos
Expand Down Expand Up @@ -133,7 +133,7 @@ object ModuleIgnite : Module("Ignite", Category.WORLD) {

CombatManager.pauseCombatForAtLeast(1)

SilentHotbar.selectSlotSilently(this, slot, 1)
SilentHotbar.selectSlotSilently(this, slot.hotbarSlotForServer, 1)

doPlacement(raycast, Hand.MAIN_HAND)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ object ModuleAutoFarm : Module("AutoFarm", Category.WORLD) {

item ?: return@repeatable

SilentHotbar.selectSlotSilently(this, item, AutoPlaceCrops.swapBackDelay.random())
SilentHotbar.selectSlotSilently(this, item.hotbarSlotForServer, AutoPlaceCrops.swapBackDelay.random())
doPlacement(rayTraceResult)

waitTicks(interactDelay.random())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ object SilentHotbar : Listenable {
}
}

/**
* Returns if the slot is currently getting modified by a given requester
*/
fun isSlotModified(requester: Any?) = hotbarState?.requester == requester

val tickHandler = handler<GameTickEvent>(priority = 1001) {
val hotbarState = hotbarState ?: return@handler

Expand Down
Loading

0 comments on commit 3e3ff43

Please sign in to comment.