Skip to content

Commit a65865f

Browse files
committed
Initial LevitationTweaks implementation
1 parent b5bec2d commit a65865f

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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

Comments
 (0)