diff --git a/gradle.properties b/gradle.properties index 9ea2474..94a6ec2 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.003 +mod_version=1.14.514.004 maven_group=org.branulf archives_base_name=branulf_toolbox # Dependencies diff --git a/src/main/java/org/branulf/toolbox/Main.java b/src/main/java/org/branulf/toolbox/Main.java index a2e5d9c..d6c778b 100644 --- a/src/main/java/org/branulf/toolbox/Main.java +++ b/src/main/java/org/branulf/toolbox/Main.java @@ -4,6 +4,7 @@ import org.branulf.toolbox.autocrossbow.AutoCrossbowHandler; import org.branulf.toolbox.crazylook.CrazyLookHandler; import org.branulf.toolbox.elytraboost.ElytraBoostCommon; import org.branulf.toolbox.elytraboost.ElytraBoostHandler; +import org.branulf.toolbox.playerdetector.PlayerDetectorHandler; import org.branulf.toolbox.scrafitiptyplus.ScrafitiptyPlusHandler; import me.shedaniel.autoconfig.AutoConfig; import me.shedaniel.autoconfig.serializer.GsonConfigSerializer; @@ -39,6 +40,7 @@ public class Main implements ClientModInitializer { public ScrafitiptyPlusHandler scrafitiptyPlusHandler; public CrazyLookHandler crazyLookHandler; public ElytraBoostHandler elytraBoostHandler; + public PlayerDetectorHandler playerDetectorHandler; @Override public void onInitializeClient() { @@ -58,6 +60,7 @@ public class Main implements ClientModInitializer { scrafitiptyPlusHandler = new ScrafitiptyPlusHandler(config); crazyLookHandler = new CrazyLookHandler(config); elytraBoostHandler = new ElytraBoostHandler(config); + playerDetectorHandler = new PlayerDetectorHandler(config); ClientTickEvents.END_CLIENT_TICK.register(client -> { while (openMainGuiKeyBinding.wasPressed()) { @@ -70,6 +73,7 @@ public class Main implements ClientModInitializer { crazyLookHandler.onClientTick(client); elytraBoostHandler.onClientTick(client); scrafitiptyPlusHandler.onClientTick(client); + playerDetectorHandler.onClientTick(client); }); new ElytraBoostCommon().onInitialize(); diff --git a/src/main/java/org/branulf/toolbox/MainScreen.java b/src/main/java/org/branulf/toolbox/MainScreen.java index ad169ad..c00e663 100644 --- a/src/main/java/org/branulf/toolbox/MainScreen.java +++ b/src/main/java/org/branulf/toolbox/MainScreen.java @@ -1,5 +1,7 @@ package org.branulf.toolbox; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.util.math.MathHelper; import org.branulf.toolbox.autocrossbow.AutoCrossbowMode; import org.branulf.toolbox.scrafitiptyplus.ScrafitiptyPlusHandler; import me.shedaniel.autoconfig.AutoConfig; @@ -7,8 +9,10 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.ClickableWidget; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import net.minecraft.client.gui.Element; import java.util.ArrayList; import java.util.List; @@ -19,8 +23,17 @@ public class MainScreen extends Screen { private static final int BUTTON_HEIGHT = 20; private static final int PADDING = 5; private static final int SECTION_SPACING = 15; + private static final int SCROLL_SPEED = 10; - private final List modButtons = new ArrayList<>(); + private double scrollY; + private int totalContentHeight; + private int scrollableAreaHeight; + private int scrollableAreaYStart; + private int scrollableAreaYEnd; + + private final List scrollableWidgets = new ArrayList<>(); + private ButtonWidget configButton; + private TextFieldWidget playerDetectorRangeField; public MainScreen(Text title) { super(title); @@ -29,49 +42,69 @@ public class MainScreen extends Screen { @Override protected void init() { super.init(); - modButtons.clear(); + this.clearChildren(); + scrollableWidgets.clear(); + scrollY = 0; - int currentY = 40; + int titleAreaHeight = this.textRenderer.fontHeight + PADDING * 2; + int configButtonAreaHeight = BUTTON_HEIGHT + PADDING * 2; + + scrollableAreaYStart = 10 + titleAreaHeight; + scrollableAreaYEnd = this.height - configButtonAreaHeight; + scrollableAreaHeight = scrollableAreaYEnd - scrollableAreaYStart; + + int currentContentY = 30; // 诸葛连弩 - currentY = addSectionTitle(Text.translatable("gui.branulf_toolbox.autocrossbow.title").formatted(Formatting.AQUA), currentY); - addAutoCrossbowButtons(currentY); - currentY += (BUTTON_HEIGHT + PADDING) * 2 + SECTION_SPACING; + currentContentY = addSectionTitle(Text.translatable("gui.branulf_toolbox.autocrossbow.title").formatted(Formatting.AQUA), currentContentY); + currentContentY = addAutoCrossbowButtons(currentContentY); + currentContentY += SECTION_SPACING; // Scrafitipty Plus - currentY = addSectionTitle(Text.translatable("gui.branulf_toolbox.scrafitipty_plus.title").formatted(Formatting.YELLOW), currentY); - addScrafitiptyPlusButtons(currentY); - currentY += (BUTTON_HEIGHT + PADDING) + SECTION_SPACING; + currentContentY = addSectionTitle(Text.translatable("gui.branulf_toolbox.scrafitipty_plus.title").formatted(Formatting.YELLOW), currentContentY); + currentContentY = addScrafitiptyPlusButtons(currentContentY); + currentContentY += SECTION_SPACING; // 视角 - currentY = addSectionTitle(Text.translatable("gui.branulf_toolbox.crazylook.title").formatted(Formatting.LIGHT_PURPLE), currentY); - addCrazyLookButtons(currentY); - currentY += (BUTTON_HEIGHT + PADDING) + SECTION_SPACING; + currentContentY = addSectionTitle(Text.translatable("gui.branulf_toolbox.crazylook.title").formatted(Formatting.LIGHT_PURPLE), currentContentY); + currentContentY = addCrazyLookButtons(currentContentY); + currentContentY += SECTION_SPACING; // 鞘翅推进 - currentY = addSectionTitle(Text.translatable("gui.branulf_toolbox.elytraboost.title").formatted(Formatting.GOLD), currentY); - addElytraBoostButtons(currentY); - currentY += (BUTTON_HEIGHT + PADDING) + SECTION_SPACING; + currentContentY = addSectionTitle(Text.translatable("gui.branulf_toolbox.elytraboost.title").formatted(Formatting.GOLD), currentContentY); + currentContentY = addElytraBoostButtons(currentContentY); + currentContentY += SECTION_SPACING; - this.addDrawableChild(ButtonWidget.builder( + // 玩家探测器 + currentContentY = addSectionTitle(Text.translatable("gui.branulf_toolbox.player_detector.title").formatted(Formatting.BLUE), currentContentY); + currentContentY = addPlayerDetectorButtons(currentContentY); + currentContentY += SECTION_SPACING; + + totalContentHeight = currentContentY; + + // 配置 + configButton = ButtonWidget.builder( Text.translatable("gui.branulf_toolbox.open_config"), button -> MinecraftClient.getInstance().setScreen(AutoConfig.getConfigScreen(ModConfig.class, this).get())) .dimensions(this.width / 2 - BUTTON_WIDTH / 2, this.height - 30, BUTTON_WIDTH, BUTTON_HEIGHT) - .build()); + .build(); + this.addDrawableChild(configButton); + clampScroll(); updateButtonStates(); } - private int addSectionTitle(Text title, int y) { + private int addSectionTitle(Text title, int currentContentY) { ButtonWidget titleButton = ButtonWidget.builder(title, button -> {}) - .dimensions(this.width / 2 - BUTTON_WIDTH, y, BUTTON_WIDTH * 2, BUTTON_HEIGHT) + .dimensions(this.width / 2 - BUTTON_WIDTH, currentContentY, BUTTON_WIDTH * 2, BUTTON_HEIGHT) .build(); titleButton.active = false; - this.addDrawableChild(titleButton); - return y + BUTTON_HEIGHT + PADDING; + scrollableWidgets.add(titleButton); + return currentContentY + BUTTON_HEIGHT + PADDING; } - private void addAutoCrossbowButtons(int startY) { + // 诸葛连弩 + private int addAutoCrossbowButtons(int currentContentY) { ModConfig.AutoCrossbowConfig config = Main.getConfig().autocrossbow; int centerX = this.width / 2; int row1StartX = centerX - (BUTTON_WIDTH * 2 + PADDING) / 2; @@ -84,10 +117,9 @@ public class MainScreen extends Screen { Main.saveConfig(); updateButtonStates(); }) - .dimensions(row1StartX, startY, BUTTON_WIDTH, BUTTON_HEIGHT) + .dimensions(row1StartX, currentContentY, BUTTON_WIDTH, BUTTON_HEIGHT) .build(); - modButtons.add(autoCrossbowEnabledOnButton); - this.addDrawableChild(autoCrossbowEnabledOnButton); + scrollableWidgets.add(autoCrossbowEnabledOnButton); ButtonWidget autoCrossbowEnabledOffButton = ButtonWidget.builder( Text.translatable("gui.branulf_toolbox.autocrossbow.enabled_off").formatted(Formatting.RED), @@ -96,12 +128,11 @@ public class MainScreen extends Screen { Main.saveConfig(); updateButtonStates(); }) - .dimensions(row1StartX + BUTTON_WIDTH + PADDING, startY, BUTTON_WIDTH, BUTTON_HEIGHT) + .dimensions(row1StartX + BUTTON_WIDTH + PADDING, currentContentY, BUTTON_WIDTH, BUTTON_HEIGHT) .build(); - modButtons.add(autoCrossbowEnabledOffButton); - this.addDrawableChild(autoCrossbowEnabledOffButton); + scrollableWidgets.add(autoCrossbowEnabledOffButton); - int row2Y = startY + BUTTON_HEIGHT + PADDING; + int row2Y = currentContentY + BUTTON_HEIGHT + PADDING; int row2StartX = centerX - (BUTTON_WIDTH * 3 + PADDING * 2) / 2; // 模式选择 @@ -114,8 +145,7 @@ public class MainScreen extends Screen { }) .dimensions(row2StartX, row2Y, BUTTON_WIDTH, BUTTON_HEIGHT) .build(); - modButtons.add(normalModeButton); - this.addDrawableChild(normalModeButton); + scrollableWidgets.add(normalModeButton); ButtonWidget rapidShootModeButton = ButtonWidget.builder( Text.translatable("branulf_toolbox.autocrossbow.mode.rapid_shoot"), @@ -126,8 +156,7 @@ public class MainScreen extends Screen { }) .dimensions(row2StartX + BUTTON_WIDTH + PADDING, row2Y, BUTTON_WIDTH, BUTTON_HEIGHT) .build(); - modButtons.add(rapidShootModeButton); - this.addDrawableChild(rapidShootModeButton); + scrollableWidgets.add(rapidShootModeButton); ButtonWidget rapidLoadModeButton = ButtonWidget.builder( Text.translatable("branulf_toolbox.autocrossbow.mode.rapid_load"), @@ -138,12 +167,13 @@ public class MainScreen extends Screen { }) .dimensions(row2StartX + 2 * (BUTTON_WIDTH + PADDING), row2Y, BUTTON_WIDTH, BUTTON_HEIGHT) .build(); - modButtons.add(rapidLoadModeButton); - this.addDrawableChild(rapidLoadModeButton); + scrollableWidgets.add(rapidLoadModeButton); + + return row2Y + BUTTON_HEIGHT + PADDING; } // Scrafitipty - private void addScrafitiptyPlusButtons(int startY) { + private int addScrafitiptyPlusButtons(int currentContentY) { ScrafitiptyPlusHandler handler = Main.getInstance().scrafitiptyPlusHandler; ButtonWidget toggleServerButton = ButtonWidget.builder( @@ -152,14 +182,14 @@ public class MainScreen extends Screen { handler.toggleWebSocketServer(); updateButtonStates(); }) - .dimensions(this.width / 2 - BUTTON_WIDTH / 2, startY, BUTTON_WIDTH, BUTTON_HEIGHT) + .dimensions(this.width / 2 - BUTTON_WIDTH / 2, currentContentY, BUTTON_WIDTH, BUTTON_HEIGHT) .build(); - modButtons.add(toggleServerButton); - this.addDrawableChild(toggleServerButton); + scrollableWidgets.add(toggleServerButton); + return currentContentY + BUTTON_HEIGHT + PADDING; } - // 视角 - private void addCrazyLookButtons(int startY) { + // 挂机 + private int addCrazyLookButtons(int currentContentY) { ModConfig.CrazyLookConfig config = Main.getConfig().crazyLook; ButtonWidget toggleCrazyLookButton = ButtonWidget.builder( @@ -169,14 +199,14 @@ public class MainScreen extends Screen { Main.saveConfig(); updateButtonStates(); }) - .dimensions(this.width / 2 - BUTTON_WIDTH / 2, startY, BUTTON_WIDTH, BUTTON_HEIGHT) + .dimensions(this.width / 2 - BUTTON_WIDTH / 2, currentContentY, BUTTON_WIDTH, BUTTON_HEIGHT) .build(); - modButtons.add(toggleCrazyLookButton); - this.addDrawableChild(toggleCrazyLookButton); + scrollableWidgets.add(toggleCrazyLookButton); + return currentContentY + BUTTON_HEIGHT + PADDING; } // 鞘翅推进 - private void addElytraBoostButtons(int startY) { + private int addElytraBoostButtons(int currentContentY) { ModConfig.ElytraBoostConfigPart config = Main.getConfig().elytraBoostConfig; ButtonWidget toggleElytraBoostButton = ButtonWidget.builder( @@ -186,62 +216,251 @@ public class MainScreen extends Screen { Main.saveConfig(); updateButtonStates(); }) - .dimensions(this.width / 2 - BUTTON_WIDTH / 2, startY, BUTTON_WIDTH, BUTTON_HEIGHT) + .dimensions(this.width / 2 - BUTTON_WIDTH / 2, currentContentY, BUTTON_WIDTH, BUTTON_HEIGHT) .build(); - modButtons.add(toggleElytraBoostButton); - this.addDrawableChild(toggleElytraBoostButton); + scrollableWidgets.add(toggleElytraBoostButton); + return currentContentY + BUTTON_HEIGHT + PADDING; + } + + // 玩家探测器 + private int addPlayerDetectorButtons(int currentContentY) { + ModConfig.PlayerDetectorConfig config = Main.getConfig().playerDetector; + int centerX = this.width / 2; + int row1StartX = centerX - BUTTON_WIDTH / 2; + + ButtonWidget toggleButton = ButtonWidget.builder( + Text.translatable("gui.branulf_toolbox.player_detector.toggle"), + button -> { + config.enabled = !config.enabled; + Main.saveConfig(); + updateButtonStates(); + }) + .dimensions(row1StartX, currentContentY, BUTTON_WIDTH, BUTTON_HEIGHT) + .build(); + scrollableWidgets.add(toggleButton); + + int row2Y = currentContentY + BUTTON_HEIGHT + PADDING; + + Text rangeLabelText = Text.translatable("gui.branulf_toolbox.player_detector.range_label"); + int labelWidth = this.textRenderer.getWidth(rangeLabelText); + int totalWidth = BUTTON_WIDTH; + int fieldWidth = totalWidth - labelWidth - PADDING; + + int startX = centerX - totalWidth / 2; + + ButtonWidget rangeLabelButton = ButtonWidget.builder(rangeLabelText, button -> {}) + .dimensions(startX, row2Y, labelWidth, BUTTON_HEIGHT) + .build(); + rangeLabelButton.active = false; + scrollableWidgets.add(rangeLabelButton); + + playerDetectorRangeField = new TextFieldWidget( + this.textRenderer, + startX + labelWidth + PADDING, + row2Y, + fieldWidth, + BUTTON_HEIGHT, + Text.translatable("gui.branulf_toolbox.player_detector.range_field") + ); + playerDetectorRangeField.setMaxLength(2); + playerDetectorRangeField.setText(String.valueOf(config.detectionRange)); + playerDetectorRangeField.setChangedListener(s -> { + try { + int value = Integer.parseInt(s); + value = MathHelper.clamp(value, 1, 128); + config.detectionRange = value; + Main.saveConfig(); + playerDetectorRangeField.setSuggestion(null); + } catch (NumberFormatException e) { + playerDetectorRangeField.setSuggestion(Text.translatable("gui.branulf_toolbox.player_detector.range_invalid").getString()); + } + }); + scrollableWidgets.add(playerDetectorRangeField); + return row2Y + BUTTON_HEIGHT + PADDING; } private void updateButtonStates() { ModConfig config = Main.getConfig(); - ScrafitiptyPlusHandler scrafitiptyPlusHandler = Main.getInstance().scrafitiptyPlusHandler; - for (ButtonWidget button : modButtons) { - // 诸葛连弩 - if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.autocrossbow.enabled_on").getString())) { - button.active = !config.autocrossbow.enabled; - } else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.autocrossbow.enabled_off").getString())) { - button.active = config.autocrossbow.enabled; - } else if (button.getMessage().getString().contains(Text.translatable("branulf_toolbox.autocrossbow.mode.normal").getString())) { - button.active = config.autocrossbow.currentMode != AutoCrossbowMode.NORMAL; - button.setMessage(Text.translatable("branulf_toolbox.autocrossbow.mode.normal") - .formatted(config.autocrossbow.currentMode == AutoCrossbowMode.NORMAL ? Formatting.GOLD : Formatting.WHITE)); - } else if (button.getMessage().getString().contains(Text.translatable("branulf_toolbox.autocrossbow.mode.rapid_shoot").getString())) { - button.active = config.autocrossbow.currentMode != AutoCrossbowMode.RAPID_FIRE_SHOOT; - button.setMessage(Text.translatable("branulf_toolbox.autocrossbow.mode.rapid_shoot") - .formatted(config.autocrossbow.currentMode == AutoCrossbowMode.RAPID_FIRE_SHOOT ? Formatting.GOLD : Formatting.WHITE)); - } else if (button.getMessage().getString().contains(Text.translatable("branulf_toolbox.autocrossbow.mode.rapid_load").getString())) { - button.active = config.autocrossbow.currentMode != AutoCrossbowMode.RAPID_FIRE_LOAD; - button.setMessage(Text.translatable("branulf_toolbox.autocrossbow.mode.rapid_load") - .formatted(config.autocrossbow.currentMode == AutoCrossbowMode.RAPID_FIRE_LOAD ? Formatting.GOLD : Formatting.WHITE)); - } - // Scrafitipty - else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.scrafitipty_plus.toggle_server").getString())) { - button.setMessage(Text.translatable("gui.branulf_toolbox.scrafitipty_plus.toggle_server") - .append(Text.literal(" (" + (scrafitiptyPlusHandler.isServerRunning() ? Text.translatable("branulf_toolbox.enabled").getString() : Text.translatable("branulf_toolbox.disabled").getString()) + ")")) - .formatted(scrafitiptyPlusHandler.isServerRunning() ? Formatting.GREEN : Formatting.RED)); - } - // 视角 - else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.crazylook.toggle").getString())) { - button.setMessage(Text.translatable("gui.branulf_toolbox.crazylook.toggle") - .append(Text.literal(" (" + (config.crazyLook.enabled ? Text.translatable("branulf_toolbox.enabled").getString() : Text.translatable("branulf_toolbox.disabled").getString()) + ")")) - .formatted(config.crazyLook.enabled ? Formatting.GREEN : Formatting.RED)); - } - // 鞘翅推进 - else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.elytraboost.toggle").getString())) { - button.setMessage(Text.translatable("gui.branulf_toolbox.elytraboost.toggle") - .append(Text.literal(" (" + (config.elytraBoostConfig.modEnabled ? Text.translatable("branulf_toolbox.enabled").getString() : Text.translatable("branulf_toolbox.disabled").getString()) + ")")) - .formatted(config.elytraBoostConfig.modEnabled ? Formatting.GREEN : Formatting.RED)); + for (ClickableWidget widget : scrollableWidgets) { + if (widget instanceof ButtonWidget button) { + // 诸葛连弩 + if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.autocrossbow.enabled_on").getString())) { + button.active = !config.autocrossbow.enabled; + } else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.autocrossbow.enabled_off").getString())) { + button.active = config.autocrossbow.enabled; + } else if (button.getMessage().getString().contains(Text.translatable("branulf_toolbox.autocrossbow.mode.normal").getString())) { + button.active = config.autocrossbow.currentMode != AutoCrossbowMode.NORMAL; + button.setMessage(Text.translatable("branulf_toolbox.autocrossbow.mode.normal") + .formatted(config.autocrossbow.currentMode == AutoCrossbowMode.NORMAL ? Formatting.GOLD : Formatting.WHITE)); + } else if (button.getMessage().getString().contains(Text.translatable("branulf_toolbox.autocrossbow.mode.rapid_shoot").getString())) { + button.active = config.autocrossbow.currentMode != AutoCrossbowMode.RAPID_FIRE_SHOOT; + button.setMessage(Text.translatable("branulf_toolbox.autocrossbow.mode.rapid_shoot") + .formatted(config.autocrossbow.currentMode == AutoCrossbowMode.RAPID_FIRE_SHOOT ? Formatting.GOLD : Formatting.WHITE)); + } else if (button.getMessage().getString().contains(Text.translatable("branulf_toolbox.autocrossbow.mode.rapid_load").getString())) { + button.active = config.autocrossbow.currentMode != AutoCrossbowMode.RAPID_FIRE_LOAD; + button.setMessage(Text.translatable("branulf_toolbox.autocrossbow.mode.rapid_load") + .formatted(config.autocrossbow.currentMode == AutoCrossbowMode.RAPID_FIRE_LOAD ? Formatting.GOLD : Formatting.WHITE)); + } + // Scrafitipty + else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.scrafitipty_plus.toggle_server").getString())) { + button.setMessage(Text.translatable("gui.branulf_toolbox.scrafitipty_plus.toggle_server") + .append(Text.literal(" (" + (scrafitiptyPlusHandler.isServerRunning() ? Text.translatable("branulf_toolbox.enabled").getString() : Text.translatable("branulf_toolbox.disabled").getString()) + ")")) + .formatted(scrafitiptyPlusHandler.isServerRunning() ? Formatting.GREEN : Formatting.RED)); + } + // 视角 + else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.crazylook.toggle").getString())) { + button.setMessage(Text.translatable("gui.branulf_toolbox.crazylook.toggle") + .append(Text.literal(" (" + (config.crazyLook.enabled ? Text.translatable("branulf_toolbox.enabled").getString() : Text.translatable("branulf_toolbox.disabled").getString()) + ")")) + .formatted(config.crazyLook.enabled ? Formatting.GREEN : Formatting.RED)); + } + // 鞘翅推进 + else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.elytraboost.toggle").getString())) { + button.setMessage(Text.translatable("gui.branulf_toolbox.elytraboost.toggle") + .append(Text.literal(" (" + (config.elytraBoostConfig.modEnabled ? Text.translatable("branulf_toolbox.enabled").getString() : Text.translatable("branulf_toolbox.disabled").getString()) + ")")) + .formatted(config.elytraBoostConfig.modEnabled ? Formatting.GREEN : Formatting.RED)); + } + // 玩家探测器 + else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.player_detector.toggle").getString())) { + button.setMessage(Text.translatable("gui.branulf_toolbox.player_detector.toggle") + .append(Text.literal(" (" + (config.playerDetector.enabled ? Text.translatable("branulf_toolbox.enabled").getString() : Text.translatable("branulf_toolbox.disabled").getString()) + ")")) + .formatted(config.playerDetector.enabled ? Formatting.GREEN : Formatting.RED)); + } } } + if (playerDetectorRangeField != null) { + playerDetectorRangeField.setText(String.valueOf(config.playerDetector.detectionRange)); + } } @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { this.renderBackground(context, mouseX, mouseY, delta); - super.render(context, mouseX, mouseY, delta); context.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, 15, 0xFFFFFF); + + context.enableScissor(0, scrollableAreaYStart, this.width, scrollableAreaHeight); + context.getMatrices().push(); + context.getMatrices().translate(0, -scrollY, 0); + + for (ClickableWidget widget : scrollableWidgets) { + if (widget.getY() + widget.getHeight() >= scrollY && widget.getY() <= scrollY + scrollableAreaHeight) { + widget.render(context, mouseX, (int)(mouseY + scrollY), delta); + } + } + + context.getMatrices().pop(); + context.disableScissor(); + + configButton.render(context, mouseX, mouseY, delta); + + renderScrollbar(context); + } + + private void clampScroll() { + scrollY = MathHelper.clamp(scrollY, 0, Math.max(0, totalContentHeight - scrollableAreaHeight)); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { + if (mouseY >= scrollableAreaYStart && mouseY <= scrollableAreaYEnd) { + scrollY = MathHelper.clamp(scrollY - verticalAmount * SCROLL_SPEED, 0, Math.max(0, totalContentHeight - scrollableAreaHeight)); + return true; + } + return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (configButton.mouseClicked(mouseX, mouseY, button)) { + this.setFocused(configButton); + return true; + } + + if (mouseY >= scrollableAreaYStart && mouseY <= scrollableAreaYEnd) { + double adjustedMouseY = mouseY + scrollY; + for (ClickableWidget widget : scrollableWidgets) { + if (widget.getY() + widget.getHeight() >= scrollY && widget.getY() <= scrollY + scrollableAreaHeight) { + if (widget.mouseClicked(mouseX, adjustedMouseY, button)) { + this.setFocused(widget); + return true; + } + } + } + } + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + if (configButton.mouseReleased(mouseX, mouseY, button)) { + return true; + } + if (mouseY >= scrollableAreaYStart && mouseY <= scrollableAreaYEnd) { + double adjustedMouseY = mouseY + scrollY; + for (ClickableWidget widget : scrollableWidgets) { + if (widget.mouseReleased(mouseX, adjustedMouseY, button)) { + return true; + } + } + } + return super.mouseReleased(mouseX, mouseY, button); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + if (configButton.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) { + return true; + } + if (this.getFocused() instanceof ClickableWidget focusedWidget && scrollableWidgets.contains(focusedWidget)) { + double adjustedMouseY = mouseY + scrollY; + if (focusedWidget.mouseDragged(mouseX, adjustedMouseY, button, deltaX, deltaY)) { + return true; + } + } + return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (this.getFocused() instanceof ClickableWidget focusedWidget && scrollableWidgets.contains(focusedWidget)) { + if (focusedWidget.keyPressed(keyCode, scanCode, modifiers)) { + return true; + } + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean charTyped(char chr, int modifiers) { + if (this.getFocused() instanceof ClickableWidget focusedWidget && scrollableWidgets.contains(focusedWidget)) { + if (focusedWidget.charTyped(chr, modifiers)) { + return true; + } + } + return super.charTyped(chr, modifiers); + } + + private void renderScrollbar(DrawContext context) { + if (totalContentHeight <= scrollableAreaHeight) { + return; + } + + int scrollbarWidth = 6; + int scrollbarX = this.width - PADDING - scrollbarWidth; + int scrollbarYStart = scrollableAreaYStart; + int scrollbarHeight = scrollableAreaHeight; + + double visibleRatio = (double) scrollableAreaHeight / totalContentHeight; + int thumbHeight = (int) (scrollbarHeight * visibleRatio); + thumbHeight = MathHelper.clamp(thumbHeight, 10, scrollbarHeight); + + double scrollRatio = scrollY / (totalContentHeight - scrollableAreaHeight); + int thumbY = (int) (scrollbarYStart + (scrollbarHeight - thumbHeight) * scrollRatio); + + context.fill(scrollbarX, scrollbarYStart, scrollbarX + scrollbarWidth, scrollbarYStart + scrollbarHeight, 0x80000000); + + context.fill(scrollbarX + 1, thumbY, scrollbarX + scrollbarWidth - 1, thumbY + thumbHeight, 0xFFFFFFFF); } @Override diff --git a/src/main/java/org/branulf/toolbox/ModConfig.java b/src/main/java/org/branulf/toolbox/ModConfig.java index 3092dc4..820f8b2 100644 --- a/src/main/java/org/branulf/toolbox/ModConfig.java +++ b/src/main/java/org/branulf/toolbox/ModConfig.java @@ -30,6 +30,11 @@ public class ModConfig implements ConfigData { @ConfigEntry.Gui.TransitiveObject public ElytraBoostConfigPart elytraBoostConfig = new ElytraBoostConfigPart(); + // 玩家探测器 + @ConfigEntry.Category("player_detector_settings") + @ConfigEntry.Gui.TransitiveObject + public PlayerDetectorConfig playerDetector = new PlayerDetectorConfig(); + public static class AutoCrossbowConfig implements ConfigData { @@ -76,4 +81,17 @@ public class ModConfig implements ConfigData { public boolean modEnabled = true; } + + public static class PlayerDetectorConfig implements ConfigData { + @ConfigEntry.Gui.Tooltip(count = 0) + public boolean enabled = false; + + @ConfigEntry.Gui.Tooltip(count = 1) + @ConfigEntry.BoundedDiscrete(min = 1, max = 64) + public int detectionRange = 16; + + @ConfigEntry.Gui.Tooltip(count = 1) + @ConfigEntry.BoundedDiscrete(min = 1, max = 20) + public int alertFrequency = 1; + } } diff --git a/src/main/java/org/branulf/toolbox/playerdetector/PlayerDetectorHandler.java b/src/main/java/org/branulf/toolbox/playerdetector/PlayerDetectorHandler.java new file mode 100644 index 0000000..45fa267 --- /dev/null +++ b/src/main/java/org/branulf/toolbox/playerdetector/PlayerDetectorHandler.java @@ -0,0 +1,62 @@ +package org.branulf.toolbox.playerdetector; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvents; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Vec3d; +import org.branulf.toolbox.ModConfig; + +public class PlayerDetectorHandler { + private final ModConfig config; + private int ticksSinceLastAlert = 0; + + public PlayerDetectorHandler(ModConfig config) { + this.config = config; + } + + public void onClientTick(MinecraftClient client) { + if (client.player == null || client.world == null || !config.playerDetector.enabled) { + return; + } + + ModConfig.PlayerDetectorConfig pdConfig = config.playerDetector; + + int ticksPerAlert = (int) (20.0f / pdConfig.alertFrequency); + if (ticksPerAlert <= 0) ticksPerAlert = 1; + + ticksSinceLastAlert++; + + if (ticksSinceLastAlert < ticksPerAlert) { + return; + } + + ticksSinceLastAlert = 0; + + double range = pdConfig.detectionRange; + Vec3d playerPos = client.player.getPos(); + + Box searchBox = new Box( + playerPos.x - range, playerPos.y - range, playerPos.z - range, + playerPos.x + range, playerPos.y + range, playerPos.z + range + ); + + boolean playerFound = client.world.getPlayers().stream() + .filter(p -> p != client.player) + .anyMatch(p -> p.squaredDistanceTo(playerPos) <= range * range); + + if (playerFound) { + client.world.playSound( + client.player, + client.player.getX(), + client.player.getY(), + client.player.getZ(), + SoundEvents.BLOCK_NOTE_BLOCK_PLING.value(), + SoundCategory.MASTER, + 1.0f, + 2.0f + ); + } + } +} diff --git a/src/main/resources/assets/branulf_toolbox/lang/en_us.json b/src/main/resources/assets/branulf_toolbox/lang/en_us.json index d79b2a6..984222f 100644 --- a/src/main/resources/assets/branulf_toolbox/lang/en_us.json +++ b/src/main/resources/assets/branulf_toolbox/lang/en_us.json @@ -11,6 +11,7 @@ "text.autoconfig.branulf_toolbox.category.scrafitipty_plus_settings": "Scrafitipty Plus Settings", "text.autoconfig.branulf_toolbox.category.crazylook_settings": "Crazy Look Settings", "text.autoconfig.branulf_toolbox.category.elytraboost_settings": "Elytra Boost Settings", + "text.autoconfig.branulf_toolbox.category.player_detector_settings": "Player Detector Settings", "gui.branulf_toolbox.autocrossbow.title": "Auto Crossbow", "gui.branulf_toolbox.autocrossbow.enabled_on": "Enable Auto Crossbow", @@ -65,5 +66,16 @@ "branulf_toolbox.elytraboost.notification.enabled": "§6[Elytra Boost] §fStatus: §aENABLED", "branulf_toolbox.elytraboost.notification.disabled": "§6[Elytra Boost] §fStatus: §cDISABLED", "text.autoconfig.branulf_toolbox.option.elytraBoostConfig.modEnabled": "Enable Elytra Boost", - "text.autoconfig.branulf_toolbox.option.elytraBoostConfig.modEnabled.@Tooltip": "Toggle the elytra boost functionality" + "text.autoconfig.branulf_toolbox.option.elytraBoostConfig.modEnabled.@Tooltip": "Toggle the elytra boost functionality", + + "gui.branulf_toolbox.player_detector.title": "Player Detector", + "gui.branulf_toolbox.player_detector.toggle": "Toggle Player Detector", + "gui.branulf_toolbox.player_detector.range_label": "Range:", + "gui.branulf_toolbox.player_detector.range_field": "Detection Range", + "gui.branulf_toolbox.player_detector.range_invalid": "Invalid number (1-64)", + "text.autoconfig.branulf_toolbox.option.playerDetector.enabled": "Enable Player Detector", + "text.autoconfig.branulf_toolbox.option.playerDetector.detectionRange": "Detection Range (Blocks)", + "text.autoconfig.branulf_toolbox.option.playerDetector.detectionRange.@Tooltip": "The radius in blocks to detect other players (1-64).", + "text.autoconfig.branulf_toolbox.option.playerDetector.alertFrequency": "Alert Frequency (times/sec)", + "text.autoconfig.branulf_toolbox.option.playerDetector.alertFrequency.@Tooltip": "How many times per second to play the alert sound when a player is detected (1-20)." } diff --git a/src/main/resources/assets/branulf_toolbox/lang/zh_cn.json b/src/main/resources/assets/branulf_toolbox/lang/zh_cn.json index 7c26495..303759c 100644 --- a/src/main/resources/assets/branulf_toolbox/lang/zh_cn.json +++ b/src/main/resources/assets/branulf_toolbox/lang/zh_cn.json @@ -11,6 +11,7 @@ "text.autoconfig.branulf_toolbox.category.scrafitipty_plus_settings": "Scrafitipty Plus 设置", "text.autoconfig.branulf_toolbox.category.crazylook_settings": "疯狂视角设置", "text.autoconfig.branulf_toolbox.category.elytraboost_settings": "鞘翅推进优化设置", + "text.autoconfig.branulf_toolbox.category.player_detector_settings": "玩家探测器设置", "gui.branulf_toolbox.autocrossbow.title": "诸葛连弩", "gui.branulf_toolbox.autocrossbow.enabled_on": "启用诸葛连弩", @@ -65,5 +66,16 @@ "branulf_toolbox.elytraboost.notification.enabled": "§6[鞘翅推进优化] §f当前状态: §a启用", "branulf_toolbox.elytraboost.notification.disabled": "§6[鞘翅推进优化] §f当前状态: §c禁用", "text.autoconfig.branulf_toolbox.option.elytraBoostConfig.modEnabled": "启用鞘翅推进优化", - "text.autoconfig.branulf_toolbox.option.elytraBoostConfig.modEnabled.@Tooltip": "是否启用鞘翅推进优化功能" + "text.autoconfig.branulf_toolbox.option.elytraBoostConfig.modEnabled.@Tooltip": "是否启用鞘翅推进优化功能", + + "gui.branulf_toolbox.player_detector.title": "玩家探测器", + "gui.branulf_toolbox.player_detector.toggle": "切换玩家探测器", + "gui.branulf_toolbox.player_detector.range_label": "范围", + "gui.branulf_toolbox.player_detector.range_field": "探测范围", + "gui.branulf_toolbox.player_detector.range_invalid": "无效数字 (1-64)", + "text.autoconfig.branulf_toolbox.option.playerDetector.enabled": "启用玩家探测器", + "text.autoconfig.branulf_toolbox.option.playerDetector.detectionRange": "探测范围(方块)", + "text.autoconfig.branulf_toolbox.option.playerDetector.detectionRange.@Tooltip": "探测其他玩家的半径范围(1-64方块)。", + "text.autoconfig.branulf_toolbox.option.playerDetector.alertFrequency": "提示频率(次/秒)", + "text.autoconfig.branulf_toolbox.option.playerDetector.alertFrequency.@Tooltip": "当探测到玩家时,每秒播放提示音的次数(1-20)。" }