diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt index e03d2c6c315..0756fe1206a 100644 --- a/src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt +++ b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt @@ -226,6 +226,7 @@ import net.ccbluex.liquidbounce.features.module.modules.render.ModuleSkinChanger import net.ccbluex.liquidbounce.features.module.modules.render.ModuleSmoothCamera import net.ccbluex.liquidbounce.features.module.modules.render.ModuleStorageESP import net.ccbluex.liquidbounce.features.module.modules.render.ModuleTNTTimer +import net.ccbluex.liquidbounce.features.module.modules.render.targeticon.ModuleTargetIcon import net.ccbluex.liquidbounce.features.module.modules.render.ModuleTracers import net.ccbluex.liquidbounce.features.module.modules.render.ModuleTrueSight import net.ccbluex.liquidbounce.features.module.modules.render.ModuleVoidESP @@ -676,6 +677,7 @@ object ModuleManager : EventListener, Collection by modules { ModuleSkinChanger, ModuleProtectionZones, ModuleCrosshair, + ModuleTargetIcon, // World ModuleAirPlace, diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/targeticon/ModuleTargetIcon.kt b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/targeticon/ModuleTargetIcon.kt new file mode 100644 index 00000000000..317081d17f6 --- /dev/null +++ b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/targeticon/ModuleTargetIcon.kt @@ -0,0 +1,138 @@ +/* + * This file is part of LiquidBounce (https://github.com/CCBlueX/LiquidBounce) + * + * Copyright (c) 2015 - 2026 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 . + */ +package net.ccbluex.liquidbounce.features.module.modules.render.targeticon + +import net.ccbluex.fastutil.toEnumSet +import net.ccbluex.liquidbounce.config.utils.TextureMode +import net.ccbluex.liquidbounce.event.events.AttackEntityEvent +import net.ccbluex.liquidbounce.event.events.OverlayRenderEvent +import net.ccbluex.liquidbounce.event.handler +import net.ccbluex.liquidbounce.features.module.ClientModule +import net.ccbluex.liquidbounce.features.module.ModuleCategories +import net.ccbluex.liquidbounce.features.module.modules.combat.killaura.ModuleKillAura +import net.ccbluex.liquidbounce.render.ClientRenderPipelines +import net.ccbluex.liquidbounce.render.drawTexQuad +import net.ccbluex.liquidbounce.render.engine.type.Color4b +import net.ccbluex.liquidbounce.utils.entity.interpolateCurrentPosition +import net.ccbluex.liquidbounce.utils.render.WorldToScreen +import net.ccbluex.liquidbounce.utils.render.textureSetup +import net.minecraft.client.renderer.texture.DynamicTexture +import net.minecraft.world.entity.LivingEntity + +object ModuleTargetIcon : ClientModule("TargetIcon", ModuleCategories.RENDER) { + + private val textureMode = choices("Source", 1) { + arrayOf( + TextureMode.Custom(it), + TextureMode.Builtin( + it, + TargetIconRegistry.WEARY, + TargetIconRegistry.entries.filter { e -> e.texture != null }.toEnumSet() + ) + ) + } + + private val onlyKillAura by boolean("OnlyKillAura", false) + private val randomIcon by boolean("RandomIcon", false) + private val iconSize by int("Size", 80, 16..256) + private val heightFraction by float("Height", 0.85f, 0f..1.5f) + private val offsetX by float("OffsetX", 0f, -200f..200f) + private val offsetY by float("OffsetY", -10f, -200f..200f) + + @Volatile private var trackedEntity: LivingEntity? = null + @Volatile private var randomTexture: DynamicTexture? = null + @Volatile private var showUntil: Long = 0L + @Volatile private var shownSince: Long = 0L + + private val currentTexture: DynamicTexture? + get() = if (randomIcon) randomTexture ?: textureMode.activeMode.texture else textureMode.activeMode.texture + + override fun onDisabled() { + showUntil = 0L + shownSince = 0L + trackedEntity = null + randomTexture = null + } + + @Suppress("unused") + private val attackHandler = handler { event -> + val target = event.entity + + if (target !is LivingEntity || !target.isAlive) return@handler + if (onlyKillAura && !ModuleKillAura.running) return@handler + + val now = System.currentTimeMillis() + val isNewTarget = now >= showUntil || trackedEntity !== target + + if (isNewTarget && randomIcon) { + randomTexture = TargetIconRegistry.entries.filter { it.texture != null }.randomOrNull()?.texture + } + + if (currentTexture == null) return@handler + + if (now >= showUntil) { + shownSince = now + } + + trackedEntity = target + showUntil = now + 1000L + } + + @Suppress("unused") + private val overlayRenderHandler = handler { event -> + val texture = currentTexture ?: return@handler + val now = System.currentTimeMillis() + + if (now >= showUntil) { + showUntil = 0L + shownSince = 0L + trackedEntity = null + randomTexture = null + return@handler + } + + val target = trackedEntity + if (target == null || !target.isAlive || target.isRemoved) { + showUntil = 0L + shownSince = 0L + trackedEntity = null + randomTexture = null + return@handler + } + + val worldPos = target.interpolateCurrentPosition(event.tickDelta) + .add(0.0, target.bbHeight.toDouble() * heightFraction, 0.0) + val screenPos = WorldToScreen.calculateScreenPos(worldPos) ?: return@handler + + val size = iconSize.toFloat() + val x0 = screenPos.x + offsetX - size * 0.5f + val y0 = screenPos.y + offsetY - size * 0.5f + + event.context.drawTexQuad( + textureSetup = texture.textureSetup, + x0 = x0, y0 = y0, + x1 = x0 + size, y1 = y0 + size, + u1 = 0f, v1 = 0f, + u2 = 1f, v2 = 1f, + argb = Color4b(255, 255, 255, 255).argb, + pipeline = ClientRenderPipelines.GUI.TexQuadNoCull, + ) + } + +} diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/targeticon/TargetIconRegistry.kt b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/targeticon/TargetIconRegistry.kt new file mode 100644 index 00000000000..7909dcfa5f9 --- /dev/null +++ b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/targeticon/TargetIconRegistry.kt @@ -0,0 +1,51 @@ +/* + * This file is part of LiquidBounce (https://github.com/CCBlueX/LiquidBounce) + * + * Copyright (c) 2015 - 2026 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 . + */ +package net.ccbluex.liquidbounce.features.module.modules.render.targeticon + +import net.ccbluex.liquidbounce.LiquidBounce +import net.ccbluex.liquidbounce.config.utils.TextureMode +import net.ccbluex.liquidbounce.utils.render.asTexture +import net.ccbluex.liquidbounce.utils.render.readNativeImage +import net.minecraft.client.renderer.texture.DynamicTexture + +@Suppress("unused") +enum class TargetIconRegistry( + override val tag: String, + hasTexture: Boolean = true, +) : TextureMode.Builtin.Preset { + NONE("None", false), + WEARY("Weary"), + ANGRY("Angry"), + SUNGLASSES("Sunglasses"), + VOMIT("Vomit"), + HEART("Heart"), + TUNG("Tung"), + BOYKISSER("Boykisser"), + EATSUKI("Eatsuki"), + CAT("Cat"), + SUNNA("Sunna"); + + override val texture: DynamicTexture? = if (hasTexture) { + LiquidBounce.resource("images/${name.lowercase()}.png") + .readNativeImage() + .asTexture { "TargetIcon: $tag" } + } else { + null + } +} diff --git a/src/main/resources/resources/liquidbounce/images/angry.png b/src/main/resources/resources/liquidbounce/images/angry.png new file mode 100644 index 00000000000..3900e75fa65 Binary files /dev/null and b/src/main/resources/resources/liquidbounce/images/angry.png differ diff --git a/src/main/resources/resources/liquidbounce/images/boykisser.png b/src/main/resources/resources/liquidbounce/images/boykisser.png new file mode 100644 index 00000000000..07674f54ee2 Binary files /dev/null and b/src/main/resources/resources/liquidbounce/images/boykisser.png differ diff --git a/src/main/resources/resources/liquidbounce/images/cat.png b/src/main/resources/resources/liquidbounce/images/cat.png new file mode 100644 index 00000000000..c5b3702cf16 Binary files /dev/null and b/src/main/resources/resources/liquidbounce/images/cat.png differ diff --git a/src/main/resources/resources/liquidbounce/images/eatsuki.png b/src/main/resources/resources/liquidbounce/images/eatsuki.png new file mode 100644 index 00000000000..cc1f36609fb Binary files /dev/null and b/src/main/resources/resources/liquidbounce/images/eatsuki.png differ diff --git a/src/main/resources/resources/liquidbounce/images/heart.png b/src/main/resources/resources/liquidbounce/images/heart.png new file mode 100644 index 00000000000..70c7b7e1ac2 Binary files /dev/null and b/src/main/resources/resources/liquidbounce/images/heart.png differ diff --git a/src/main/resources/resources/liquidbounce/images/sunglasses.png b/src/main/resources/resources/liquidbounce/images/sunglasses.png new file mode 100644 index 00000000000..06bf2f24c1a Binary files /dev/null and b/src/main/resources/resources/liquidbounce/images/sunglasses.png differ diff --git a/src/main/resources/resources/liquidbounce/images/sunna.png b/src/main/resources/resources/liquidbounce/images/sunna.png new file mode 100644 index 00000000000..f81973f2c3e Binary files /dev/null and b/src/main/resources/resources/liquidbounce/images/sunna.png differ diff --git a/src/main/resources/resources/liquidbounce/images/tung.png b/src/main/resources/resources/liquidbounce/images/tung.png new file mode 100644 index 00000000000..afb5efec19a Binary files /dev/null and b/src/main/resources/resources/liquidbounce/images/tung.png differ diff --git a/src/main/resources/resources/liquidbounce/images/vomit.png b/src/main/resources/resources/liquidbounce/images/vomit.png new file mode 100644 index 00000000000..d1c67571cdc Binary files /dev/null and b/src/main/resources/resources/liquidbounce/images/vomit.png differ diff --git a/src/main/resources/resources/liquidbounce/images/weary.png b/src/main/resources/resources/liquidbounce/images/weary.png new file mode 100644 index 00000000000..5eedcd212cf Binary files /dev/null and b/src/main/resources/resources/liquidbounce/images/weary.png differ diff --git a/src/main/resources/resources/liquidbounce/lang/en_us.json b/src/main/resources/resources/liquidbounce/lang/en_us.json index e4bf10f26c3..2d311ad5117 100644 --- a/src/main/resources/resources/liquidbounce/lang/en_us.json +++ b/src/main/resources/resources/liquidbounce/lang/en_us.json @@ -679,6 +679,7 @@ "liquidbounce.module.snapTap.description": "Prioritize the last pressed movement key.", "liquidbounce.module.scaffold.description": "Automatically places blocks beneath your feet allowing you to walk across gaps.", "liquidbounce.module.serverCrasher.description": "Attempts to crash the server by exploiting known bugs.", + "liquidbounce.module.targetIcon.description": "Render a Image when attacking someone.", "liquidbounce.module.serverCrasher.messages.singleplayer": "Server Crasher is intended for multiplayer servers only.", "liquidbounce.module.serverCrasher.messages.signExploit.missingProtocolTranslator": "ViaFabricPlus is required for this exploit to work.", "liquidbounce.module.serverCrasher.messages.signExploit.wrongProtocolVersion": "Select 1.8 as protocol version for this exploit to work.",