Skip to content

Commit 3cbe5cf

Browse files
committed
Added ShieldSwing. Updated NoShieldOverlay
1 parent e6e6020 commit 3cbe5cf

6 files changed

Lines changed: 299 additions & 11 deletions

File tree

src/main/java/net/wurstclient/hack/HackList.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ public final class HackList implements UpdateListener
247247
public final SeedMapperHelperHack seedMapperHelperHack =
248248
new SeedMapperHelperHack();
249249
public final SignEspHack signEspHack = new SignEspHack();
250+
public final ShieldSwingHack shieldSwingHack = new ShieldSwingHack();
250251
public final WorkstationEspHack workstationEspHack =
251252
new WorkstationEspHack();
252253
public final RedstoneEspHack redstoneEspHack = new RedstoneEspHack();

src/main/java/net/wurstclient/hacks/AutoTotemHack.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import net.wurstclient.settings.CheckboxSetting;
2121
import net.wurstclient.settings.SliderSetting;
2222
import net.wurstclient.settings.SliderSetting.ValueDisplay;
23+
import net.wurstclient.util.ChatUtils;
2324
import net.wurstclient.util.InventoryUtils;
2425

2526
@SearchTags({"auto totem", "offhand", "off-hand"})
@@ -42,6 +43,7 @@ public final class AutoTotemHack extends Hack implements UpdateListener
4243
private int totems;
4344
private int timer;
4445
private boolean wasTotemInOffhand;
46+
private boolean shieldSwingWasEnabledBeforeAutoTotem;
4547

