feat: 自动换备用装备 + 背包满自动丢垃圾 (v2.1.0)
新增 AutoGear 服务: - 护甲/工具耐久过低时自动从背包换上同类备用装备(工具按镐/斧/锹/锄/剑类别, 护甲按头/胸/腿/脚部位匹配),选剩余耐久最高的候选 - 通过 clickSlot 三段点击交换(拿起旧装备->与背包新装备互换->就位), 走正规网络包,客户端/服务端同步,单人/多人均可用 - 控制器改为换装优先:能换就不打断挖矿,无备用才暂停/下界返程 - 装备保护状态下持续尝试换装,换上后自动恢复任务 新增 AutoTrash 服务: - 背包满时自动丢弃 trashItems 清单里的垃圾(默认:圆石/深板岩/凝灰岩/ 泥土/花岗岩/闪长岩/安山岩/下界岩/黑石/玄武岩,GUI 可改) - 方块类垃圾保留 trashReserve 个(默认 64)供 Baritone 搭路垫脚; 满背且无其他垃圾可丢时只丢一组腾位,尽量保住搭路方块 - clickSlot(THROW) 丢整组,每 tick 限 4 组防数据包风暴 配套修复: - NetherMiner: 修复血量低时每帧 cancelAll 导致回程寻路反复被取消卡死的 bug; markGearBroken 现在真正触发 RETURN_PORTAL 阶段;产物计数改用注册表 id + 矿物->掉落物映射 + 任务开始基线(老库存不再被误计为战果) - 两个 Miner 的背包满判定加 20 tick 去抖,给自动丢垃圾留出腾位时间 - GUI 新增两个开关与垃圾清单编辑区,切换任务/开关时保留输入框未应用的编辑 - 装备低耐久检查的手持工具仅限真正的工具,不再误伤钓鱼竿等杂物
这个提交包含在:
@@ -2,9 +2,14 @@ package autodig.client.miner;
|
||||
|
||||
import autodig.client.bridge.BaritoneBridge;
|
||||
import autodig.client.config.DigConfig;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
@@ -16,7 +21,8 @@ import net.minecraft.world.World;
|
||||
* 死亡重生的返回逻辑由控制器统一处理,本类每次 tick 依据当前所在维度
|
||||
* 自适应地执行对应阶段。</p>
|
||||
*
|
||||
* <p>下界无死亡不掉落,比主世界更"惜命":血量低或装备耐久低时主动触发返程。</p>
|
||||
* <p>下界无死亡不掉落,比主世界更"惜命":血量低或装备耐久低时主动触发返程。
|
||||
* 背包满采用去抖判定(连续 1 秒都满才算真满),给自动丢垃圾服务留出腾位时间。</p>
|
||||
*/
|
||||
public final class NetherMiner {
|
||||
|
||||
@@ -33,6 +39,31 @@ public final class NetherMiner {
|
||||
RETURN_PORTAL
|
||||
}
|
||||
|
||||
/** 背包满去抖阈值(tick):连续这么久都满才认定为真满 */
|
||||
private static final int FULL_DEBOUNCE_TICKS = 20;
|
||||
|
||||
/** 常见矿物方块 id -> 掉落物品 id(挖掘产物与方块本身不同的情况) */
|
||||
private static final Map<String, String> ORE_TO_DROP = Map.ofEntries(
|
||||
Map.entry("diamond_ore", "diamond"),
|
||||
Map.entry("deepslate_diamond_ore", "diamond"),
|
||||
Map.entry("emerald_ore", "emerald"),
|
||||
Map.entry("deepslate_emerald_ore", "emerald"),
|
||||
Map.entry("lapis_ore", "lapis_lazuli"),
|
||||
Map.entry("deepslate_lapis_ore", "lapis_lazuli"),
|
||||
Map.entry("redstone_ore", "redstone"),
|
||||
Map.entry("deepslate_redstone_ore", "redstone"),
|
||||
Map.entry("coal_ore", "coal"),
|
||||
Map.entry("deepslate_coal_ore", "coal"),
|
||||
Map.entry("copper_ore", "raw_copper"),
|
||||
Map.entry("deepslate_copper_ore", "raw_copper"),
|
||||
Map.entry("iron_ore", "raw_iron"),
|
||||
Map.entry("deepslate_iron_ore", "raw_iron"),
|
||||
Map.entry("gold_ore", "raw_gold"),
|
||||
Map.entry("deepslate_gold_ore", "raw_gold"),
|
||||
Map.entry("nether_gold_ore", "gold_nugget"),
|
||||
Map.entry("nether_quartz_ore", "quartz"));
|
||||
// ancient_debris 挖掉落后就是 ancient_debris 物品,走默认映射
|
||||
|
||||
private Stage stage = Stage.GO_COMMIT;
|
||||
private boolean done;
|
||||
/** 结束原因是否为「背包已满」 */
|
||||
@@ -41,12 +72,19 @@ public final class NetherMiner {
|
||||
private boolean fled;
|
||||
/** 结束原因是否为「装备耐久过低触发返程」 */
|
||||
private boolean gearBroken;
|
||||
/** 外部(控制器)要求的强制返程 */
|
||||
private boolean forceReturn;
|
||||
/** 是否已派发过挖矿指令(用于区分「尚未开始」和「已经挖完」) */
|
||||
private boolean miningStarted;
|
||||
/** 派发节流 */
|
||||
private long lastIssue;
|
||||
/** 已挖到的目标矿物数量(通过扫描背包近似) */
|
||||
/** 已挖到的目标矿物数量(按背包产物计数,扣除任务开始时的基线) */
|
||||
private int minedCount;
|
||||
/** 任务开始时背包里已有的目标产物数量(避免老库存被误计为战果) */
|
||||
private int baselineCount;
|
||||
private boolean baselineSet;
|
||||
/** 背包满连续持续的 tick 数(去抖) */
|
||||
private int fullTicks;
|
||||
|
||||
public NetherMiner(BaritoneBridge bridge) {
|
||||
this.bridge = bridge;
|
||||
@@ -59,8 +97,12 @@ public final class NetherMiner {
|
||||
this.full = false;
|
||||
this.fled = false;
|
||||
this.gearBroken = false;
|
||||
this.forceReturn = false;
|
||||
this.miningStarted = false;
|
||||
this.minedCount = 0;
|
||||
this.baselineCount = 0;
|
||||
this.baselineSet = false;
|
||||
this.fullTicks = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,13 +121,14 @@ public final class NetherMiner {
|
||||
boolean inOverworld = dim == World.OVERWORLD;
|
||||
boolean inNether = dim == World.NETHER;
|
||||
|
||||
// 下界惜命:血量过低时触发返程
|
||||
if (inNether && config.netherFleeHealthThreshold > 0
|
||||
// 下界惜命:血量过低时触发返程(只触发一次,避免反复取消回程寻路)
|
||||
if (inNether && this.stage != Stage.RETURN_PORTAL && !this.done
|
||||
&& config.netherFleeHealthThreshold > 0
|
||||
&& this.healthPercent(player) < config.netherFleeHealthThreshold) {
|
||||
this.fled = true;
|
||||
this.miningStarted = false;
|
||||
this.bridge.cancelAll();
|
||||
if (config.netherAutoReturn) {
|
||||
this.bridge.cancelAll();
|
||||
this.stage = Stage.RETURN_PORTAL;
|
||||
} else {
|
||||
this.done = true;
|
||||
@@ -93,6 +136,13 @@ public final class NetherMiner {
|
||||
return this.done;
|
||||
}
|
||||
|
||||
// 外部标记(装备耐久过低等):直接进入返程
|
||||
if (this.forceReturn && inNether && this.stage != Stage.RETURN_PORTAL) {
|
||||
this.miningStarted = false;
|
||||
this.bridge.cancelAll();
|
||||
this.stage = Stage.RETURN_PORTAL;
|
||||
}
|
||||
|
||||
switch (this.stage) {
|
||||
case GO_COMMIT -> {
|
||||
// 选择去哪个维度
|
||||
@@ -114,7 +164,20 @@ public final class NetherMiner {
|
||||
// 意外回到主世界,重走流程
|
||||
this.stage = Stage.GO_COMMIT;
|
||||
this.miningStarted = false;
|
||||
} else if (this.inventoryFull(player)) {
|
||||
return this.done;
|
||||
}
|
||||
// 记录任务开始时背包已有的产物数量作为基线
|
||||
if (!this.baselineSet) {
|
||||
this.baselineCount = this.countProducts(config, player);
|
||||
this.baselineSet = true;
|
||||
}
|
||||
// 背包满去抖:连续一段时间都满才认定为真满(给自动丢垃圾留时间)
|
||||
if (this.inventoryFull(player)) {
|
||||
this.fullTicks++;
|
||||
} else {
|
||||
this.fullTicks = 0;
|
||||
}
|
||||
if (this.fullTicks >= FULL_DEBOUNCE_TICKS) {
|
||||
// 背包已满:期望回程则去传送门;不回程时无处可放,直接结束
|
||||
this.full = true;
|
||||
this.miningStarted = false;
|
||||
@@ -123,7 +186,7 @@ public final class NetherMiner {
|
||||
} else {
|
||||
this.done = true;
|
||||
}
|
||||
} else if (this.minedEnough(config)) {
|
||||
} else if (this.minedEnough(config, player)) {
|
||||
// 已挖够目标数量
|
||||
this.miningStarted = false;
|
||||
if (config.netherAutoReturn) {
|
||||
@@ -132,7 +195,7 @@ public final class NetherMiner {
|
||||
this.done = true;
|
||||
}
|
||||
} else if (this.miningStarted && !this.bridge.isActive()) {
|
||||
// 挖矿进程已跑完(附近无矿可挖)
|
||||
// 挖矿进程已跑完(挖够数量或附近无矿可挖)
|
||||
this.miningStarted = false;
|
||||
if (config.netherAutoReturn) {
|
||||
this.stage = Stage.RETURN_PORTAL;
|
||||
@@ -163,35 +226,40 @@ public final class NetherMiner {
|
||||
return this.done;
|
||||
}
|
||||
|
||||
/** 判断是否已挖够目标矿物数量 */
|
||||
private boolean minedEnough(DigConfig config) {
|
||||
/** 判断是否已挖够目标矿物数量(按背包产物计数,扣除任务开始时的基线)。 */
|
||||
private boolean minedEnough(DigConfig config, PlayerEntity player) {
|
||||
if (config.netherTargetCount <= 0) {
|
||||
return false; // -1 或 0 表示无限挖
|
||||
}
|
||||
// 通过扫描背包中目标矿物数量近似判断
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client.player == null) {
|
||||
return false;
|
||||
this.minedCount = this.countProducts(config, player) - this.baselineCount;
|
||||
return this.minedCount >= config.netherTargetCount;
|
||||
}
|
||||
|
||||
/** 统计背包中目标矿物产物的总数量(方块 id 映射到掉落物 id)。 */
|
||||
private int countProducts(DigConfig config, PlayerEntity player) {
|
||||
List<String> ores = config.getNetherOreList();
|
||||
if (ores.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
var ores = config.getNetherOreList();
|
||||
Set<String> products = new HashSet<>();
|
||||
for (String ore : ores) {
|
||||
products.add(ORE_TO_DROP.getOrDefault(ore, ore));
|
||||
}
|
||||
int total = 0;
|
||||
for (int i = 0; i < 36; i++) {
|
||||
var stack = client.player.getInventory().main.get(i);
|
||||
ItemStack stack = player.getInventory().main.get(i);
|
||||
if (stack.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// 通过物品的 toString 粗略匹配矿物名
|
||||
String itemName = stack.getItem().toString();
|
||||
for (String ore : ores) {
|
||||
// ancient_debris -> ancient_debris, nether_gold_ore -> nether_gold_ore
|
||||
if (itemName.contains(ore) || ore.contains(itemName)) {
|
||||
count += stack.getCount();
|
||||
break;
|
||||
}
|
||||
if (products.contains(itemId(stack))) {
|
||||
total += stack.getCount();
|
||||
}
|
||||
}
|
||||
this.minedCount = count;
|
||||
return count >= config.netherTargetCount;
|
||||
return total;
|
||||
}
|
||||
|
||||
private static String itemId(ItemStack stack) {
|
||||
return Registries.ITEM.getId(stack.getItem()).toString();
|
||||
}
|
||||
|
||||
private void issue(Runnable action) {
|
||||
@@ -234,13 +302,14 @@ public final class NetherMiner {
|
||||
return this.gearBroken;
|
||||
}
|
||||
|
||||
/** 标记装备耐久过低,触发返程 */
|
||||
/** 标记装备耐久过低,触发强制返程 */
|
||||
public void markGearBroken() {
|
||||
this.gearBroken = true;
|
||||
this.forceReturn = true;
|
||||
this.miningStarted = false;
|
||||
}
|
||||
|
||||
/** @return 已挖到的目标矿物数量 */
|
||||
/** @return 本次任务新挖到的目标矿物数量 */
|
||||
public int getMinedCount() {
|
||||
return this.minedCount;
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户