From bfc37b3b6178db3faef532d438b79c2266c36d39 Mon Sep 17 00:00:00 2001 From: BRanulf_Explode Date: Tue, 29 Jul 2025 09:14:11 +0800 Subject: [PATCH] weeeee --- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/autocrossbow/AutoCrossbowMod.java | 389 ++++++++++++++++-- .../client/AutoCrossbowModClient.java | 11 - .../assets/autocrossbow/lang/en_us.json | 17 +- .../assets/autocrossbow/lang/zh_cn.json | 17 +- 6 files changed, 379 insertions(+), 59 deletions(-) delete mode 100644 src/main/java/com/example/autocrossbow/client/AutoCrossbowModClient.java diff --git a/gradle.properties b/gradle.properties index 0eafa5d..ae6fe8b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ minecraft_version=1.21.4 yarn_mappings=1.21.4+build.8 loader_version=0.16.10 # Mod Properties -mod_version=1.14.514.004 +mod_version=1.14.514.006 maven_group=com.example archives_base_name=autocrossbow # Dependencies diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3fcb4de..b5fafcf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https://mirrors.aliyun.com/macports/distfiles/gradle/gradle-8.14.1-bin.zip +distributionUrl=https://mirrors.aliyun.com/macports/distfiles/gradle/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/com/example/autocrossbow/AutoCrossbowMod.java b/src/main/java/com/example/autocrossbow/AutoCrossbowMod.java index 0acd8f8..866ac5a 100644 --- a/src/main/java/com/example/autocrossbow/AutoCrossbowMod.java +++ b/src/main/java/com/example/autocrossbow/AutoCrossbowMod.java @@ -1,46 +1,103 @@ package com.example.autocrossbow; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.minecraft.client.MinecraftClient; import net.minecraft.client.option.KeyBinding; import net.minecraft.client.util.InputUtil; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.CrossbowItem; import net.minecraft.item.ItemStack; +import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket; +import net.minecraft.screen.slot.SlotActionType; +import net.minecraft.text.ClickEvent; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Hand; - import org.lwjgl.glfw.GLFW; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import java.util.Arrays; +import java.util.Locale; + public class AutoCrossbowMod implements ClientModInitializer { + private static boolean enabled = false; - private static KeyBinding toggleKeyBinding; - private static boolean wasUsing = false; + private static AutoCrossbowMode currentMode = AutoCrossbowMode.NORMAL; + + + private static KeyBinding toggleModKeyBinding; + private static KeyBinding toggleModeKeyBinding; + + + private static boolean isRightClickPressed = false; + private static boolean wasRightClickPressed = false; + private static int chargeProgress = 0; + private static long lastActionTime = 0; + private static final long ACTION_COOLDOWN_MS = 50; + + public enum AutoCrossbowMode { + NORMAL("normal"), // 普通自动 + RAPID_FIRE_SHOOT("rapid_shoot"), // 连发发射 + RAPID_FIRE_LOAD("rapid_load"); // 连发装填 + + private final String name; + + AutoCrossbowMode(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static AutoCrossbowMode fromString(String text) { + for (AutoCrossbowMode mode : AutoCrossbowMode.values()) { + if (mode.name.equalsIgnoreCase(text)) { + return mode; + } + } + return null; + } + + public AutoCrossbowMode next() { + return values()[(this.ordinal() + 1) % values().length]; + } + } + @Override public void onInitializeClient() { - toggleKeyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding( - "key.autocrossbow.toggle", + + toggleModKeyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "key.autocrossbow.toggle_mod", InputUtil.Type.KEYSYM, - GLFW.GLFW_KEY_UNKNOWN, // 默认UNKNOW,自己去设置 + GLFW.GLFW_KEY_UNKNOWN, "category.autocrossbow" )); + toggleModeKeyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "key.autocrossbow.toggle_mode", + InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_UNKNOWN, + "category.autocrossbow" + )); + + ClientTickEvents.END_CLIENT_TICK.register(client -> { - while (toggleKeyBinding.wasPressed()) { - enabled = !enabled; - if (client.player != null) { - client.player.sendMessage( - Text.translatable("message.autocrossbow.status", - Text.translatable(enabled ? "message.autocrossbow.enabled" : "message.autocrossbow.disabled")), - true - ); - } - } + handleKeybinds(client); if (!enabled || client.player == null || client.interactionManager == null) { return; @@ -50,38 +107,290 @@ public class AutoCrossbowMod implements ClientModInitializer { Hand hand = Hand.MAIN_HAND; ItemStack stack = player.getStackInHand(hand); + isRightClickPressed = client.options.useKey.isPressed(); + if (!(stack.getItem() instanceof CrossbowItem)) { + + resetActionStates(); return; } - boolean isUsing = client.options.useKey.isPressed(); - - if (CrossbowItem.isCharged(stack) && isUsing) { - client.interactionManager.interactItem(player, hand); - return; + + switch (currentMode) { + case NORMAL: + handleNormalMode(client, player, hand, stack); + break; + case RAPID_FIRE_SHOOT: + handleRapidFireShootMode(client, player, hand, stack); + break; + case RAPID_FIRE_LOAD: + handleRapidFireLoadMode(client, player, hand, stack); + break; } - if (isUsing) { - if (!player.isUsingItem()) { - client.interactionManager.interactItem(player, hand); - chargeProgress = 0; - } - else if (player.getActiveHand() == hand) { - chargeProgress++; + + wasRightClickPressed = isRightClickPressed; + }); - int pullTime = CrossbowItem.getPullTime(stack, player); + + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register(ClientCommandManager.literal("autocrossbow") + .executes(context -> { + + PlayerEntity player = MinecraftClient.getInstance().player; + if (player != null) { + player.sendMessage(Text.translatable("command.autocrossbow.status", + Text.translatable(enabled ? "message.autocrossbow.enabled" : "message.autocrossbow.disabled"), + Text.translatable("message.autocrossbow.mode." + currentMode.getName())), false); - // TODO 目前测试稳定的只有快速装填3,其他有待优化 - if (chargeProgress >= pullTime * 1.1f) { - client.interactionManager.stopUsingItem(player); - wasUsing = true; - } - } - } - else if (wasUsing) { - wasUsing = false; - chargeProgress = 0; - } + + MutableText toggleEnabledText = Text.translatable("command.autocrossbow.toggle_enabled") + .styled(s -> s.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/autocrossbow enabled " + !enabled))); + player.sendMessage(toggleEnabledText, false); + + + MutableText toggleModeText = Text.translatable("command.autocrossbow.toggle_mode") + .styled(s -> s.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/autocrossbow mode " + currentMode.next().getName()))); + player.sendMessage(toggleModeText, false); + } + return 1; + }) + .then(ClientCommandManager.literal("enabled") + .then(ClientCommandManager.argument("value", BoolArgumentType.bool()) + .executes(context -> { + enabled = BoolArgumentType.getBool(context, "value"); + PlayerEntity player = MinecraftClient.getInstance().player; + if (player != null) { + player.sendMessage(Text.translatable("message.autocrossbow.status", + Text.translatable(enabled ? "message.autocrossbow.enabled" : "message.autocrossbow.disabled"), + Text.translatable("message.autocrossbow.mode." + currentMode.getName())), false); + } + return 1; + }) + ) + ) + .then(ClientCommandManager.literal("mode") + .then(ClientCommandManager.argument("modeName", StringArgumentType.word()) + .suggests(MODE_SUGGESTION_PROVIDER) + .executes(context -> { + String modeName = StringArgumentType.getString(context, "modeName"); + AutoCrossbowMode newMode = AutoCrossbowMode.fromString(modeName); + PlayerEntity player = MinecraftClient.getInstance().player; + if (newMode != null) { + currentMode = newMode; + if (player != null) { + player.sendMessage(Text.translatable("message.autocrossbow.mode_changed", + Text.translatable("message.autocrossbow.mode." + currentMode.getName())), false); + } + } else { + if (player != null) { + player.sendMessage(Text.translatable("command.autocrossbow.invalid_mode", modeName), false); + } + } + return 1; + }) + ) + ) + ); }); } + + + private void handleKeybinds(MinecraftClient client) { + while (toggleModKeyBinding.wasPressed()) { + enabled = !enabled; + if (client.player != null) { + client.player.sendMessage( + Text.translatable("message.autocrossbow.status", + Text.translatable(enabled ? "message.autocrossbow.enabled" : "message.autocrossbow.disabled"), + Text.translatable("message.autocrossbow.mode." + currentMode.getName())), + true + ); + } + } + + while (toggleModeKeyBinding.wasPressed()) { + currentMode = currentMode.next(); + if (client.player != null) { + client.player.sendMessage( + Text.translatable("message.autocrossbow.mode_changed", + Text.translatable("message.autocrossbow.mode." + currentMode.getName())), + true + ); + } + } + } + + + private void resetActionStates() { + isRightClickPressed = false; + wasRightClickPressed = false; + chargeProgress = 0; + lastActionTime = 0; + } + + + private void handleNormalMode(MinecraftClient client, PlayerEntity player, Hand hand, ItemStack stack) { + if (CrossbowItem.isCharged(stack) && isRightClickPressed) { + + client.interactionManager.interactItem(player, hand); + return; + } + + if (isRightClickPressed) { + + if (!player.isUsingItem()) { + client.interactionManager.interactItem(player, hand); + chargeProgress = 0; + } else if (player.getActiveHand() == hand) { + chargeProgress++; + + int pullTime = CrossbowItem.getPullTime(stack, player); + + + if (chargeProgress >= pullTime * 1.1f) { + client.interactionManager.stopUsingItem(player); + } + } + } else if (wasRightClickPressed) { + + chargeProgress = 0; + } + } + + + private void handleRapidFireShootMode(MinecraftClient client, PlayerEntity player, Hand hand, ItemStack stack) { + if (!isRightClickPressed) { + + return; + } + + + if (System.currentTimeMillis() - lastActionTime < ACTION_COOLDOWN_MS) { + return; + } + + if (CrossbowItem.isCharged(stack)) { + + client.interactionManager.interactItem(player, hand); + player.swingHand(hand); + lastActionTime = System.currentTimeMillis(); + } + + + int nextSlot = findNextCrossbow(player.getInventory(), true, player.getInventory().selectedSlot); + if (nextSlot != -1) { + swapToSlot(client, player, nextSlot); + } else { + + player.sendMessage(Text.translatable("message.autocrossbow.no_more_charged"), true); + currentMode = AutoCrossbowMode.NORMAL; + } + } + + + private void handleRapidFireLoadMode(MinecraftClient client, PlayerEntity player, Hand hand, ItemStack stack) { + if (!isRightClickPressed) { + + return; + } + + + if (player.isUsingItem() && player.getActiveHand() == hand) { + chargeProgress++; + int pullTime = CrossbowItem.getPullTime(stack, player); + + if (chargeProgress >= pullTime * 1.1f) { + + client.interactionManager.stopUsingItem(player); + chargeProgress = 0; + lastActionTime = System.currentTimeMillis(); + + + int nextSlot = findNextCrossbow(player.getInventory(), false, player.getInventory().selectedSlot); + if (nextSlot != -1) { + swapToSlot(client, player, nextSlot); + } else { + player.sendMessage(Text.translatable("message.autocrossbow.no_more_uncharged"), true); + currentMode = AutoCrossbowMode.NORMAL; + } + } + } else if (!CrossbowItem.isCharged(stack)) { + + + if (System.currentTimeMillis() - lastActionTime < ACTION_COOLDOWN_MS) { + return; + } + client.interactionManager.interactItem(player, hand); + chargeProgress = 0; + lastActionTime = System.currentTimeMillis(); + } else { + + + if (System.currentTimeMillis() - lastActionTime < ACTION_COOLDOWN_MS) { + return; + } + int nextSlot = findNextCrossbow(player.getInventory(), false, player.getInventory().selectedSlot); + if (nextSlot != -1) { + swapToSlot(client, player, nextSlot); + lastActionTime = System.currentTimeMillis(); + } else { + player.sendMessage(Text.translatable("message.autocrossbow.no_more_uncharged"), true); + currentMode = AutoCrossbowMode.NORMAL; + } + } + } + + private int findNextCrossbow(PlayerInventory inventory, boolean chargedRequired, int currentSelectedSlot) { + + for (int i = 0; i < PlayerInventory.HOTBAR_SIZE; i++) { + if (i == currentSelectedSlot) continue; + + ItemStack stack = inventory.getStack(i); + if (stack.getItem() instanceof CrossbowItem) { + boolean isCharged = CrossbowItem.isCharged(stack); + if (chargedRequired == isCharged) { + return i; + } + } + } + + + for (int i = PlayerInventory.HOTBAR_SIZE; i < inventory.main.size(); i++) { + ItemStack stack = inventory.getStack(i); + if (stack.getItem() instanceof CrossbowItem) { + boolean isCharged = CrossbowItem.isCharged(stack); + if (chargedRequired == isCharged) { + return i; + } + } + } + return -1; + } + + private void swapToSlot(MinecraftClient client, PlayerEntity player, int targetSlot) { + if (client.interactionManager == null || player.playerScreenHandler == null) return; + + if (targetSlot >= 0 && targetSlot < PlayerInventory.HOTBAR_SIZE) { + player.getInventory().selectedSlot = targetSlot; + + client.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(targetSlot)); + } else { + + client.interactionManager.clickSlot( + player.playerScreenHandler.syncId, + targetSlot, + player.getInventory().selectedSlot, + SlotActionType.SWAP, + player + ); + } + } + + private static final SuggestionProvider MODE_SUGGESTION_PROVIDER = (context, builder) -> { + Arrays.stream(AutoCrossbowMode.values()) + .map(AutoCrossbowMode::getName) + .forEach(builder::suggest); + return builder.buildFuture(); + }; } diff --git a/src/main/java/com/example/autocrossbow/client/AutoCrossbowModClient.java b/src/main/java/com/example/autocrossbow/client/AutoCrossbowModClient.java deleted file mode 100644 index dfce9fa..0000000 --- a/src/main/java/com/example/autocrossbow/client/AutoCrossbowModClient.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.autocrossbow.client; - -import net.fabricmc.api.ClientModInitializer; - -public class AutoCrossbowModClient implements ClientModInitializer { - - // 额 - @Override - public void onInitializeClient() { - } -} diff --git a/src/main/resources/assets/autocrossbow/lang/en_us.json b/src/main/resources/assets/autocrossbow/lang/en_us.json index a74d7d5..736e751 100644 --- a/src/main/resources/assets/autocrossbow/lang/en_us.json +++ b/src/main/resources/assets/autocrossbow/lang/en_us.json @@ -1,7 +1,18 @@ { - "key.autocrossbow.toggle": "Toggle Auto Crossbow", + "key.autocrossbow.toggle_mod": "Toggle Auto Crossbow Mod", + "key.autocrossbow.toggle_mode": "Cycle Crossbow Mode", "category.autocrossbow": "Auto Crossbow Mod", - "message.autocrossbow.status": "Auto Crossbow: %s", + "message.autocrossbow.status": "Auto Crossbow: %s, Mode: %s", "message.autocrossbow.enabled": "Enabled", - "message.autocrossbow.disabled": "Disabled" + "message.autocrossbow.disabled": "Disabled", + "message.autocrossbow.mode_changed": "Crossbow Mode: %s", + "message.autocrossbow.mode.normal": "Normal", + "message.autocrossbow.mode.rapid_shoot": "Rapid Fire (Shoot)", + "message.autocrossbow.mode.rapid_load": "Rapid Fire (Load)", + "message.autocrossbow.no_more_charged": "No more charged crossbows found!", + "message.autocrossbow.no_more_uncharged": "No more uncharged crossbows found!", + "command.autocrossbow.status": "§b[Auto Crossbow]§f Status: %s, Mode: %s", + "command.autocrossbow.toggle_enabled": "Click to toggle mod enabled/disabled", + "command.autocrossbow.toggle_mode": "Click to cycle mode", + "command.autocrossbow.invalid_mode": "Invalid mode: '%s'. Available modes: normal, rapid_shoot, rapid_load." } diff --git a/src/main/resources/assets/autocrossbow/lang/zh_cn.json b/src/main/resources/assets/autocrossbow/lang/zh_cn.json index 0d81f53..c7d6f9e 100644 --- a/src/main/resources/assets/autocrossbow/lang/zh_cn.json +++ b/src/main/resources/assets/autocrossbow/lang/zh_cn.json @@ -1,7 +1,18 @@ { - "key.autocrossbow.toggle": "切换连弩模式", + "key.autocrossbow.toggle_mod": "切换诸葛连弩开关", + "key.autocrossbow.toggle_mode": "切换连弩模式", "category.autocrossbow": "诸葛连弩", - "message.autocrossbow.status": "§b[诸葛连弩]§f 状态: %s", + "message.autocrossbow.status": "§b[诸葛连弩]§f 状态:%s,模式:%s", "message.autocrossbow.enabled": "§a已启用", - "message.autocrossbow.disabled": "§c已禁用" + "message.autocrossbow.disabled": "§c已禁用", + "message.autocrossbow.mode_changed": "连弩模式:%s", + "message.autocrossbow.mode.normal": "正常模式", + "message.autocrossbow.mode.rapid_shoot": "连发 - 发射模式", + "message.autocrossbow.mode.rapid_load": "连发 - 装填模式", + "message.autocrossbow.no_more_charged": "背包中没有更多已上膛的弩了!", + "message.autocrossbow.no_more_uncharged": "背包中没有更多未上膛的弩了!", + "command.autocrossbow.status": "§b[诸葛连弩]§f 状态:%s,模式:%s", + "command.autocrossbow.toggle_enabled": "点击切换模组启用/禁用", + "command.autocrossbow.toggle_mode": "点击切换模式", + "command.autocrossbow.invalid_mode": "无效模式:“%s”。可用模式:normal, rapid_shoot, rapid_load。" }