diff --git a/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/element/elements/Keystrokes.kt b/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/element/elements/Keystrokes.kt index 777539371f3..48fd3a97f17 100644 --- a/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/element/elements/Keystrokes.kt +++ b/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/element/elements/Keystrokes.kt @@ -13,7 +13,9 @@ import net.ccbluex.liquidbounce.ui.font.GameFontRenderer import net.ccbluex.liquidbounce.utils.extensions.lerpWith import net.ccbluex.liquidbounce.utils.extensions.safeDiv import net.ccbluex.liquidbounce.utils.render.ColorSettingsInteger +import net.ccbluex.liquidbounce.utils.render.ColorUtils import net.ccbluex.liquidbounce.utils.render.RenderUtils +import net.ccbluex.liquidbounce.utils.render.RenderUtils.withOutline import java.awt.Color import kotlin.math.nextDown @@ -26,9 +28,11 @@ class Keystrokes : Element("Keystrokes", 2.0, 34.0) { private val renderBorder by boolean("RenderBorder", false) private val borderColors = ColorSettingsInteger(this, "Border") { renderBorder }.with(Color.BLUE) private val borderWidth by float("BorderWidth", 1.5F, 0.5F..5F) { renderBorder } - private val shrinkOnPress by boolean("ShrinkOnPress", true) - private val shrinkPercentage by int("ShrinkPercentage", 90, 50..100, suffix = "%") { shrinkOnPress } - private val shrinkSpeed by int("ShrinkSpeed", 2, 0..5, suffix = "Ticks") { shrinkOnPress } + private val onPressAnimation by choices( + "OnPressAnimationMode", arrayOf("None", "Shrink", "Fill", "ReverseFill"), "Shrink" + ) + private val shrinkPercentage by int("ShrinkPercentage", 90, 50..100, suffix = "%") { onPressAnimation == "Shrink" } + private val shrinkSpeed by int("ShrinkSpeed", 2, 0..5, suffix = "Ticks") { onPressAnimation == "Shrink" } private var shadow by boolean("Text-Shadow", true) private val font by font("Font", Fonts.fontSemibold35) @@ -45,32 +49,33 @@ class Keystrokes : Element("Keystrokes", 2.0, 34.0) { private val borderColor get() = borderColors.color() + private val nonFillModes = arrayOf("None", "Shrink") + data class GridKey( val row: Int, val column: Int, val text: String, var scale: Float = 1f, val keystrokes: Keystrokes, - var color: Color = keystrokes.rectColor + var color: Color = keystrokes.rectColor, + var normalT: Float = 0F ) { fun updateState(isPressed: Boolean) { val min = (keystrokes.shrinkPercentage / 100f).nextDown() - val targetScale = if (isPressed && keystrokes.shrinkOnPress) min else 1f + val targetScale = if (isPressed && keystrokes.onPressAnimation in keystrokes.nonFillModes) min else 1f val deltaTime = RenderUtils.deltaTimeNormalized(keystrokes.shrinkSpeed).takeIf { it != 0.0 } ?: 1F + normalT = (normalT..if (isPressed) 0f else 1f).lerpWith(RenderUtils.deltaTimeNormalized()) + scale = (scale..targetScale).lerpWith(deltaTime) val t = 1f - (scale - min safeDiv 1f - min) - val baseColor = keystrokes.rectColor - val targetColor = keystrokes.pressColor - - val r = (baseColor.red + (targetColor.red - baseColor.red) * t).toInt() - val g = (baseColor.green + (targetColor.green - baseColor.green) * t).toInt() - val b = (baseColor.blue + (targetColor.blue - baseColor.blue) * t).toInt() - val a = (baseColor.alpha + (targetColor.alpha - baseColor.alpha) * t).toInt() - - color = Color(r, g, b, a) + color = if (keystrokes.onPressAnimation !in keystrokes.nonFillModes) { + keystrokes.rectColor + } else { + ColorUtils.interpolateColor(keystrokes.rectColor, keystrokes.pressColor, t) + } } } @@ -81,7 +86,7 @@ class Keystrokes : Element("Keystrokes", 2.0, 34.0) { private val gridLayout = arrayOf( GridKey(1, 1, "W", keystrokes = this), GridKey(2, 0, "A", keystrokes = this), - GridKey(2, 1, "S", keystrokes = this), + GridKey(2, 1, "S", keystrokes = this), GridKey(2, 2, "D", keystrokes = this), GridKey(3, 1, "Space", keystrokes = this) ) @@ -118,7 +123,7 @@ class Keystrokes : Element("Keystrokes", 2.0, 34.0) { val isPressed = movementKeys[key]?.isKeyDown == true gridKey.updateState(isPressed) - val scaledBoxSize = boxSize * scale + val scaledBoxSize = boxSize * if (onPressAnimation == "None") 1f else scale val scaledPadding = (boxSize - scaledBoxSize) / 2 val adjustedStartX = startX + scaledPadding @@ -129,6 +134,37 @@ class Keystrokes : Element("Keystrokes", 2.0, 34.0) { adjustedStartX, adjustedY, adjustedEndX, adjustedY + scaledBoxSize, color.rgb, radius ) + if (onPressAnimation !in nonFillModes) { + val reverse = onPressAnimation != "Fill" + + withOutline(main = { + val size = boxSize * gridKey.normalT.let { if (!reverse) 1 - it else it } + val padding1 = (boxSize - size) / 2 + + val adjustedStartX1 = startX + padding1 + val adjustedEndX1 = endX - padding1 + val adjustedY1 = currentY + padding1 + + RenderUtils.drawRoundedRect( + adjustedStartX1, + adjustedY1, + adjustedEndX1, + adjustedY1 + size, + if (reverse) 0 else pressColor.rgb, + radius + ) + }, toOutline = { + RenderUtils.drawRoundedRect( + adjustedStartX, + adjustedY, + adjustedEndX, + adjustedY + scaledBoxSize, + if (reverse) pressColor.rgb else 0, + radius + ) + }) + } + if (renderBorder) { RenderUtils.drawRoundedBorder( adjustedStartX, diff --git a/src/main/java/net/ccbluex/liquidbounce/utils/render/RenderUtils.kt b/src/main/java/net/ccbluex/liquidbounce/utils/render/RenderUtils.kt index b726b7b4f1a..40208e2aca6 100644 --- a/src/main/java/net/ccbluex/liquidbounce/utils/render/RenderUtils.kt +++ b/src/main/java/net/ccbluex/liquidbounce/utils/render/RenderUtils.kt @@ -79,6 +79,31 @@ object RenderUtils : MinecraftInstance { glPopMatrix() } + inline fun withOutline(main: () -> Unit, toOutline: () -> Unit) { + disableFastRender() + OutlineUtils.checkSetupFBO() + glPushMatrix() + glDisable(GL_ALPHA_TEST) + glEnable(GL_STENCIL_TEST) + glClear(GL_STENCIL_BUFFER_BIT) + + glStencilFunc(GL_ALWAYS, 1, 1) + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE) + glStencilMask(1) + + main() + + glStencilFunc(GL_EQUAL, 0, 1) + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP) + glStencilMask(0) + + toOutline() + + glStencilMask(0xFF) + glDisable(GL_STENCIL_TEST) + glEnable(GL_ALPHA_TEST) + glPopMatrix() + } fun deltaTimeNormalized(ticks: Int = 1) = (deltaTime safeDivD ticks * 50.0).coerceAtMost(1.0)