Skip to content

Commit c4bb20c

Browse files
committed
feat: implement sword blocking feature with configurable key binding
1 parent 9945004 commit c4bb20c

File tree

7 files changed

+94
-28
lines changed

7 files changed

+94
-28
lines changed

CHANGELOG.md

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,3 @@
1-
Rewrite & New Features
1+
New Features:
22

3-
Held Item Customisation
4-
- Added fully configurable arm base position (X, Y, Z anchor and height bob scale)
5-
- Added item transform overrides — independently control size, position (X/Y/Z), and rotation (X/Y/Z) of the held item
6-
- Both arm position and item transform support separate values for main hand and off-hand
7-
- Master toggle and per-feature sub-toggles for arm position and item transform independently
8-
9-
Swing Animation
10-
- Added customisable swing arc — control pre-rotation, Y/Z/X arc amounts, and counter-rotation
11-
- Added customisable swing drift — control X/Y/Z translation during the swing
12-
- Added swing speed multiplier with optional ignore for Haste/Mining Fatigue effects
13-
- Added option to disable swing bobbing
14-
- Added option to disable the swing animation entirely
15-
16-
Entity Scaling
17-
- Rewrote player and entity scaling to use render state instead of PoseStack transforms,
18-
fixing visual glitches and improving performance
19-
- Added per-tick scale caching to avoid redundant lookups each frame
20-
21-
Other
22-
- Added Show Own Nametag in Third Person
23-
- Optimised mod performance
3+
feat: add client side sword block when holding right click with a sword

src/main/java/com/github/kd_gaming1/scaleme/ScaleMe.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,59 @@
22

33
import com.github.kd_gaming1.scaleme.command.Commands;
44
import com.github.kd_gaming1.scaleme.config.ScaleMeConfig;
5+
import com.github.kd_gaming1.scaleme.util.BlockingState;
56
import com.github.kd_gaming1.scaleme.util.HypixelLocationState;
67
import eu.midnightdust.lib.config.MidnightConfig;
78
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
89
import net.azureaaron.hmapi.network.HypixelNetworking;
910
import net.azureaaron.hmapi.network.packet.v1.s2c.LocationUpdateS2CPacket;
1011
import net.fabricmc.api.ClientModInitializer;
11-
12+
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
13+
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
1214
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
1315
//? if >=1.21.11 {
1416
/*import net.minecraft.util.Util;
15-
*///?} else {
17+
*///?} else {
1618
import net.minecraft.Util;
1719
//?}
20+
import net.minecraft.client.KeyMapping;
21+
import net.minecraft.tags.ItemTags;
22+
import org.lwjgl.glfw.GLFW;
1823
import org.slf4j.Logger;
1924
import org.slf4j.LoggerFactory;
2025

2126
public class ScaleMe implements ClientModInitializer {
27+
2228
public static final String MOD_ID = "scaleme";
2329
public static final Logger LOGGER = LoggerFactory.getLogger("Scale Me");
2430

25-
2631
@Override
2732
public void onInitializeClient() {
2833
MidnightConfig.init(MOD_ID, ScaleMeConfig.class);
2934

30-
HypixelNetworking.registerToEvents(Util.make(new Object2IntOpenHashMap<>(), map -> map.put(LocationUpdateS2CPacket.ID, 1)));
35+
HypixelNetworking.registerToEvents(
36+
Util.make(new Object2IntOpenHashMap<>(), map -> map.put(LocationUpdateS2CPacket.ID, 1))
37+
);
3138

3239
HypixelLocationState.register();
3340

34-
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> HypixelLocationState.reset());
41+
ClientPlayConnectionEvents.DISCONNECT.register(
42+
(handler, client) -> HypixelLocationState.reset()
43+
);
44+
45+
KeyMapping blockKey = KeyBindingHelper.registerKeyBinding(new KeyMapping(
46+
"key.scaleme.sword_block",
47+
GLFW.GLFW_MOUSE_BUTTON_RIGHT,
48+
KeyMapping.Category.GAMEPLAY
49+
));
50+
51+
ClientTickEvents.END_CLIENT_TICK.register(client -> {
52+
if (client.player == null) return;
53+
BlockingState.isBlocking = ScaleMeConfig.enableSwordBlock
54+
&& blockKey.isDown()
55+
&& client.player.getMainHandItem().is(ItemTags.SWORDS);
56+
});
3557

36-
// Register commands
3758
Commands.register();
3859
}
3960
}

