diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/mixins/minecraft/entity/MixinLivingEntity.java b/src/main/java/net/ccbluex/liquidbounce/injection/mixins/minecraft/entity/MixinLivingEntity.java index 5db01493a69..00a875ce789 100644 --- a/src/main/java/net/ccbluex/liquidbounce/injection/mixins/minecraft/entity/MixinLivingEntity.java +++ b/src/main/java/net/ccbluex/liquidbounce/injection/mixins/minecraft/entity/MixinLivingEntity.java @@ -28,7 +28,7 @@ import net.ccbluex.liquidbounce.features.module.modules.combat.elytratarget.ModuleElytraTarget; import net.ccbluex.liquidbounce.features.module.modules.movement.*; import net.ccbluex.liquidbounce.features.module.modules.render.DoRender; -import net.ccbluex.liquidbounce.features.module.modules.render.ModuleAnimations; +import net.ccbluex.liquidbounce.features.module.modules.render.animations.ModuleAnimations; import net.ccbluex.liquidbounce.features.module.modules.render.ModuleAntiBlind; import net.ccbluex.liquidbounce.features.module.modules.render.hitfx.ModuleHitFX; import net.ccbluex.liquidbounce.features.module.modules.world.scaffold.ModuleScaffold; diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/mixins/minecraft/item/MixinItemInHandRenderer.java b/src/main/java/net/ccbluex/liquidbounce/injection/mixins/minecraft/item/MixinItemInHandRenderer.java index db88fce3fe8..78d5c9c0f84 100644 --- a/src/main/java/net/ccbluex/liquidbounce/injection/mixins/minecraft/item/MixinItemInHandRenderer.java +++ b/src/main/java/net/ccbluex/liquidbounce/injection/mixins/minecraft/item/MixinItemInHandRenderer.java @@ -25,8 +25,9 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Axis; import net.ccbluex.liquidbounce.features.module.modules.combat.ModuleSwordBlock; -import net.ccbluex.liquidbounce.features.module.modules.render.ModuleAnimations; +import net.ccbluex.liquidbounce.features.module.modules.render.animations.ModuleAnimations; import net.ccbluex.liquidbounce.features.module.modules.render.ModuleSilentHotbar; +import net.ccbluex.liquidbounce.features.module.modules.render.animations.SwingAnimations; import net.ccbluex.liquidbounce.utils.client.SilentHotbar; import net.ccbluex.liquidbounce.utils.item.ItemCategorizationsKt; import net.ccbluex.liquidbounce.utils.render.FirstPersonShieldTint; @@ -37,6 +38,7 @@ import net.minecraft.client.renderer.item.ItemStackRenderState; import net.minecraft.core.component.DataComponents; import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.HumanoidArm; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; @@ -46,10 +48,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.*; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @@ -82,6 +81,34 @@ private void hookFirstPersonShieldTint( original.call(instance, poseStack, submitNodeCollector, lightCoords, overlayCoords, outlineColor); } + @Inject( + method = "swingArm", + at = @At("HEAD"), + cancellable = true + ) + private void cancelVanillaSwing( + float attack, + PoseStack poseStack, + int invert, + HumanoidArm arm, + CallbackInfo ci + ) { + AbstractClientPlayer player = Minecraft.getInstance().player; + + if (player == null || !ModuleAnimations.INSTANCE.getRunning() || !SwingAnimations.INSTANCE.getEnabled() || ModuleSwordBlock.shouldAnimateSwordBlock(player)) { + return; + } + + SwingAnimations.INSTANCE.onRenderItem( + player, + InteractionHand.MAIN_HAND, + attack, + poseStack + ); + + ci.cancel(); + } + @Inject(method = "submitArmWithItem", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;pushPose()V", shift = At.Shift.AFTER)) private void hookRenderFirstPersonItem( AbstractClientPlayer player, float frameInterp, float xRot, InteractionHand hand, float attack, ItemStack itemStack, float inverseArmHeight, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, int lightCoords, CallbackInfo ci) { @@ -246,7 +273,6 @@ private int hookItemUseItem(int original, @Local(argsOnly = true, name = "player if (ModuleSwordBlock.shouldAnimateSwordBlock(player)) { return 7200; } - return original; } 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 674df89241b..537bfeb6de0 100644 --- a/src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt +++ b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/ModuleManager.kt @@ -184,7 +184,7 @@ import net.ccbluex.liquidbounce.features.module.modules.player.cheststealer.Modu 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.offhand.ModuleOffhand -import net.ccbluex.liquidbounce.features.module.modules.render.ModuleAnimations +import net.ccbluex.liquidbounce.features.module.modules.render.animations.ModuleAnimations import net.ccbluex.liquidbounce.features.module.modules.render.ModuleAntiBlind import net.ccbluex.liquidbounce.features.module.modules.render.ModuleAspect import net.ccbluex.liquidbounce.features.module.modules.render.ModuleAutoF5 diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/ModuleAnimations.kt b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/animations/ModuleAnimations.kt similarity index 61% rename from src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/ModuleAnimations.kt rename to src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/animations/ModuleAnimations.kt index 046ad5e59d8..fe8013a39ca 100644 --- a/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/ModuleAnimations.kt +++ b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/animations/ModuleAnimations.kt @@ -16,7 +16,8 @@ * 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 + +package net.ccbluex.liquidbounce.features.module.modules.render.animations import com.mojang.blaze3d.vertex.PoseStack import com.mojang.math.Axis @@ -28,9 +29,10 @@ import net.ccbluex.liquidbounce.event.events.PlayerStrideEvent 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.ModuleSwordBlock +import net.ccbluex.liquidbounce.utils.math.toRadians import net.minecraft.util.Mth import net.minecraft.world.entity.HumanoidArm +import org.joml.Quaternionf /** * Animations module @@ -48,6 +50,7 @@ object ModuleAnimations : ClientModule("Animations", ModuleCategories.RENDER, al tree(MainHand) tree(OffHand) tree(EquipOffset) + tree(SwingAnimations) } object MainHand : ToggleableValueGroup(this, "MainHand", false) { @@ -75,10 +78,16 @@ object ModuleAnimations : ClientModule("Animations", ModuleCategories.RENDER, al * of a sword. * This choice is only used when the [ModuleSwordBlock] module is enabled. */ + + val swingAnimation = SwingAnimations val blockAnimationChoice = choices( "BlockingAnimation", OneSevenAnimation, arrayOf( OneSevenAnimation, - PushdownAnimation + PushdownAnimation, + SigmaAnimation, + ExhibitionAnimation, + AvatarAnimation, + DortwareAnimation ) ) @@ -116,6 +125,8 @@ object ModuleAnimations : ClientModule("Animations", ModuleCategories.RENDER, al /** * A choice that aims to transform the held item transformation during the swing progress. */ + + abstract class AnimationMode(name: String) : Mode(name) { override val parent: ModeValueGroup<*> @@ -176,4 +187,136 @@ object ModuleAnimations : ClientModule("Animations", ModuleCategories.RENDER, al } + object SigmaAnimation : AnimationMode("Sigma") { + + private val translateY by float("Y", 0.1f, 0.05f..0.3f) + + override fun transform(matrices: PoseStack, arm: HumanoidArm, equipProgress: Float, swingProgress: Float) { + + val sine = Mth.sin(Mth.sqrt(swingProgress) * Math.PI) + val sideM = if (arm == HumanoidArm.RIGHT) 1.0f else -1.0f + + val rot = Quaternionf().rotationAxis( + (-sine * 27.5f * sideM).toRadians(), + -8.0f * sideM, + 0.0f, + 9.0f + ).rotateAxis( + (-sine * 45.0f * sideM).toRadians(), + 1.0f * sideM, + sine / 2.0f, + 0.0f + ) + matrices.mulPose(rot) + + matrices.translate(0.0f, translateY, 0.0f) + applySwingOffset(matrices, arm, 0f) + } + } + + object ExhibitionAnimation : AnimationMode("Exhibition") { + + private val translateY by float("Y", 0.1f, 0.05f..0.3f) + + override fun transform(matrices: PoseStack, arm: HumanoidArm, equipProgress: Float, swingProgress: Float) { + + val sine = Mth.sin(Mth.sqrt(swingProgress) * Math.PI) + val sideM = if (arm == HumanoidArm.RIGHT) 1.0f else -1.0f + + matrices.translate(0.0, -0.1, 0.0) + + applySwingOffset(matrices, arm, 0f) + + matrices.translate(0.1F, 0.4F, -0.1F) + + val rot = Quaternionf().rotationAxis( + (-sine * 30.0F * sideM).toRadians(), + sine.div(2F), + 0.0F, + 9.0F + ).rotateAxis( + (-sine * 50.0F * sideM).toRadians(), + 0.8F * sideM, + sine.div(2F), + 0F + ) + matrices.mulPose(rot) + + matrices.translate(0.0f, translateY - 0.2f, 0.0f) + + } + + } + + object AvatarAnimation : AnimationMode("Avatar") { + + private val translateY by float("Y", 0.1f, 0.05f..0.3f) + + override fun transform(matrices: PoseStack, arm: HumanoidArm, equipProgress: Float, swingProgress: Float) { + + val sine = Mth.sin(Mth.sqrt(swingProgress) * Math.PI) + val sine1 = Mth.sin(swingProgress * swingProgress * Math.PI) + val sideM = if (arm == HumanoidArm.RIGHT) 1.0f else -1.0f + + matrices.translate(0.2F * sideM, translateY, 0F) + + val rot = Quaternionf().rotationAxis( + (0.0F * sideM).toRadians(), + 0.0F, + 1.0F, + 0.0F + ).rotateY( + (sine1 * -20.0F * sideM).toRadians() + ).rotateZ( + (sine * -20.0F * sideM).toRadians() + ).rotateAxis( + (sine * -40.0F * sideM).toRadians(), + 1.0F * sideM, + 0.0F, + 0.0F + ) + matrices.mulPose(rot) + + applySwingOffset(matrices, arm, 0f) + } + } + + object DortwareAnimation : AnimationMode("Dortware") { + + private val translateY by float("Y", 0.1f, 0.05f..0.3f) + + override fun transform(matrices: PoseStack, arm: HumanoidArm, equipProgress: Float, swingProgress: Float) { + + val sine = Mth.sin(Mth.sqrt(swingProgress) * Math.PI) + val sqrtSwing = Mth.sqrt(swingProgress) + val altSine = Mth.sin(sqrtSwing * Math.PI - 3) + + val rot = Quaternionf().rotationAxis( + (-sine * 10).toRadians(), + 0.0f, + 15.0f, + 200.0f + ).rotateAxis( + (-sine * 10f).toRadians(), + 300.0f, + sine.div(2f), + 1.0f + ) + matrices.mulPose(rot) + + matrices.translate(3.4, 0.3, -0.4) + matrices.translate(-2.10f, -0.2f, 0.1f) + + val rot1 = Quaternionf().rotationAxis( + (altSine * 13.0f).toRadians(), + -10.0f, + -1.4f, + -10.0f + ) + matrices.mulPose(rot1) + + matrices.translate(if(arm == HumanoidArm.RIGHT) -1f else -2f, translateY, 0f) + + } + } } diff --git a/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/animations/SwingAnimations.kt b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/animations/SwingAnimations.kt new file mode 100644 index 00000000000..8db4cd4e705 --- /dev/null +++ b/src/main/kotlin/net/ccbluex/liquidbounce/features/module/modules/render/animations/SwingAnimations.kt @@ -0,0 +1,154 @@ +/* + * 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.animations + +import com.mojang.blaze3d.vertex.PoseStack +import com.mojang.math.Axis +import net.ccbluex.liquidbounce.config.types.group.ToggleableValueGroup +import net.ccbluex.liquidbounce.config.types.list.Tagged +import net.minecraft.client.player.AbstractClientPlayer +import net.minecraft.util.Mth +import net.minecraft.world.InteractionHand +import net.minecraft.world.entity.HumanoidArm + +object SwingAnimations : ToggleableValueGroup(ModuleAnimations, "SwingAnimations", false) { + + val mode by enumChoice("Mode", Mode.Spin) + + enum class Mode(override val tag: String) : Tagged { + Swipe("Swipe"), Spin("Spin"), Hook("Hook"), Dash("Dash"), + Tap("Tap"), Inject("Inject"), Slap("Slap"), Akrien("Akrien"), + Smooth("Smooth"), Power("Power"), Feast("Feast") + } + + private fun fSin(v: Float) = Mth.sin(v.toDouble()) + private fun fSqrt(v: Float) = Mth.sqrt(v) + private const val PI = Math.PI.toFloat() + + fun onRenderItem(player: AbstractClientPlayer, + hand: InteractionHand, + swingProgress: Float, + poseStack: PoseStack + ) { + val isMainHand = hand == InteractionHand.MAIN_HAND + val arm = if (isMainHand) player.mainArm else player.mainArm.opposite + + applySwing(poseStack, swingProgress, arm) + } + + @Suppress("LongMethod") + private fun applySwing(poseStack: PoseStack, + swing: Float, + arm: HumanoidArm + ) { + val i = if (arm == HumanoidArm.RIGHT) 1 else -1 + val fSqrt = fSqrt(swing) + val g = fSin(fSqrt * PI) + val n = -0.4f * g + val sin1 = fSin(swing * swing * PI) + val sin2 = fSin(fSqrt(swing) * PI) + val sinSmooth = (fSin(swing * PI) * 0.5f) + + when (mode) { + Mode.Swipe -> { + poseStack.mulPose(Axis.YP.rotationDegrees(i * (45.0f + swing * -20.0f))) + poseStack.mulPose(Axis.ZP.rotationDegrees(i * g * -70.0f)) + poseStack.mulPose(Axis.XP.rotationDegrees(-70f)) + poseStack.mulPose(Axis.YP.rotationDegrees(i * -45.0f)) + } + Mode.Spin -> { + poseStack.mulPose(Axis.XP.rotationDegrees(swing * -360f)) + } + Mode.Hook -> { + poseStack.mulPose(Axis.XP.rotationDegrees(50f)) + poseStack.mulPose(Axis.YP.rotationDegrees(i * (-30f * (1f - g) - 30f))) + poseStack.mulPose(Axis.ZP.rotationDegrees(i * 110f)) + } + Mode.Dash -> { + poseStack.mulPose(Axis.XP.rotationDegrees(50f)) + poseStack.mulPose(Axis.YP.rotationDegrees(i * (-60f * g - 50f))) + poseStack.mulPose(Axis.ZP.rotationDegrees(i * 110f)) + } + Mode.Tap -> { + poseStack.mulPose(Axis.XP.rotationDegrees(50f)) + poseStack.mulPose(Axis.YP.rotationDegrees(i * -60f)) + poseStack.mulPose(Axis.ZP.rotationDegrees(i * (110f + 20f * g))) + } + Mode.Inject -> { + poseStack.translate(0.0, 0.0, (-g / 4.0)) + poseStack.mulPose(Axis.XP.rotationDegrees(-120f)) + } + Mode.Slap -> { + poseStack.mulPose(Axis.XP.rotationDegrees(-fSin(swing * 3f) * 60f)) + poseStack.mulPose(Axis.ZP.rotationDegrees(i * -60f * g)) + } + Mode.Akrien -> { + if (swing > 0) { + poseStack.mulPose(Axis.YP.rotationDegrees(i * 45f)) + poseStack.mulPose(Axis.XP.rotationDegrees(g * -85.0f)) + poseStack.translate(i * -0.1, 0.28, 0.2) + poseStack.mulPose(Axis.XP.rotationDegrees(-85.0f)) + } else { + val m = 0.2f * fSin(fSqrt * PI * 2f) + val f2 = -0.2f * fSin(swing * PI) + poseStack.translate(i * n.toDouble(), m.toDouble(), f2.toDouble()) + applySwingOffset(poseStack, arm, swing) + } + } + + Mode.Smooth -> { + poseStack.mulPose(Axis.YP.rotationDegrees(i * (45.0f + sin1 * -20.0f))) + poseStack.mulPose(Axis.ZP.rotationDegrees(i * sin2 * -20.0f)) + poseStack.mulPose(Axis.XP.rotationDegrees(sin2 * -80.0f)) + poseStack.mulPose(Axis.YP.rotationDegrees(i * -45.0f)) + poseStack.translate(0.0, -0.1, 0.0) + } + + Mode.Power -> { + poseStack.translate((-sinSmooth * sinSmooth * sin1 * i).toDouble(), 0.0, 0.0) + poseStack.mulPose(Axis.YP.rotationDegrees(i * 61f)) + poseStack.mulPose(Axis.ZP.rotationDegrees(sin2)) + poseStack.mulPose(Axis.YP.rotationDegrees(sin2 * sin1 * -5.0f)) + poseStack.mulPose(Axis.XP.rotationDegrees(sin2 * sin1 * -30.0f)) + poseStack.mulPose(Axis.XP.rotationDegrees(-60.0f)) + poseStack.mulPose(Axis.XP.rotationDegrees(sinSmooth * -60.0f)) + } + + Mode.Feast -> { + poseStack.mulPose(Axis.YP.rotationDegrees(i * 30f)) + poseStack.mulPose(Axis.YP.rotationDegrees(sin2 * 75.0f * i)) + poseStack.mulPose(Axis.XP.rotationDegrees(sin2 * -45.0f)) + poseStack.mulPose(Axis.YP.rotationDegrees(i * 30f)) + poseStack.mulPose(Axis.XP.rotationDegrees(-80.0f)) + poseStack.mulPose(Axis.YP.rotationDegrees(i * 35f)) + } + } + } + + private fun applySwingOffset(poseStack: PoseStack, arm: HumanoidArm, swing: Float) { + val i = if (arm == HumanoidArm.RIGHT) 1 else -1 + val f1 = fSin(swing * swing * PI) + poseStack.mulPose(Axis.YP.rotationDegrees(i * (45.0f + f1 * -20.0f))) + val g = fSin(fSqrt(swing) * PI) + poseStack.mulPose(Axis.ZP.rotationDegrees(i * g * -20.0f)) + poseStack.mulPose(Axis.XP.rotationDegrees(g * -80.0f)) + poseStack.mulPose(Axis.YP.rotationDegrees(i * -45.0f)) + } +}