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.TickEvent
22+ import com.lambda.event.listener.SafeListener.Companion.listen
23+ import com.lambda.module.Module
24+ import com.lambda.module.tag.ModuleTag
25+ import com.lambda.threading.runSafe
26+ import net.minecraft.client.network.ClientPlayerEntity
27+ import net.minecraft.item.ItemStack
28+ import net.minecraft.item.Items
29+ import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket
30+ import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket
31+ import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket
32+ import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket
33+ import net.minecraft.screen.PlayerScreenHandler
34+ import net.minecraft.screen.slot.SlotActionType
35+ import net.minecraft.util.Hand
36+ import net.minecraft.util.hit.HitResult
37+
38+ object BetterFirework : Module(
39+ name = " BetterFirework" ,
40+ description = " Improves firework usage" ,
41+ tag = ModuleTag .MOVEMENT ,
42+ ) {
43+ private var autoTakeoff by setting(" Auto Takeoff" , true )
44+ private var silent by setting(" Silent" , true )
45+ private var swing by setting(" Swing " , true )
46+ private var middleClick by setting(" Middle click" , true )
47+ private var middleClickCancel by setting(" Middle Click Cancel" , false , visibility = { middleClick })
48+
49+ private var state = BetterFireworkState .IDLE
50+ private var openDelay = 0
51+
52+ init {
53+ listen<TickEvent .Pre > {
54+ if (openDelay > 0 ) {
55+ openDelay--
56+ if (openDelay == 0 && state == BetterFireworkState .WAIT_AIRBORNE ) {
57+ state = BetterFireworkState .IDLE
58+ startFlying()
59+ }
60+ }
61+ }
62+ }
63+
64+ fun onInteract (): Boolean {
65+ if (! autoTakeoff) return false
66+
67+ runSafe {
68+ if (player.inventory.selectedStack?.item == Items .FIREWORK_ROCKET ) {
69+ if (mc.crosshairTarget != null && mc.crosshairTarget!! .type != HitResult .Type .MISS ) {
70+ return false
71+ }
72+
73+ mc.itemUseCooldown + = 4
74+ return startFlying()
75+ }
76+ }
77+ return false
78+ }
79+
80+ // Do on pick so mc does not select blocks on middle click after we start flying
81+ fun onPick () =
82+ runSafe {
83+ if (! middleClick) return false
84+ if (mc.crosshairTarget?.type == HitResult .Type .BLOCK && ! middleClickCancel) {
85+ return false
86+ }
87+ if (player.isGliding) {
88+ startFirework(silent)
89+ } else {
90+ startFlying()
91+ }
92+ return true
93+ } ? : false // Edouardo: :3333333
94+
95+ fun SafeContext.startFlying (): Boolean {
96+ if (player.isOnGround) {
97+ state = BetterFireworkState .WAIT_AIRBORNE
98+ player.jump()
99+ openDelay = 1 ; // Magic number
100+ return true
101+ }
102+
103+ if (player.isGliding) return false
104+
105+ if (canOpenElytra(player)) {
106+ connection.sendPacket(ClientCommandC2SPacket (player, ClientCommandC2SPacket .Mode .START_FALL_FLYING ))
107+ startFirework(silent)
108+ return true
109+ }
110+
111+ return false
112+ }
113+
114+ fun canOpenElytra (player : ClientPlayerEntity ): Boolean {
115+ return ! player.abilities.flying && ! player.hasVehicle() && ! player.isClimbing && player.checkGliding()
116+ }
117+
118+ fun SafeContext.sendSwing () {
119+ if (swing) {
120+ player.swingHand(Hand .MAIN_HAND )
121+ } else {
122+ connection.sendPacket(HandSwingC2SPacket (Hand .MAIN_HAND ))
123+ }
124+ }
125+
126+ fun SafeContext.startFirework (silent : Boolean ): Boolean {
127+ val fireworkSlot = getFireworkAtHotbar()
128+ if (fireworkSlot != - 1 ) {
129+ player.networkHandler.sendPacket(UpdateSelectedSlotC2SPacket (fireworkSlot))
130+ player.networkHandler.sendPacket(PlayerInteractItemC2SPacket (Hand .MAIN_HAND , 0 , player.yaw, player.pitch))
131+ sendSwing()
132+ player.networkHandler.sendPacket(UpdateSelectedSlotC2SPacket (player.getInventory().selectedSlot))
133+ return true
134+ } else if (silent) {
135+ val fireworkIndex = getFireworkInInventoryScreen()
136+ if (fireworkIndex != - 1 ) {
137+ interaction.clickSlot(player.playerScreenHandler.syncId, fireworkIndex, 0 , SlotActionType .SWAP , mc.player)
138+
139+ if (player.getInventory().selectedSlot != 0 ) {
140+ player.networkHandler.sendPacket(UpdateSelectedSlotC2SPacket (0 ))
141+ player.networkHandler.sendPacket(PlayerInteractItemC2SPacket (Hand .MAIN_HAND , 0 , player.yaw, player.pitch))
142+ sendSwing()
143+ player.networkHandler.sendPacket(UpdateSelectedSlotC2SPacket (player.getInventory().selectedSlot))
144+ } else {
145+ player.networkHandler.sendPacket(PlayerInteractItemC2SPacket (Hand .MAIN_HAND , 0 , player.getYaw(), player.getPitch()))
146+ sendSwing()
147+ }
148+
149+ interaction.clickSlot(player.playerScreenHandler.syncId, fireworkIndex, 0 , SlotActionType .SWAP , mc.player)
150+ return true
151+ }
152+ }
153+ return false
154+ }
155+
156+ private fun SafeContext.getFireworkAtHotbar (): Int {
157+ for (i in 0 .. 8 ) {
158+ val itemStack: ItemStack = player.getInventory().getStack(i)
159+ if (itemStack.item != = Items .FIREWORK_ROCKET ) continue
160+ return i
161+ }
162+ return - 1
163+ }
164+
165+ private fun SafeContext.getFireworkInInventoryScreen (): Int {
166+ val screenHandler: PlayerScreenHandler = player.playerScreenHandler
167+ for (i in PlayerScreenHandler .INVENTORY_START .. <PlayerScreenHandler .INVENTORY_END ) {
168+ val itemStack = screenHandler.getSlot(i).stack
169+ if (itemStack.item != = Items .FIREWORK_ROCKET ) continue
170+ return i
171+ }
172+ return - 1
173+ }
174+
175+ enum class BetterFireworkState {
176+ IDLE ,
177+ WAIT_AIRBORNE
178+ }
179+ }
0 commit comments