src/main/java/com/github/kd_gaming1/scaleme/config/ScaleMeConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ public class ScaleMeConfig extends MidnightConfig {
136136
@Entry(category = ANIM)
137137
public static boolean enableAnimOverrides = false;
138138

139+
@Entry(category = ANIM)
140+
public static boolean enableSwordBlock = false;
141+
139142
@Entry(category = ANIM)
140143
public static boolean disableSwingBobbing = false;
141144

src/main/java/com/github/kd_gaming1/scaleme/mixin/ItemInHandRendererMixin.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.github.kd_gaming1.scaleme.mixin;
22

33
import com.github.kd_gaming1.scaleme.config.ScaleMeConfig;
4+
import com.github.kd_gaming1.scaleme.util.BlockingState;
45
import com.github.kd_gaming1.scaleme.util.HandContext;
56

67
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
@@ -10,13 +11,16 @@
1011
import net.minecraft.client.player.AbstractClientPlayer;
1112
import net.minecraft.client.renderer.ItemInHandRenderer;
1213
import net.minecraft.client.renderer.SubmitNodeCollector;
14+
import net.minecraft.tags.ItemTags;
1315
import net.minecraft.world.entity.HumanoidArm;
1416
import net.minecraft.world.InteractionHand;
17+
import net.minecraft.world.item.ItemDisplayContext;
1518
import net.minecraft.world.item.ItemStack;
1619
import net.minecraft.util.Mth;
1720

1821
import org.spongepowered.asm.mixin.Mixin;
1922
import org.spongepowered.asm.mixin.Shadow;
23+
import org.spongepowered.asm.mixin.Unique;
2024
import org.spongepowered.asm.mixin.injection.At;
2125
import org.spongepowered.asm.mixin.injection.Inject;
2226
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -25,6 +29,7 @@
2529
@Mixin(ItemInHandRenderer.class)
2630
public class ItemInHandRendererMixin {
2731

32+
@Unique
2833
private static final float PI = (float) Math.PI;
2934

3035
@Shadow private void applyItemArmTransform(PoseStack poseStack, HumanoidArm arm, float inverseArmHeight) {}
@@ -165,4 +170,47 @@ public class ItemInHandRendererMixin {
165170

166171
poseStack.translate(invert * baseX, baseY + inverseArmHeight * heightScale, baseZ);
167172
}
173+
174+
/** Applies a blocking pose when holding a sword and right-clicking. */
175+
@Inject(method = "renderArmWithItem", at = @At("HEAD"), cancellable = true)
176+
private void scaleme$applySwordBlockPose(
177+
AbstractClientPlayer player,
178+
float tickDelta, float pitch,
179+
InteractionHand hand,
180+
float swingProgress,
181+
ItemStack heldItem,
182+
float equipProgress,
183+
PoseStack poseStack,
184+
SubmitNodeCollector collector,
185+
int packedLight,
186+
CallbackInfo ci) {
187+
188+
if (!BlockingState.isBlocking || hand != InteractionHand.MAIN_HAND || !heldItem.is(ItemTags.SWORDS)) return;
189+
ci.cancel();
190+
191+
poseStack.pushPose();
192+
193+
HumanoidArm arm = player.getMainArm();
194+
int armSideSign = (arm == HumanoidArm.RIGHT) ? 1 : -1;
195+
196+
applyItemArmTransform(poseStack, arm, equipProgress);
197+
poseStack.translate((float) armSideSign * -0.14142136F, 0.08F, 0.14142136F);
198+
poseStack.mulPose(Axis.XP.rotationDegrees(-102.25F));
199+
poseStack.mulPose(Axis.YP.rotationDegrees((float) armSideSign * 13.365F));
200+
poseStack.mulPose(Axis.ZP.rotationDegrees((float) armSideSign * 78.05F));
201+
202+
// Self-cast required: Mixin class can't directly extend ItemInHandRenderer
203+
((ItemInHandRenderer) (Object) this).renderItem(
204+
player,
205+
heldItem,
206+
arm == HumanoidArm.RIGHT
207+
? ItemDisplayContext.FIRST_PERSON_RIGHT_HAND
208+
: ItemDisplayContext.FIRST_PERSON_LEFT_HAND,
209+
poseStack,
210+
collector,
211+
packedLight
212+
);
213+
214+
poseStack.popPose();
215+
}
168216
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.github.kd_gaming1.scaleme.util;
2+
3+
/** Shared state indicating whether the player is currently in a sword-block pose. */
4+
public final class BlockingState {
5+
public static boolean isBlocking = false;
6+
7+
private BlockingState() {}
8+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"key.scaleme.sword_block": "Sword Block"
3+
}

src/main/resources/assets/scaleme/lang/en_us.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@
9696

9797
"scaleme.midnightconfig.animDesc": "Master switch for all animation customisation. Swing speed, shape, and bobbing are all controlled by this toggle.",
9898

99+
"scaleme.midnightconfig.enableSwordBlock": "Enable Sword Block [does not requires Animation Master]",
100+
"scaleme.midnightconfig.enableSwordBlock.tooltip": "Lets you hold right-click with a sword to enter a blocking pose, similar to pre-1.9.\nThe keybind defaults to right mouse button — rebind it in Controls if needed.",
101+
99102
"scaleme.midnightconfig.enableAnimOverrides": "Enable Animation Changes [MASTER]",
100103
"scaleme.midnightconfig.enableAnimOverrides.tooltip": "Turns on all custom animation settings below. Disable this to keep the default Minecraft swing animations entirely.\n\nControls:\n• Swing bobbing suppression\n• Swing speed and speed effect ignoring\n• Disable swing animation entirely\n• Custom Swing Shape (arc and drift sliders)",
101104

0 commit comments

Comments
 (0)