From 334395617d84b1c15592fa651cfde623c7f6d5ca Mon Sep 17 00:00:00 2001 From: stivais Date: Fri, 8 Nov 2024 23:13:14 +0200 Subject: [PATCH] hud manager, ui transformations, long standing bug fixes --- .../mixin/mixins/MixinNetworkManager.java | 2 +- .../mixin/mixins/MixinEventBus.java | 46 --- .../mixin/mixins/MixinNetworkManager.java | 2 +- .../src/main/resources/mixins.odinclient.json | 1 - .../github/stivais/ui/animation/Animating.kt | 56 +++ .../ui/constraints/measurements/Animatable.kt | 30 +- .../sizes/{Aspect.kt => AspectRatio.kt} | 2 +- .../com/github/stivais/ui/elements/Element.kt | 70 ++-- .../github/stivais/ui/elements/impl/Block.kt | 19 +- .../github/stivais/ui/elements/impl/Group.kt | 2 +- .../github/stivais/ui/elements/impl/Layout.kt | 10 +- .../github/stivais/ui/elements/impl/Popup.kt | 48 ++- .../stivais/ui/elements/impl/TextElement.kt | 32 +- .../stivais/ui/elements/scope/ElementScope.kt | 39 +- .../com/github/stivais/ui/events/events.kt | 1 - .../kotlin/com/github/stivais/ui/impl/Test.kt | 159 -------- .../github/stivais/ui/impl/Testing Command.kt | 1 - .../stivais/ui/renderer/impl/NVGRenderer.kt | 2 - .../stivais/ui/transforms/Transforms.kt | 134 +++++++ .../kotlin/com/github/stivais/ui/utils/dsl.kt | 2 +- src/main/kotlin/me/odinmain/OdinMain.kt | 9 +- .../me/odinmain/commands/impl/OdinCommand.kt | 5 +- .../kotlin/me/odinmain/events/dsl/EventDSL.kt | 30 -- .../kotlin/me/odinmain/features/Hud Editor.kt | 344 ----------------- .../kotlin/me/odinmain/features/Module.kt | 152 ++------ .../me/odinmain/features/ModuleManager.kt | 36 -- .../kotlin/me/odinmain/features/huds/HUD.kt | 94 +++++ .../me/odinmain/features/huds/HUDManager.kt | 351 ++++++++++++++++++ .../features/impl/dungeon/LeapMenu.kt | 19 +- .../features/impl/dungeon/WarpCooldown.kt | 9 +- .../features/impl/nether/EnrageDisplay.kt | 55 +-- .../features/impl/render/BPSDisplay.kt | 65 ++-- .../features/impl/render/CPSDisplay.kt | 47 +-- .../odinmain/features/impl/render/ClickGUI.kt | 143 +++---- .../features/impl/render/ServerHud.kt | 22 +- .../me/odinmain/features/settings/Setting.kt | 104 +++--- .../features/settings/impl/ActionSetting.kt | 47 --- .../features/settings/impl/BooleanSetting.kt | 79 ++-- .../features/settings/impl/ColorSetting.kt | 126 ++++--- .../features/settings/impl/DropdownSetting.kt | 43 ++- .../features/settings/impl/HUDSetting.kt | 36 +- .../features/settings/impl/KeybindSetting.kt | 134 ++++--- .../features/settings/impl/NumberSetting.kt | 103 ++--- .../features/settings/impl/SelectorSetting.kt | 109 +++--- .../features/settings/impl/StringSetting.kt | 83 +++-- .../features/settings/impl/UISetting.kt | 41 +- .../kotlin/me/odinmain/utils/ServerUtils.kt | 2 + .../me/odinmain/utils/skyblock/ItemUtils.kt | 6 +- .../kotlin/me/odinmain/utils/ui/UIHandler.kt | 55 +++ .../odinmain/utils/ui/elements}/TextInput.kt | 64 +++- .../kotlin/me/odinmain/utils/ui/general.kt | 54 --- .../kotlin/me/odinmain/utils/ui/utilities.kt | 119 ++++++ 52 files changed, 1661 insertions(+), 1583 deletions(-) delete mode 100644 odinclient/src/main/java/me/odinclient/mixin/mixins/MixinEventBus.java rename src/main/kotlin/com/github/stivais/ui/constraints/sizes/{Aspect.kt => AspectRatio.kt} (88%) delete mode 100644 src/main/kotlin/com/github/stivais/ui/impl/Test.kt create mode 100644 src/main/kotlin/com/github/stivais/ui/transforms/Transforms.kt delete mode 100644 src/main/kotlin/me/odinmain/events/dsl/EventDSL.kt delete mode 100644 src/main/kotlin/me/odinmain/features/Hud Editor.kt create mode 100644 src/main/kotlin/me/odinmain/features/huds/HUD.kt create mode 100644 src/main/kotlin/me/odinmain/features/huds/HUDManager.kt delete mode 100644 src/main/kotlin/me/odinmain/features/settings/impl/ActionSetting.kt create mode 100644 src/main/kotlin/me/odinmain/utils/ui/UIHandler.kt rename src/main/kotlin/{com/github/stivais/ui/elements/impl => me/odinmain/utils/ui/elements}/TextInput.kt (90%) delete mode 100644 src/main/kotlin/me/odinmain/utils/ui/general.kt create mode 100644 src/main/kotlin/me/odinmain/utils/ui/utilities.kt diff --git a/odin/src/main/java/me/odin/mixin/mixins/MixinNetworkManager.java b/odin/src/main/java/me/odin/mixin/mixins/MixinNetworkManager.java index 009d69903..7c5105865 100644 --- a/odin/src/main/java/me/odin/mixin/mixins/MixinNetworkManager.java +++ b/odin/src/main/java/me/odin/mixin/mixins/MixinNetworkManager.java @@ -24,7 +24,7 @@ private void onReceivePacket(ChannelHandlerContext context, Packet packet, Ca @Inject(method = {"sendPacket(Lnet/minecraft/network/Packet;)V"}, at = {@At("HEAD")}, cancellable = true) private void onSendPacket(Packet packet, CallbackInfo ci) { - if (!ServerUtils.INSTANCE.handleSendPacket(packet)) { + if (!ServerUtils.handleSendPacket(packet)) { if (postAndCatch(new PacketSentEvent(packet)) && !ci.isCancelled()) ci.cancel(); } diff --git a/odinclient/src/main/java/me/odinclient/mixin/mixins/MixinEventBus.java b/odinclient/src/main/java/me/odinclient/mixin/mixins/MixinEventBus.java deleted file mode 100644 index 2917dc1bb..000000000 --- a/odinclient/src/main/java/me/odinclient/mixin/mixins/MixinEventBus.java +++ /dev/null @@ -1,46 +0,0 @@ -package me.odinclient.mixin.mixins; - -import me.odinmain.events.dsl.EventDSL; -import me.odinmain.events.dsl.Listener; -import net.minecraftforge.fml.common.eventhandler.Event; -import net.minecraftforge.fml.common.eventhandler.EventBus; -import net.minecraftforge.fml.common.eventhandler.EventPriority; -import net.minecraftforge.fml.common.eventhandler.IEventListener; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.ArrayList; -import java.util.concurrent.ConcurrentHashMap; - -@Mixin(EventBus.class) -public class MixinEventBus { - - @Shadow - private ConcurrentHashMap> listeners; - - @Final - @Shadow - private int busID; - - @Inject(method = "register(Ljava/lang/Object;)V", at = @At("RETURN"), remap = false) - private void onRegister(Object obj, CallbackInfo ci) { - if (obj instanceof EventDSL) { - ArrayList list = listeners.computeIfAbsent(obj, k -> new ArrayList<>()); - ArrayList> dslListeners = ((EventDSL) obj).getListeners(); - list.addAll(dslListeners); - - for (Listener listener : dslListeners) { - try { - Event event = listener.getEvent().getConstructor().newInstance(); - event.getListenerList().register(busID, EventPriority.NORMAL, listener); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } -} diff --git a/odinclient/src/main/java/me/odinclient/mixin/mixins/MixinNetworkManager.java b/odinclient/src/main/java/me/odinclient/mixin/mixins/MixinNetworkManager.java index 8049a9e85..6e1febe53 100644 --- a/odinclient/src/main/java/me/odinclient/mixin/mixins/MixinNetworkManager.java +++ b/odinclient/src/main/java/me/odinclient/mixin/mixins/MixinNetworkManager.java @@ -24,7 +24,7 @@ private void onReceivePacket(ChannelHandlerContext context, Packet packet, Ca @Inject(method = {"sendPacket(Lnet/minecraft/network/Packet;)V"}, at = {@At("HEAD")}, cancellable = true) private void onSendPacket(Packet packet, CallbackInfo ci) { - if (!ServerUtils.INSTANCE.handleSendPacket(packet)) { + if (!ServerUtils.handleSendPacket(packet)) { if (postAndCatch(new PacketSentEvent(packet)) && !ci.isCancelled()) ci.cancel(); } diff --git a/odinclient/src/main/resources/mixins.odinclient.json b/odinclient/src/main/resources/mixins.odinclient.json index 3ecd53143..36a4957c1 100644 --- a/odinclient/src/main/resources/mixins.odinclient.json +++ b/odinclient/src/main/resources/mixins.odinclient.json @@ -6,7 +6,6 @@ "mixins": [ "accessors.IBlockAccessor", "mixins.MixinChunk", - "mixins.MixinEventBus", "mixins.MixinNetworkManager", "mixins.MixinRendererLivingEntity", "mixins.MixinS1CPacketEntityMetadata", diff --git a/src/main/kotlin/com/github/stivais/ui/animation/Animating.kt b/src/main/kotlin/com/github/stivais/ui/animation/Animating.kt index 7582364ee..d27caeea9 100644 --- a/src/main/kotlin/com/github/stivais/ui/animation/Animating.kt +++ b/src/main/kotlin/com/github/stivais/ui/animation/Animating.kt @@ -5,6 +5,62 @@ sealed interface Animating { fun animate(duration: Float, type: Animations): Animation? interface Swapping : Animating { + fun swap() + + class Impl(var from: Float, var to: Float) : Swapping { + + private var current: Float = 0f + + private var before: Float? = null + + var animation: Animation? = null + private set + + fun get(): Float { + animation?.let { anim -> + val progress = anim.get() + val from = before ?: from + current = from + (to - from) * progress + + if (anim.finished) { + animation = null + before = null + swap() + } + return current + } + return from + } + + override fun swap() { + val temp = to + to = from + from = temp + } + + override fun animate(duration: Float, type: Animations): Animation? { + if (duration == 0f) { + swap() + } else { + if (animation != null) { + before = current + swap() + animation = Animation(duration * animation!!.get(), type) + } else { + animation = Animation(duration, type) + } + } + return animation + } + } + + companion object { + /** + * Constant version for [Animating.Swapping.Impl], intended for delegating and overriding get() + */ + @JvmStatic + val Impl = Impl(0f, 0f) + } } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/stivais/ui/constraints/measurements/Animatable.kt b/src/main/kotlin/com/github/stivais/ui/constraints/measurements/Animatable.kt index aa5afb158..f59bb2acd 100644 --- a/src/main/kotlin/com/github/stivais/ui/constraints/measurements/Animatable.kt +++ b/src/main/kotlin/com/github/stivais/ui/constraints/measurements/Animatable.kt @@ -3,7 +3,9 @@ package com.github.stivais.ui.constraints.measurements import com.github.stivais.ui.animation.Animating import com.github.stivais.ui.animation.Animation import com.github.stivais.ui.animation.Animations -import com.github.stivais.ui.constraints.* +import com.github.stivais.ui.constraints.Constraint +import com.github.stivais.ui.constraints.Measurement +import com.github.stivais.ui.constraints.Type import com.github.stivais.ui.elements.Element /** @@ -17,7 +19,10 @@ import com.github.stivais.ui.elements.Element * * @see Animatable.Raw */ -class Animatable(var from: Constraint, var to: Constraint): Measurement, Animating.Swapping { +class Animatable( + var from: Constraint, + var to: Constraint +): Measurement, Animating.Swapping by Animating.Swapping.Impl { constructor(from: Constraint, to: Constraint, swapIf: Boolean) : this(from, to) { if (swapIf) { @@ -55,27 +60,6 @@ class Animatable(var from: Constraint, var to: Constraint): Measurement, Animati return from.get(element, type) } - override fun animate(duration: Float, type: Animations): Animation? { - if (duration == 0f) { - swap() - } else { - if (animation != null) { - before = current - swap() - animation = Animation(duration * animation!!.get(), type) - } else { - animation = Animation(duration, type) - } - } - return animation - } - - override fun swap() { - val temp = to - to = from - from = temp - } - override fun reliesOnChild(): Boolean { return from.reliesOnChild() || to.reliesOnChild() } diff --git a/src/main/kotlin/com/github/stivais/ui/constraints/sizes/Aspect.kt b/src/main/kotlin/com/github/stivais/ui/constraints/sizes/AspectRatio.kt similarity index 88% rename from src/main/kotlin/com/github/stivais/ui/constraints/sizes/Aspect.kt rename to src/main/kotlin/com/github/stivais/ui/constraints/sizes/AspectRatio.kt index 4291d3a0e..123fbeb0c 100644 --- a/src/main/kotlin/com/github/stivais/ui/constraints/sizes/Aspect.kt +++ b/src/main/kotlin/com/github/stivais/ui/constraints/sizes/AspectRatio.kt @@ -5,7 +5,7 @@ import com.github.stivais.ui.constraints.Size import com.github.stivais.ui.constraints.Type import com.github.stivais.ui.elements.Element -class Aspect(private val ratio: Float) : Size { +class AspectRatio(private val ratio: Float) : Size { override fun get(element: Element, type: Type): Float { return if (type.axis == HORIZONTAL) element.height * ratio else element.width / ratio } diff --git a/src/main/kotlin/com/github/stivais/ui/elements/Element.kt b/src/main/kotlin/com/github/stivais/ui/elements/Element.kt index cc7c8c5f7..e939cddc9 100644 --- a/src/main/kotlin/com/github/stivais/ui/elements/Element.kt +++ b/src/main/kotlin/com/github/stivais/ui/elements/Element.kt @@ -14,6 +14,7 @@ import com.github.stivais.ui.events.Lifetime import com.github.stivais.ui.events.Mouse import com.github.stivais.ui.operation.UIOperation import com.github.stivais.ui.renderer.Renderer +import com.github.stivais.ui.transforms.Transforms import com.github.stivais.ui.utils.loop abstract class Element(constraints: Constraints?, var color: Color? = null) { @@ -71,25 +72,10 @@ abstract class Element(constraints: Constraints?, var color: Color? = null) { } // + private var transforms: ArrayList? = null - // todo: rework - - var alphaAnim: Animatable? = null - var rotateAnim: Animatable? = null - - var alpha = 1f - set(value) { - field = value.coerceIn(0f, 1f) - } - + // this is needed to track current scale var scale = 1f - set(value) { - field = value.coerceAtLeast(0f) - } - - var scaledCentered = true - - var rotation = 0f // @@ -127,18 +113,23 @@ abstract class Element(constraints: Constraints?, var color: Color? = null) { it.position(x, y) it.positionChildren() } - if (constraints.width.reliesOnChild()) width = constraints.width.get(this, Type.W) - if (constraints.height.reliesOnChild()) height = constraints.height.get(this, Type.H) + sy + val widthRelies = constraints.width.reliesOnChild() + val heightRelies = constraints.height.reliesOnChild() + + if (widthRelies) width = constraints.width.get(this, Type.W) + if (heightRelies) height = constraints.height.get(this, Type.H) + sy + + if (widthRelies || heightRelies) parent?.redrawInternal = true } - private var _redraw = true + var redrawInternal = true var redraw: Boolean - get() = _redraw + get() = redrawInternal set(value) { if (value) { val element = getElementToRedraw() - element._redraw = true + element.redrawInternal = true } } @@ -168,8 +159,8 @@ abstract class Element(constraints: Constraints?, var color: Color? = null) { // IDK is it worth fun preRender() { - if (_redraw) { - _redraw = false + if (redrawInternal) { + redrawInternal = false size() positionChildren() clip() @@ -187,30 +178,8 @@ abstract class Element(constraints: Constraints?, var color: Color? = null) { fun render() { if (!renders) return renderer.push() - if (alphaAnim != null) { - alpha = alphaAnim!!.get(this, Type.X) - } - if (rotateAnim != null) { - rotation = rotateAnim!!.get(this, Type.X) - } - if (alpha != 1f) { - renderer.globalAlpha(alpha) - } - if (scale != 1f) { - var x = x - var y = y - if (scaledCentered) { - x += width / 2f - y += height / 2f - } - renderer.translate(x, y) - renderer.scale(scale, scale) - renderer.translate(-x, -y) - } - if (rotation != 0f) { - renderer.translate(x + width / 2f, y + height / 2f) - renderer.rotate(rotation) - renderer.translate(-(x + width / 2f), -(y + height / 2f)) + transforms?.loop { + it.apply(this, renderer) } draw() if (scissors) renderer.pushScissor(x, y, width, height) @@ -243,6 +212,11 @@ abstract class Element(constraints: Constraints?, var color: Color? = null) { ui.operations!!.add(operation) } + fun addTransform(transform: Transforms) { + if (transforms == null) transforms = arrayListOf() + transforms!!.add(transform) + } + fun addElement(element: Element) { onElementAdded(element) if (elements == null) elements = arrayListOf() diff --git a/src/main/kotlin/com/github/stivais/ui/elements/impl/Block.kt b/src/main/kotlin/com/github/stivais/ui/elements/impl/Block.kt index 3643a1315..f2593d456 100644 --- a/src/main/kotlin/com/github/stivais/ui/elements/impl/Block.kt +++ b/src/main/kotlin/com/github/stivais/ui/elements/impl/Block.kt @@ -2,7 +2,9 @@ package com.github.stivais.ui.elements.impl import com.github.stivais.ui.color.Color import com.github.stivais.ui.color.alpha -import com.github.stivais.ui.constraints.* +import com.github.stivais.ui.constraints.Constraints +import com.github.stivais.ui.constraints.Measurement +import com.github.stivais.ui.constraints.Type import com.github.stivais.ui.elements.Element import com.github.stivais.ui.renderer.Gradient as GradientType @@ -10,6 +12,8 @@ import com.github.stivais.ui.renderer.Gradient as GradientType open class Block( constraints: Constraints?, color: Color, + var outlineColor: Color? = null, + var outlineThickness: Measurement? = null, protected val radius: FloatArray? = null ) : Element(constraints, color) { @@ -19,16 +23,13 @@ open class Block( } } - var outlineColor: Color? = null - var outline: Measurement? = null - override fun draw() { if (radius == null) { if (color!!.alpha != 0) { renderer.rect(x, y, width, height, color!!.get(this)) } if (outlineColor != null && outlineColor!!.alpha != 0) { - val thickness = outline!!.get(this, Type.W) + val thickness = outlineThickness!!.get(this, Type.W) renderer.hollowRect(x, y, width, height, thickness, outlineColor!!.get(this), 0f) } } else { @@ -36,7 +37,7 @@ open class Block( renderer.rect(x, y, width, height, color!!.rgba, radius[0], radius[1], radius[2], radius[3]) } if (outlineColor != null && outlineColor!!.alpha != 0) { - val thickness = outline!!.get(this, Type.W) + val thickness = outlineThickness!!.get(this, Type.W) renderer.hollowRect(x, y, width, height, thickness, outlineColor!!.rgba, radius[0], radius[1], radius[2], radius[3]) } } @@ -48,14 +49,14 @@ open class Block( private val color2: Color, radius: FloatArray?, private val gradient: GradientType - ) : Block(constraints, color1, radius) { + ) : Block(constraints, color1, null, null, radius) { override fun draw() { if (radius == null) { if (color!!.alpha != 0) { renderer.gradientRect(x, y, width, height, color!!.get(this), color2.get(this), gradient) } if (outlineColor != null && outlineColor!!.alpha != 0) { - val thickness = outline!!.get(this, Type.W) + val thickness = outlineThickness!!.get(this, Type.W) renderer.hollowRect(x, y, width, height, thickness, outlineColor!!.get(this), 0f) } } else { @@ -63,7 +64,7 @@ open class Block( renderer.gradientRect(x, y, width, height, color!!.rgba, color2.rgba, gradient, radius[0], radius[1], radius[2], radius[3]) } if (outlineColor != null && outlineColor!!.alpha != 0) { - val thickness = outline!!.get(this, Type.W) + val thickness = outlineThickness!!.get(this, Type.W) renderer.hollowRect(x, y, width, height, thickness, outlineColor!!.rgba, radius[0], radius[1], radius[2], radius[3]) } } diff --git a/src/main/kotlin/com/github/stivais/ui/elements/impl/Group.kt b/src/main/kotlin/com/github/stivais/ui/elements/impl/Group.kt index 935d4cb39..eb4a924ac 100644 --- a/src/main/kotlin/com/github/stivais/ui/elements/impl/Group.kt +++ b/src/main/kotlin/com/github/stivais/ui/elements/impl/Group.kt @@ -8,6 +8,6 @@ import com.github.stivais.ui.utils.replaceUndefined class Group(constraints: Constraints?) : Element(constraints.replaceUndefined(w = Bounding, h = Bounding)) { override fun draw() { -// renderer.hollowRect(x, y, width, height, 1f, java.awt.Color.WHITE.rgb) + // doesn't draw } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/stivais/ui/elements/impl/Layout.kt b/src/main/kotlin/com/github/stivais/ui/elements/impl/Layout.kt index a4d48cb92..a91e53962 100644 --- a/src/main/kotlin/com/github/stivais/ui/elements/impl/Layout.kt +++ b/src/main/kotlin/com/github/stivais/ui/elements/impl/Layout.kt @@ -15,7 +15,7 @@ import org.jetbrains.annotations.MustBeInvokedByOverriders // todo: delete open class Layout( constraints: Constraints?, - protected val padding: Size?, + var padding: Size?, ) : Element(constraints?.replaceUndefined(w = Bounding, h = Bounding)) { protected var lastLink: OldLinked? = null @@ -30,8 +30,8 @@ open class Layout( lastLink = link if (padding != null && element !is Divider) { - val padding = if (padding !is Percent) padding else percentFix(padding) - createDivider(amount = padding) + val padding = padding as? Percent + if (padding != null) createDivider(amount = padding) } } if (c.y is Undefined) { @@ -54,8 +54,8 @@ open class Layout( lastLink = link if (padding != null && element !is Divider && elements != null) { - val padding = if (padding !is Percent) padding else percentFix(padding) - createDivider(amount = padding) + val padding = padding as? Percent + if (padding != null) createDivider(amount = padding) } } super.onElementAdded(element) diff --git a/src/main/kotlin/com/github/stivais/ui/elements/impl/Popup.kt b/src/main/kotlin/com/github/stivais/ui/elements/impl/Popup.kt index 5bc1ebf74..a359bf454 100644 --- a/src/main/kotlin/com/github/stivais/ui/elements/impl/Popup.kt +++ b/src/main/kotlin/com/github/stivais/ui/elements/impl/Popup.kt @@ -1,33 +1,34 @@ package com.github.stivais.ui.elements.impl -import com.github.stivais.ui.animation.Animation import com.github.stivais.ui.animation.Animations import com.github.stivais.ui.constraints.Constraints import com.github.stivais.ui.constraints.size import com.github.stivais.ui.constraints.sizes.Bounding import com.github.stivais.ui.elements.scope.ElementDSL import com.github.stivais.ui.elements.scope.ElementScope -import com.github.stivais.ui.operation.AnimationOperation +import com.github.stivais.ui.transforms.Transforms.Alpha +import com.github.stivais.ui.transforms.Transforms.Scale import com.github.stivais.ui.utils.seconds -class Popup(element: Group, private val smooth: Boolean) : ElementScope(element) { +class Popup( + element: Group, + private val alphaAnimation: Alpha.Animated, + private val scaleAnimation: Scale.Animated, + private val smooth: Boolean +) : ElementScope(element) { fun closePopup(smooth: Boolean = this.smooth) { var finished = false if (smooth) { - // todo: clean animations like these up - AnimationOperation(Animation(0.1.seconds, Animations.EaseInQuint).onFinish { finished = true }) { - element.alpha = 1f - it - element.scale = 1f - it - }.add() + alphaAnimation.animate(0.25.seconds, Animations.EaseInQuint) + scaleAnimation.animate(0.25.seconds, Animations.EaseInQuint)?.onFinish { + finished = true + } } else { finished = true } - operation { - if (finished) { - ui.main.removeElement(element) - } + if (finished) ui.main.removeElement(element) finished } } @@ -38,14 +39,23 @@ fun ElementDSL.popup( smooth: Boolean = false, block: Popup.() -> Unit, ): Popup { - val group = Group(constraints) + + val alphaAnimation = Alpha.Animated(from = 0f, to = 1f) + val scaleAnimation = Scale.Animated(from = 0f, to = 1f, centered = true) + + val group = Group(constraints).apply { + addTransform(alphaAnimation) + addTransform(scaleAnimation) + } ui.main.addElement(group) - if (smooth) { + redraw() - AnimationOperation(Animation(0.25.seconds, Animations.EaseOutQuint)) { - group.alpha = it - group.scale = it - }.add() + if (smooth) { + alphaAnimation.animate(0.25.seconds, Animations.EaseOutQuint) + scaleAnimation.animate(0.25.seconds, Animations.EaseOutQuint) + } else { + alphaAnimation.swap() + scaleAnimation.swap() } - return Popup(group, smooth).also(block) + return Popup(group, alphaAnimation, scaleAnimation, smooth).also(block) } \ No newline at end of file diff --git a/src/main/kotlin/com/github/stivais/ui/elements/impl/TextElement.kt b/src/main/kotlin/com/github/stivais/ui/elements/impl/TextElement.kt index 975347e39..efe3eac7e 100644 --- a/src/main/kotlin/com/github/stivais/ui/elements/impl/TextElement.kt +++ b/src/main/kotlin/com/github/stivais/ui/elements/impl/TextElement.kt @@ -1,31 +1,34 @@ package com.github.stivais.ui.elements.impl -import com.github.stivais.ui.UI import com.github.stivais.ui.color.Color -import com.github.stivais.ui.constraints.Positions -import com.github.stivais.ui.constraints.Size -import com.github.stivais.ui.constraints.Type +import com.github.stivais.ui.constraints.* import com.github.stivais.ui.constraints.measurements.Pixel -import com.github.stivais.ui.constraints.px +import com.github.stivais.ui.constraints.measurements.Undefined import com.github.stivais.ui.elements.Element import com.github.stivais.ui.elements.scope.ElementScope import com.github.stivais.ui.renderer.Font -import com.github.stivais.ui.utils.replaceUndefined open class TextElement( - text: String, - val font: Font = UI.defaultFont, - color: Color = Color.WHITE, - constraints: Positions? = null, + string: String, + val font: Font, + color: Color, + constraints: Positions = at(Undefined, Undefined), size: Size, -) : Element(constraints.replaceUndefined(w = 0.px, h = size), color) { +) : Element(constraints, color) { - open var text: String = text + private val textWidth = 0.px + + init { + constraints.width = textWidth + constraints.height = size + } + + open var text: String = string set(value) { if (field == value) return field = value redraw = true - previousHeight = 0f // forces recalculation + previousHeight = 0f } // uses to check if width should be recalculated as it is expensive to do so @@ -52,7 +55,7 @@ open class TextElement( val supplier: () -> Any?, font: Font, color: Color, - constraints: Positions?, + constraints: Positions = at(Undefined, Undefined), size: Size ) : TextElement(supplier().toString(), font, color, constraints, size) { @@ -64,6 +67,7 @@ open class TextElement( } class TextScope(text: TextElement) : ElementScope(text) { + var string: String get() = element.text set(value) { diff --git a/src/main/kotlin/com/github/stivais/ui/elements/scope/ElementScope.kt b/src/main/kotlin/com/github/stivais/ui/elements/scope/ElementScope.kt index dce4d2260..bafc5c31b 100644 --- a/src/main/kotlin/com/github/stivais/ui/elements/scope/ElementScope.kt +++ b/src/main/kotlin/com/github/stivais/ui/elements/scope/ElementScope.kt @@ -5,6 +5,8 @@ import com.github.stivais.ui.animation.Animations import com.github.stivais.ui.color.Color import com.github.stivais.ui.constraints.* import com.github.stivais.ui.constraints.measurements.Animatable +import com.github.stivais.ui.constraints.measurements.Undefined +import com.github.stivais.ui.constraints.sizes.Copying import com.github.stivais.ui.elements.Element import com.github.stivais.ui.elements.impl.* import com.github.stivais.ui.events.* @@ -12,7 +14,8 @@ import com.github.stivais.ui.operation.UIOperation import com.github.stivais.ui.renderer.Font import com.github.stivais.ui.renderer.Gradient import com.github.stivais.ui.renderer.Image -import com.github.stivais.ui.utils.radii +import com.github.stivais.ui.transforms.Transforms +import com.github.stivais.ui.utils.radius open class ElementScope(val element: E) { @@ -93,9 +96,11 @@ open class ElementScope(val element: E) { fun block( constraints: Constraints? = null, color: Color, + outlineColor: Color? = null, + outlineThickness: Measurement? = null, radius: FloatArray? = null, block: BlockScope.() -> Unit = {} - ) = create(BlockScope(Block(constraints, color, radius)), block) + ) = create(BlockScope(Block(constraints, color, outlineColor, outlineThickness, radius)), block) @DSL fun block( @@ -110,7 +115,7 @@ open class ElementScope(val element: E) { fun text( text: String, font: Font = UI.defaultFont, - pos: Positions? = null, + pos: Positions = at(Undefined, Undefined), size: Size = 50.percent, color: Color = Color.WHITE, block: TextScope.() -> Unit = {} @@ -121,27 +126,16 @@ open class ElementScope(val element: E) { text: () -> Any?, font: Font = UI.defaultFont, color: Color = Color.WHITE, - pos: Positions? = null, + pos: Positions = at(Undefined, Undefined), size: Size = 50.percent, block: TextScope.() -> Unit = {} ) = create(TextScope(TextElement.Supplied(text, font, color, pos, size)), block) - @DSL - fun textInput( - text: String = "", - placeholder: String = "", - constraints: Positions? = null, - size: Size = 50.percent, - maxWidth: Size? = null, - censored: Boolean = false, - onTextChange: (string: String) -> Unit - ) = create(TextScope(TextInput(text, placeholder, constraints, size, maxWidth, censored, onTextChange = onTextChange))) - @DSL fun image( image: Image, constraints: Constraints? = null, - radius: FloatArray = 0.radii(), + radius: FloatArray = 0.radius(), dsl: ElementScope.() -> Unit = {} ) = create(ElementScope(ImageElement(image, constraints, radius)), dsl) @@ -149,7 +143,7 @@ open class ElementScope(val element: E) { fun image( image: String, constraints: Constraints? = null, - radius: FloatArray = 0.radii(), + radius: FloatArray = 0.radius(), dsl: ElementScope.() -> Unit = {} ) = create(ElementScope(ImageElement(Image(image), constraints, radius)), dsl) @@ -280,6 +274,10 @@ open class ElementScope(val element: E) { element.addOperation(this) } + fun transform(transform: Transforms) { + element.addTransform(transform) + } + fun Element.scope(block: ElementScope<*>.() -> Unit) { add() block(createScope()) @@ -292,12 +290,12 @@ open class BlockScope(block: Block) : ElementScope(block) { get() = element.outlineColor val outline: Measurement? - get() = element.outline + get() = element.outlineThickness @DSL fun outline(color: Color, thickness: Measurement = 1.px) { element.outlineColor = color - element.outline = thickness + element.outlineThickness = thickness } } @@ -312,6 +310,9 @@ open class LayoutScope(layout: Layout) : ElementScope(layout) { fun divider(amount: Size) { element.createDivider(amount) } + + @DSL + fun section(size: Size, block: ElementScope.() -> Unit) = group(size(Copying, size), block) } @DslMarker diff --git a/src/main/kotlin/com/github/stivais/ui/events/events.kt b/src/main/kotlin/com/github/stivais/ui/events/events.kt index ea8374631..6fbe23b52 100644 --- a/src/main/kotlin/com/github/stivais/ui/events/events.kt +++ b/src/main/kotlin/com/github/stivais/ui/events/events.kt @@ -93,7 +93,6 @@ interface Key : Event { interface Focused : Event { - // todo: migrate data class Clicked(val button: Int = 0 /* this doesn't matter for accepting event */) : Focused { override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/src/main/kotlin/com/github/stivais/ui/impl/Test.kt b/src/main/kotlin/com/github/stivais/ui/impl/Test.kt deleted file mode 100644 index 030675539..000000000 --- a/src/main/kotlin/com/github/stivais/ui/impl/Test.kt +++ /dev/null @@ -1,159 +0,0 @@ -package com.github.stivais.ui.impl - -import com.github.stivais.ui.UI -import com.github.stivais.ui.color.Color -import com.github.stivais.ui.constraints.Constraints -import com.github.stivais.ui.constraints.constrain -import com.github.stivais.ui.constraints.copies -import com.github.stivais.ui.constraints.px -import com.github.stivais.ui.elements.Element -import com.github.stivais.ui.elements.impl.Grid -import com.github.stivais.ui.events.Lifetime -import com.github.stivais.ui.renderer.Framebuffer -import me.odinmain.OdinMain.mc -import me.odinmain.utils.skyblock.heldItem -import me.odinmain.utils.skyblock.modMessage -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.RenderHelper -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import org.lwjgl.opengl.GL11 - - -fun basic() = UI { - -// Column(at(Center, Center), 5.px, 5.px).scope { -//// block(constrain(100.px, 100.px, 200.px, 200.px), Color.RED) -// text("wtf", pos = at(x = Center), size = 20.px) -// text("a", pos = at(x = Center), size = 20.px) -// text("123", pos = at(x = Center), size = 20.px) -// text("bcdef", pos = at(x = Center), size = 20.px) -// text("ghjijk", pos = at(x = Center), size = 20.px) -// } - - - -// Grid(copies()).scope { -// repeat(4) { -// group(size(50.percent, 50.percent)) { -// image( -// "https://mc-heads.net/avatar/Stivais/128" -// ) -// } -// } -// } - val heldItem = heldItem ?: return@UI modMessage("no held item") - - val item2 = ItemStack(Item.getByNameOrId("minecraft:fireworks")) - - Grid(copies()).scope { - repeat(5) { - Test(constrain(w = (100 + it * 10).px, h = (100 + it * 10).px), heldItem).add() - } -// Test(constrain(w = 100.px, h = 100.px), heldItem).add() -// Test(constrain(w = 100.px, h = 100.px), heldItem).add() -// Test(constrain(w = 100.px, h = 100.px), heldItem).add() -// Test(constrain(w = 100.px, h = 100.px), heldItem).add() - } - -} - -// idk doesn t seem to work -class Test(constraints: Constraints, val item: ItemStack) : Element(constraints) { - -// var framebuffer: Framebuffer? = null -// set(value) { -// if (field == value) return -// field = value -// } - - private var framebuffer: Framebuffer? = null - - init { - registerEvent(Lifetime.Uninitialized) { - if (framebuffer != null) renderer.destroyFramebuffer(framebuffer!!) - true - } - } - - override fun preDraw() { - if (framebuffer == null && (width != 0f && height != 0f)) { - framebuffer = renderer.createFramebuffer(width + x / 100f, height + y / 100f) - - GlStateManager.pushMatrix() - renderer.push() - renderer.bindFramebuffer(framebuffer!!) - - GlStateManager.enableDepth() - GlStateManager.enableAlpha() - GlStateManager.enableBlend() - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA) - GlStateManager.enableTexture2D() - - // setup overlay rendering - val w = (width).toDouble() - val h = (height).toDouble() - GL11.glMatrixMode(GL11.GL_PROJECTION) - GL11.glPushMatrix() - GL11.glLoadIdentity() - GL11.glOrtho(0.0, w, h, 0.0, 1000.0, 3000.0) - GL11.glMatrixMode(GL11.GL_MODELVIEW) - GL11.glPushMatrix() - GL11.glLoadIdentity() - GL11.glPushMatrix() - GL11.glTranslatef(0f, 0f, -2000f) - GL11.glScalef(5f, 5f, 1f) - - RenderHelper.enableStandardItemLighting() - RenderHelper.enableGUIStandardItemLighting() - mc.renderItem.renderItemIntoGUI(item, 0, 0) - - RenderHelper.disableStandardItemLighting() - - // resetoverlay renderer - GL11.glPopMatrix() - GL11.glPopMatrix() - GL11.glMatrixMode(GL11.GL_PROJECTION) - GL11.glPopMatrix() - GL11.glMatrixMode(GL11.GL_MODELVIEW) - - GlStateManager.disableTexture2D() - GlStateManager.disableDepth() - GlStateManager.disableAlpha() - GlStateManager.disableBlend() - renderer.unbindFramebuffer() - renderer.pop() - GlStateManager.popMatrix() - -// var errorCode = GL11.glGetError() -// -// while (errorCode != GL11.GL_NO_ERROR) { -// val errorMessage = when (errorCode) { -// GL11.GL_INVALID_ENUM -> "INVALID_ENUM" -// GL11.GL_INVALID_VALUE -> "INVALID_VALUE" -// GL11.GL_INVALID_OPERATION -> "INVALID_OPERATION" -// GL11.GL_STACK_OVERFLOW -> "STACK_OVERFLOW" -// GL11.GL_STACK_UNDERFLOW -> "STACK_UNDERFLOW" -// GL11. GL_OUT_OF_MEMORY -> "OUT_OF_MEMORY" -// else -> "UNKNOWN_ERROR" -// } -// println("OpenGL error: $errorMessage ($errorCode)") -// errorCode = GL11.glGetError() // Continue checking for more errors -// } -// renderer.push() -// renderer.bindFramebuffer(framebuffer!!) -// renderer.beginFrame(1920f, 1080f) -// renderer.text("hi", x, y, 20f) -// renderer.endFrame() -// renderer.unbindFramebuffer() -// renderer.pop() - } - } - - override fun draw() { - renderer.hollowRect(x, y, width, height, 1f, Color.WHITE.rgba) - val fbo = framebuffer ?: return -// renderer.translate(x, y) - renderer.drawFramebuffer(fbo, x, y) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/github/stivais/ui/impl/Testing Command.kt b/src/main/kotlin/com/github/stivais/ui/impl/Testing Command.kt index 6c7ff473e..79b6af04c 100644 --- a/src/main/kotlin/com/github/stivais/ui/impl/Testing Command.kt +++ b/src/main/kotlin/com/github/stivais/ui/impl/Testing Command.kt @@ -7,7 +7,6 @@ import me.odinmain.features.impl.dungeon.LeapMenu.leapMenu val `ui command` = commodore("ui") { literal("test").runs { - open(basic()) } literal("leap").runs { open(leapMenu()) diff --git a/src/main/kotlin/com/github/stivais/ui/renderer/impl/NVGRenderer.kt b/src/main/kotlin/com/github/stivais/ui/renderer/impl/NVGRenderer.kt index 5f622af0a..ba6c29de2 100644 --- a/src/main/kotlin/com/github/stivais/ui/renderer/impl/NVGRenderer.kt +++ b/src/main/kotlin/com/github/stivais/ui/renderer/impl/NVGRenderer.kt @@ -310,8 +310,6 @@ class NVGRenderer(private val wrapper: Lwjgl3Wrapper) : Renderer, Lwjgl3Wrapper } private fun getIDFromFont(font: Font): Int { -// return fonts.computeIfAbsent(font) {nvgCreateFontMem(vg, font.name, font.buffer, 0)} - return fonts[font] ?: nvgCreateFontMem(vg, font.name, font.buffer, 0).also { fonts[font] = it } diff --git a/src/main/kotlin/com/github/stivais/ui/transforms/Transforms.kt b/src/main/kotlin/com/github/stivais/ui/transforms/Transforms.kt new file mode 100644 index 000000000..cff0794b5 --- /dev/null +++ b/src/main/kotlin/com/github/stivais/ui/transforms/Transforms.kt @@ -0,0 +1,134 @@ +package com.github.stivais.ui.transforms + +import com.github.stivais.ui.animation.Animating +import com.github.stivais.ui.elements.Element +import com.github.stivais.ui.elements.scope.ElementDSL +import com.github.stivais.ui.renderer.Renderer +import kotlin.reflect.KProperty + +private fun runScale(element: Element, renderer: Renderer, amount: Float, centered: Boolean) { + var x = element.x + var y = element.y + if (centered) { + x += element.width / 2f + y += element.height / 2f + } + renderer.translate(x, y) + renderer.scale(amount, amount) + renderer.translate(-x, -y) +} + +private fun runRotation(element: Element, renderer: Renderer, amount: Float) { + val x = element.x + element.width / 2f + val y = element.y + element.height / 2f + renderer.translate(x, y) + renderer.rotate(Math.toRadians(amount.toDouble()).toFloat()) + renderer.translate(-x, -y) +} + +fun interface Transforms { + + fun apply(element: Element, renderer: Renderer) + + class Scale(var amount: Float, private val centered: Boolean) : Transforms { + override fun apply(element: Element, renderer: Renderer) { + runScale(element, renderer, amount, centered) + element.scale = amount + } + + operator fun getValue(thisRef: Any?, property: KProperty<*>): Float { + return amount + } + + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Float) { + amount = value + } + + class Animated private constructor( + private val impl: Animating.Swapping.Impl, + private val centered: Boolean + ) : Transforms, Animating.Swapping by impl { + + constructor( + from: Float, + to: Float, + centered: Boolean + ) : this(Animating.Swapping.Impl(from, to), centered) + + override fun apply(element: Element, renderer: Renderer) { + val amount = impl.get() + runScale(element, renderer, amount, centered) + element.scale = amount + } + } + } + + class Alpha(var amount: Float) : Transforms { + override fun apply(element: Element, renderer: Renderer) { + if (amount != 1f) { + renderer.globalAlpha(amount) + } + } + + class Animated private constructor( + private val impl: Animating.Swapping.Impl, + ) : Transforms, Animating.Swapping by impl { + + constructor( + from: Float, + to: Float, + ) : this(Animating.Swapping.Impl(from, to)) + + override fun apply(element: Element, renderer: Renderer) { + val amount = impl.get() + if (amount != 1f) { + renderer.globalAlpha(amount) + } + } + } + } + + class Rotation(var amount: Float) : Transforms { + override fun apply(element: Element, renderer: Renderer) { + runRotation(element, renderer, amount) + } + + class Animated private constructor( + private val impl: Animating.Swapping.Impl, + ) : Transforms, Animating.Swapping by impl { + constructor( + from: Float, + to: Float, + ) : this(Animating.Swapping.Impl(from, to)) + + override fun apply(element: Element, renderer: Renderer) { + val amount = impl.get() + runRotation(element, renderer, amount) + } + } + } +} + +fun ElementDSL.scale(from: Float, to: Float, centered: Boolean = true): Transforms.Scale.Animated { + val transform = Transforms.Scale.Animated(from, to, centered) + transform(transform) + return transform +} + +fun ElementDSL.alpha(from: Float, to: Float): Transforms.Alpha.Animated { + val transform = Transforms.Alpha.Animated(from, to) + transform(transform) + return transform +} + +fun ElementDSL.alpha(amount: Float): Transforms.Alpha { + val transform = Transforms.Alpha(amount) + transform(transform) + return transform +} + +fun ElementDSL.rotation(from: Float, to: Float): Transforms.Rotation.Animated { + val transform = Transforms.Rotation.Animated(from, to) + transform(transform) + return transform +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/stivais/ui/utils/dsl.kt b/src/main/kotlin/com/github/stivais/ui/utils/dsl.kt index c4e250463..67f3e581c 100644 --- a/src/main/kotlin/com/github/stivais/ui/utils/dsl.kt +++ b/src/main/kotlin/com/github/stivais/ui/utils/dsl.kt @@ -6,7 +6,7 @@ import com.github.stivais.ui.constraints.Constraint import com.github.stivais.ui.constraints.measurements.Animatable @JvmName("radiiThis") -fun Number.radii() = radius(this) +fun Number.radius() = radius(this) /** * Takes 4 numbers, and creates a [FloatArray] with those values diff --git a/src/main/kotlin/me/odinmain/OdinMain.kt b/src/main/kotlin/me/odinmain/OdinMain.kt index 398cc8651..5bc10ad02 100644 --- a/src/main/kotlin/me/odinmain/OdinMain.kt +++ b/src/main/kotlin/me/odinmain/OdinMain.kt @@ -1,5 +1,7 @@ package me.odinmain +import com.github.stivais.ui.UIScreen +import com.github.stivais.ui.impl.`ui command` import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -13,6 +15,7 @@ import me.odinmain.config.DungeonWaypointConfig import me.odinmain.config.PBConfig import me.odinmain.events.EventDispatcher import me.odinmain.features.ModuleManager +import me.odinmain.features.huds.HUDManager import me.odinmain.features.impl.render.ClickGUI import me.odinmain.features.impl.render.DevPlayers import me.odinmain.features.impl.render.WaypointManager @@ -60,7 +63,7 @@ object OdinMain { EventDispatcher, Executor, ModuleManager, WaypointManager, DevPlayers, SkyblockPlayer, ScanUtils, HighlightRenderer, //OdinUpdater, - SplitsManager, RenderUtils2D, + SplitsManager, RenderUtils2D, UIScreen, this ).forEach { MinecraftForge.EVENT_BUS.register(it) } @@ -69,7 +72,8 @@ object OdinMain { termSimCommand, chatCommandsCommand, devCommand, highlightCommand, waypointCommand, dungeonWaypointsCommand, - petCommand, visualWordsCommand, PosMsgCommand + petCommand, visualWordsCommand, PosMsgCommand, + `ui command` ) } @@ -87,6 +91,7 @@ object OdinMain { } } + HUDManager.setupHUDs() val name = mc.session?.username?.takeIf { !it.matches(Regex("Player\\d{2,3}")) } ?: return scope.launch(Dispatchers.IO) { diff --git a/src/main/kotlin/me/odinmain/commands/impl/OdinCommand.kt b/src/main/kotlin/me/odinmain/commands/impl/OdinCommand.kt index cb97b5b0b..e84faba4e 100644 --- a/src/main/kotlin/me/odinmain/commands/impl/OdinCommand.kt +++ b/src/main/kotlin/me/odinmain/commands/impl/OdinCommand.kt @@ -3,10 +3,10 @@ package me.odinmain.commands.impl import com.github.stivais.commodore.utils.GreedyString import com.github.stivais.ui.UIScreen.Companion.open import me.odinmain.commands.commodore +import me.odinmain.features.huds.HUDManager import me.odinmain.features.impl.dungeon.dungeonwaypoints.DungeonWaypoints import me.odinmain.features.impl.render.ClickGUI import me.odinmain.features.impl.render.ClickGUI.clickGUI -import me.odinmain.features.openHUDEditor import me.odinmain.utils.ServerUtils import me.odinmain.utils.equalsOneOf import me.odinmain.utils.fillItemFromSack @@ -21,7 +21,7 @@ val mainCommand = commodore("od", "odin") { } literal("edithuds").runs { - openHUDEditor().open() + HUDManager.makeHUDEditor().open() } literal("ep").runs { @@ -76,7 +76,6 @@ val mainCommand = commodore("od", "odin") { §3- /od ep §7» §8Refills ender pearls up to 16. §3- /od ij §7» §8Refills inflatable Jerry's up to 64. §3- /od sl §7» §8Refills spirit leaps up to 16. - §3- /od sc §7» §8Tries to open SkyCrypt for the specified user in default browser. §3- /spcmd §7» §8Use /spcmd cmds for command list. §3- /visualwords §7» §8Command to replace words in the game. """.trimIndent() diff --git a/src/main/kotlin/me/odinmain/events/dsl/EventDSL.kt b/src/main/kotlin/me/odinmain/events/dsl/EventDSL.kt deleted file mode 100644 index d4badd6d7..000000000 --- a/src/main/kotlin/me/odinmain/events/dsl/EventDSL.kt +++ /dev/null @@ -1,30 +0,0 @@ -package me.odinmain.events.dsl - -import net.minecraftforge.fml.common.eventhandler.IEventListener -import net.minecraftforge.fml.common.eventhandler.Event as ForgeEvent - -open class EventDSL { - val listeners: ArrayList> = ArrayList() - - inline fun onEvent(crossinline block: (E) -> Unit) { - listeners.add( - object : Listener(E::class.java) { - override fun invokeEvent(event: E) { - block(event) - } - } - ) - } -} - -abstract class Listener(val event: Class) : IEventListener { - - override fun invoke(event: ForgeEvent) { - if (event.isCancelable && event.isCanceled) return - @Suppress("UNCHECKED_CAST") - val it = event as? E ?: return - invokeEvent(it) - } - - abstract fun invokeEvent(event: E) -} \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/Hud Editor.kt b/src/main/kotlin/me/odinmain/features/Hud Editor.kt deleted file mode 100644 index f9e2401b6..000000000 --- a/src/main/kotlin/me/odinmain/features/Hud Editor.kt +++ /dev/null @@ -1,344 +0,0 @@ -package me.odinmain.features - -import com.github.stivais.ui.UI -import com.github.stivais.ui.UIScreen.Companion.init -import com.github.stivais.ui.color.Color -import com.github.stivais.ui.color.withAlpha -import com.github.stivais.ui.constraints.* -import com.github.stivais.ui.constraints.measurements.Pixel -import com.github.stivais.ui.constraints.positions.Linked -import com.github.stivais.ui.constraints.sizes.Bounding -import com.github.stivais.ui.constraints.sizes.Copying -import com.github.stivais.ui.elements.impl.Popup -import com.github.stivais.ui.elements.impl.canvas -import com.github.stivais.ui.elements.impl.popup -import com.github.stivais.ui.elements.scope.ElementDSL -import com.github.stivais.ui.elements.scope.hoverEffect -import com.github.stivais.ui.utils.loop -import com.github.stivais.ui.utils.radius -import com.github.stivais.ui.utils.seconds -import me.odinmain.config.Config -import me.odinmain.features.ModuleManager.HUDs -import me.odinmain.features.ModuleManager.setupHUD -import me.odinmain.features.impl.render.ClickGUI -import me.odinmain.features.impl.render.ClickGUI.`gray 26` -import me.odinmain.features.impl.render.ClickGUI.`gray 38` -import me.odinmain.features.settings.Setting.Companion.elementWidth -import me.odinmain.utils.ui.outline -import kotlin.math.abs -import kotlin.math.sign -import me.odinmain.features.ModuleManager.hudUI as screen - -private var selection: Popup? = null - -private var snapX: Float = -1f -private var snapY: Float = -1f - -fun openHUDEditor() = UI { - - elementWidth = 180.px - - // snapping lines - canvas(copies()) { renderer -> - if (snapX != -1f) renderer.line(snapX, 0f, snapX, ui.main.height, 1f, Color.WHITE.rgba) - if (snapY != -1f) renderer.line(0f, snapY, ui.main.width, snapY, 1f, Color.WHITE.rgba) - }.add() - - var advancedOptions: Popup? = null - - onClick { - advancedOptions?.closePopup() - advancedOptions = null - - selection?.closePopup() - selection = null - false - } - - // preview huds - HUDs.loop { hud -> - val drawable = hud.Drawable(constrain(hud.x, hud.y, Bounding, Bounding), preview = true).add() - hud.builder(Module.HUDScope(drawable).apply { - - // converts to px for mutability - afterCreation { - element.constraints.x = element.x.px - element.constraints.y = element.y.px - } - // converts back to percent based value - onRemove { - hud.x.percent = element.x / ui.main.width - hud.y.percent = element.y / ui.main.height - } - - // resize - onScroll { (amount) -> - hud.scale += 0.1f * amount.sign - true - } - - // dragging - var removePopup = false - - onClick { - advancedOptions?.closePopup() - advancedOptions = null - - selection?.closePopup() - selection = selectionPopup(pressed = true, arrayListOf(element)) - removePopup = true - true - } - onRelease { - if (removePopup) { - selection?.closePopup() - selection = null - removePopup = false - } - } - - // advanced options - - onClick(1) { - advancedOptions?.closePopup() - advancedOptions = popup( - constraints = at(Linked(element) + 15.px, element.y.px), - smooth = true - ) { - // really slow, since rebuilding every frame, but it's simple - operation { - refreshHUD() - advancedOptions == null - } - - column(constrain(0.px, 0.px, Bounding, Bounding)) { - block( - size(180.px, 35.px), - color = `gray 26`, - radius(tl = 5, tr = 5) - ) { - text("HUD (temp name)") - } - column(size(180.px, Bounding)) { - background(color = Color.RGB(38, 38, 38, 0.7f)) - hud.settings.loop { - it.apply { - createElement() - } - } - - group(size(Copying, h = 40.px)) { - block( - size(90.percent, 70.percent), - color = `gray 38`, - radius(5) - ) { - hoverEffect(0.25.seconds) - outline(ClickGUI.color) - text("Reset") - } - } - } - block( - size(180.px, 10.px), - color = `gray 26`, - radius(bl = 5, br = 5) - ) - } - // consume event - onClick { true } - } - true - } - }) - } - - // multi-"editing" - selection() - - // removes actual hud elements, so it can display previewed ones, and refresh when closed - onCreation { - screen.empty() - } - onRemove { - HUDs.loop { - setupHUD(it) - screen.init() - Config.save() - } - } -} - -// not done -private fun ElementDSL.selection() { - val x = 0.px - val y = 0.px - val w = 0.px - val h = 0.px - - val box = block( - constraints = constrain(x, y, w, h), - color = Color.BLUE.withAlpha(0.25f) - ) { - outline(Color.BLUE) - enabled = false - } - - var clickedX = 0f - var clickedY = 0f - - onClick { - box.enabled = true - clickedX = ui.mx - clickedY = ui.my - - x.pixels = clickedX - y.pixels = clickedY - w.pixels = 0f - h.pixels = 0f - true - } - onRelease { - if (selection != null) { - if (!selection!!.element.isInside(ui.mx, ui.my)) { - selection!!.closePopup() - selection = null - } - } - if (box.enabled) { - val selectedHUDs = arrayListOf() - - element.elements?.loop { - if (it is Module.HUD.Drawable && it.enabled && it.intersects(box.element)) { - selectedHUDs.add(it) - } - } - selection?.closePopup() - selection = selectionPopup(pressed = false, selectedHUDs) - redraw() - - box.enabled = false - } - } - onMouseMove { - if (box.enabled) { - val newW = ui.mx - clickedX - val newH = ui.my - clickedY - if (newW < 0f) x.pixels = clickedX + newW - if (newH < 0f) y.pixels = clickedY + newH - w.pixels = abs(newW) - h.pixels = abs(newH) - box.redraw() - true - } else { - false - } - } -} - -fun ElementDSL.selectionPopup(pressed: Boolean, selectedHUDs: ArrayList): Popup { - var minX = 9999f - var minY = 9999f - var maxX = 0f - var maxY = 0f - selectedHUDs.loop { - if (it.x < minX) minX = it.x - if (it.x + it.screenWidth() > maxX) maxX = it.screenWidth() + it.x - if (it.y < minY) minY = it.y - if (it.y + it.screenHeight() > maxY) maxY = it.screenHeight() + it.y - } - val px = minX.px - val py = minY.px - return popup(constraints = constrain(px, py, (maxX - minX).px, (maxY - minY).px), smooth = true) { - - outline( - constraints = copies(), - color = Color.WHITE - ) - - var mouseDown = pressed - var tx = ui.mx - (px.pixels) - var ty = ui.my - (py.pixels) - - onClick { - mouseDown = true - tx = ui.mx - (px.pixels) - ty = ui.my - (py.pixels) - true - } - onRelease { - mouseDown = false - snapX = -1f - snapY = -1f - } - // maybe make it snapping? - onMouseMove { - if (mouseDown) { - var newX = (ui.mx - tx) - var newY = (ui.my - ty) - - snapX = -1f - snapY = -1f - - val centerX = parent!!.width / 2 - val centerY = parent!!.height / 2 - - // Check for center snapping - if (abs(newX + element.screenWidth() / 2 - centerX) <= SNAP_THRESHOLD) { - newX = centerX - element.screenWidth() / 2 - snapX = centerX - } - if (abs(newY + element.screenHeight() / 2 - centerY) <= SNAP_THRESHOLD) { - newY = centerY - element.screenHeight() / 2 - snapY = centerY - } - - parent?.elements?.loop { other -> - if (other is Module.HUD.Drawable && !selectedHUDs.contains(other)) { - - // horizontal edges - if (abs(newY - other.y) <= SNAP_THRESHOLD) { - newY = other.y - snapY = newY - } else if (abs(newY + element.screenHeight() - other.y) <= SNAP_THRESHOLD) { - newY = other.y - element.screenHeight() - snapY = other.y - } else if (abs(newY - (other.y + other.screenHeight())) <= SNAP_THRESHOLD) { - newY = other.y + other.screenHeight() - snapY = newY - } - - // vertical edges - if (abs(newX - other.x) <= SNAP_THRESHOLD) { - newX = other.x - snapX = newX - } else if (abs(newX + element.screenWidth() - other.x) <= SNAP_THRESHOLD) { - newX = other.x - element.screenWidth() - snapX = other.x - } else if (abs(newX - (other.x + other.screenWidth())) <= SNAP_THRESHOLD) { - newX = other.x + other.screenWidth() - snapX = newX - } - } - } - - val lastX = px.pixels - val lastY = py.pixels - - px.pixels = newX.coerceIn(0f, parent!!.width - element.screenWidth()) - py.pixels = newY.coerceIn(0f, parent!!.height - element.screenHeight()) - - selectedHUDs.loop { - if (lastX != px.pixels) (it.constraints.x as Pixel).pixels += px.pixels - lastX - if (lastY != py.pixels) (it.constraints.y as Pixel).pixels += py.pixels - lastY - } - redraw() - true - } else { - false - } - } - } -} - -private const val SNAP_THRESHOLD = 5f diff --git a/src/main/kotlin/me/odinmain/features/Module.kt b/src/main/kotlin/me/odinmain/features/Module.kt index d0b4bf09f..733d47ddb 100644 --- a/src/main/kotlin/me/odinmain/features/Module.kt +++ b/src/main/kotlin/me/odinmain/features/Module.kt @@ -1,14 +1,7 @@ package me.odinmain.features -import com.github.stivais.ui.constraints.Constraints -import com.github.stivais.ui.constraints.measurements.Percent -import com.github.stivais.ui.elements.Element -import com.github.stivais.ui.elements.scope.ElementScope import me.odinmain.OdinMain -import me.odinmain.events.dsl.EventDSL -import me.odinmain.events.impl.PacketReceivedEvent -import me.odinmain.events.impl.PacketSentEvent -import me.odinmain.features.ModuleManager.setupHUD +import me.odinmain.features.huds.HUDScope import me.odinmain.features.impl.render.ClickGUI import me.odinmain.features.settings.AlwaysActive import me.odinmain.features.settings.Setting @@ -17,14 +10,11 @@ import me.odinmain.features.settings.impl.Keybinding import me.odinmain.utils.clock.Executable import me.odinmain.utils.clock.Executor import me.odinmain.utils.clock.Executor.Companion.register -import me.odinmain.utils.clock.Executor.LimitedExecutor import me.odinmain.utils.skyblock.modMessage import net.minecraft.network.Packet import net.minecraftforge.common.MinecraftForge import org.lwjgl.input.Keyboard -import kotlin.reflect.KProperty0 import kotlin.reflect.full.hasAnnotation -import kotlin.reflect.jvm.isAccessible /** * Class that represents a module. And handles all the settings. @@ -36,7 +26,7 @@ abstract class Module( @Transient var description: String = "", @Transient val tag: TagType = TagType.NONE, toggled: Boolean = false, -) : EventDSL() { +) { /** * Category for this module. @@ -120,8 +110,6 @@ abstract class Module( } } - operator fun > K.unaryPlus(): K = register(this) - fun getSettingByName(name: String?): Setting<*>? { for (setting in settings) { if (setting.name.equals(name, ignoreCase = true)) { @@ -131,20 +119,6 @@ abstract class Module( return null } - inline fun > onPacket(crossinline block: (packet: T) -> Unit) { - onEvent { (packet) -> - if (T::class.java.isInstance(packet)) { - block(packet as T) - } - } - onEvent { (packet) -> - if (T::class.java.isInstance(packet)) { - block(packet as T) - } - } - } - - // todo: replace /** * Helper function to make cleaner code, and more performance, since we don't need multiple registers for packet received events. * @@ -173,13 +147,36 @@ abstract class Module( ModuleManager.messageFunctions.add(ModuleManager.MessageFunction(filter, shouldRun, func)) } - // todo: use new event stuff + /** + * Runs the given function when a Chat Packet is sent with the same message as the given text (or contains the given text) (Case Sensitive!) + * + * @param text The text to look for. + * @param contains If the function should run when the message only contains the text but does not necessarily equal it. + * @param shouldRun Boolean getter to decide if the function should run at any given time, could check if the option is enabled for instance. + * @param func The function to run if the message matches or contains the given text and shouldRun returns true. + * + * @author Bonsai + */ + fun onMessage(text: String, contains: Boolean, shouldRun: () -> Boolean = { alwaysActive || enabled }, func: (String) -> Unit) { + val regex = + if (contains) + ".*${Regex.escape(text)}.*".toRegex() + else + Regex.escape(text).toRegex() + + ModuleManager.messageFunctions.add(ModuleManager.MessageFunction(regex, shouldRun, func)) + } + +// fun onMessageCancellable(filter: Regex, shouldRun: () -> Boolean = { alwaysActive || enabled }, func: (ChatPacketEvent) -> Unit) { +// ModuleManager.cancellableMessageFunctions.add(ModuleManager.MessageFunctionCancellable(filter, shouldRun, func)) +// } + fun onWorldLoad(func: () -> Unit) { ModuleManager.worldLoadFunctions.add(func) } fun execute(delay: Long, repeats: Int, profileName: String = "${this.name} Executor", shouldRun: () -> Boolean = { this.enabled || this.alwaysActive }, func: Executable) { - LimitedExecutor(delay, repeats, profileName, shouldRun, func).register() + Executor.LimitedExecutor(delay, repeats, profileName, shouldRun, func).register() } fun execute(delay: () -> Long, profileName: String = "${this.name} Executor", shouldRun: () -> Boolean = { this.enabled || this.alwaysActive }, func: Executable) { @@ -190,11 +187,21 @@ abstract class Module( Executor(delay, profileName, shouldRun, func).register() } - fun HUD.setting(name: String, desciption: String = ""): HUDSetting { - return HUDSetting(name, this, desciption) + @Suppress("FunctionName") + fun HUD( + name: String, + description: String, + block: HUDScope.() -> Unit + ): HUDSetting { + val setting = HUDSetting( + name, + me.odinmain.features.huds.HUD(name, this, block), + description + ) + register(setting) + return setting } - // this is unused @Deprecated("remove") enum class TagType { @@ -214,83 +221,4 @@ abstract class Module( } } } - - // figure way to separate from module file while still having reference to it - // make related settings get taken from module and placed in hud and make it save there - open inner class HUD( - val x: Percent, - val y: Percent, - var enabled: Boolean = true, - val builder: HUDScope.() -> Unit - ) { - val defaultX: Float = x.percent - val defaultY: Float = y.percent - - val settings: ArrayList> = arrayListOf() - - fun setting(vararg settings: KProperty0<*>): HUD { - for (property in settings) { - property.isAccessible = true - val delegate = property.getDelegate() as? Setting<*> ?: throw IllegalArgumentException( - "Invalid Arguments. Must use a delegated setting as the argument for the HUD setting." - ) - this.settings.add(delegate) - delegate.hide() - } - return this - } - - var scale = 1f - set(value) { - field = value.coerceAtLeast(1f) - } - - init { - ModuleManager.HUDs.add(this) - setupHUD(this) - } - - inner class Drawable(constraints: Constraints, val preview: Boolean) : Element(constraints) { - - override var enabled: Boolean = true - get() = field && this@HUD.enabled && this@Module.enabled - - init { - scaledCentered = false - scale = this@HUD.scale - - } - - fun refresh(scope: HUDScope) { - removeAll() - builder.invoke(scope) - } - - override fun draw() { - scale = this@HUD.scale - } - override fun onElementAdded(element: Element) { - } - } - } - - class HUDScope(element: HUD.Drawable) : ElementScope(element) { - - inline val preview: Boolean get() = element.preview - - inline fun needs(crossinline block: () -> Boolean) { - if (!element.preview) { - operation { - element.enabled = block() - false - } - } - } - - fun refreshHUD() { - element.removeAll() - element.refresh(this) - element.redraw = true - } - } } \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/ModuleManager.kt b/src/main/kotlin/me/odinmain/features/ModuleManager.kt index e4599dfcb..bf6caa837 100644 --- a/src/main/kotlin/me/odinmain/features/ModuleManager.kt +++ b/src/main/kotlin/me/odinmain/features/ModuleManager.kt @@ -1,10 +1,5 @@ package me.odinmain.features -import com.github.stivais.ui.UI -import com.github.stivais.ui.UIScreen.Companion.init -import com.github.stivais.ui.constraints.constrain -import com.github.stivais.ui.constraints.sizes.Bounding -import me.odinmain.OdinMain.mc import me.odinmain.events.impl.* import me.odinmain.features.impl.dungeon.* import me.odinmain.features.impl.dungeon.dungeonwaypoints.DungeonWaypoints @@ -17,10 +12,7 @@ import me.odinmain.features.impl.nether.* import me.odinmain.features.impl.render.* import me.odinmain.features.impl.skyblock.* import me.odinmain.features.settings.impl.KeybindSetting -import me.odinmain.utils.clock.Executor -import me.odinmain.utils.profile import net.minecraft.network.Packet -import net.minecraftforge.client.event.RenderWorldLastEvent import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent @@ -32,32 +24,6 @@ import net.minecraftforge.fml.common.gameevent.TickEvent */ object ModuleManager { - val HUDs = arrayListOf() - - internal val hudUI = UI().init() - - fun setupHUD(hud: Module.HUD) { - val drawable = hud.Drawable(constrain(hud.x, hud.y, Bounding, Bounding), preview = false) - hudUI.main.addElement(drawable) - hud.builder(Module.HUDScope(drawable)) - } - - private var previousWidth: Int = 0 - private var previousHeight: Int = 0 - - @SubscribeEvent - fun onRender(event: RenderWorldLastEvent) { - val w = mc.framebuffer.framebufferWidth - val h = mc.framebuffer.framebufferHeight - if (w != previousWidth || h != previousHeight) { - hudUI.resize(w, h) - previousWidth = w - previousHeight = h - } - hudUI.render() - } - - // todo: cleanup data class PacketFunction>( val type: Class, val function: (T) -> Unit, @@ -66,10 +32,8 @@ object ModuleManager { data class MessageFunction(val filter: Regex, val shouldRun: () -> Boolean, val function: (String) -> Unit) - // todo: cleanup data class TickTask(var ticksLeft: Int, val server: Boolean, val function: () -> Unit) - // todo: cleanup val packetFunctions = mutableListOf>>() val messageFunctions = mutableListOf() val worldLoadFunctions = mutableListOf<() -> Unit>() diff --git a/src/main/kotlin/me/odinmain/features/huds/HUD.kt b/src/main/kotlin/me/odinmain/features/huds/HUD.kt new file mode 100644 index 000000000..fc9f8d67c --- /dev/null +++ b/src/main/kotlin/me/odinmain/features/huds/HUD.kt @@ -0,0 +1,94 @@ +package me.odinmain.features.huds + +import com.github.stivais.ui.constraints.Constraints +import com.github.stivais.ui.constraints.percent +import com.github.stivais.ui.constraints.sizes.Bounding +import com.github.stivais.ui.elements.Element +import com.github.stivais.ui.elements.scope.ElementScope +import com.github.stivais.ui.transforms.Transforms +import me.odinmain.features.Module +import me.odinmain.features.settings.Setting +import me.odinmain.features.settings.impl.NumberSetting + +class HUD( + val name: String, + val module: Module, + val builder: HUDScope.() -> Unit +) { + val x = NumberSetting("x", 2.5f, 0f, 100f).hide() + val y = NumberSetting("y", 2.5f, 0f, 100f).hide() + val scale = NumberSetting("Scale", 1f, 0.2f, 5f, increment = 0.1f) + + /** + * Settings tied to this HUD, these get saved inside the [setting][me.odinmain.features.settings.impl.HUDSetting] + */ + val settings: ArrayList> = arrayListOf(x, y, scale) + + init { + HUDManager.HUDs.add(this) + } + + fun registerSettings(vararg settings: Setting<*>) { + for (setting in settings) { + if (module.settings.contains(setting)) { + module.settings.remove(setting) + } + this.settings.add(setting) + } + } + + inner class Representation( + val preview: Boolean + ) : Element(Constraints(x.value.percent, y.value.percent, Bounding, Bounding)) { + + override var enabled: Boolean = true + get() = field && this@HUD.module.enabled + + var scaleTransformation by Transforms.Scale(this@HUD.scale.value, centered = false).also { + addTransform(it) + } + + override fun draw() { + // no-op + } + override fun onElementAdded(element: Element) { + // no-op + // elements here shouldn't be centered by default + } + + fun refresh(scope: HUDScope) { + removeAll() + builder.invoke(scope) + scaleTransformation = this@HUD.scale.value + redrawInternal = true + } + } + + // saving util + fun getSettingByName(name: String?): Setting<*>? { + for (setting in settings) { + if (setting.name.equals(name, ignoreCase = true)) { + return setting + } + } + return null + } +} + +class HUDScope(element: HUD.Representation) : ElementScope(element) { + + inline val preview get() = element.preview + + inline fun needs(crossinline block: () -> Boolean) { + if (!element.preview) { + operation { + element.enabled = block() + false + } + } + } + + fun refreshHUD() { + element.refresh(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/huds/HUDManager.kt b/src/main/kotlin/me/odinmain/features/huds/HUDManager.kt new file mode 100644 index 000000000..6459c052e --- /dev/null +++ b/src/main/kotlin/me/odinmain/features/huds/HUDManager.kt @@ -0,0 +1,351 @@ +package me.odinmain.features.huds + +import com.github.stivais.ui.UI +import com.github.stivais.ui.color.Color +import com.github.stivais.ui.color.withAlpha +import com.github.stivais.ui.constraints.* +import com.github.stivais.ui.constraints.measurements.Pixel +import com.github.stivais.ui.constraints.sizes.Bounding +import com.github.stivais.ui.elements.impl.Popup +import com.github.stivais.ui.elements.impl.popup +import com.github.stivais.ui.elements.scope.ElementDSL +import com.github.stivais.ui.elements.scope.ElementScope +import com.github.stivais.ui.elements.scope.hoverEffect +import com.github.stivais.ui.utils.loop +import com.github.stivais.ui.utils.radius +import com.github.stivais.ui.utils.seconds +import me.odinmain.config.Config +import me.odinmain.features.impl.render.ClickGUI +import me.odinmain.features.impl.render.ClickGUI.`gray 26` +import me.odinmain.features.impl.render.ClickGUI.`gray 38` +import me.odinmain.features.impl.render.ClickGUI.hoverInformation +import me.odinmain.features.settings.Setting +import me.odinmain.features.settings.Setting.Renders.Companion.elementWidth +import me.odinmain.features.settings.Setting.Renders.Companion.onValueChanged +import me.odinmain.utils.ui.UIHandler +import me.odinmain.utils.ui.outline +import kotlin.math.abs +import kotlin.math.sign + +object HUDManager { + + val HUDs = arrayListOf() + + private var UI: UIHandler? = null + + private var selected: Popup? = null + private var snapLineX: Float = -1f + private var snapLineY: Float = -1f + private const val SNAP_THRESHOLD = 5f + + fun setupHUDs() { + UI = UIHandler(UI { + HUDs.loop { hud -> + val representation = hud.Representation(preview = false) + representation.add() + hud.builder(HUDScope(representation)) + } + }) + UI!!.open() + } + + fun makeHUDEditor() = UI { + elementWidth = 180.px + + var hudOptions: Popup? = null + + HUDs.loop { hud -> + val representation = hud.Representation(preview = true) + representation.add() + hud.builder(HUDScope(representation).apply { + afterCreation { + element.constraints.x = element.x.px + element.constraints.y = element.y.px + } + onRemove { + hud.x.value = (element.x / ui.main.width) * 100f + hud.y.value = (element.y / ui.main.height) * 100f + } + onScroll { (amount) -> + hud.scale.value += 0.1f * amount.sign + representation.scaleTransformation = hud.scale.value + true + } + + var removeSelected = false + + onClick { + hudOptions?.closePopup() + hudOptions = null + + selected?.closePopup() + selected = createSelection(pressed = true, arrayListOf(element)) + removeSelected = true + true + } + onRelease { + if (removeSelected) { + selected?.closePopup() + selected = null + removeSelected = false + } + } + + //-------------// + // hud options // + //-------------// + onClick(button = 1) { + hudOptions?.closePopup() + hudOptions = popup( + constraints = at((element.x + element.screenWidth() + 20).px, element.y.px), + smooth = true + ) { + column(size(Bounding, Bounding)) { + block( + size(180.px, 35.px), + color = `gray 26`, + radius = radius(tl = 5, tr = 5) + ) { + text(hud.name) + } + column(size(w = 180.px, h = Bounding)) { + block( + copies(), + color = Color.RGB(38, 38, 38, 0.7f) + ) + hud.settings.loop { + if (!it.hidden && it is Setting.Renders) { + it.apply { + + val drawableScope = ElementScope(it.Drawable()).apply { + hoverInformation(it.description) + onValueChanged { refreshHUD() } + } + create(drawableScope).create() + } + } + } + section(size = 40.px) { + // maybe make you confirm with an [are you sure] + block( + size(90.percent, 70.percent), + color = `gray 38`, + radius = 5.radius() + ) { + hoverEffect(0.25.seconds) + outline(ClickGUI.color) + text("Reset") + + onClick { + hud.settings.loop { + it.reset() + } + true + } + } + } + } + block( + size(180.px, 10.px), + color = `gray 26`, + radius = radius(bl = 5, br = 5) + ) + } + // used to consume any click events, so they do not click through + onClick { true } + } + true + } + }) + } + + // used purely to close hud options/selections + onClick { + hudOptions?.closePopup() + hudOptions = null + selected?.closePopup() + selected = null + false + } + + dragSelection() + + onCreation { + UI?.close() + } + onRemove { + setupHUDs() + Config.save() + } + } + + private fun ElementDSL.createSelection(pressed: Boolean, selectedHUDs: ArrayList): Popup { + // get bounding box of selectedHUDs + var minX = 9999f; var minY = 9999f // I wish I could rust :( + var maxX = 0f; var maxY = 0f + selectedHUDs.loop { + minX = minOf(minX, it.x) + maxX = maxOf(maxX, it.x + it.screenWidth()) + minY = minOf(minY, it.y) + maxY = maxOf(maxY, it.y + it.screenHeight()) + } + + val px = minX.px + val py = minY.px + + return popup(constraints = constrain(px, py, (maxX - minX).px, (maxY - minY).px), smooth = true) { + outline( + constraints = copies(), + color = Color.WHITE + ) + + var mouseDown = pressed + var offsetX = ui.mx - px.pixels + var offsetY = ui.my - py.pixels + + onClick { + mouseDown = true + offsetX = ui.mx - px.pixels + offsetY = ui.my - py.pixels + true + } + onRelease { + mouseDown = false + snapLineX = -1f + snapLineY = -1f + } + onMouseMove { + if (mouseDown) { + var newX = (ui.mx - offsetX) + var newY = (ui.my - offsetY) + + snapLineX = -1f + snapLineY = -1f + + val centerX = parent!!.width / 2 + val centerY = parent!!.height / 2 + + // Check for center snapLineping + if (abs(newX + element.screenWidth() / 2 - centerX) <= SNAP_THRESHOLD) { + newX = centerX - element.screenWidth() / 2 + snapLineX = centerX + } + if (abs(newY + element.screenHeight() / 2 - centerY) <= SNAP_THRESHOLD) { + newY = centerY - element.screenHeight() / 2 + snapLineY = centerY + } + + parent?.elements?.loop { other -> + if (other is HUD.Representation && !selectedHUDs.contains(other)) { + + // horizontal edges + if (abs(newY - other.y) <= SNAP_THRESHOLD) { + newY = other.y + snapLineY = newY + } else if (abs(newY + element.screenHeight() - other.y) <= SNAP_THRESHOLD) { + newY = other.y - element.screenHeight() + snapLineY = other.y + } else if (abs(newY - (other.y + other.screenHeight())) <= SNAP_THRESHOLD) { + newY = other.y + other.screenHeight() + snapLineY = newY + } + + // vertical edges + if (abs(newX - other.x) <= SNAP_THRESHOLD) { + newX = other.x + snapLineX = newX + } else if (abs(newX + element.screenWidth() - other.x) <= SNAP_THRESHOLD) { + newX = other.x - element.screenWidth() + snapLineX = other.x + } else if (abs(newX - (other.x + other.screenWidth())) <= SNAP_THRESHOLD) { + newX = other.x + other.screenWidth() + snapLineX = newX + } + } + } + + val lastX = px.pixels + val lastY = py.pixels + + px.pixels = newX.coerceIn(0f, parent!!.width - element.screenWidth()) + py.pixels = newY.coerceIn(0f, parent!!.height - element.screenHeight()) + + selectedHUDs.loop { + if (lastX != px.pixels) (it.constraints.x as Pixel).pixels += px.pixels - lastX + if (lastY != py.pixels) (it.constraints.y as Pixel).pixels += py.pixels - lastY + } + redraw() + true + } else { + false + } + } + } + } + + private fun ElementDSL.dragSelection() { + val x = 0.px + val y = 0.px + val w = 0.px + val h = 0.px + + val box = block( + constraints = constrain(x, y, w, h), + color = Color.BLUE.withAlpha(0.25f) + ) { + outline(Color.BLUE) + enabled = false + } + + var clickedX = 0f + var clickedY = 0f + + onClick { + box.enabled = true + clickedX = ui.mx + clickedY = ui.my + + x.pixels = clickedX + y.pixels = clickedY + w.pixels = 0f + h.pixels = 0f + true + } + onRelease { + if (selected != null) { + if (!selected!!.element.isInside(ui.mx, ui.my)) { + selected!!.closePopup() + selected = null + } + } + if (box.enabled) { + val selectedHUDs = arrayListOf() + + element.elements?.loop { + if (it is HUD.Representation && it.enabled && it.intersects(box.element)) { + selectedHUDs.add(it) + } + } + selected?.closePopup() + selected = createSelection(pressed = false, selectedHUDs) + redraw() + + box.enabled = false + } + } + onMouseMove { + if (box.enabled) { + val newW = ui.mx - clickedX + val newH = ui.my - clickedY + if (newW < 0f) x.pixels = clickedX + newW + if (newH < 0f) y.pixels = clickedY + newH + w.pixels = abs(newW) + h.pixels = abs(newH) + box.redraw() + true + } else { + false + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/impl/dungeon/LeapMenu.kt b/src/main/kotlin/me/odinmain/features/impl/dungeon/LeapMenu.kt index 08ecacd5f..e8eda2b9b 100644 --- a/src/main/kotlin/me/odinmain/features/impl/dungeon/LeapMenu.kt +++ b/src/main/kotlin/me/odinmain/features/impl/dungeon/LeapMenu.kt @@ -4,10 +4,14 @@ import com.github.stivais.ui.UI import com.github.stivais.ui.UIScreen.Companion.open import com.github.stivais.ui.animation.Animations import com.github.stivais.ui.color.Color -import com.github.stivais.ui.constraints.* +import com.github.stivais.ui.constraints.constrain +import com.github.stivais.ui.constraints.copies import com.github.stivais.ui.constraints.measurements.Animatable +import com.github.stivais.ui.constraints.percent +import com.github.stivais.ui.constraints.size import com.github.stivais.ui.elements.impl.Grid -import com.github.stivais.ui.utils.* +import com.github.stivais.ui.utils.radius +import com.github.stivais.ui.utils.seconds import io.github.moulberry.notenoughupdates.NEUApi import me.odinmain.events.impl.GuiEvent import me.odinmain.features.Module @@ -17,10 +21,13 @@ import me.odinmain.features.impl.dungeon.LeapHelper.worldLoad import me.odinmain.features.impl.render.ClickGUI.`gray 38` import me.odinmain.features.settings.Setting.Companion.withDependency import me.odinmain.features.settings.impl.* -import me.odinmain.utils.* -import me.odinmain.utils.skyblock.* +import me.odinmain.utils.equalsOneOf +import me.odinmain.utils.name import me.odinmain.utils.skyblock.dungeon.DungeonClass import me.odinmain.utils.skyblock.dungeon.DungeonPlayer +import me.odinmain.utils.skyblock.getItemIndexInContainerChest +import me.odinmain.utils.skyblock.modMessage +import me.odinmain.utils.skyblock.partyMessage import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.inventory.ContainerChest import net.minecraftforge.client.event.GuiOpenEvent @@ -67,13 +74,13 @@ object LeapMenu : Module( val block = block( constraints = constrain(x, y, sizeX, sizeY), color = `gray 38`, - radius = 12.radii() + radius = 12.radius() ) { image( it.skinImage, constraints = constrain(5.percent, 10.percent, 30.percent, 80.percent), - 12f.radii() + 12f.radius() ) column(constraints = constrain(38.percent, 40.percent)) { text(it.name, size = 20.percent, color = it.clazz.color) diff --git a/src/main/kotlin/me/odinmain/features/impl/dungeon/WarpCooldown.kt b/src/main/kotlin/me/odinmain/features/impl/dungeon/WarpCooldown.kt index 9c1a45e11..2e0f93273 100644 --- a/src/main/kotlin/me/odinmain/features/impl/dungeon/WarpCooldown.kt +++ b/src/main/kotlin/me/odinmain/features/impl/dungeon/WarpCooldown.kt @@ -1,6 +1,5 @@ package me.odinmain.features.impl.dungeon -import com.github.stivais.ui.constraints.percent import com.github.stivais.ui.constraints.px import me.odinmain.features.Module import me.odinmain.features.settings.impl.BooleanSetting @@ -14,8 +13,8 @@ object WarpCooldown : Module ( private val showUnit by BooleanSetting("Show unit", default = false, description = "Displays unit of time for the cooldown.").hide() private val HUD by TextHUD( - 2.5.percent, - 2.5.percent, + "Warp HUD", + description = "Displays the cooldown." ) { color, font -> if (preview) { text( @@ -33,9 +32,7 @@ object WarpCooldown : Module ( size = 30.px ) and text({ "${(lastUpdate - System.currentTimeMillis()) / 1000}${if (showUnit) "s" else ""}" }, font = font) } - }.setting( - ::showUnit - ).setting("Warp HUD") + } private var lastUpdate: Long = System.currentTimeMillis() diff --git a/src/main/kotlin/me/odinmain/features/impl/nether/EnrageDisplay.kt b/src/main/kotlin/me/odinmain/features/impl/nether/EnrageDisplay.kt index 938c0367d..fcb73f27b 100644 --- a/src/main/kotlin/me/odinmain/features/impl/nether/EnrageDisplay.kt +++ b/src/main/kotlin/me/odinmain/features/impl/nether/EnrageDisplay.kt @@ -1,10 +1,10 @@ package me.odinmain.features.impl.nether import com.github.stivais.ui.animation.Animations -import com.github.stivais.ui.constraints.measurements.Animatable -import com.github.stivais.ui.constraints.percent import com.github.stivais.ui.constraints.px +import com.github.stivais.ui.transforms.Transforms import com.github.stivais.ui.utils.seconds +import me.odinmain.events.impl.PacketReceivedEvent import me.odinmain.events.impl.RealServerTick import me.odinmain.features.Module import me.odinmain.features.settings.impl.BooleanSetting @@ -13,29 +13,30 @@ import me.odinmain.utils.skyblock.skyblockID import me.odinmain.utils.ui.TextHUD import me.odinmain.utils.ui.and import net.minecraft.network.play.server.S29PacketSoundEffect +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent object EnrageDisplay : Module( name = "Enrage Display", - description = "Timer for cooldown of reaper armor enrage" + description = "Displays the Reaper armor's ability duration." ) { private val unit by SelectorSetting("Unit", arrayListOf("Seconds", "Ticks")) private val showUnit by BooleanSetting("Show unit", default = false) // test - private val animatable = Animatable(0.0.px, 1.px) + private val animation = Transforms.Alpha.Animated(from = 0f, to = 1f) - private val HUD = TextHUD(2.5.percent, 2.5.percent) { color, font -> - if (!preview) element.alphaAnim = animatable + private val HUD = TextHUD( + "Enrage Display", + "Displays the duration on screen." + ) { color, font -> + if (!preview) transform(animation) text( "Enrage ", color = color, font = font, size = 30.px ) and text({ getDisplay(if (preview) 120 else enrageTimer) }, font = font) - }.setting( - ::showUnit, - ::unit, - ).setting("Enrage Display") + } private fun getDisplay(ticks: Int): String { return when (unit) { @@ -46,24 +47,26 @@ object EnrageDisplay : Module( private var enrageTimer = -1 - init { - onPacket { packet: S29PacketSoundEffect -> - if (packet.soundName == "mob.zombie.remedy" && packet.pitch == 1.0f && packet.volume == 0.5f) { - if ( - mc.thePlayer?.getCurrentArmor(0)?.skyblockID == "REAPER_BOOTS" && - mc.thePlayer?.getCurrentArmor(1)?.skyblockID == "REAPER_LEGGINGS" && - mc.thePlayer?.getCurrentArmor(2)?.skyblockID == "REAPER_CHESTPLATE" - ) { - enrageTimer = 120 - animatable.animate(0.25.seconds, Animations.EaseOutQuint) - } + @SubscribeEvent + fun onPacket(event: PacketReceivedEvent) { + val packet = event.packet as? S29PacketSoundEffect ?: return + if (packet.soundName == "mob.zombie.remedy" && packet.pitch == 1.0f && packet.volume == 0.5f) { + if ( + mc.thePlayer?.getCurrentArmor(0)?.skyblockID == "REAPER_BOOTS" && + mc.thePlayer?.getCurrentArmor(1)?.skyblockID == "REAPER_LEGGINGS" && + mc.thePlayer?.getCurrentArmor(2)?.skyblockID == "REAPER_CHESTPLATE" + ) { + enrageTimer = 120 + animation.animate(0.25.seconds, Animations.EaseOutQuint) } } - onEvent { - enrageTimer-- - if (enrageTimer == 0) { - animatable.animate(0.25.seconds, Animations.EaseOutQuint) - } + } + + @SubscribeEvent + fun onTick(event: RealServerTick) { + enrageTimer-- + if (enrageTimer == 0) { + animation.animate(0.25.seconds, Animations.EaseOutQuint) } } } \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/impl/render/BPSDisplay.kt b/src/main/kotlin/me/odinmain/features/impl/render/BPSDisplay.kt index b25e51a85..af39eb7d2 100644 --- a/src/main/kotlin/me/odinmain/features/impl/render/BPSDisplay.kt +++ b/src/main/kotlin/me/odinmain/features/impl/render/BPSDisplay.kt @@ -1,57 +1,70 @@ package me.odinmain.features.impl.render import com.github.stivais.ui.color.Color -import com.github.stivais.ui.constraints.percent import com.github.stivais.ui.constraints.px +import com.github.stivais.ui.constraints.size +import me.odinmain.events.impl.PacketSentEvent import me.odinmain.features.Module import me.odinmain.features.settings.impl.BooleanSetting import me.odinmain.utils.round +import me.odinmain.utils.ui.TextHUD import me.odinmain.utils.ui.and import net.minecraft.network.play.client.C07PacketPlayerDigging.Action.START_DESTROY_BLOCK +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent import kotlin.math.roundToInt import net.minecraft.network.play.client.C07PacketPlayerDigging as PacketPlayerDigging +// todo: Fix number going to infinity somehow object BPSDisplay : Module( name = "BPS Display", description = "Displays how many blocks you're breaking per second." ) { private val roundNumber by BooleanSetting("Round number", true, description = "If the number should be rounded.") - private val hud by HUD(0.percent, 0.percent) { + private val hud by TextHUD("HUD", description = "Display") { color, font -> text( text = "BPS ", - color = Color.GREEN, + font = font, + color = color, size = 30.px - ) and text({ if (roundNumber) bps.roundToInt() else bps.round(1) }) - }.setting("Display", "") + ) and text({ if (roundNumber) bps.roundToInt() else bps.round(1) }, font = font) + } + + val hudTest = me.odinmain.features.huds.HUD( + "HUD", + this + ) { + block(size(100.px, 100.px), Color.RED) + } private var startTime = 0L private var isBreaking = false private var blocksBroken = 0 private var lastBrokenBlock = 0L private var bps = 0.0 - - // this doesn't work properly Ithink Someone please fix - init { - onPacket { packet: PacketPlayerDigging -> - if (packet.status != START_DESTROY_BLOCK) return@onPacket - if (startTime == 0L) startTime = System.currentTimeMillis() - isBreaking = true - blocksBroken++ - lastBrokenBlock = System.currentTimeMillis() - } - onEvent { - if (!isBreaking) return@onEvent - val secondsElapsed = (System.currentTimeMillis() - startTime) / 1000.0 - bps = (blocksBroken / secondsElapsed).round(2).toDouble() - if (System.currentTimeMillis() - lastBrokenBlock > 1000) { - bps = 0.0 - isBreaking = false - blocksBroken = 0 - startTime = 0 - lastBrokenBlock = 0 - } + + @SubscribeEvent + fun onPacket(event: PacketSentEvent) { + val packet = event.packet as? PacketPlayerDigging ?: return + if (packet.status != START_DESTROY_BLOCK) return + if (startTime == 0L) startTime = System.currentTimeMillis() + isBreaking = true + blocksBroken++ + lastBrokenBlock = System.currentTimeMillis() + } + + @SubscribeEvent + fun onTick(event: ClientTickEvent) { + if (!isBreaking) return + val secondsElapsed = (System.currentTimeMillis() - startTime) / 1000.0 + bps = (blocksBroken / secondsElapsed).round(2).toDouble() + if (System.currentTimeMillis() - lastBrokenBlock > 1000) { + bps = 0.0 + isBreaking = false + blocksBroken = 0 + startTime = 0 + lastBrokenBlock = 0 } } } \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/impl/render/CPSDisplay.kt b/src/main/kotlin/me/odinmain/features/impl/render/CPSDisplay.kt index 09e051759..6b9b89772 100644 --- a/src/main/kotlin/me/odinmain/features/impl/render/CPSDisplay.kt +++ b/src/main/kotlin/me/odinmain/features/impl/render/CPSDisplay.kt @@ -1,17 +1,15 @@ package me.odinmain.features.impl.render import com.github.stivais.ui.color.Color -import com.github.stivais.ui.constraints.percent -import com.github.stivais.ui.constraints.px +import me.odinmain.events.impl.PacketSentEvent import me.odinmain.features.Module import me.odinmain.features.settings.Setting.Companion.withDependency import me.odinmain.features.settings.impl.BooleanSetting import me.odinmain.features.settings.impl.ColorSetting import me.odinmain.features.settings.impl.DropdownSetting import me.odinmain.features.settings.impl.SelectorSetting -import me.odinmain.utils.ui.TextHUD -import me.odinmain.utils.ui.and import net.minecraft.network.play.client.C08PacketPlayerBlockPlacement +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent object CPSDisplay : Module( @@ -29,34 +27,23 @@ object CPSDisplay : Module( private val leftClicks = mutableListOf() private val rightClicks = mutableListOf() - // todo: make it look somewhat how it used to - private val HUD by TextHUD( - 2.5.percent, - 2.5.percent, - ) { color, font -> - text( - "CPS ", - color = color, - font = font, - size = 30.px - ) and text({ rightClicks.size }, font = font) - }.setting("CPS") - - init { - onEvent { - if (leftClicks.isNotEmpty() && System.currentTimeMillis() - leftClicks.first() > 1000) { - leftClicks.removeFirst() - } - if (rightClicks.isNotEmpty() && System.currentTimeMillis() - rightClicks.first() > 1000) { - rightClicks.removeFirst() - } + @SubscribeEvent + fun onTick(event: ClientTickEvent) { + if (leftClicks.size != 0 && System.currentTimeMillis() - leftClicks.first() > 1000) { + leftClicks.removeFirst() } - onPacket { - if (countPackets) { - if (rightClicks.isEmpty() || System.currentTimeMillis() - rightClicks.last() > 5) { - onRightClick() - } + if (rightClicks.size != 0 && System.currentTimeMillis() - rightClicks.first() > 1000) { + rightClicks.removeFirst() + } + } + + @SubscribeEvent + fun onPacket(event: PacketSentEvent) { + if (event.packet !is C08PacketPlayerBlockPlacement) return + if (countPackets) { + if (rightClicks.size == 0 || System.currentTimeMillis() - rightClicks.last() > 5) { + onRightClick() } } } diff --git a/src/main/kotlin/me/odinmain/features/impl/render/ClickGUI.kt b/src/main/kotlin/me/odinmain/features/impl/render/ClickGUI.kt index 16c9f3c22..9356575c3 100644 --- a/src/main/kotlin/me/odinmain/features/impl/render/ClickGUI.kt +++ b/src/main/kotlin/me/odinmain/features/impl/render/ClickGUI.kt @@ -1,9 +1,7 @@ package me.odinmain.features.impl.render import com.github.stivais.ui.UI -import com.github.stivais.ui.UIScreen import com.github.stivais.ui.UIScreen.Companion.open -import com.github.stivais.ui.animation.Animation import com.github.stivais.ui.animation.Animations import com.github.stivais.ui.color.Color import com.github.stivais.ui.color.blue @@ -15,10 +13,13 @@ import com.github.stivais.ui.constraints.sizes.Bounding import com.github.stivais.ui.elements.impl.Popup import com.github.stivais.ui.elements.impl.popup import com.github.stivais.ui.elements.scope.ElementDSL +import com.github.stivais.ui.elements.scope.ElementScope import com.github.stivais.ui.elements.scope.draggable import com.github.stivais.ui.elements.scope.hoverEffect -import com.github.stivais.ui.operation.AnimationOperation -import com.github.stivais.ui.utils.* +import com.github.stivais.ui.utils.animate +import com.github.stivais.ui.utils.loop +import com.github.stivais.ui.utils.radius +import com.github.stivais.ui.utils.seconds import kotlinx.coroutines.launch import me.odinmain.OdinMain import me.odinmain.OdinMain.scope @@ -26,9 +27,11 @@ import me.odinmain.config.Config import me.odinmain.features.Category import me.odinmain.features.Module import me.odinmain.features.ModuleManager +import me.odinmain.features.huds.HUDManager import me.odinmain.features.settings.AlwaysActive -import me.odinmain.features.settings.Setting.Companion.elementWidth +import me.odinmain.features.settings.Setting import me.odinmain.features.settings.Setting.Companion.withDependency +import me.odinmain.features.settings.Setting.Renders.Companion.elementWidth import me.odinmain.features.settings.impl.* import me.odinmain.utils.capitalizeFirst import me.odinmain.utils.sendDataToServer @@ -36,6 +39,9 @@ import me.odinmain.utils.skyblock.LocationUtils import me.odinmain.utils.skyblock.createClickStyle import me.odinmain.utils.skyblock.getChatBreak import me.odinmain.utils.skyblock.modMessage +import me.odinmain.utils.ui.lifetimeAnimations +import me.odinmain.utils.ui.onHover +import me.odinmain.utils.ui.textInput import net.minecraft.event.ClickEvent import net.minecraft.util.ChatComponentText import org.lwjgl.input.Keyboard @@ -46,14 +52,17 @@ object ClickGUI: Module( key = Keyboard.KEY_RSHIFT, description = "Allows you to customize the UI." ) { - val color by ColorSetting("Color", Color.RGB(50, 150, 220), allowAlpha = false, description = "Color mainly used within the UI") + /** + * Main color used within the mod. + */ + val color by ColorSetting("Color", Color.RGB(50, 150, 220), allowAlpha = false, description = "Main color theme for Odin.") val enableNotification by BooleanSetting("Enable chat notifications", true, description = "Sends a message when you toggle a module with a keybind") val forceHypixel by BooleanSetting("Force Hypixel", false, description = "Forces the hypixel check to be on (Mainly used for development. Only use if you know what you're doing)") // make useful someday - val updateMessage by SelectorSetting("Update Message", "Beta", arrayListOf("Beta", "Full", "None")).hide() + val updateMessage by SelectorSetting("Update Message", arrayListOf("Full", "Beta", "None")).hide() val devMessages by BooleanSetting("Dev Messages", true, description = "Enables dev messages in chat.").withDependency { DevPlayers.isDev } // make dev-specific modules and put this there val devSize by BooleanSetting("Dev Size", true, description = "Toggles client side dev size.").withDependency { DevPlayers.isDev } @@ -75,7 +84,13 @@ object ClickGUI: Module( } }.withDependency { DevPlayers.isDev } - val action by ActionSetting("Open HUD Editor", description = "Opens the HUD Editor, allowing you to reposition HUDs") { /*OdinMain.display = EditHUDGui*/ } + val action by ActionSetting("Open HUD Editor", description = "Opens the HUD Editor, allowing you to reposition HUDs") { + HUDManager.makeHUDEditor().open() + } + + private val panelSettings by MapSetting("panel.data", mutableMapOf()).also { setting -> + Category.entries.forEach { setting.value[it] = PanelData(x = 10f + 260f * it.ordinal, y = 10f, extended = true) } + } private var joined by BooleanSetting("first.join", false).hide() private var warned by BooleanSetting("ui.branch.warning", false).hide() @@ -83,10 +98,6 @@ object ClickGUI: Module( var firstTimeOnVersion = false - private val panelSettings by MapSetting("panel.data", mutableMapOf()).also { setting -> - Category.entries.forEach { setting.value[it] = PanelData(x = 10f + 260f * it.ordinal, y = 10f, extended = true) } - } - init { // todo: cleanup execute(250) { @@ -170,7 +181,10 @@ object ClickGUI: Module( } draggable(moves = parent!!) } - // modules + + //---------// + // modules // + //---------// column(size(h = Animatable(from = Bounding, to = 0.px, swapIf = !data.extended))) { background(color = Color.RGB(38, 38, 38, 0.7f)) scissors() @@ -189,11 +203,13 @@ object ClickGUI: Module( } } - // search bar - block(constrain(y = 80.percent, w = 25.percent, h = 5.percent), color = `gray 38`, radius = 10.radii()) { - textInput(placeholder = "Search") { str -> + //------------// + // search bar // + //------------// + block(constrain(y = 80.percent, w = 25.percent, h = 5.percent), color = `gray 38`, radius = 10.radius()) { + textInput(placeholder = "Search") { (str) -> moduleElements.loop { (module, element) -> - element.enabled = module.name.contains(str, true) // do we want to add an option for search bar to also find setting names + element.enabled = module.name.contains(str, true) } this@UI.redraw() } @@ -204,24 +220,20 @@ object ClickGUI: Module( draggable(button = 1) } - openAnim(0.5.seconds, Animations.EaseOutQuint) - closeAnim(0.5.seconds, Animations.EaseInBack) - - // for fun icl - if (!warned) { - uiBranchWarning() - } + lifetimeAnimations(duration = 0.5.seconds, Animations.EaseOutQuint, Animations.EaseInBack) + if (!warned) uiBranchWarning() } private fun ElementDSL.module(module: Module) = column(size(h = Animatable(from = 32.px, to = Bounding))) { - // used to lazily load setting elements, as they're not visible until clicked and most of them go unsee n + // used to lazily load setting elements, as they're not visible until clicked and most of them go unseen var loaded = false + val color = Color.Animated(from = `gray 26`, to = this@ClickGUI.color, swapIf = module.enabled) block( size(240.px, 32.px), color = color ) { - hoverInfo(description = module.description) + hoverInformation(description = module.description) hoverEffect(0.1.seconds) text( text = module.name, @@ -235,7 +247,15 @@ object ClickGUI: Module( // load settings if haven't yet if (!loaded) { loaded = true - module.settings.loop { if (!it.hidden) it.apply { this@column.createElement() } } + module.settings.loop { + if (!it.hidden && it is Setting.Renders) { + it.apply { + val drawableScope = ElementScope(it.Drawable()) + drawableScope.hoverInformation(it.description) + this@column.create(drawableScope).create() + } + } + } redraw() } parent()!!.height.animate(0.25.seconds, Animations.EaseOutQuint); true @@ -243,84 +263,43 @@ object ClickGUI: Module( } } - // todo: move out and cleanup - fun ElementDSL.openAnim( - duration: Float, - animation: Animations, - ) { - onCreation { - // test - AnimationOperation(Animation(duration, animation)) { - element.alpha = it - element.scale = it - }.add() - } - } - - // todo: move out and cleanup - fun ElementDSL.closeAnim(duration: Float, animation: Animations) { - onRemove { - UIScreen.closeAnimHandler = ui.window as UIScreen - // test - AnimationOperation(Animation(duration, animation).onFinish { - UIScreen.closeAnimHandler = null - }) { - element.alpha = 1f - it - element.scale = 1f - it - }.add() - } - } - - fun ElementDSL.hoverInfo(description: String) { + fun ElementDSL.hoverInformation(description: String) { if (description.isEmpty()) return var popup: Popup? = null - onHover(1.seconds) { + + onHover(duration = 1.seconds) { if (popup != null) return@onHover + val x = if (element.x >= ui.main.width / 2f) (element.x - 5).px.alignRight else (element.x + element.width + 5).px val y = (element.y + 5).px - popup = popup(at(x, y)) { + + popup = popup(constrain(x, y, Bounding, Bounding), smooth = true) { block( constraints = constrain(0.px, 0.px, Bounding + 10.px, 30.px), color = `gray 38`, - radius = 5.radii() + radius = 5.radius() ) { outline(this@ClickGUI.color, 2.px) - text(text = description) + text(description, pos = at(x = 6.px)) } - element.alphaAnim = Animatable(0.px, 1.px).apply { animate(0.25.seconds) } } } onMouseExit { popup?.let { - it.element.alphaAnim?.animate(0.25.seconds, Animations.Linear)?.onFinish { - it.closePopup() - popup = null - } + it.closePopup() + popup = null } } } - fun ElementDSL.onHover(duration: Float, block: () -> Unit) { - onMouseEnter { - val start = System.nanoTime() - operation { - if (System.nanoTime() - start >= duration) { - block() - return@operation true - } - !element.isInside(ui.mx, ui.my) || !element.renders - } - } - } // for ui branch warning - private fun ElementDSL.uiBranchWarning() { - popup(copies()) { + popup(copies(), smooth = true) { column(size(Bounding + 50.px, Bounding + 10.px), padding = 5.px) { // background - block(copies(), color = `gray 38`, radius = 10.radii()).outline(this@ClickGUI.color, thickness = 3.px) + block(copies(), color = `gray 38`, radius = 10.radius()).outline(this@ClickGUI.color, thickness = 3.px) divider(15.px) text("WARNING", size = 30.px) @@ -329,11 +308,11 @@ object ClickGUI: Module( text("If you downloaded this from GitHub Actions, ensure you don't download from the ui branch accidentally.", size = 20.px) divider(10.px) - block(size(Bounding + 30.px, Bounding + 10.px), this@ClickGUI.color, radius = 5.radii()) { + block(size(Bounding + 30.px, Bounding + 10.px), this@ClickGUI.color, radius = 5.radius()) { text("I understand", size = 25.px) hoverEffect(0.1.seconds) onClick { - closePopup(smooth = true) + closePopup() warned = true; true } } diff --git a/src/main/kotlin/me/odinmain/features/impl/render/ServerHud.kt b/src/main/kotlin/me/odinmain/features/impl/render/ServerHud.kt index 63a323f44..5c62a8d9b 100644 --- a/src/main/kotlin/me/odinmain/features/impl/render/ServerHud.kt +++ b/src/main/kotlin/me/odinmain/features/impl/render/ServerHud.kt @@ -2,7 +2,6 @@ package me.odinmain.features.impl.render -import com.github.stivais.ui.constraints.percent import com.github.stivais.ui.constraints.px import me.odinmain.features.Module import me.odinmain.utils.ServerUtils @@ -13,30 +12,39 @@ object ServerHud : Module( name = "Performance Display", description = "Displays certain performance-related metrics, like ping, TPS and FPS." ) { - private val fpsHUD by TextHUD(2.percent, 2.percent) { color, font -> + private val fpsHUD by TextHUD( + "FPS", + description = "Display's your fps on screen." + ) { color, font -> text( text = "FPS ", color = color, size = 30.px ) and text({ getFPS() }, font = font) - }.setting("FPS") + } - private val pingHUD by TextHUD(8.percent, 2.percent) { color, font -> + private val pingHUD by TextHUD( + "Ping", + description = "Display's your ping on screen." + ) { color, font -> text( text = "FPS ", color = color, size = 30.px ) and text({ ServerUtils.averagePing.toInt() }, font = font) - }.setting("FPS") + } - private val tpsHUD by TextHUD(14.percent, 2.percent) { color, font -> + private val tpsHUD by TextHUD( + "TPS", + description = "Display's your tps on screen." + ) { color, font -> text( text = "TPS ", color = color, size = 30.px, font = font, ) and text({ ServerUtils.averageTps.toInt() }, font = font) - }.setting("TPS") + } fun getFPS() = mc.debug.split(" ")[0].toIntOrNull() ?: 0 } \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/settings/Setting.kt b/src/main/kotlin/me/odinmain/features/settings/Setting.kt index c4917c8a2..013e3d306 100644 --- a/src/main/kotlin/me/odinmain/features/settings/Setting.kt +++ b/src/main/kotlin/me/odinmain/features/settings/Setting.kt @@ -6,14 +6,18 @@ import com.github.stivais.ui.constraints.measurements.Animatable import com.github.stivais.ui.constraints.measurements.Pixel import com.github.stivais.ui.constraints.px import com.github.stivais.ui.constraints.size +import com.github.stivais.ui.constraints.sizes.Bounding +import com.github.stivais.ui.constraints.sizes.Copying import com.github.stivais.ui.elements.Element import com.github.stivais.ui.elements.scope.ElementDSL -import com.github.stivais.ui.elements.scope.ElementScope -import com.github.stivais.ui.utils.animate +import com.github.stivais.ui.elements.scope.LayoutScope +import com.github.stivais.ui.events.Event +import com.github.stivais.ui.transforms.Transforms import com.github.stivais.ui.utils.seconds import com.google.gson.Gson import com.google.gson.GsonBuilder import me.odinmain.features.Module +import me.odinmain.features.settings.Setting.Renders.Companion.elementWidth import kotlin.properties.PropertyDelegateProvider import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -27,6 +31,7 @@ import kotlin.reflect.KProperty * @param hidden If setting shouldn't ever appear in the UI * @param description Description for the setting */ +// todo: remove hidden param abstract class Setting ( val name: String, var hidden: Boolean = false, @@ -46,7 +51,7 @@ abstract class Setting ( /** * Dependency for if it should be shown in the UI * - * @see Drawable + * @see DrawableOld */ var visibilityDependency: (() -> Boolean)? = null @@ -79,9 +84,6 @@ abstract class Setting ( companion object { - // temp - var elementWidth: Pixel = 240.px - /** * [Gson] for saving and loading settings */ @@ -90,7 +92,7 @@ abstract class Setting ( /** * Adds a dependency for a setting, for it to only be rendered if it matches true * - * @see Drawable + * @see DrawableOld */ fun , T> K.withDependency(dependency: () -> Boolean): K { visibilityDependency = dependency @@ -98,59 +100,73 @@ abstract class Setting ( } } - /** - * Creates the elements required for the [UI][me.odinmain.features.impl.render.ClickGUI.clickGUI] - * - * It is highly recommended to use [setting] as your base element, - * because it handles complex features that isn't feasible without - * - * ``` - * // Do: - * override fun ElementDSL.createElement() { - * setting(height) { - * // actual elements inside - * } - * } - * - * // Don't do: - * override fun ElementDSL.createElement() { - * // elements - * } - * ``` - * - * @see [setting] - */ - open fun ElementDSL.createElement() {} + interface Renders { - /** - * Intended to be used as a base in [createElement] to easily provide animations for settings with potential requirements - */ - protected fun ElementDSL.setting(height: Size, block: ElementDSL.() -> Unit = {}): ElementScope<*> { - return create(ElementScope(Drawable(height)), block) + fun ElementDSL.create() + + companion object { + /** + * Creates a group (or column) with the current width needed according the UIs it's used in. + */ + fun ElementDSL.setting( + height: Size = 40.px, + scope: ElementDSL.() -> Unit + ) { + group(size(Copying, height), scope) + } + + fun ElementDSL.settingColumn( + height: Size = Bounding, + scope: LayoutScope.() -> Unit + ) { + column(size(Copying, height), null, scope) + } + + /** + * Current width for settings elements inside the UIs it's used in + */ + var elementWidth: Pixel = 240.px + + inline fun ElementDSL.onValueChanged(crossinline block: () -> Unit) { + element.registerEvent(ValueUpdated) { + block() + redraw() + false + } + } + } + + + data object ValueUpdated : Event } - // TODO: Find out why ClickGUI runs 2x slower until a setting visibility changes - // TODO: Make custom event to tell if value has changed so it can update visually if it was changed externally - protected inner class Drawable(height: Size) : Element(size(elementWidth, Animatable(from = height, to = 0.px))) { + inner class Drawable : Element(size(elementWidth, Animatable(from = Bounding, to = 0.px))) { + + private var visible = visibilityDependency?.invoke() ?: true - private var visible: Boolean = visibilityDependency?.invoke() ?: true + private var lastValue: T = value + + private var alphaAnimation = Transforms.Alpha.Animated(to = 0f, from = 1f) init { - alphaAnim = Animatable(from = 1.px, to = 0.px) - scissors = true + addTransform(alphaAnimation) if (!visible) { (constraints.height as Animatable).swap() - (alphaAnim as Animatable).swap() + alphaAnimation.swap() } } override fun draw() { if ((visibilityDependency?.invoke() != false) != visible) { visible = !visible - constraints.height.animate(0.25.seconds, Animations.EaseInOutQuint) - alphaAnim!!.animate(0.25.seconds, Animations.EaseInOutQuint) + (constraints.height as Animatable).animate(0.25.seconds, Animations.EaseInOutQuint) + alphaAnimation.animate(0.25.seconds, Animations.EaseInOutQuint) redraw = true } + if (lastValue != value) { + lastValue = value + ui.eventManager.dispatchToAll(Renders.ValueUpdated, this) + } } } } \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/settings/impl/ActionSetting.kt b/src/main/kotlin/me/odinmain/features/settings/impl/ActionSetting.kt deleted file mode 100644 index 3edd9b06e..000000000 --- a/src/main/kotlin/me/odinmain/features/settings/impl/ActionSetting.kt +++ /dev/null @@ -1,47 +0,0 @@ -package me.odinmain.features.settings.impl - -import com.github.stivais.ui.constraints.* -import com.github.stivais.ui.elements.scope.* -import com.github.stivais.ui.utils.radius -import com.github.stivais.ui.utils.seconds -import me.odinmain.features.impl.render.ClickGUI -import me.odinmain.features.impl.render.ClickGUI.`gray 38` -import me.odinmain.features.settings.Setting - -/** - * Setting, which contains a function that gets ran when clicked inside the UI - * - * This setting doesn't contain any value - * - * @param action Function that gets ran - */ -class ActionSetting( - name: String, - description: String = "", - action: ElementDSL.() -> Unit = {} -) : Setting Unit>(name, false, description) { - - override val default: ElementDSL.() -> Unit = action - - override var value: ElementDSL.() -> Unit = default - - override fun ElementScope<*>.createElement() { - setting(40.px) { - block( - constraints = size(95.percent, 75.percent), - color = `gray 38`, - radius = radius(all = 5) - ) { - text( - text = name, - ) - onClick { - value.invoke(this) - true - } - hoverEffect(0.25.seconds) - outline(ClickGUI.color) - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/settings/impl/BooleanSetting.kt b/src/main/kotlin/me/odinmain/features/settings/impl/BooleanSetting.kt index 9c149b98e..e0cc8ed7f 100644 --- a/src/main/kotlin/me/odinmain/features/settings/impl/BooleanSetting.kt +++ b/src/main/kotlin/me/odinmain/features/settings/impl/BooleanSetting.kt @@ -1,17 +1,23 @@ package me.odinmain.features.settings.impl import com.github.stivais.ui.animation.Animations -import com.github.stivais.ui.color.* +import com.github.stivais.ui.color.Color +import com.github.stivais.ui.color.color +import com.github.stivais.ui.color.darker import com.github.stivais.ui.constraints.* import com.github.stivais.ui.constraints.measurements.Animatable -import com.github.stivais.ui.elements.scope.* -import com.github.stivais.ui.utils.* +import com.github.stivais.ui.elements.scope.ElementDSL +import com.github.stivais.ui.elements.scope.hoverEffect +import com.github.stivais.ui.utils.radius +import com.github.stivais.ui.utils.seconds import com.google.gson.JsonElement import com.google.gson.JsonPrimitive import me.odinmain.features.impl.render.ClickGUI import me.odinmain.features.impl.render.ClickGUI.`gray 38` import me.odinmain.features.settings.Saving import me.odinmain.features.settings.Setting +import me.odinmain.features.settings.Setting.Renders.Companion.onValueChanged +import me.odinmain.features.settings.Setting.Renders.Companion.setting /** * A setting that represents a boolean. @@ -21,60 +27,55 @@ class BooleanSetting( override val default: Boolean = false, hidden: Boolean = false, description: String = "", -): Setting(name, hidden, description), Saving { +): Setting(name, hidden, description), Saving, Setting.Renders { override var value: Boolean = default - var enabled: Boolean by this::value - override fun write(): JsonElement { - return JsonPrimitive(enabled) + return JsonPrimitive(value) } override fun read(element: JsonElement?) { - if (element?.asBoolean != enabled) { - enabled = !enabled - } - } - - override fun ElementScope<*>.createElement() { - setting(40.px) { - text(text = name, pos = at(x = 6.px), size = 40.percent) - switch( - constrain(x = -6.px, w = 35.px, h = 50.percent), - color = Color.Animated(from = `gray 38`, to = ClickGUI.color), - on = value - ).onClick { - value = !value - true - } + if (element?.asBoolean != value) { + value = !value } } - fun ElementDSL.switch( - constraints: Constraints? = null, - color: Color.Animated, - on: Boolean = false - ): BlockScope { - val color2 = color.color2 + override fun ElementDSL.create() = setting { + text( + name, + pos = at(x = 6.px), + size = 40.percent + ) val pointer = Animatable(from = 30.percent.center, to = 70.percent.center) - if (on) { + val color = Color.Animated(from = `gray 38`, to = ClickGUI.color) + + if (value) { pointer.swap() color.swap() } - return block(constraints, color, radius = 9.radii()) { - outline(color { color2.rgba.darker() }, thickness = 1.5.px) - hoverEffect() + + block( + constrain(x = -(6.px), w = 35.px, h = 50.percent), + color, + radius = 10.radius() + ) { + onClick { + value = !value + true + } block( constrain(x = pointer, w = 50.percent, h = 80.percent), color = Color.WHITE, - radius = 8.radii() + radius = 8.radius() ) - onClick { - color.animate(0.25.seconds) - pointer.animate(0.25.seconds, Animations.EaseInOutQuint) - false - } + outline(color = color { ClickGUI.color.rgba.darker() }, thickness = 1.5.px) + hoverEffect() + } + + onValueChanged { + color.animate(0.25.seconds, Animations.Linear) + pointer.animate(0.25.seconds, Animations.EaseInOutQuint) } } } \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/settings/impl/ColorSetting.kt b/src/main/kotlin/me/odinmain/features/settings/impl/ColorSetting.kt index 154454162..a26f46c96 100644 --- a/src/main/kotlin/me/odinmain/features/settings/impl/ColorSetting.kt +++ b/src/main/kotlin/me/odinmain/features/settings/impl/ColorSetting.kt @@ -1,25 +1,23 @@ package me.odinmain.features.settings.impl import com.github.stivais.ui.animation.Animations -import com.github.stivais.ui.color.* +import com.github.stivais.ui.color.Color +import com.github.stivais.ui.color.colorFrom +import com.github.stivais.ui.color.toHSB +import com.github.stivais.ui.color.toHexString import com.github.stivais.ui.constraints.* import com.github.stivais.ui.constraints.measurements.Animatable -import com.github.stivais.ui.constraints.sizes.Aspect -import com.github.stivais.ui.constraints.sizes.Bounding -import com.github.stivais.ui.constraints.sizes.Copying +import com.github.stivais.ui.constraints.sizes.AspectRatio import com.github.stivais.ui.elements.scope.ElementDSL -import com.github.stivais.ui.elements.scope.ElementScope import com.github.stivais.ui.elements.scope.slider import com.github.stivais.ui.renderer.Gradient.LeftToRight import com.github.stivais.ui.renderer.Gradient.TopToBottom -import com.github.stivais.ui.utils.radii +import com.github.stivais.ui.utils.radius import com.github.stivais.ui.utils.seconds import com.google.gson.JsonElement import com.google.gson.JsonPrimitive import me.odinmain.features.settings.Saving import me.odinmain.features.settings.Setting -import me.odinmain.utils.skyblock.modMessage -import java.awt.Color.HSBtoRGB class ColorSetting( name: String, @@ -45,75 +43,75 @@ class ColorSetting( return JsonPrimitive(value.toHexString()) } - override fun ElementScope<*>.createElement() { - val size = Animatable(from = 40.px, to = Bounding) - val alpha = Animatable(0.px, 1.px) - - val hueMax = color { HSBtoRGB(value.hue, 1f, 1f) } - - setting(size) { - group(constrain(0.px, 0.px, w = Copying, h = 40.px)) { - text( - text = name, - pos = at(x = 6.px), - size = 40.percent - ) - // color preview + dropdown button thingy - block( - constraints = constrain(x = -(6.px), w = 30.px, h = 50.percent), - color = transparentFix, - radius = 5.radii() - ) { - outline(color = color { value.withAlpha(255).rgba }) - block( - constraints = indent(2), - color = value, - radius = 4.radii() - ) - onClick { - size.animate(0.25.seconds, Animations.EaseInOutQuint) - alpha.animate(0.25.seconds, Animations.Linear) - this@setting.redraw() - true - } - } - } - - column(constraints = constrain(0.px, 40.px, w = Copying)) { - element.alphaAnim = alpha - text("wip, need popup color picker", size = 10.px) - saturationAndBrightness(hueMax) - divider(10.px) - hueSlider() - if (allowAlpha) { - modMessage("a") - divider(10.px) - alphaSlider(hueMax) - } - } - } - } +// override fun ElementScope<*>.createElement() { +// val size = Animatable(from = 40.px, to = Bounding) +// val alpha = Animatable(0.px, 1.px) +// +// val hueMax = color { HSBtoRGB(value.hue, 1f, 1f) } +// +// settingOld(size) { +// group(constrain(0.px, 0.px, w = Copying, h = 40.px)) { +// text( +// text = name, +// pos = at(x = 6.px), +// size = 40.percent +// ) +// // color preview + dropdown button thingy +// block( +// constraints = constrain(x = -(6.px), w = 30.px, h = 50.percent), +// color = transparentFix, +// radius = 5.radius() +// ) { +// outline(color = color { value.withAlpha(255).rgba }) +// block( +// constraints = indent(2), +// color = value, +// radius = 4.radius() +// ) +// onClick { +// size.animate(0.25.seconds, Animations.EaseInOutQuint) +// alpha.animate(0.25.seconds, Animations.Linear) +// this@settingOld.redraw() +// true +// } +// } +// } +// +// column(constraints = constrain(0.px, 40.px, w = Copying)) { +// element.alphaAnim = alpha +// text("wip, need popup color picker", size = 10.px) +// saturationAndBrightness(hueMax) +// divider(10.px) +// hueSlider() +// if (allowAlpha) { +// modMessage("a") +// divider(10.px) +// alphaSlider(hueMax) +// } +// } +// } +// } private fun ElementDSL.saturationAndBrightness(hueMax: Color) { val x = Animatable.Raw((228f * value.saturation).coerceIn(8f, 220f)) val y = Animatable.Raw((170f * (1f - value.brightness)).coerceIn(8f, 220f)) block( - size(w = 95.percent, h = Aspect(228f / 170f)), + size(w = 95.percent, h = AspectRatio(228f / 170f)), colors = Color.WHITE to hueMax, - radius = 5.radii(), + radius = 5.radius(), gradient = LeftToRight ) { block( constraints = copies(), // temp fix until I figure out why nanovg doesn't render anything under 0.2f alpha colors = transparentFix to Color.BLACK, - radius = 5.radii(), + radius = 5.radius(), gradient = TopToBottom ) { block( constraints = constrain(x.center, y.center, w = 10.px, h = 10.px), color = value, - radius = 5.radii() + radius = 5.radius() ).outline(Color.WHITE) } slider( @@ -141,12 +139,12 @@ class ColorSetting( image( "/assets/odinmain/clickgui/HueGradient.png", size(w = 95.percent, h = 15.px), - radius = 5.radii() + radius = 5.radius() ) { block( constraints = constrain(x.center, w = 10.px, h = 10.px), color = value, - radius = 5.radii() + radius = 5.radius() ).outline(Color.WHITE) slider( accepts = true, @@ -165,13 +163,13 @@ class ColorSetting( block( size(w = 95.percent, h = 15.px), colors = transparentFix to hueMax, - radius = 5.radii(), + radius = 5.radius(), gradient = LeftToRight ) { block( constraints = constrain(x.center, w = 10.px, h = 10.px), color = value, - radius = 5.radii() + radius = 5.radius() ).outline(Color.WHITE) slider( accepts = true, diff --git a/src/main/kotlin/me/odinmain/features/settings/impl/DropdownSetting.kt b/src/main/kotlin/me/odinmain/features/settings/impl/DropdownSetting.kt index ddd95a6a0..602ba8f15 100644 --- a/src/main/kotlin/me/odinmain/features/settings/impl/DropdownSetting.kt +++ b/src/main/kotlin/me/odinmain/features/settings/impl/DropdownSetting.kt @@ -3,13 +3,13 @@ package me.odinmain.features.settings.impl import com.github.stivais.ui.animation.Animations import com.github.stivais.ui.constraints.at import com.github.stivais.ui.constraints.constrain -import com.github.stivais.ui.constraints.measurements.Animatable import com.github.stivais.ui.constraints.percent import com.github.stivais.ui.constraints.px import com.github.stivais.ui.elements.scope.ElementDSL -import com.github.stivais.ui.renderer.Image +import com.github.stivais.ui.transforms.rotation import com.github.stivais.ui.utils.seconds import me.odinmain.features.settings.Setting +import me.odinmain.features.settings.Setting.Renders.Companion.setting /** * A setting intended to show or hide other settings in the GUI. @@ -19,30 +19,29 @@ import me.odinmain.features.settings.Setting class DropdownSetting( name: String, override val default: Boolean = false -): Setting(name, false, "") { +): Setting(name, false, ""), Setting.Renders { override var value: Boolean = default - var enabled: Boolean by this::value - - override fun ElementDSL.createElement() { - setting(40.px) { - text( - text = name, - pos = at(6.px), - size = 40.percent + override fun ElementDSL.create() = setting { + text( + name, + pos = at(6.px), + size = 40.percent + ) + image( + image = "/assets/odinmain/clickgui/chevron.svg", + constraints = constrain(-6.px, w = 30.px, h = 30.px) + ) { + val rotate = rotation( + from = 270f, + to = 90f ) - image( - Image("/assets/odinmain/clickgui/chevron.svg", type = Image.Type.VECTOR), - constraints = constrain(-6.px, w = 30.px, h = 30.px) - ) { - val rotate = Animatable(from = Math.toRadians(270.0).px, to = Math.toRadians(90.0).px) - element.rotateAnim = rotate - onClick { - rotate.animate(0.25.seconds, Animations.EaseInOutQuint) - value = !value - true - } + onClick { + rotate.animate(0.25.seconds, Animations.EaseInOutQuint) + value = !value + this@setting.redraw() + true } } } diff --git a/src/main/kotlin/me/odinmain/features/settings/impl/HUDSetting.kt b/src/main/kotlin/me/odinmain/features/settings/impl/HUDSetting.kt index 2b4e0a8eb..e28571a9b 100644 --- a/src/main/kotlin/me/odinmain/features/settings/impl/HUDSetting.kt +++ b/src/main/kotlin/me/odinmain/features/settings/impl/HUDSetting.kt @@ -1,44 +1,42 @@ package me.odinmain.features.settings.impl -import com.github.stivais.ui.elements.scope.ElementDSL +import com.github.stivais.ui.utils.loop import com.google.gson.JsonElement import com.google.gson.JsonObject -import me.odinmain.features.Module +import me.odinmain.features.huds.HUD import me.odinmain.features.settings.Saving import me.odinmain.features.settings.Setting class HUDSetting( name: String, - hud: Module.HUD, + hud: HUD, description: String, -) : Setting(name, false, description), Saving { +) : Setting(name, false, description), Saving { - override val default: Module.HUD = hud - override var value: Module.HUD = hud + override val default: HUD = hud + override var value: HUD = hud override fun write(): JsonElement { return JsonObject().apply { - addProperty("x", value.x.percent) - addProperty("y", value.y.percent) - addProperty("scale", value.scale) - addProperty("enabled", value.enabled) + for (setting in value.settings) { + if (setting !is Saving) continue + add(setting.name, setting.write()) + } } } override fun read(element: JsonElement?) { element?.asJsonObject?.apply { - value.x.percent = get("x").asFloat - value.y.percent = get("y").asFloat - value.scale = get("scale").asFloat - value.enabled = get("enabled").asBoolean + for (entry in entrySet()) { + val setting = value.getSettingByName(entry.key) as? Saving ?: continue + setting.read(entry.value) + } } } override fun reset() { - super.reset() - } - - override fun ElementDSL.createElement() { - // todo: style it like a dropdown, where it drops down settings under the hud + value.settings.loop { + it.reset() + } } } \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/settings/impl/KeybindSetting.kt b/src/main/kotlin/me/odinmain/features/settings/impl/KeybindSetting.kt index 73d0c70e7..6d9676e33 100644 --- a/src/main/kotlin/me/odinmain/features/settings/impl/KeybindSetting.kt +++ b/src/main/kotlin/me/odinmain/features/settings/impl/KeybindSetting.kt @@ -1,17 +1,21 @@ package me.odinmain.features.settings.impl -import com.github.stivais.ui.color.Color import com.github.stivais.ui.constraints.* import com.github.stivais.ui.constraints.measurements.Animatable import com.github.stivais.ui.constraints.sizes.Bounding -import com.github.stivais.ui.elements.scope.* -import com.github.stivais.ui.utils.* +import com.github.stivais.ui.elements.scope.ElementDSL +import com.github.stivais.ui.elements.scope.focuses +import com.github.stivais.ui.elements.scope.hoverEffect +import com.github.stivais.ui.utils.animate +import com.github.stivais.ui.utils.radius +import com.github.stivais.ui.utils.seconds import com.google.gson.JsonElement import com.google.gson.JsonPrimitive import me.odinmain.features.impl.render.ClickGUI import me.odinmain.features.impl.render.ClickGUI.`gray 38` import me.odinmain.features.settings.Saving import me.odinmain.features.settings.Setting +import me.odinmain.features.settings.Setting.Renders.Companion.setting import org.lwjgl.input.Keyboard.* import org.lwjgl.input.Mouse @@ -19,26 +23,12 @@ class KeybindSetting( name: String, override val default: Keybinding, description: String, -) : Setting(name, false, description), Saving { +) : Setting(name, false, description), Saving, Setting.Renders { constructor(name: String, key: Int, description: String) : this(name, Keybinding(key), description) override var value: Keybinding = default - /** - * Action to do, when keybinding is pressed - * - * Note: Action is always invoked, even if module isn't enabled. - */ - fun onPress(block: () -> Unit): KeybindSetting { - value.onPress = block - return this - } - - override fun reset() { - value.key = default.key - } - private val keyName: String get() { val key = value.key @@ -56,56 +46,8 @@ class KeybindSetting( } } -// private fun isConflicting(): Boolean { -// return mc.gameSettings.keyBindings.any { it.keyCode == value.key } && value.key != 0 && ClickGUI.showBindConfliction -// } - - override fun ElementScope<*>.createElement() { - setting(40.px) { - text( - text = name, - pos = at(x = 6.px), - size = 40.percent, - ) - block( - constraints = constrain(x = -6.px, w = Bounding + 6.px, h = 70.percent), - color = `gray 38`, - radius = radius(5) - ) { - val display = text( - text = keyName, - color = /*if (isConflicting()) conflictingColor else */Color.WHITE - ) - onFocusedClick { (button) -> - value.key = -100 + button - ui.unfocus() - true - } - onKeyPressed { (code) -> - value.key = when (code) { - KEY_ESCAPE, KEY_BACK -> 0 - KEY_NUMPADENTER, KEY_RETURN -> value.key - else -> code - } - ui.unfocus() - true - } - onFocusGain { - outlineColor!!.animate(0.25.seconds) - outline!!.animate(0.25.seconds) - } - onFocusLost { - display.string = keyName -// val target = if (isConflicting()) conflictingColor else Color.WHITE -// display.animateColor(to = target, duration = 0.1.seconds) - outlineColor!!.animate(0.25.seconds) - outline!!.animate(0.25.seconds) - } - hoverEffect() - focuses() - outline(color = ClickGUI.color, Animatable(from = 1.px, to = 2.5.px)) - } - } + override fun reset() { + value.key = default.key } override fun write(): JsonElement { @@ -118,10 +60,58 @@ class KeybindSetting( } } -// private companion object { -// @JvmField -// val conflictingColor: Color = Color.RGB(240, 70, 70) -// } + override fun ElementDSL.create() = setting { + text( + text = name, + pos = at(x = 6.px), + size = 40.percent, + ) + block( + constraints = constrain(x = -6.px, w = Bounding + 6.px, h = 70.percent), + color = `gray 38`, + radius = radius(5) + ) { + val display = text( + text = keyName, + ) + onFocusedClick { (button) -> + value.key = -100 + button + ui.unfocus() + true + } + onKeyPressed { (code) -> + value.key = when (code) { + KEY_ESCAPE, KEY_BACK -> 0 + KEY_NUMPADENTER, KEY_RETURN -> value.key + else -> code + } + ui.unfocus() + true + } + onFocusGain { + outlineColor!!.animate(0.25.seconds) + outline!!.animate(0.25.seconds) + } + onFocusLost { + display.string = keyName + outlineColor!!.animate(0.25.seconds) + outline!!.animate(0.25.seconds) + } + hoverEffect() + focuses() + outline(color = ClickGUI.color, Animatable(from = 1.px, to = 2.5.px)) + } + } + + /** + * Action to do, when keybinding is pressed + * + * Note: Action is always invoked, even if module isn't enabled. + */ + fun onPress(block: () -> Unit): KeybindSetting { + value.onPress = block + return this + } } class Keybinding(var key: Int) { diff --git a/src/main/kotlin/me/odinmain/features/settings/impl/NumberSetting.kt b/src/main/kotlin/me/odinmain/features/settings/impl/NumberSetting.kt index bddfcf854..77783bb6b 100644 --- a/src/main/kotlin/me/odinmain/features/settings/impl/NumberSetting.kt +++ b/src/main/kotlin/me/odinmain/features/settings/impl/NumberSetting.kt @@ -8,10 +8,9 @@ import com.github.stivais.ui.constraints.measurements.Animatable import com.github.stivais.ui.constraints.percent import com.github.stivais.ui.constraints.px import com.github.stivais.ui.constraints.sizes.Copying -import com.github.stivais.ui.elements.scope.ElementScope +import com.github.stivais.ui.elements.scope.ElementDSL import com.github.stivais.ui.elements.scope.hoverEffect -import com.github.stivais.ui.elements.scope.slider -import com.github.stivais.ui.utils.radii +import com.github.stivais.ui.utils.radius import com.github.stivais.ui.utils.seconds import com.google.gson.JsonElement import com.google.gson.JsonPrimitive @@ -19,6 +18,9 @@ import me.odinmain.features.impl.render.ClickGUI import me.odinmain.features.impl.render.ClickGUI.`gray 26` import me.odinmain.features.settings.Saving import me.odinmain.features.settings.Setting +import me.odinmain.features.settings.Setting.Renders.Companion.elementWidth +import me.odinmain.features.settings.Setting.Renders.Companion.onValueChanged +import me.odinmain.features.settings.Setting.Renders.Companion.setting import me.odinmain.utils.round import kotlin.math.floor import kotlin.math.round @@ -31,7 +33,6 @@ import kotlin.math.round * @param increment The increment for the setting * @param unit The suffix for value in the UI (It is recommended to set this for better UX) */ -// TODO: Option to use a text field that only accepts numbers instead of slider @Suppress("UNCHECKED_CAST") class NumberSetting( name: String, @@ -42,7 +43,7 @@ class NumberSetting( hidden: Boolean = false, description: String = "", val unit: String = "", -) : Setting(name, hidden, description), Saving where E : Number, E : Comparable { +) : Setting(name, hidden, description), Saving, Setting.Renders where E : Number, E : Comparable { override var value: E = default @@ -62,44 +63,62 @@ class NumberSetting( return "$number$unit" } - override fun ElementScope<*>.createElement() { - setting(44.px) { - text( - text = name, - pos = at(6.px, 10.px), - size = 35.percent - ) - val display = text( - text = text, - pos = at(x = -(6.px), y = 10.px), - size = 35.percent - ) - - val sliderAmount = Animatable.Raw((((value.toDouble() - min) / (max - min)) * (elementWidth.pixels * 0.95)).toFloat()) + override fun ElementDSL.create() = setting(45.px) { + text( + name, + pos = at(6.px, 10.px), + size = 35.percent + ) + val display = text( + text, + pos = at(x = -(6.px), y = 10.px), + size = 35.percent + ) + + val sliderWidth = Animatable.Raw((((value.toDouble() - min) / (max - min)) * (elementWidth.pixels * 0.95)).toFloat()) + + block( + constraints = constrain(y = 75.percent, w = 95.percent, h = 20.percent), + color = `gray 26`, + radius = 4.radius() + ) { block( - constraints = constrain(y = 75.percent, w = 95.percent, h = 20.percent), - color = `gray 26`, - radius = 4.radii() - ) { - block( - constraints = constrain(0.px, 0.px, sliderAmount, Copying), - color = ClickGUI.color, - radius = 4.radii() - ).hoverEffect(handler = this@block) - - slider( - onChange = { percent, _, wasClick -> - val to = percent * element.width - if (wasClick) { - sliderAmount.animate(to = to, 0.75.seconds, Animations.EaseOutQuint) - } else { - sliderAmount.to(to = to) - } - this@setting.redraw() - set(percent * (max - min) + min) - display.string = text - } - ) + constraints = constrain(0.px, 0.px, sliderWidth, Copying), + color = ClickGUI.color, + radius = 4.radius() + ).hoverEffect(handler = this@block) + + var dragging = false + // used to animate on only click + var first = true + + onClick { + first = true + dragging = true + val percent = ((ui.mx - element.x).coerceIn(0f, element.width) / element.width).round(2).toFloat() + set(percent * (max - min) + min) + true + } + onMouseMove { + if (dragging) { + val percent = ((ui.mx - element.x).coerceIn(0f, element.width) / element.width).round(2).toFloat() + set(percent * (max - min) + min) + } + dragging + } + onRelease { + dragging = false + } + + onValueChanged { + val to = ((value.toDouble() - min) / (max - min) * element.width).toFloat() + if (first || !dragging) { + first = false + sliderWidth.animate(to = to, 0.75.seconds, Animations.EaseOutQuint) + } else { + sliderWidth.to(to = to) + } + display.string = text } } } diff --git a/src/main/kotlin/me/odinmain/features/settings/impl/SelectorSetting.kt b/src/main/kotlin/me/odinmain/features/settings/impl/SelectorSetting.kt index 8d3e9aa20..48fb988eb 100644 --- a/src/main/kotlin/me/odinmain/features/settings/impl/SelectorSetting.kt +++ b/src/main/kotlin/me/odinmain/features/settings/impl/SelectorSetting.kt @@ -6,8 +6,8 @@ import com.github.stivais.ui.constraints.* import com.github.stivais.ui.constraints.measurements.Animatable import com.github.stivais.ui.constraints.sizes.Bounding import com.github.stivais.ui.constraints.sizes.Copying -import com.github.stivais.ui.elements.impl.TextScope -import com.github.stivais.ui.elements.scope.ElementScope +import com.github.stivais.ui.elements.scope.ElementDSL +import com.github.stivais.ui.transforms.Transforms import com.github.stivais.ui.utils.animate import com.github.stivais.ui.utils.radius import com.github.stivais.ui.utils.seconds @@ -17,6 +17,8 @@ import me.odinmain.features.impl.render.ClickGUI import me.odinmain.features.impl.render.ClickGUI.`gray 38` import me.odinmain.features.settings.Saving import me.odinmain.features.settings.Setting +import me.odinmain.features.settings.Setting.Renders.Companion.onValueChanged +import me.odinmain.features.settings.Setting.Renders.Companion.setting /** * Setting that lets you pick between an array of strings. @@ -27,7 +29,7 @@ class SelectorSetting( var options: ArrayList, hidden: Boolean = false, description: String = "", -) : Setting(name, hidden, description), Saving { +) : Setting(name, hidden, description), Saving, Setting.Renders { constructor( name: String, @@ -70,77 +72,78 @@ class SelectorSetting( return options.map { it.lowercase() }.indexOf(string.lowercase()).coerceIn(0, options.size - 1) } - override fun ElementScope<*>.createElement() { + override fun ElementDSL.create() = setting(height = Bounding) { + column(size(w = Copying)) { + val alpha = Transforms.Alpha.Animated(from = 0f, to = 1f) + val height = Animatable(from = 0.px, to = Bounding) + val thickness = Animatable(from = 1.px, to = 1.75.px) - var text: TextScope? = null - // temp/test - val alphaAnim = Animatable(0.25.px, 1.px) + section(40.px) { + text( + name, + pos = at(x = 6.px), + size = 40.percent + ) + block( + constraints = constrain(x = -(6.px), w = Bounding + 6.px, h = 75.percent), + color = `gray 38`, + radius = 5.radius() + ) { + outline(ClickGUI.color, thickness = thickness) - val height = Animatable(from = 40.px, to = (50 + 32 * options.size).px) - val thickness = Animatable(from = 1.px, to = 1.75.px) - - setting(height) { - column(copies()) { - group(constraints = size(w = Copying, h = 40.px)) { - text( - text = name, - pos = at(x = 6.px), - size = 40.percent + val text = text( + options[value], + pos = at(x = 6.px) ) + onValueChanged { + text.string = options[value] + } + + onClick { + alpha.animate(0.25.seconds, Animations.EaseInOutQuint) + height.animate(0.25.seconds, Animations.EaseInOutQuint) + thickness.animate(0.25.seconds, Animations.EaseInOutQuint) + this@column.redraw() + true + } + } + } + column(size(w = Copying, h = height)) { + transform(alpha) + divider(10.px) + + column(size(w = 95.percent)) { block( - constraints = constrain(x = -6.px, w = Bounding + 6.px, h = 75.percent), + constraints = copies(), color = `gray 38`, - radius = radius(5) + radius = 5.radius() ) { outline( - color = ClickGUI.color, - thickness - ) - text = text( - text = options[value], - pos = at(x = 6.px) + ClickGUI.color, + thickness = 1.5.px ) - onClick { - alphaAnim.animate(0.25.seconds, Animations.EaseInOutQuint) - height.animate(0.25.seconds, Animations.EaseInOutQuint) - thickness.animate(0.25.seconds, Animations.EaseInOutQuint) - this@setting.redraw() - true - } } - } - divider(5.px) - column(size(w = 95.percent, h = Bounding)) { - element.alphaAnim = alphaAnim - // background - block( - constraints = copies(), - color = `gray 38`, - radius = radius(all = 5) - ).outline(color = ClickGUI.color, thickness = 1.5.px) - // options - // they're transparent, except for the outline which is animated on hover for ((index, option) in options.withIndex()) { - val color = Color.Animated(from = Color.TRANSPARENT, to = Color.RGB(150, 150, 150, 0.2f)) block( - constraints = size(w = Copying, h = 32.px), - color = color, - radius = radius(5) + constraints = size(Copying, h = 32.px), + color = Color.Animated( + from = Color.TRANSPARENT, + to = Color.RGB(150, 150, 150, 0.2f) + ), + radius = 5.radius() ) { + text(option) onClick { - text!!.string = option value = index - alphaAnim.animate(0.25.seconds, Animations.EaseInOutQuint) + alpha.animate(0.25.seconds, Animations.EaseInOutQuint) height.animate(0.25.seconds, Animations.EaseInOutQuint) thickness.animate(0.25.seconds, Animations.EaseInOutQuint) - this@setting.redraw() true } onMouseEnterExit { - color.animate(duration = 0.05.seconds) + color?.animate(duration = 0.05.seconds) } - text(text = option) } } } diff --git a/src/main/kotlin/me/odinmain/features/settings/impl/StringSetting.kt b/src/main/kotlin/me/odinmain/features/settings/impl/StringSetting.kt index bcdaee312..191275ffa 100644 --- a/src/main/kotlin/me/odinmain/features/settings/impl/StringSetting.kt +++ b/src/main/kotlin/me/odinmain/features/settings/impl/StringSetting.kt @@ -1,15 +1,17 @@ package me.odinmain.features.settings.impl -import com.github.stivais.ui.color.* +import com.github.stivais.ui.animation.Animations +import com.github.stivais.ui.color.Color +import com.github.stivais.ui.color.brighter +import com.github.stivais.ui.color.color import com.github.stivais.ui.constraints.* import com.github.stivais.ui.constraints.measurements.Animatable -import com.github.stivais.ui.constraints.positions.Center -import com.github.stivais.ui.elements.Element -import com.github.stivais.ui.elements.impl.TextInput -import com.github.stivais.ui.elements.scope.ElementScope +import com.github.stivais.ui.constraints.sizes.AspectRatio +import com.github.stivais.ui.constraints.sizes.Copying +import com.github.stivais.ui.elements.scope.ElementDSL import com.github.stivais.ui.renderer.Image import com.github.stivais.ui.utils.animate -import com.github.stivais.ui.utils.radii +import com.github.stivais.ui.utils.radius import com.github.stivais.ui.utils.seconds import com.google.gson.JsonElement import com.google.gson.JsonPrimitive @@ -17,6 +19,9 @@ import me.odinmain.features.impl.render.ClickGUI import me.odinmain.features.impl.render.ClickGUI.`gray 38` import me.odinmain.features.settings.Saving import me.odinmain.features.settings.Setting +import me.odinmain.features.settings.Setting.Renders.Companion.setting +import me.odinmain.utils.ui.elements.TextInput +import me.odinmain.utils.ui.textInput /** * Setting that lets you type a string. @@ -28,7 +33,7 @@ class StringSetting( var length: Int = 20, hidden: Boolean = false, description: String = "", -) : Setting(name, hidden, description), Saving { +) : Setting(name, hidden, description), Saving, Setting.Renders { override var value: String = default set(value) { @@ -52,48 +57,56 @@ class StringSetting( } } - override fun ElementScope<*>.createElement() { - val thickness = Animatable(from = 1.px, to = 1.75.px) - val hover = Color.Animated(from = `gray 38`, to = `gray 38 brighter`) - setting(70.px) { - text( - text = name, - pos = at(x = 6.px, y = 8.px), - size = 16.px - ) + override fun ElementDSL.create() = setting(70.px) { + column(size(w = Copying), padding = 5.px) { + section(size = 20.px) { + text( + name, + pos = at(x = 6.px), + size = 80.percent + ) + } + val thickness = Animatable(from = 1.px, to = 1.75.px) + val hover = Color.Animated(from = `gray 38`, to = color { `gray 38`.rgba.brighter(1.2) }) + block( - constraints = constrain(y = 30.px, w = 95.percent, h = 30.px), + size(w = 95.percent, h = 30.px), color = hover, - radius = 5.radii() + outlineColor = ClickGUI.color, + outlineThickness = thickness, + radius = 5.radius() ) { + outline( + ClickGUI.color, + thickness, + ) + onMouseEnterExit { + hover.animate(0.25.seconds, Animations.Linear) + } + val maxWidth = if (censors) 80.percent else 95.percent val input = textInput( text = value, - constraints = at(6.px, y = Center + 1.px), // todo: fix text offset + constraints = at(x = 6.px), size = 50.percent, maxWidth = maxWidth, censored = censors, - onTextChange = { str -> // todo: change this to an event and allow for cancelling - value = if (str.length <= length) str else value + onTextChange = { event -> + val str = event.string + if (str.length <= length) value = str else event.cancel() } ).apply { onFocusGain { thickness.animate(0.25.seconds) } onFocusLost { thickness.animate(0.25.seconds) } } onClick { - input.focusThis(); true - } - onMouseEnterExit { - hover.animate(0.25.seconds) + input.focusThis() + true } - outline(ClickGUI.color, thickness) - scissors() - - // testing if (censors) { image( Image("/assets/odinmain/clickgui/visibility-show.svg", type = Image.Type.VECTOR), - constraints = constrain(-6.px, w = Test(), h = 75.percent) + constraints = constrain(-6.px, w = AspectRatio(1f), h = 75.percent) ) { onClick { (input.element as TextInput).censorInput = !(input.element).censorInput @@ -109,14 +122,4 @@ class StringSetting( } } } - - class Test : Size { - override fun get(element: Element, type: Type): Float { - return (if (type.axis == Constraint.HORIZONTAL) element.height else element.width) - } - } - - private companion object { - private val `gray 38 brighter` = color { `gray 38`.rgba.brighter(1.2) } - } } \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/features/settings/impl/UISetting.kt b/src/main/kotlin/me/odinmain/features/settings/impl/UISetting.kt index dc58da6c3..8b905c4c3 100644 --- a/src/main/kotlin/me/odinmain/features/settings/impl/UISetting.kt +++ b/src/main/kotlin/me/odinmain/features/settings/impl/UISetting.kt @@ -1,20 +1,51 @@ package me.odinmain.features.settings.impl import com.github.stivais.ui.constraints.Size +import com.github.stivais.ui.constraints.percent +import com.github.stivais.ui.constraints.px +import com.github.stivais.ui.constraints.size +import com.github.stivais.ui.elements.scope.ElementDSL import com.github.stivais.ui.elements.scope.ElementScope +import com.github.stivais.ui.elements.scope.hoverEffect +import com.github.stivais.ui.utils.radius +import com.github.stivais.ui.utils.seconds +import me.odinmain.features.impl.render.ClickGUI +import me.odinmain.features.impl.render.ClickGUI.`gray 38` import me.odinmain.features.settings.Setting +import me.odinmain.features.settings.Setting.Renders.Companion.setting // doesn't save -// todo:rename and cleanup -class UISetting(height: Size, val block: ElementScope<*>.() -> Unit) : Setting("", false, "") { +class UISetting( + height: Size, + description: String = "", + val scope: ElementScope<*>.() -> Unit +) : Setting("", false, description), Setting.Renders { override val default: Any? = null override var value: Any? = null private val _height = height - override fun ElementScope<*>.createElement() { - setting(_height) { - block() + override fun ElementDSL.create() = setting(_height, scope) +} + +@Suppress("FunctionName") +fun ActionSetting( + name: String, + description: String = "", + action: ElementDSL.() -> Unit +): UISetting = UISetting(40.px, description) { + block( + constraints = size(95.percent, 75.percent), + color = `gray 38`, + radius = 5.radius() + ) { + hoverEffect(0.25.seconds) + outline(ClickGUI.color) + text(name) + + onClick { + action.invoke(this) + true } } } \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/utils/ServerUtils.kt b/src/main/kotlin/me/odinmain/utils/ServerUtils.kt index d0261d8b6..03a5ad280 100644 --- a/src/main/kotlin/me/odinmain/utils/ServerUtils.kt +++ b/src/main/kotlin/me/odinmain/utils/ServerUtils.kt @@ -14,8 +14,10 @@ import net.minecraftforge.event.world.WorldEvent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent object ServerUtils { + private val packets = ArrayList>() + @JvmStatic fun handleSendPacket(packet: Packet<*>): Boolean { return packets.remove(packet) } diff --git a/src/main/kotlin/me/odinmain/utils/skyblock/ItemUtils.kt b/src/main/kotlin/me/odinmain/utils/skyblock/ItemUtils.kt index 06964e423..ea3b92987 100644 --- a/src/main/kotlin/me/odinmain/utils/skyblock/ItemUtils.kt +++ b/src/main/kotlin/me/odinmain/utils/skyblock/ItemUtils.kt @@ -11,7 +11,9 @@ import net.minecraft.client.renderer.RenderHelper import net.minecraft.entity.Entity import net.minecraft.inventory.ContainerChest import net.minecraft.item.ItemStack -import net.minecraft.nbt.* +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.NBTTagList +import net.minecraft.nbt.NBTTagString import net.minecraftforge.common.util.Constants /** @@ -160,7 +162,7 @@ private val rarityRegex: Regex = Regex("§l(?${ItemRarity.entries.joinTo fun getRarity(lore: List): ItemRarity? { // Start from the end since the rarity is usually the last line or one of the last. for (i in lore.indices.reversed()) { - val rarity = rarityRegex.find(lore[i])?.groups["rarity"]?.value ?: continue + val rarity = rarityRegex.find(lore[i])?.groups?.get("rarity")?.value ?: continue return ItemRarity.entries.find { it.loreName == rarity } } return null diff --git a/src/main/kotlin/me/odinmain/utils/ui/UIHandler.kt b/src/main/kotlin/me/odinmain/utils/ui/UIHandler.kt new file mode 100644 index 000000000..c0793b8f7 --- /dev/null +++ b/src/main/kotlin/me/odinmain/utils/ui/UIHandler.kt @@ -0,0 +1,55 @@ +package me.odinmain.utils.ui + +import com.github.stivais.ui.UI +import com.github.stivais.ui.Window +import me.odinmain.OdinMain.mc +import net.minecraftforge.client.event.RenderWorldLastEvent +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import org.lwjgl.input.Mouse +import org.lwjgl.opengl.Display + +/** + * Class that draws handles a [UI][com.github.stivais.ui.UI] instance via MC events. + * + * It is required to call [open] and [close] otherwise it will mess up with the eventbus. + */ +class UIHandler(private val ui: UI, private val onlyRender: Boolean = false) : Window { + + private var previousWidth: Int = 0 + private var previousHeight: Int = 0 + + fun open() { + ui.initialize(Display.getWidth(), Display.getHeight(), this) + MinecraftForge.EVENT_BUS.register(this) + } + + fun close() { + ui.cleanup() + MinecraftForge.EVENT_BUS.unregister(this) + } + + @SubscribeEvent + fun onRender(event: RenderWorldLastEvent) { + val w = mc.framebuffer.framebufferWidth + val h = mc.framebuffer.framebufferHeight + if (w != previousWidth || h != previousHeight) { + ui.resize(w, h) + previousWidth = w + previousHeight = h + } + if (!onlyRender) { + ui.eventManager.apply { + val mx = Mouse.getX().toFloat() + val my = previousHeight - Mouse.getY() - 1f + + if (this.mouseX != mx || this.mouseY != my || check()) { + onMouseMove(mx, my) + } + } + } + ui.render() + } + + // todo: add mouse click and other stuff +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/stivais/ui/elements/impl/TextInput.kt b/src/main/kotlin/me/odinmain/utils/ui/elements/TextInput.kt similarity index 90% rename from src/main/kotlin/com/github/stivais/ui/elements/impl/TextInput.kt rename to src/main/kotlin/me/odinmain/utils/ui/elements/TextInput.kt index 569747065..5db0b4744 100644 --- a/src/main/kotlin/com/github/stivais/ui/elements/impl/TextInput.kt +++ b/src/main/kotlin/me/odinmain/utils/ui/elements/TextInput.kt @@ -1,4 +1,4 @@ -package com.github.stivais.ui.elements.impl +package me.odinmain.utils.ui.elements import com.github.stivais.ui.UI import com.github.stivais.ui.color.Color @@ -7,11 +7,14 @@ import com.github.stivais.ui.color.darker import com.github.stivais.ui.constraints.Positions import com.github.stivais.ui.constraints.Size import com.github.stivais.ui.constraints.Type +import com.github.stivais.ui.constraints.at +import com.github.stivais.ui.elements.impl.TextElement +import com.github.stivais.ui.events.Event import com.github.stivais.ui.events.Focused import com.github.stivais.ui.events.Key import com.github.stivais.ui.events.Mouse -import me.odinmain.utils.* import me.odinmain.utils.skyblock.devMessage +import me.odinmain.utils.writeToClipboard import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiScreen import net.minecraft.util.ChatAllowedCharacters @@ -20,34 +23,36 @@ import kotlin.math.abs import kotlin.math.max import kotlin.math.min -/* -* TODO -* - needs cleanup -*/ +// odin exclusive +// official element will come when keyboard input gets redone class TextInput( text: String, private val placeholder: String, position: Positions? = null, size: Size, - val widthLimit: Size? = null, + private val widthLimit: Size? = null, censor: Boolean = false, - val onlyNumbers: Boolean = false, - val onTextChange: (string: String) -> Unit = {} -) : TextElement(text, UI.defaultFont, Color.WHITE, position, size) { + private val onlyNumbers: Boolean = false, + onTextChange: (event: TextChanged) -> Unit +) : TextElement(text, UI.defaultFont, Color.WHITE, position ?: at(), size) { override var text: String = text set(value) { if (field == value) return - field = value - redraw = true - previousHeight = 0f - // text input stuff - if (history.last() != value) history.add(value) - if (censorInput) censorCache = buildString { repeat(text.length) { append('*') } } + val event = TextChanged(value) + accept(event) + if (!event.cancelled) { + field = value + redraw = true + previousHeight = 0f - updateCaret() - onTextChange(value) + // text input stuff + if (history.last() != value) history.add(value) + if (censorInput) censorCache = buildString { repeat(text.length) { append('*') } } + + updateCaret() + } } private val _text: String @@ -138,6 +143,11 @@ class TextInput( } init { + TextChanged().register { + onTextChange(it) + false + } + Focused.Gained register { Keyboard.enableRepeatEvents(true) setCaretPositionBasedOnMouse() @@ -431,4 +441,22 @@ class TextInput( fun isKeyComboCtrlZ(keyID: Int): Boolean { return keyID == Keyboard.KEY_Z && isCtrlKeyDown() && !isShiftKeyDown() && !isAltKeyDown() } + + data class TextChanged(val string: String = "") : Event { + + var cancelled: Boolean = false + + fun cancel() { + cancelled = true + } + + override fun hashCode(): Int { + return 9999 + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + return other is TextChanged + } + } } \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/utils/ui/general.kt b/src/main/kotlin/me/odinmain/utils/ui/general.kt deleted file mode 100644 index 55471d870..000000000 --- a/src/main/kotlin/me/odinmain/utils/ui/general.kt +++ /dev/null @@ -1,54 +0,0 @@ -package me.odinmain.utils.ui - -import com.github.stivais.ui.color.Color -import com.github.stivais.ui.constraints.Constraints -import com.github.stivais.ui.constraints.measurements.Percent -import com.github.stivais.ui.constraints.positions.Linked -import com.github.stivais.ui.elements.impl.TextScope -import com.github.stivais.ui.elements.scope.ElementDSL -import com.github.stivais.ui.renderer.Font -import me.odinmain.features.Module -import me.odinmain.features.Module.HUDScope -import me.odinmain.features.settings.impl.ColorSetting -import me.odinmain.features.settings.impl.SelectorSetting - -// todo: better solution -infix fun TextScope.and(other: TextScope) { - other.element.constraints.x = Linked(element) - other.size = size -} - -fun ElementDSL.outline(constraints: Constraints, color: Color, radius: FloatArray? = null) = block(constraints, Color.TRANSPARENT, radius).outline(color) - -/** - * Makes a HUD, that uses common settings found in text-based HUDs. - * - * @param x Default position on screen. - * @param y Default position on screen. - */ -@Suppress("FunctionName") -inline fun Module.TextHUD( - x: Percent, - y: Percent, - crossinline block: HUDScope.(Color, Font) -> Unit -): Module.HUD { - - val colorSetting = ColorSetting("Color", Color.RGB(50, 150, 220), allowAlpha = false).hide() - // todo: make a custom ui setting that looks similar to selector setting, however each value corresponds to the font - val fontSetting = SelectorSetting("Font", arrayListOf("Regular", "Minecraft")).hide() - - register( - colorSetting, - fontSetting, - ) - return this.HUD(x, y) { - val font = when (fontSetting.value) { - 1 -> Font("Minecraft", "/assets/odinmain/fonts/Minecraft-Regular.otf") - else -> Font("Regular", "/assets/odinmain/fonts/Regular.otf") - } - block(colorSetting.value, font) - }.apply { - settings.add(colorSetting) - settings.add(fontSetting) - } -} \ No newline at end of file diff --git a/src/main/kotlin/me/odinmain/utils/ui/utilities.kt b/src/main/kotlin/me/odinmain/utils/ui/utilities.kt new file mode 100644 index 000000000..09c3965e2 --- /dev/null +++ b/src/main/kotlin/me/odinmain/utils/ui/utilities.kt @@ -0,0 +1,119 @@ +package me.odinmain.utils.ui + +import com.github.stivais.ui.UIScreen +import com.github.stivais.ui.animation.Animations +import com.github.stivais.ui.color.Color +import com.github.stivais.ui.constraints.* +import com.github.stivais.ui.constraints.positions.Linked +import com.github.stivais.ui.elements.impl.TextScope +import com.github.stivais.ui.elements.scope.ElementDSL +import com.github.stivais.ui.renderer.Font +import com.github.stivais.ui.transforms.alpha +import com.github.stivais.ui.transforms.scale +import me.odinmain.features.Module +import me.odinmain.features.huds.HUDScope +import me.odinmain.features.settings.impl.ColorSetting +import me.odinmain.features.settings.impl.HUDSetting +import me.odinmain.features.settings.impl.SelectorSetting +import me.odinmain.utils.ui.elements.TextInput + +val regularFont = Font("Regular", "/assets/odinmain/fonts/Regular.otf") +val mcFont = Font("Minecraft", "/assets/odinmain/fonts/Minecraft-Regular.otf") + +// todo: better solution +infix fun TextScope.and(other: TextScope) { + other.element.constraints.x = Linked(element) + other.size = size +} + +fun ElementDSL.outline( + constraints: Constraints, + color: Color, + thickness: Measurement = 1.px, + radius: FloatArray? = null +) { + block(constraints, Color.TRANSPARENT, color, thickness, radius) +} + +/** + * Makes a HUD, that uses common settings found in text-based HUDs. + * + * @param color Default color for the setting provided + */ +@Suppress("FunctionName") +inline fun Module.TextHUD( + name: String, + description: String, + color: Color = Color.RGB(50, 150, 220), + crossinline block: HUDScope.(Color, Font) -> Unit +): HUDSetting { + val colorSetting = ColorSetting("Color", color, allowAlpha = false) + val fontSetting = SelectorSetting("Font", arrayListOf("Regular", "Minecraft")) + // copy of selector setting, where each entry is a different font representing it + + val hudSetting = HUD(name, description) { + val font = when (fontSetting.value) { + 1 -> mcFont + else -> regularFont + } + block(colorSetting.value, font) + } + hudSetting.value.registerSettings( + colorSetting, + fontSetting, + ) + return hudSetting +} + +/** + * Allows code to be run after certain amount of time hovering over the element + */ +inline fun ElementDSL.onHover(duration: Float, crossinline block: () -> Unit) { + onMouseEnter { + val start = System.nanoTime() + operation { + if (System.nanoTime() - start >= duration) { + block() + return@operation true + } + !element.isInside(ui.mx, ui.my) || !element.renders + } + } +} + +/** + * Scale and alpha animation that gets ran when element is initialized and is uninitialized. + * + * @param duration length of the animation when ran + * @param typeInitialized style of the animation when element is created + * @param typeUninitialized style of the animation when element is removed + */ +fun ElementDSL.lifetimeAnimations( + duration: Float, + typeInitialized: Animations, + typeUninitialized: Animations, +) { + val scaleAnimation = scale(from = 0f, to = 1f, centered = true) + val alphaAnimation = alpha(from = 0f, to = 1f) + + scaleAnimation.animate(duration, typeInitialized) + alphaAnimation.animate(duration, typeInitialized) + + onRemove { + UIScreen.closeAnimHandler = ui.window as UIScreen + scaleAnimation.animate(duration, typeUninitialized) + alphaAnimation.animate(duration, typeUninitialized)?.onFinish { + UIScreen.closeAnimHandler = null + } + } +} + +fun ElementDSL.textInput( + text: String = "", + placeholder: String = "", + constraints: Positions? = null, + size: Size = 50.percent, + maxWidth: Size? = null, + censored: Boolean = false, + onTextChange: (event: TextInput.TextChanged) -> Unit +) = create(TextScope(TextInput(text, placeholder, constraints, size, maxWidth, censored, onTextChange = onTextChange))) \ No newline at end of file