4648
public AutoTotemHack()
4749
{
@@ -67,6 +69,17 @@ public String getRenderName()
6769
@Override
6870
protected void onEnable()
6971
{
72+
if(WURST.getHax().shieldSwingHack.isEnabled())
73+
{
74+
shieldSwingWasEnabledBeforeAutoTotem = true;
75+
WURST.getHax().shieldSwingHack.setEnabled(false);
76+
ChatUtils
77+
.warning("ShieldSwing disabled because AutoTotem is enabled.");
78+
}else
79+
{
80+
shieldSwingWasEnabledBeforeAutoTotem = false;
81+
}
82+
7083
nextTickSlot = -1;
7184
totems = 0;
7285
timer = 0;
@@ -78,6 +91,14 @@ protected void onEnable()
7891
protected void onDisable()
7992
{
8093
EVENTS.remove(UpdateListener.class, this);
94+
95+
if(shieldSwingWasEnabledBeforeAutoTotem
96+
&& !WURST.getHax().shieldSwingHack.isEnabled())
97+
{
98+
shieldSwingWasEnabledBeforeAutoTotem = false;
99+
WURST.getHax().shieldSwingHack.setEnabled(true);
100+
ChatUtils.message("ShieldSwing restored.");
101+
}
81102
}
82103

83104
@Override

src/main/java/net/wurstclient/hacks/NoShieldOverlayHack.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public NoShieldOverlayHack()
3535

3636
public void adjustShieldPosition(PoseStack matrixStack, boolean blocking)
3737
{
38-
if(!isEnabled())
38+
if(!shouldAdjustShieldPosition())
3939
return;
4040

4141
if(blocking)
@@ -44,5 +44,15 @@ public void adjustShieldPosition(PoseStack matrixStack, boolean blocking)
4444
matrixStack.translate(0, -nonBlockingOffset.getValue(), 0);
4545
}
4646

47+
private boolean shouldAdjustShieldPosition()
48+
{
49+
if(isEnabled())
50+
return true;
51+
52+
ShieldSwingHack shieldSwingHack = WURST.getHax().shieldSwingHack;
53+
return shieldSwingHack != null
54+
&& shieldSwingHack.shouldUseNoShieldOverlay();
55+
}
56+
4757
// See ItemInHandRendererMixin
4858
}
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
/*
2+
* Copyright (c) 2014-2026 Wurst-Imperium and contributors.
3+
*
4+
* This source code is subject to the terms of the GNU General Public
5+
* License, version 3. If a copy of the GPL was not distributed with this
6+
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
7+
*/
8+
package net.wurstclient.hacks;
9+
10+
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
11+
import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen;
12+
import net.minecraft.client.gui.screens.inventory.InventoryScreen;
13+
import net.minecraft.client.player.LocalPlayer;
14+
import net.minecraft.world.InteractionHand;
15+
import net.minecraft.world.entity.Entity;
16+
import net.minecraft.world.item.ItemStack;
17+
import net.minecraft.world.item.Items;
18+
import net.minecraft.world.phys.EntityHitResult;
19+
import net.wurstclient.Category;
20+
import net.wurstclient.SearchTags;
21+
import net.wurstclient.events.HandleInputListener;
22+
import net.wurstclient.hack.Hack;
23+
import net.wurstclient.mixinterface.IKeyMapping;
24+
import net.wurstclient.mixinterface.IMultiPlayerGameMode;
25+
import net.wurstclient.settings.CheckboxSetting;
26+
import net.wurstclient.util.EntityUtils;
27+
import net.wurstclient.util.InventoryUtils;
28+
import net.wurstclient.util.ChatUtils;
29+
30+
@SearchTags({"shield", "shield swing", "swing while blocking",
31+
"attack while blocking"})
32+
public final class ShieldSwingHack extends Hack implements HandleInputListener
33+
{
34+
private final CheckboxSetting autoHoldShield =
35+
new CheckboxSetting("Auto hold shield",
36+
"Automatically holds right-click while this hack is enabled and you"
37+
+ " have a shield in your offhand.",
38+
false);
39+
40+
private final CheckboxSetting autoEquipShield =
41+
new CheckboxSetting("Auto equip shield",
42+
"Automatically moves a shield from your inventory into your offhand"
43+
+ " while this hack is enabled.",
44+
false);
45+
46+
private final CheckboxSetting useNoShieldOverlay =
47+
new CheckboxSetting("Use NoShieldOverlay",
48+
"Applies NoShieldOverlay while ShieldSwing is enabled, even if"
49+
+ " NoShieldOverlay itself is turned off.",
50+
false);
51+
52+
private boolean attackKeyWasDown;
53+
private boolean forcingUseKey;
54+
private int nextTickSlot = -1;
55+
private boolean autoTotemWasEnabledBeforeShieldSwing;
56+
57+
public ShieldSwingHack()
58+
{
59+
super("ShieldSwing");
60+
setCategory(Category.COMBAT);
61+
addSetting(autoHoldShield);
62+
addSetting(autoEquipShield);
63+
addSetting(useNoShieldOverlay);
64+
}
65+
66+
@Override
67+
protected void onEnable()
68+
{
69+
if(WURST.getHax().autoTotemHack.isEnabled())
70+
{
71+
autoTotemWasEnabledBeforeShieldSwing = true;
72+
WURST.getHax().autoTotemHack.setEnabled(false);
73+
ChatUtils
74+
.warning("AutoTotem disabled because ShieldSwing is enabled.");
75+
}else
76+
{
77+
autoTotemWasEnabledBeforeShieldSwing = false;
78+
}
79+
80+
attackKeyWasDown = false;
81+
forcingUseKey = false;
82+
nextTickSlot = -1;
83+
EVENTS.add(HandleInputListener.class, this);
84+
}
85+
86+
@Override
87+
protected void onDisable()
88+
{
89+
releaseUseKey();
90+
finishShieldSwap();
91+
EVENTS.remove(HandleInputListener.class, this);
92+
93+
if(autoTotemWasEnabledBeforeShieldSwing
94+
&& !WURST.getHax().autoTotemHack.isEnabled())
95+
{
96+
autoTotemWasEnabledBeforeShieldSwing = false;
97+
WURST.getHax().autoTotemHack.setEnabled(true);
98+
ChatUtils.message("AutoTotem restored.");
99+
}
100+
}
101+
102+
@Override
103+
public void onHandleInput()
104+
{
105+
if(MC.player == null || MC.gameMode == null || MC.options == null)
106+
{
107+
releaseUseKey();
108+
attackKeyWasDown = false;
109+
nextTickSlot = -1;
110+
return;
111+
}
112+
113+
finishShieldSwap();
114+
tryAutoEquipShield();
115+
tryAutoHoldShield();
116+
117+
if(shouldRestoreAutoTotemNow())
118+
{
119+
setEnabled(false);
120+
return;
121+
}
122+
123+
if(MC.screen != null)
124+
{
125+
attackKeyWasDown = false;
126+
return;
127+
}
128+
129+
boolean attackKeyDown = MC.options.keyAttack.isDown();
130+
boolean attackKeyPressed = attackKeyDown && !attackKeyWasDown;
131+
attackKeyWasDown = attackKeyDown;
132+
if(!attackKeyPressed)
133+
return;
134+
135+
LocalPlayer player = MC.player;
136+
if(!player.isUsingItem() || !player.getUseItem().is(Items.SHIELD))
137+
return;
138+
139+
if(!(MC.hitResult instanceof EntityHitResult eHitResult))
140+
return;
141+
142+
Entity target = eHitResult.getEntity();
143+
if(target == null || !EntityUtils.IS_ATTACKABLE.test(target))
144+
return;
145+
146+
WURST.getHax().autoSwordHack.setSlot(target);
147+
MC.gameMode.attack(player, target);
148+
player.swing(InteractionHand.MAIN_HAND);
149+
}
150+
151+
public boolean shouldUseNoShieldOverlay()
152+
{
153+
return isEnabled() && useNoShieldOverlay.isChecked();
154+
}
155+
156+
private void tryAutoHoldShield()
157+
{
158+
boolean shouldForce = autoHoldShield.isChecked() && MC.screen == null
159+
&& hasShieldInOffhand(MC.player);
160+
161+
if(shouldForce)
162+
{
163+
MC.options.keyUse.setDown(true);
164+
forcingUseKey = true;
165+
return;
166+
}
167+
168+
releaseUseKey();
169+
}
170+
171+
private void tryAutoEquipShield()
172+
{
173+
if(!autoEquipShield.isChecked())
174+
return;
175+
176+
if(hasShieldInOffhand(MC.player))
177+
return;
178+
179+
if(MC.player.containerMenu != null
180+
&& !MC.player.containerMenu.getCarried().isEmpty())
181+
return;
182+
183+
if(MC.screen instanceof AbstractContainerScreen
184+
&& !(MC.screen instanceof InventoryScreen
185+
|| MC.screen instanceof CreativeModeInventoryScreen))
186+
return;
187+
188+
int shieldSlot = InventoryUtils.indexOf(this::isShield, 40);
189+
if(shieldSlot < 0 || shieldSlot == 40)
190+
return;
191+
192+
moveToOffhand(InventoryUtils.toNetworkSlot(shieldSlot));
193+
}
194+
195+
private void moveToOffhand(int itemSlot)
196+
{
197+
boolean offhandEmpty = MC.player.getOffhandItem().isEmpty();
198+
IMultiPlayerGameMode interactionManager = IMC.getInteractionManager();
199+
interactionManager.windowClick_PICKUP(itemSlot);
200+
interactionManager.windowClick_PICKUP(45);
201+
202+
if(!offhandEmpty)
203+
nextTickSlot = itemSlot;
204+
}
205+
206+
private void finishShieldSwap()
207+
{
208+
if(nextTickSlot == -1 || MC.player == null)
209+
return;
210+
211+
IMultiPlayerGameMode interactionManager = IMC.getInteractionManager();
212+
interactionManager.windowClick_PICKUP(nextTickSlot);
213+
nextTickSlot = -1;
214+
}
215+
216+
private boolean hasShieldInOffhand(LocalPlayer player)
217+
{
218+
return isShield(player.getOffhandItem());
219+
}
220+
221+
private boolean isShield(ItemStack stack)
222+
{
223+
return stack != null && stack.is(Items.SHIELD);
224+
}
225+
226+
private void releaseUseKey()
227+
{
228+
if(!forcingUseKey || MC.options == null)
229+
return;
230+
231+
IKeyMapping.get(MC.options.keyUse).resetPressedState();
232+
forcingUseKey = false;
233+
}
234+
235+
private boolean shouldRestoreAutoTotemNow()
236+
{
237+
if(!autoTotemWasEnabledBeforeShieldSwing || MC.player == null)
238+
return false;
239+
240+
if(hasShieldInOffhand(MC.player))
241+
return false;
242+
243+
boolean canAutoEquip = autoEquipShield.isChecked()
244+
&& InventoryUtils.indexOf(this::isShield, 40, false) >= 0;
245+
return !canAutoEquip;
246+
}
247+
}

src/main/java/net/wurstclient/mixin/ItemInHandRendererMixin.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,25 @@
2424
public abstract class ItemInHandRendererMixin
2525
{
2626
/**
27-
* This mixin is injected into the `BLOCK` case of the `item.getUseAction()`
28-
* switch.
27+
* Apply blocking offset from a guaranteed entry point. Some game versions
28+
* change internal call order/ordinals for the BLOCK branch.
2929
*/
3030
@Inject(
3131
method = "renderArmWithItem(Lnet/minecraft/client/player/AbstractClientPlayer;FFLnet/minecraft/world/InteractionHand;FLnet/minecraft/world/item/ItemStack;FLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;I)V",
32-
at = @At(value = "INVOKE",
33-
target = "Lnet/minecraft/client/renderer/ItemInHandRenderer;applyItemArmTransform(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/world/entity/HumanoidArm;F)V",
34-
ordinal = 3))
32+
at = @At("HEAD"))
3533
private void onApplyEquipOffsetBlocking(AbstractClientPlayer player,
3634
float tickProgress, float pitch, InteractionHand hand,
3735
float swingProgress, ItemStack item, float equipProgress,
3836
PoseStack matrices, SubmitNodeCollector entityRenderCommandQueue,
3937
int light, CallbackInfo ci)
4038
{
41-
// lower shield when blocking
39+
boolean blocking = player.isUsingItem()
40+
&& player.getUseItem().getItem() == Items.SHIELD;
41+
42+
// lower shield using the live blocking state
4243
if(item.getItem() == Items.SHIELD)
4344
WurstClient.INSTANCE.getHax().noShieldOverlayHack
44-
.adjustShieldPosition(matrices, true);
45+
.adjustShieldPosition(matrices, blocking);
4546
}
4647

4748
/**
@@ -59,9 +60,15 @@ private void onApplySwingOffsetNotBlocking(AbstractClientPlayer player,
5960
PoseStack matrices, SubmitNodeCollector entityRenderCommandQueue,
6061
int light, CallbackInfo ci)
6162
{
62-
// lower shield when not blocking
63+
// Keep non-blocking adjustment near the original vanilla swing
64+
// transform.
6365
if(item.getItem() == Items.SHIELD)
64-
WurstClient.INSTANCE.getHax().noShieldOverlayHack
65-
.adjustShieldPosition(matrices, false);
66+
{
67+
boolean blocking = player.isUsingItem()
68+
&& player.getUseItem().getItem() == Items.SHIELD;
69+
if(!blocking)
70+
WurstClient.INSTANCE.getHax().noShieldOverlayHack
71+
.adjustShieldPosition(matrices, false);
72+
}
6673
}
6774
}

src/main/resources/assets/wurst/translations/en_us.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@
302302
"description.wurst.hack.search": "Helps you to find specific blocks by highlighting them in rainbow or fixed color. Use the Query input to search for partial keywords or create your own list of blocks to highlight.",
303303
"description.wurst.hack.seedmapperhelper": "Builds and runs SeedMapper sm:* commands from a button-based interface whenever the SeedMapper mod is installed, including highlight, locate, sample, and source helpers.",
304304
"description.wurst.hack.signesp": "Highlights nearby signs",
305+
"hack.wurst.shieldswing": "ShieldSwing",
306+
"description.wurst.hack.shieldswing": "Lets you attack entities while actively blocking with a shield.",
305307
"description.wurst.hack.signframept": "Right-clicking item frames (with items in them) or signs forwards the interaction to the block behind them. Use the hack settings to enable Frames, Signs, or both. Hold sneak to interact with the frame or sign normally.",
306308
"hack.wurst.silkonly": "SilkOnly",
307309
"description.wurst.hack.silkonly": "Prevents you from breaking selected blocks unless your held tool has Silk Touch.",

0 commit comments

Comments
 (0)