From f1d9d2fd30ec2c01201dc5c214c03431495a66fc Mon Sep 17 00:00:00 2001 From: lyizai <3945107889@qq.com> Date: Sat, 29 Aug 2026 17:58:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E5=93=8D=E5=BA=94=E5=BC=8F?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E3=80=81=E4=BB=BB=E5=8A=A1=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E9=80=89=E9=A1=B9=E5=8A=A8=E6=80=81=E6=98=BE=E7=A4=BA=E3=80=81?= =?UTF-8?q?=E7=9F=BF=E7=89=A9=E8=BE=93=E5=85=A5=E6=A1=86=E3=80=81=E7=A9=BA?= =?UTF-8?q?=E9=97=B2=E4=B8=8D=E4=BF=9D=E5=91=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AutoDigGui: 相对坐标布局自适应屏幕宽度,分组标题清晰 - 选中主世界/下界时才显示对应设置区块 - 矿物选择改为文本输入框(支持完整自定义),不再是不可见的按钮循环 - 数值调整用 +/- 按钮,当前值居中显示 - 底部按钮自适应三等分 - DigController: 空闲(NONE)模式下不触发保命/装备保护/杀戮光环 - 修复 CheckboxWidget width 编译错误 --- .../client/controller/DigController.java | 21 +- .../java/autodig/client/gui/AutoDigGui.java | 369 ++++++++++-------- 2 files changed, 216 insertions(+), 174 deletions(-) diff --git a/src/main/java/autodig/client/controller/DigController.java b/src/main/java/autodig/client/controller/DigController.java index 9eeabb5..15dcd58 100644 --- a/src/main/java/autodig/client/controller/DigController.java +++ b/src/main/java/autodig/client/controller/DigController.java @@ -91,8 +91,16 @@ public final class DigController { } } - // 保命优先:血线过低时向上撤离(不冒进挖矿)。 - // cancelAll 只在刚进入 FLEE 时执行一次,避免每帧取消刚派发的寻路 + // 未选择任务时归位:空闲模式下不触发保命/装备保护/杀戮光环等自动行为 + if (config.task == MineTask.NONE) { + if (this.state != State.IDLE) { + this.bridge.cancelAll(); + this.setState(State.IDLE, "任务已停止,进入空闲状态"); + } + return; + } + + // 保命优先:仅在挖矿任务进行中生效,血线过低时向上撤离 if (config.survivalFirst && this.healthPercent(player) < config.healthFleeThreshold) { if (this.state != State.FLEE) { this.bridge.cancelAll(); @@ -102,15 +110,6 @@ public final class DigController { return; } - // 未选择任务时归位 - if (config.task == MineTask.NONE) { - if (this.state != State.IDLE) { - this.bridge.cancelAll(); - this.setState(State.IDLE, "任务已停止,进入空闲状态"); - } - return; - } - this.drive(config, player); } diff --git a/src/main/java/autodig/client/gui/AutoDigGui.java b/src/main/java/autodig/client/gui/AutoDigGui.java index c754ccd..1d7b531 100644 --- a/src/main/java/autodig/client/gui/AutoDigGui.java +++ b/src/main/java/autodig/client/gui/AutoDigGui.java @@ -9,194 +9,211 @@ 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.CheckboxWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.text.Text; /** - * AutoDig 游戏内 GUI。 - * - *
用于选择挖矿模式(主世界 / 下界),并快捷设置:保命优先、优先绕开岩浆、 - * 自动切换工具、自动进食、杀戮光环(含下界协同)、重生后回死亡点、挖完回主世界, - * 装备耐久保护、矿物选择,以及鱼骨层数 / 巷道长度 / 目标数量等参数。 - * 界面中的改动在"开始/停止"或常规关闭(含 ESC)时同步到 mod 全局配置并落盘; - * "仅关闭"则丢弃未应用的改动。
+ * AutoDig 游戏内 GUI - 响应式布局,根据任务动态显示选项。 */ public final class AutoDigGui extends Screen { private final AutoDigModClient mod; private final DigConfig config; private final DigController controller; - /** 关闭界面时是否把改动写回 mod 配置("仅关闭"会置 false) */ private boolean applyOnClose = true; - // 动态数值按钮(点击 +/- 后刷新显示) + // UI 组件引用 + private TextFieldWidget overworldOresField; + private TextFieldWidget netherOresField; + + // 数值显示按钮 private ButtonWidget levelBtn; private ButtonWidget tunnelBtn; private ButtonWidget countBtn; private ButtonWidget armorDurBtn; private ButtonWidget toolDurBtn; private ButtonWidget fleeHBtn; - // 矿物输入框状态 - private String overworldOresDraft; - private String netherOresDraft; - private int oreInputFocused = -1; // 0 = overworld, 1 = nether + + // 任务按钮(用于刷新高亮) + private ButtonWidget btnNone; + private ButtonWidget btnOverworld; + private ButtonWidget btnNether; public AutoDigGui(AutoDigModClient mod) { super(Text.literal("AutoDig")); this.mod = mod; this.config = mod.config().copy(); this.controller = mod.controller(); - this.overworldOresDraft = this.config.overworldOres; - this.netherOresDraft = this.config.netherOres; } @Override protected void init() { this.clearChildren(); - int colX = 26; - int x = colX; - int y = 34; + int centerX = this.width / 2; + int panelW = Math.min(380, this.width - 40); + int left = centerX - panelW / 2; + int right = left + panelW; + int y = 30; + int gap = 4; + int rowH = 20; + int checkboxGap = 24; + int sectionGap = 14; + + // ===== 标题 ===== + // 居中已在 render 处理 + // ===== 任务选择 ===== - this.taskButton("空闲", MineTask.NONE, x, y); - this.taskButton("主世界", MineTask.OVERWORLD, x + 110, y); - this.taskButton("下界", MineTask.NETHER, x + 220, y); + int btnW = (panelW - gap * 2) / 3; + this.btnNone = taskButton("空闲", MineTask.NONE, left, y, btnW); + this.btnOverworld = taskButton("主世界", MineTask.OVERWORLD, left + btnW + gap, y, btnW); + this.btnNether = taskButton("下界", MineTask.NETHER, left + (btnW + gap) * 2, y, btnW); + y += rowH + sectionGap; - // ===== 开关设置 ===== - y += 30; - this.check("保命优先", colX, y, () -> this.config.survivalFirst, v -> this.config.survivalFirst = v); - this.check("优先绕开岩浆", colX + 150, y, () -> this.config.avoidLava, v -> this.config.avoidLava = v); - y += 24; - this.check("自动进食", colX, y, () -> this.config.autoEat, v -> this.config.autoEat = v); - this.check("自动切换工具", colX + 150, y, () -> this.config.autoTool, v -> this.config.autoTool = v); - y += 24; - this.check("杀戮光环", colX, y, () -> this.config.killAura, v -> this.config.killAura = v); - this.check("重生后回死亡点", colX + 150, y, () -> this.config.respawnReturn, v -> this.config.respawnReturn = v); - y += 24; - this.check("下界挖完回主世界", colX, y, () -> this.config.netherAutoReturn, v -> this.config.netherAutoReturn = v); - this.check("避难时绝杀不冒进", colX + 150, y, () -> this.config.killAuraSafe, v -> this.config.killAuraSafe = v); - y += 24; - this.check("下界协同杀戮光环", colX, y, () -> this.config.killAuraInNether, v -> this.config.killAuraInNether = v); - this.check("护甲耐久保护", colX + 150, y, () -> this.config.armorDurabilityProtection, v -> this.config.armorDurabilityProtection = v); - y += 24; - this.check("工具耐久保护", colX, y, () -> this.config.toolDurabilityProtection, v -> this.config.toolDurabilityProtection = v); + // ===== 通用开关(所有任务共用)===== + y = addSectionHeader(y, left, "通用设置", panelW); + y = addCheckboxRow(y, left, panelW, + "保命优先", () -> config.survivalFirst, v -> config.survivalFirst = v, + "优先绕开岩浆", () -> config.avoidLava, v -> config.avoidLava = v); + y = addCheckboxRow(y, left, panelW, + "自动进食", () -> config.autoEat, v -> config.autoEat = v, + "自动切换工具", () -> config.autoTool, v -> config.autoTool = v); + y = addCheckboxRow(y, left, panelW, + "杀戮光环", () -> config.killAura, v -> config.killAura = v, + "重生后回死亡点", () -> config.respawnReturn, v -> config.respawnReturn = v); + y = addCheckboxRow(y, left, panelW, + "避难时绝杀不冒进", () -> config.killAuraSafe, v -> config.killAuraSafe = v, + "下界协同杀戮光环", () -> config.killAuraInNether, v -> config.killAuraInNether = v); + y = addCheckboxRow(y, left, panelW, + "护甲耐久保护", () -> config.armorDurabilityProtection, v -> config.armorDurabilityProtection = v, + "工具耐久保护", () -> config.toolDurabilityProtection, v -> config.toolDurabilityProtection = v); + y += sectionGap; - // ===== 数值参数 ===== - y += 32; - this.levelBtn = this.numberRow("挖掘层(Y)", colX, y, () -> this.config.overworldLevel, v -> this.config.overworldLevel = v, -64, 320); - y += 24; - this.tunnelBtn = this.numberRow("巷道长度", colX, y, () -> this.config.tunnelLength, v -> this.config.tunnelLength = v, 4, 512); - y += 24; - this.countBtn = this.numberRow("下界目标数量", colX, y, () -> this.config.netherTargetCount, v -> this.config.netherTargetCount = v, -1, 512); - y += 24; - this.armorDurBtn = this.numberRow("护甲耐久阈值%", colX, y, () -> this.config.armorDurabilityThreshold, v -> this.config.armorDurabilityThreshold = v, 1, 100); - y += 24; - this.toolDurBtn = this.numberRow("工具耐久阈值%", colX, y, () -> this.config.toolDurabilityThreshold, v -> this.config.toolDurabilityThreshold = v, 1, 100); - y += 24; - this.fleeHBtn = this.numberRow("下界返程血量%", colX, y, () -> this.config.netherFleeHealthThreshold, v -> this.config.netherFleeHealthThreshold = v, 0, 100); + // ===== 主世界专用设置 ===== + if (config.task == MineTask.OVERWORLD) { + y = addSectionHeader(y, left, "主世界设置", panelW); + this.levelBtn = numberRow("挖掘层 (Y)", left, y, panelW, + () -> config.overworldLevel, v -> config.overworldLevel = v, -64, 320); + y += rowH + gap; + this.tunnelBtn = numberRow("主干巷道长度", left, y, panelW, + () -> config.tunnelLength, v -> config.tunnelLength = v, 4, 512); + y += rowH + gap; - // ===== 矿物选择 ===== - y += 32; - this.addDrawableChild(ButtonWidget.builder(Text.literal("主世界矿物: " + this.overworldOresDraft), b -> { - // 循环切换预设矿物组合 - this.overworldOresDraft = nextOverworldOres(this.overworldOresDraft); - this.refreshOreButtons(); - }).dimensions(colX, y, 300, 20).build()); - y += 24; - this.addDrawableChild(ButtonWidget.builder(Text.literal("下界矿物: " + this.netherOresDraft), b -> { - // 循环切换预设矿物组合 - this.netherOresDraft = nextNetherOres(this.netherOresDraft); - this.refreshOreButtons(); - }).dimensions(colX, y, 300, 20).build()); - - // ===== 底部操作按钮 ===== - y += 32; - ButtonWidget start = this.textButton("开始 (保存并运行)", b -> this.start(), x, y, 140); - ButtonWidget stop = this.textButton("停止", b -> this.stop(), x + 146, y, 90); - ButtonWidget saveClose = this.textButton("仅关闭", b -> this.closeOnly(), x + 242, y, 80); - this.addDrawableChild(start); - this.addDrawableChild(stop); - this.addDrawableChild(saveClose); - } - - /** 主世界矿物预设循环 */ - private static String[] OVERWORLD_ORE_PRESETS = { - "diamond_ore,deepslate_diamond_ore", - "diamond_ore,deepslate_diamond_ore,iron_ore,deepslate_iron_ore,coal_ore,deepslate_coal_ore", - "diamond_ore,deepslate_diamond_ore,emerald_ore,deepslate_emerald_ore", - "gold_ore,deepslate_gold_ore", - "lapis_ore,deepslate_lapis_ore", - "redstone_ore,deepslate_redstone_ore", - "copper_ore,deepslate_copper_ore", - "iron_ore,deepslate_iron_ore", - "coal_ore,deepslate_coal_ore" - }; - - /** 下界矿物预设循环 */ - private static String[] NETHER_ORE_PRESETS = { - "ancient_debris", - "ancient_debris,nether_gold_ore", - "nether_gold_ore", - "nether_quartz_ore", - "ancient_debris,nether_gold_ore,nether_quartz_ore" - }; - - private String nextOverworldOres(String current) { - for (int i = 0; i < OVERWORLD_ORE_PRESETS.length; i++) { - if (OVERWORLD_ORE_PRESETS[i].equals(current)) { - return OVERWORLD_ORE_PRESETS[(i + 1) % OVERWORLD_ORE_PRESETS.length]; - } + // 矿物输入框 + y = addTextFieldRow(y, left, panelW, "目标矿物 (逗号分隔)", + config.overworldOres, tf -> this.overworldOresField = tf, + v -> config.overworldOres = v); + y += sectionGap; } - return OVERWORLD_ORE_PRESETS[0]; - } - private String nextNetherOres(String current) { - for (int i = 0; i < NETHER_ORE_PRESETS.length; i++) { - if (NETHER_ORE_PRESETS[i].equals(current)) { - return NETHER_ORE_PRESETS[(i + 1) % NETHER_ORE_PRESETS.length]; - } + // ===== 下界专用设置 ===== + if (config.task == MineTask.NETHER) { + y = addSectionHeader(y, left, "下界设置", panelW); + this.countBtn = numberRow("目标数量 (-1无限)", left, y, panelW, + () -> config.netherTargetCount, v -> config.netherTargetCount = v, -1, 512); + y += rowH + gap; + y = addCheckboxRow(y, left, panelW, + "挖完/背包满/血量低/耐久低 自动回主世界", () -> config.netherAutoReturn, v -> config.netherAutoReturn = v, + "", () -> false, v -> {}); // 单列占满 + y += rowH + gap; + this.fleeHBtn = numberRow("返程血量阈值%", left, y, panelW, + () -> config.netherFleeHealthThreshold, v -> config.netherFleeHealthThreshold = v, 0, 100); + y += rowH + gap; + + // 矿物输入框 + y = addTextFieldRow(y, left, panelW, "目标矿物 (逗号分隔)", + config.netherOres, tf -> this.netherOresField = tf, + v -> config.netherOres = v); + y += sectionGap; } - return NETHER_ORE_PRESETS[0]; + + // ===== 装备耐久阈值(通用,但仅在开启保护时有效)===== + if (config.armorDurabilityProtection || config.toolDurabilityProtection) { + y = addSectionHeader(y, left, "装备耐久阈值", panelW); + this.armorDurBtn = numberRow("护甲耐久 %", left, y, panelW, + () -> config.armorDurabilityThreshold, v -> config.armorDurabilityThreshold = v, 1, 100); + y += rowH + gap; + this.toolDurBtn = numberRow("工具耐久 %", left, y, panelW, + () -> config.toolDurabilityThreshold, v -> config.toolDurabilityThreshold = v, 1, 100); + y += sectionGap; + } + + // ===== 底部按钮 ===== + int btnY = Math.max(y, this.height - 48); + int bW = (panelW - gap * 2) / 3; + this.addDrawableChild(textButton("开始", b -> start(), left, btnY, bW)); + this.addDrawableChild(textButton("停止", b -> stop(), left + bW + gap, btnY, bW)); + this.addDrawableChild(textButton("仅关闭", b -> closeOnly(), left + (bW + gap) * 2, btnY, bW)); } - private void refreshOreButtons() { - // 按钮在 init() 中创建,文本更新需要重新 init - this.init(); + // ========== 布局辅助 ========== + + private int addSectionHeader(int y, int left, String title, int panelW) { + // 画分隔线 + this.addDrawableChild(ButtonWidget.builder(Text.literal(""), b -> {}).dimensions(left, y + 10, panelW, 1).build()); + // 标题文本会在 render 中绘制,这里只预留空间 + return y + 18; } - private ButtonWidget taskButton(String label, MineTask task, int x, int y) { - // 选中的任务用 [ ] 包裹,形成高亮提示 - boolean selected = this.config.task == task; - String text = selected ? "[" + label + "]" : label; - ButtonWidget b = this.textButton(text, button -> { - this.config.task = task; - this.init(); // 刷新选中高亮 - }, x, y, 104); - this.addDrawableChild(b); - return b; + private int addCheckboxRow(int y, int left, int panelW, + String label1, java.util.function.BooleanSupplier g1, Consumer