1+ /*
2+ * Copyright 2025 Lambda
3+ *
4+ * This program is free software: you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation, either version 3 of the License, or
7+ * (at your option) any later version.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+ package com.lambda.module.modules.movement
19+
20+ import com.lambda.context.SafeContext
21+ import com.lambda.event.events.ClientEvent
22+ import com.lambda.event.events.MovementEvent
23+ import com.lambda.event.events.TickEvent
24+ import com.lambda.event.listener.SafeListener.Companion.listen
25+ import com.lambda.module.Module
26+ import com.lambda.module.tag.ModuleTag
27+ import com.lambda.util.Communication.info
28+ import com.lambda.util.KeyboardUtils.isKeyPressed
29+ import com.lambda.util.NamedEnum
30+ import com.lambda.util.math.MathUtils.toInt
31+ import com.lambda.util.player.MovementUtils.isInputting
32+ import com.lambda.util.player.MovementUtils.motionY
33+ import com.lambda.util.player.MovementUtils.setSpeed
34+ import net.minecraft.entity.effect.StatusEffectInstance
35+ import net.minecraft.entity.effect.StatusEffects.LEVITATION
36+ import org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_CONTROL
37+ import java.time.Duration
38+ import java.time.LocalDateTime
39+
40+ object LevitationTweaks : Module(
41+ name = " LevitationTweaks" ,
42+ description = " Abuse the levitation effect" ,
43+ defaultTags = setOf(ModuleTag .MOVEMENT )
44+ ) {
45+ private val mode by setting(" Mode" , Mode .UNCP )
46+
47+ // REMOVE
48+ private val restore by setting(" Restore" , true , description = " Restore levitation on module disable" ) { mode == Mode .REMOVE }
49+
50+ // UNCP
51+ private val constantUpFactor by setting(
52+ " Idle Up Speed" , unit = " %" , description = " Permanent upwards motion" , defaultValue = 2.0 , range = 0.0 .. 100.0
53+ ) { mode == Mode .UNCP }
54+ private val control by setting(" Control" , true , ) { mode == Mode .UNCP }
55+ private val strafeBoost by setting(" Strafe Boost" , true , ) { mode == Mode .UNCP && control }
56+ private val strafeBoostSpeed by setting(
57+ " Boost Speed" , unit = " %" , defaultValue = 100 , range = 0 .. 120
58+ ) { mode == Mode .UNCP && control && strafeBoost}
59+ private val timer by setting(
60+ " Timer" , true
61+ ) { mode == Mode .UNCP && control && strafeBoost}
62+ private val timerBoost by setting(" Timer Boost" , 1.08 , 1.0 .. 1.2 , 0.01 ) { mode == Mode .UNCP && control && timer}
63+
64+ private val controlDownSpeed by setting(
65+ " Control Down Speed" , unit = " %" , defaultValue = 100 , range = 0 .. 300
66+ ) { mode == Mode .UNCP && control}
67+ private val controlUpSpeed by setting(
68+ " Control Up Speed" , unit = " %" , defaultValue = 100 , range = 0 .. 140
69+ ) { mode == Mode .UNCP && control}
70+
71+
72+
73+ private enum class Mode (override val displayName : String ) : NamedEnum {
74+ REMOVE (" Remove" ), UNCP (" NCP New" )
75+ }
76+
77+ private var capturedEffect: StatusEffectInstance ? = null
78+ private var wearOffTime: LocalDateTime ? = null
79+ private var canMove = false
80+
81+ init {
82+ // Checks if levitation is applied.
83+ fun SafeContext.checkForLevitationEffect (): StatusEffectInstance ? {
84+ return player.activeStatusEffects.entries.firstOrNull { it.value.effectType == LEVITATION }?.value
85+ }
86+
87+ onDisable {
88+ /* RESTORE EFFECT AFTER EARLY DISABLE */
89+ val now = LocalDateTime .now()
90+ if (mode != Mode .UNCP && restore && capturedEffect != null && now.isBefore(wearOffTime)) {
91+ // info("Reapplying effect: $capturedEffect")
92+ val durationTicks = wearOffTime?.let { Duration .between(now, it).seconds.toInt() * 20 } ? : 0
93+ // TODO: refactor this ugly time difference calculation
94+ val newEffect = StatusEffectInstance (LEVITATION , durationTicks, capturedEffect!! .amplifier)
95+ info(" Reapplied levitation for ${durationTicks / 20 } seconds" )
96+ player.addStatusEffect(newEffect)
97+ }
98+
99+ canMove = false
100+ capturedEffect = null // Reset on disable
101+ }
102+
103+ onEnable {
104+ canMove = false
105+ capturedEffect = null // Reset when enabled again
106+ }
107+
108+
109+ listen<TickEvent .Post > {
110+ // Capture effect to apply magic on later
111+ if (capturedEffect == null ) {
112+ capturedEffect = checkForLevitationEffect()
113+ if (capturedEffect == null ) return @listen // Still not available, wait for next tick
114+ wearOffTime = LocalDateTime .now().plusSeconds(capturedEffect!! .duration.toLong() / 20 )
115+ }
116+
117+ if (LocalDateTime .now().isAfter(wearOffTime)) {
118+ info(" Effect wore off." )
119+ capturedEffect == null
120+ disable()
121+ }
122+
123+ when (mode) {
124+ Mode .REMOVE -> {
125+ if (checkForLevitationEffect() != null ) player.removeStatusEffect(LEVITATION )
126+ }
127+ Mode .UNCP -> {
128+ if (checkForLevitationEffect() != null ) canMove = true
129+ }
130+ }
131+ }
132+
133+ listen<MovementEvent .Player .Pre > { event ->
134+ info(event.toString())
135+ if (mode != Mode .UNCP || ! canMove) return @listen
136+
137+ player.motionY = when {
138+ player.isSneaking && control -> { // BOOST DOWN
139+ // TODO: find out how to initiate downwards movement without a flag
140+ /* player.motionX *= 0.5
141+ player.motionZ *= 0.5*/
142+ - (0.4 * (controlDownSpeed / 100 ))
143+ }
144+ player.input.jumping && control -> (0.12 * (controlUpSpeed / 100 )) // BOOST UP
145+ else -> 0.06 * (constantUpFactor / 100 ) // idle motion up
146+ }
147+
148+ if (isKeyPressed(GLFW_KEY_LEFT_CONTROL ) && strafeBoost && control) {setSpeed(Speed .NCP_BASE_SPEED * isInputting.toInt() * (strafeBoostSpeed/ 100 ))}
149+ }
150+
151+ listen<ClientEvent .TimerUpdate > {
152+ if (mode != Mode .UNCP ) return @listen
153+ if (! isKeyPressed(GLFW_KEY_LEFT_CONTROL ) || ! timer || ! canMove || ! strafeBoost) return @listen
154+ it.speed = timerBoost
155+ }
156+ }
157+
158+ }
0 commit comments