feat: v2.2.1 主世界找矿绕道 + 工具适用性检查;移除自动火把
- 移除自动火把功能(按反馈不需要) - 主世界挖矿新增矿物绕道:每秒扫描周围10格目标矿物并自动绕道挖掉, 解决巷道壁旁钻石不挖的问题(此前只走巷道不主动找矿) - 新增工具适用性检查:用 isSuitableFor 校验手持工具能否采掘目标矿物, 远古残骸等自动换钻石镐;无合适工具时提示并暂停矿物挖掘 - 鱼骨支巷长度改为可配置 branchLength(默认10,原固定6) - 目标矿物 id 兼容 minecraft: 前缀(带不带均可识别)
这个提交包含在:
@@ -5,13 +5,14 @@
|
|||||||
## 功能特性
|
## 功能特性
|
||||||
|
|
||||||
### 挖矿任务
|
### 挖矿任务
|
||||||
- **主世界模式**:在指定 Y 层沿玩家朝向挖鱼骨巷道(主干 + 两侧侧支),自动返回地表;可自定义挖掘层、巷道长度、目标矿物(默认钻石)。
|
- **主世界模式**:在指定 Y 层沿玩家朝向挖鱼骨巷道(主干 + 两侧侧支),自动返回地表;可自定义挖掘层、主干长度、支巷长度、目标矿物(默认钻石)。巷道推进同时会**扫描周围 10 格的目标矿物并自动绕道挖掉**,巷道壁旁的钻石不漏挖。
|
||||||
- **下界模式**:自动进传送门进入下界挖矿(默认远古残骸),挖够数量 / 背包满 / 血量低 / 装备耐久低时自动回主世界;支持无限挖(目标数量 -1)。
|
- **下界模式**:自动进传送门进入下界挖矿(默认远古残骸),挖够数量 / 背包满 / 血量低 / 装备耐久低时自动回主世界;支持无限挖(目标数量 -1)。
|
||||||
|
- **目标矿物 id**:带不带 `minecraft:` 前缀均可识别(如 `diamond_ore` 与 `minecraft:diamond_ore` 等价)。
|
||||||
|
|
||||||
### 智能辅助(可逐项开关)
|
### 智能辅助(可逐项开关)
|
||||||
- **自动切换工具**:由 Baritone 自动选择最合适的镐/锹等工具。
|
- **工具自动选择**:挖掘前校验手持工具能否采掘目标矿物(如远古残骸必须钻石镐以上,铁镐只会白挖不掉落),不适用时自动从背包换上能采掘且耐久最高的工具;没有合适工具时提示并暂停矿物挖掘,放入工具后自动恢复。
|
||||||
|
- **自动切换工具**:由 Baritone 在挖掘途中自动选择最合适的工具。
|
||||||
- **自动进食**:饥饿度低时自动吃背包食物,跳过金苹果等特殊/珍贵食物。
|
- **自动进食**:饥饿度低时自动吃背包食物,跳过金苹果等特殊/珍贵食物。
|
||||||
- **自动插火把**:隧道黑暗时自动从背包取火把放置,防止刷怪。
|
|
||||||
- **自动换备用装备**:护甲/工具耐久过低时自动从背包换上同类备用装备。
|
- **自动换备用装备**:护甲/工具耐久过低时自动从背包换上同类备用装备。
|
||||||
- **提前丢垃圾**:背包接近满(已用 ≥30 格)时就提前丢弃圆石、深板岩等垃圾,为矿物腾出空间,避免被误判"背包已满需返回"。
|
- **提前丢垃圾**:背包接近满(已用 ≥30 格)时就提前丢弃圆石、深板岩等垃圾,为矿物腾出空间,避免被误判"背包已满需返回"。
|
||||||
- **杀戮光环**:自动攻击附近真正敌对生物,不误伤铁傀儡、僵尸猪灵、末影人等中立生物。
|
- **杀戮光环**:自动攻击附近真正敌对生物,不误伤铁傀儡、僵尸猪灵、末影人等中立生物。
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
|
|
||||||
## 版本记录
|
## 版本记录
|
||||||
|
|
||||||
- **v2.2.0**:新增实时状态 HUD、自动插火把、`/autodig status` 命令;自动进食跳过特殊食物;背包接近满提前丢垃圾防误判返程;杀戮光环不再误伤中立生物;GUI 补全进食/保命/攻击范围等设置。
|
- **v2.2.1**:移除自动火把;新增主世界矿物绕道挖掘(附近 10 格目标矿物自动挖掉,不漏矿);新增工具适用性检查(远古残骸等自动换钻石镐);支巷长度可配置(默认 10);矿物 id 兼容 `minecraft:` 前缀。
|
||||||
|
- **v2.2.0**:新增实时状态 HUD、`/autodig status` 命令;自动进食跳过特殊食物;背包接近满提前丢垃圾防误判返程;杀戮光环不再误伤中立生物;GUI 补全进食/保命/攻击范围等设置。
|
||||||
- **v2.1.0**:自动换备用装备 + 背包满自动丢垃圾。
|
- **v2.1.0**:自动换备用装备 + 背包满自动丢垃圾。
|
||||||
- **v2.0.x**:GUI 响应式布局、主世界/下界模式分离、装备耐久保护、自定义矿物。
|
- **v2.0.x**:GUI 响应式布局、主世界/下界模式分离、装备耐久保护、自定义矿物。
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ yarn_mappings=1.21.4+build.8
|
|||||||
loader_version=0.19.3
|
loader_version=0.19.3
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=2.2.0
|
mod_version=2.2.1
|
||||||
maven_group=autodig
|
maven_group=autodig
|
||||||
archives_base_name=autodig
|
archives_base_name=autodig
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ public final class ConfigStore {
|
|||||||
}
|
}
|
||||||
c.overworldLevel = clamp(c.overworldLevel, -64, 320);
|
c.overworldLevel = clamp(c.overworldLevel, -64, 320);
|
||||||
c.tunnelLength = clamp(c.tunnelLength, 4, 512);
|
c.tunnelLength = clamp(c.tunnelLength, 4, 512);
|
||||||
|
c.branchLength = clamp(c.branchLength, 1, 64);
|
||||||
c.netherTargetCount = clamp(c.netherTargetCount, -1, 512);
|
c.netherTargetCount = clamp(c.netherTargetCount, -1, 512);
|
||||||
if (c.overworldOres == null || c.overworldOres.isBlank()) {
|
if (c.overworldOres == null || c.overworldOres.isBlank()) {
|
||||||
c.overworldOres = "diamond_ore,deepslate_diamond_ore";
|
c.overworldOres = "diamond_ore,deepslate_diamond_ore";
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ public class DigConfig {
|
|||||||
public boolean autoEat = true;
|
public boolean autoEat = true;
|
||||||
/** 饥饿度低于该值时开始进食(20 为满值) */
|
/** 饥饿度低于该值时开始进食(20 为满值) */
|
||||||
public int eatHungerThreshold = 8;
|
public int eatHungerThreshold = 8;
|
||||||
/** 自动插火把(隧道黑暗时自动从背包取火把放置,防止刷怪) */
|
|
||||||
public boolean autoTorch = true;
|
|
||||||
|
|
||||||
// ---- 保命/安全 ----
|
// ---- 保命/安全 ----
|
||||||
/** 保命优先:生命过低时暂停挖矿并向上撤离 */
|
/** 保命优先:生命过低时暂停挖矿并向上撤离 */
|
||||||
@@ -66,7 +64,9 @@ public class DigConfig {
|
|||||||
public int overworldLevel = -58;
|
public int overworldLevel = -58;
|
||||||
/** 鱼骨主干巷道长度(方块) */
|
/** 鱼骨主干巷道长度(方块) */
|
||||||
public int tunnelLength = 60;
|
public int tunnelLength = 60;
|
||||||
/** 主世界要挖的矿物方块 ID 列表(逗号分隔,如 "diamond_ore,deepslate_diamond_ore") */
|
/** 鱼骨侧支巷道长度(方块);与主干间距 8 相配时 8 为全覆盖、10+ 有少量重叠覆盖 */
|
||||||
|
public int branchLength = 10;
|
||||||
|
/** 主世界要挖的矿物方块 ID 列表(逗号分隔,带不带 "minecraft:" 前缀均可) */
|
||||||
public String overworldOres = "diamond_ore,deepslate_diamond_ore";
|
public String overworldOres = "diamond_ore,deepslate_diamond_ore";
|
||||||
|
|
||||||
// ---- 下界模式参数 ----
|
// ---- 下界模式参数 ----
|
||||||
@@ -114,7 +114,6 @@ public class DigConfig {
|
|||||||
c.autoTool = this.autoTool;
|
c.autoTool = this.autoTool;
|
||||||
c.autoEat = this.autoEat;
|
c.autoEat = this.autoEat;
|
||||||
c.eatHungerThreshold = this.eatHungerThreshold;
|
c.eatHungerThreshold = this.eatHungerThreshold;
|
||||||
c.autoTorch = this.autoTorch;
|
|
||||||
c.survivalFirst = this.survivalFirst;
|
c.survivalFirst = this.survivalFirst;
|
||||||
c.healthFleeThreshold = this.healthFleeThreshold;
|
c.healthFleeThreshold = this.healthFleeThreshold;
|
||||||
c.armorDurabilityProtection = this.armorDurabilityProtection;
|
c.armorDurabilityProtection = this.armorDurabilityProtection;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import autodig.client.miner.NetherMiner;
|
|||||||
import autodig.client.miner.OverworldMiner;
|
import autodig.client.miner.OverworldMiner;
|
||||||
import autodig.client.service.AutoEat;
|
import autodig.client.service.AutoEat;
|
||||||
import autodig.client.service.AutoGear;
|
import autodig.client.service.AutoGear;
|
||||||
import autodig.client.service.AutoTorch;
|
|
||||||
import autodig.client.service.AutoTrash;
|
import autodig.client.service.AutoTrash;
|
||||||
import autodig.client.service.DeathRecorder;
|
import autodig.client.service.DeathRecorder;
|
||||||
import autodig.client.service.KillAura;
|
import autodig.client.service.KillAura;
|
||||||
@@ -36,7 +35,6 @@ public final class DigController {
|
|||||||
private final KillAura killAura = new KillAura();
|
private final KillAura killAura = new KillAura();
|
||||||
private final AutoGear autoGear = new AutoGear();
|
private final AutoGear autoGear = new AutoGear();
|
||||||
private final AutoTrash autoTrash = new AutoTrash();
|
private final AutoTrash autoTrash = new AutoTrash();
|
||||||
private final AutoTorch autoTorch = new AutoTorch();
|
|
||||||
private final DeathRecorder deathRecorder = new DeathRecorder();
|
private final DeathRecorder deathRecorder = new DeathRecorder();
|
||||||
private final SessionStats stats = new SessionStats();
|
private final SessionStats stats = new SessionStats();
|
||||||
private final OverworldMiner overworld = new OverworldMiner(this.bridge);
|
private final OverworldMiner overworld = new OverworldMiner(this.bridge);
|
||||||
@@ -46,6 +44,8 @@ public final class DigController {
|
|||||||
private BlockPos resumeTarget;
|
private BlockPos resumeTarget;
|
||||||
private long lastIssue;
|
private long lastIssue;
|
||||||
private long lastWarn;
|
private long lastWarn;
|
||||||
|
/** 上一次"没有合适工具"提示的时间戳(节流) */
|
||||||
|
private long lastToolWarn;
|
||||||
/** 返回死亡点的截止时间(超时后直接续任务,避免不可达时死循环) */
|
/** 返回死亡点的截止时间(超时后直接续任务,避免不可达时死循环) */
|
||||||
private long returnDeadline;
|
private long returnDeadline;
|
||||||
/** 装备耐久保护触发前所处的状态,用于恢复后回到该状态 */
|
/** 装备耐久保护触发前所处的状态,用于恢复后回到该状态 */
|
||||||
@@ -60,11 +60,10 @@ public final class DigController {
|
|||||||
}
|
}
|
||||||
PlayerEntity player = client.player;
|
PlayerEntity player = client.player;
|
||||||
|
|
||||||
// 常驻能力:杀戮光环、自动进食、背包满丢垃圾、自动火把、换装冷却
|
// 常驻能力:杀戮光环、自动进食、背包满丢垃圾、换装冷却
|
||||||
this.killAura.tick(config);
|
this.killAura.tick(config);
|
||||||
this.autoEat.tick(config);
|
this.autoEat.tick(config);
|
||||||
this.autoTrash.tick(config);
|
this.autoTrash.tick(config);
|
||||||
this.autoTorch.tick(config);
|
|
||||||
this.autoGear.tick();
|
this.autoGear.tick();
|
||||||
|
|
||||||
// 死亡处理:记录死亡次数,暂停任务,重生后按需回到死亡坐标
|
// 死亡处理:记录死亡次数,暂停任务,重生后按需回到死亡坐标
|
||||||
@@ -133,10 +132,29 @@ public final class DigController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.drive(config, player);
|
// 工具适用性保障:确保手持工具能采掘目标矿物(如远古残骸必须钻石镐)
|
||||||
|
boolean canMineOres = true;
|
||||||
|
if (config.autoTool) {
|
||||||
|
canMineOres = this.autoGear.ensureToolForOres(config, player);
|
||||||
|
if (!canMineOres) {
|
||||||
|
this.warnToolInsufficient(config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drive(DigConfig config, PlayerEntity player) {
|
this.drive(config, player, canMineOres);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 背包中没有能采掘目标矿物的工具时,每 5 秒提示一次。 */
|
||||||
|
private void warnToolInsufficient(DigConfig config) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - this.lastToolWarn > 5000) {
|
||||||
|
this.lastToolWarn = now;
|
||||||
|
String ores = config.task == MineTask.NETHER ? config.netherOres : config.overworldOres;
|
||||||
|
this.sendChat("背包中没有能采掘 " + ores + " 的工具(如需要钻石镐),矿物绕道挖掘已暂停;放入合适工具后自动恢复");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drive(DigConfig config, PlayerEntity player, boolean canMineOres) {
|
||||||
switch (this.state) {
|
switch (this.state) {
|
||||||
case IDLE -> {
|
case IDLE -> {
|
||||||
// Baritone 未就绪时无法挖矿,节流提示并停留在空闲,避免静默无响应
|
// Baritone 未就绪时无法挖矿,节流提示并停留在空闲,避免静默无响应
|
||||||
@@ -156,7 +174,7 @@ public final class DigController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case OVERWORLD -> {
|
case OVERWORLD -> {
|
||||||
if (this.overworld.tick(config)) {
|
if (this.overworld.tick(config, canMineOres)) {
|
||||||
this.bridge.cancelAll();
|
this.bridge.cancelAll();
|
||||||
if (this.overworld.isFull()) {
|
if (this.overworld.isFull()) {
|
||||||
// 背包已满无法继续,直接结束任务,避免空转循环
|
// 背包已满无法继续,直接结束任务,避免空转循环
|
||||||
|
|||||||
@@ -115,13 +115,11 @@ public final class AutoDigGui extends Screen {
|
|||||||
y = this.checkPair(y, "护甲耐久保护", () -> this.config.armorDurabilityProtection, v -> this.config.armorDurabilityProtection = v,
|
y = this.checkPair(y, "护甲耐久保护", () -> this.config.armorDurabilityProtection, v -> this.config.armorDurabilityProtection = v,
|
||||||
"工具耐久保护", () -> this.config.toolDurabilityProtection, v -> this.config.toolDurabilityProtection = v);
|
"工具耐久保护", () -> this.config.toolDurabilityProtection, v -> this.config.toolDurabilityProtection = v);
|
||||||
y = this.checkPair(y, "自动换备用装备", () -> this.config.autoGearSwap, v -> this.config.autoGearSwap = v,
|
y = this.checkPair(y, "自动换备用装备", () -> this.config.autoGearSwap, v -> this.config.autoGearSwap = v,
|
||||||
"自动插火把", () -> this.config.autoTorch, v -> this.config.autoTorch = v);
|
"背包满丢垃圾", () -> this.config.autoTrash, v -> {
|
||||||
y = this.checkPair(y, "背包满丢垃圾", () -> this.config.autoTrash, v -> {
|
|
||||||
this.syncFields(); // 保留输入框中未应用的编辑
|
this.syncFields(); // 保留输入框中未应用的编辑
|
||||||
this.config.autoTrash = v;
|
this.config.autoTrash = v;
|
||||||
this.init(); // 切换后显示/隐藏垃圾设置区
|
this.init(); // 切换后显示/隐藏垃圾设置区
|
||||||
},
|
});
|
||||||
null, null, null);
|
|
||||||
y += 10;
|
y += 10;
|
||||||
|
|
||||||
y = this.header(y, "进食/保命/攻击阈值");
|
y = this.header(y, "进食/保命/攻击阈值");
|
||||||
@@ -157,7 +155,9 @@ public final class AutoDigGui extends Screen {
|
|||||||
v -> this.config.overworldLevel = v, -64, 320);
|
v -> this.config.overworldLevel = v, -64, 320);
|
||||||
y = this.numberRow(y, "巷道长度", () -> this.config.tunnelLength,
|
y = this.numberRow(y, "巷道长度", () -> this.config.tunnelLength,
|
||||||
v -> this.config.tunnelLength = v, 4, 512);
|
v -> this.config.tunnelLength = v, 4, 512);
|
||||||
y = this.fieldRow(y, "目标矿物(逗号分隔,如 diamond_ore,deepslate_diamond_ore)",
|
y = this.numberRow(y, "支巷长度", () -> this.config.branchLength,
|
||||||
|
v -> this.config.branchLength = v, 1, 64);
|
||||||
|
y = this.fieldRow(y, "目标矿物(逗号分隔,带不带 minecraft: 前缀均可)",
|
||||||
this.config.overworldOres, tf -> this.overworldOresField = tf);
|
this.config.overworldOres, tf -> this.overworldOresField = tf);
|
||||||
y += 10;
|
y += 10;
|
||||||
} else if (this.config.task == MineTask.NETHER) {
|
} else if (this.config.task == MineTask.NETHER) {
|
||||||
@@ -169,7 +169,7 @@ public final class AutoDigGui extends Screen {
|
|||||||
null, null, null);
|
null, null, null);
|
||||||
y = this.numberRow(y, "返程血量阈值", () -> this.config.netherFleeHealthThreshold,
|
y = this.numberRow(y, "返程血量阈值", () -> this.config.netherFleeHealthThreshold,
|
||||||
v -> this.config.netherFleeHealthThreshold = v, 0, 100);
|
v -> this.config.netherFleeHealthThreshold = v, 0, 100);
|
||||||
y = this.fieldRow(y, "目标矿物(逗号分隔,如 ancient_debris,nether_gold_ore)",
|
y = this.fieldRow(y, "目标矿物(逗号分隔,带不带 minecraft: 前缀均可)",
|
||||||
this.config.netherOres, tf -> this.netherOresField = tf);
|
this.config.netherOres, tf -> this.netherOresField = tf);
|
||||||
y += 10;
|
y += 10;
|
||||||
}
|
}
|
||||||
@@ -401,7 +401,6 @@ public final class AutoDigGui extends Screen {
|
|||||||
target.avoidLava = this.config.avoidLava;
|
target.avoidLava = this.config.avoidLava;
|
||||||
target.autoEat = this.config.autoEat;
|
target.autoEat = this.config.autoEat;
|
||||||
target.eatHungerThreshold = this.config.eatHungerThreshold;
|
target.eatHungerThreshold = this.config.eatHungerThreshold;
|
||||||
target.autoTorch = this.config.autoTorch;
|
|
||||||
target.autoTool = this.config.autoTool;
|
target.autoTool = this.config.autoTool;
|
||||||
target.killAura = this.config.killAura;
|
target.killAura = this.config.killAura;
|
||||||
target.killAuraSafe = this.config.killAuraSafe;
|
target.killAuraSafe = this.config.killAuraSafe;
|
||||||
@@ -411,6 +410,7 @@ public final class AutoDigGui extends Screen {
|
|||||||
target.netherAutoReturn = this.config.netherAutoReturn;
|
target.netherAutoReturn = this.config.netherAutoReturn;
|
||||||
target.overworldLevel = this.config.overworldLevel;
|
target.overworldLevel = this.config.overworldLevel;
|
||||||
target.tunnelLength = this.config.tunnelLength;
|
target.tunnelLength = this.config.tunnelLength;
|
||||||
|
target.branchLength = this.config.branchLength;
|
||||||
target.overworldOres = this.config.overworldOres;
|
target.overworldOres = this.config.overworldOres;
|
||||||
target.netherOres = this.config.netherOres;
|
target.netherOres = this.config.netherOres;
|
||||||
target.netherTargetCount = this.config.netherTargetCount;
|
target.netherTargetCount = this.config.netherTargetCount;
|
||||||
|
|||||||
@@ -3,9 +3,15 @@ package autodig.client.miner;
|
|||||||
import autodig.client.bridge.BaritoneBridge;
|
import autodig.client.bridge.BaritoneBridge;
|
||||||
import autodig.client.config.DigConfig;
|
import autodig.client.config.DigConfig;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.registry.Registries;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,9 +19,13 @@ import net.minecraft.util.math.BlockPos;
|
|||||||
*
|
*
|
||||||
* <p>在指定的 Y 层,沿玩家朝向挖一条"主干隧道",并在主干上每隔
|
* <p>在指定的 Y 层,沿玩家朝向挖一条"主干隧道",并在主干上每隔
|
||||||
* {@code branchGap} 格向两侧各开一条侧支巷道,形成鱼骨状分布。
|
* {@code branchGap} 格向两侧各开一条侧支巷道,形成鱼骨状分布。
|
||||||
* 推进实现为:把鱼骨图案转成一串目标坐标 {@link GoalBlock},按顺序
|
* 推进实现为:把鱼骨图案转成一串目标坐标,按顺序派 Baritone 逐个前往;
|
||||||
* 派 Baritone 逐个前往;因为允许挖方块,Baritone 会走直线把挡路的方块
|
* 因为允许挖方块,Baritone 会走直线把挡路的方块挖掉,从而挖出巷道。
|
||||||
* 挖掉,从而挖出巷道。自动切换工具由 Baritone 的 {@code autoTool} 负责。</p>
|
* 自动切换工具由 Baritone 的 {@code autoTool} 负责。</p>
|
||||||
|
*
|
||||||
|
* <p>智能找矿:推进巷道的同时,周期性扫描玩家周围(默认 10 格)的目标矿物;
|
||||||
|
* 发现后临时绕道前往该矿物将其挖掉,再回到巷道计划继续推进。
|
||||||
|
* 这样巷道壁旁暴露的钻石等目标矿不会漏挖。</p>
|
||||||
*
|
*
|
||||||
* <p>遇到背包已满时提前结束,把控制权交还控制器(用于返回地表)。</p>
|
* <p>遇到背包已满时提前结束,把控制权交还控制器(用于返回地表)。</p>
|
||||||
*/
|
*/
|
||||||
@@ -37,9 +47,26 @@ public final class OverworldMiner {
|
|||||||
private long lastLog;
|
private long lastLog;
|
||||||
/** 背包满连续持续的 tick 数(去抖:给自动丢垃圾留出腾位时间) */
|
/** 背包满连续持续的 tick 数(去抖:给自动丢垃圾留出腾位时间) */
|
||||||
private int fullTicks;
|
private int fullTicks;
|
||||||
|
/** 矿物绕道目标;非 null 时优先前往挖掘该矿物 */
|
||||||
|
private BlockPos detourTarget;
|
||||||
|
/** 绕道已持续的 tick 数(超时放弃,避免卡死) */
|
||||||
|
private int detourTicks;
|
||||||
|
/** 上一次扫描附近矿物的时间戳(节流) */
|
||||||
|
private long lastOreScan;
|
||||||
|
/** 上次放弃的绕道点与时间(同一点短时间内不再重试,避免卡在不可达矿上) */
|
||||||
|
private BlockPos abandonedTarget;
|
||||||
|
private long abandonedAt;
|
||||||
|
|
||||||
/** 背包满去抖阈值(tick):连续这么久都满才认定为真满 */
|
/** 背包满去抖阈值(tick):连续这么久都满才认定为真满 */
|
||||||
private static final int FULL_DEBOUNCE_TICKS = 20;
|
private static final int FULL_DEBOUNCE_TICKS = 20;
|
||||||
|
/** 矿物扫描间隔(毫秒) */
|
||||||
|
private static final long ORE_SCAN_INTERVAL_MS = 1000;
|
||||||
|
/** 矿物扫描半径(方块) */
|
||||||
|
private static final int ORE_SCAN_RADIUS = 10;
|
||||||
|
/** 单次绕道超时(tick):超过则放弃该矿点回到巷道计划 */
|
||||||
|
private static final int DETOUR_TIMEOUT_TICKS = 400;
|
||||||
|
/** 同一矿点放弃后的重试冷却(毫秒) */
|
||||||
|
private static final long ABANDON_COOLDOWN_MS = 30_000;
|
||||||
|
|
||||||
public OverworldMiner(BaritoneBridge bridge) {
|
public OverworldMiner(BaritoneBridge bridge) {
|
||||||
this.bridge = bridge;
|
this.bridge = bridge;
|
||||||
@@ -59,7 +86,7 @@ public final class OverworldMiner {
|
|||||||
this.yLevel = config.overworldLevel;
|
this.yLevel = config.overworldLevel;
|
||||||
BlockPos stop = player.getBlockPos();
|
BlockPos stop = player.getBlockPos();
|
||||||
this.start = new BlockPos(stop.getX(), this.yLevel, stop.getZ());
|
this.start = new BlockPos(stop.getX(), this.yLevel, stop.getZ());
|
||||||
int branchLen = 6;
|
int branchLen = Math.max(1, config.branchLength);
|
||||||
int branchGap = 8;
|
int branchGap = 8;
|
||||||
// 用玩家水平朝向(而非移动方向)确定挖矿方向,站着不动时也稳定
|
// 用玩家水平朝向(而非移动方向)确定挖矿方向,站着不动时也稳定
|
||||||
net.minecraft.util.math.Direction facing = player.getHorizontalFacing();
|
net.minecraft.util.math.Direction facing = player.getHorizontalFacing();
|
||||||
@@ -70,6 +97,10 @@ public final class OverworldMiner {
|
|||||||
this.done = false;
|
this.done = false;
|
||||||
this.full = false;
|
this.full = false;
|
||||||
this.fullTicks = 0;
|
this.fullTicks = 0;
|
||||||
|
this.detourTarget = null;
|
||||||
|
this.detourTicks = 0;
|
||||||
|
this.abandonedTarget = null;
|
||||||
|
this.abandonedAt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BlockPos> buildPlan(BlockPos base, int y, int trunkLen, int branchLen, int branchGap, int axis, int sign) {
|
private List<BlockPos> buildPlan(BlockPos base, int y, int trunkLen, int branchLen, int branchGap, int axis, int sign) {
|
||||||
@@ -101,8 +132,9 @@ public final class OverworldMiner {
|
|||||||
* 每帧推进一次。背包已满或走完全程则返回已完成。
|
* 每帧推进一次。背包已满或走完全程则返回已完成。
|
||||||
*
|
*
|
||||||
* @param config 挖矿配置
|
* @param config 挖矿配置
|
||||||
|
* @param canMineOres 手持工具是否足以挖目标矿物(false 时跳过矿物绕道,避免白挖不掉落)
|
||||||
*/
|
*/
|
||||||
public boolean tick(DigConfig config) {
|
public boolean tick(DigConfig config, boolean canMineOres) {
|
||||||
if (this.done) {
|
if (this.done) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -128,6 +160,12 @@ public final class OverworldMiner {
|
|||||||
System.out.println("[AutoDig] overworld 结束:计划完成 step=" + this.step + " size=" + this.plan.size());
|
System.out.println("[AutoDig] overworld 结束:计划完成 step=" + this.step + " size=" + this.plan.size());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- 矿物绕道挖掘:优先把附近暴露的目标矿物挖掉 ----
|
||||||
|
if (this.handleOreDetour(config, client, player, canMineOres)) {
|
||||||
|
return false; // 绕道期间不推进巷道
|
||||||
|
}
|
||||||
|
|
||||||
BlockPos target = this.plan.get(this.step);
|
BlockPos target = this.plan.get(this.step);
|
||||||
double dist = this.distanceSq(player, target);
|
double dist = this.distanceSq(player, target);
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
@@ -152,6 +190,101 @@ public final class OverworldMiner {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理矿物绕道挖掘。
|
||||||
|
*
|
||||||
|
* @return true 表示当前正在绕道挖矿(调用方应暂停巷道推进)
|
||||||
|
*/
|
||||||
|
private boolean handleOreDetour(DigConfig config, MinecraftClient client, PlayerEntity player, boolean canMineOres) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (this.detourTarget != null) {
|
||||||
|
BlockState state = client.world.getBlockState(this.detourTarget);
|
||||||
|
if (!this.isTargetOre(config, state)) {
|
||||||
|
// 已挖掉或目标失效:回到巷道计划
|
||||||
|
this.detourTarget = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.detourTicks++;
|
||||||
|
if (this.detourTicks > DETOUR_TIMEOUT_TICKS) {
|
||||||
|
// 太久没挖到(不可达等):放弃该点并冷却
|
||||||
|
System.out.println("[AutoDig] overworld 绕道超时,放弃矿点: " + this.detourTarget);
|
||||||
|
this.abandonedTarget = this.detourTarget;
|
||||||
|
this.abandonedAt = now;
|
||||||
|
this.detourTarget = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (now - this.lastIssue > 250) {
|
||||||
|
this.bridge.gotoBlock(this.detourTarget);
|
||||||
|
this.lastIssue = now;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 节流扫描附近目标矿物
|
||||||
|
if (!canMineOres || now - this.lastOreScan < ORE_SCAN_INTERVAL_MS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.lastOreScan = now;
|
||||||
|
BlockPos ore = this.findNearbyOre(config, client, player);
|
||||||
|
if (ore != null) {
|
||||||
|
this.detourTarget = ore;
|
||||||
|
this.detourTicks = 0;
|
||||||
|
this.bridge.gotoBlock(ore);
|
||||||
|
this.lastIssue = now;
|
||||||
|
System.out.println("[AutoDig] overworld 发现目标矿物,绕道挖掘: " + ore);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 在玩家周围扫描最近的目标矿物方块。 */
|
||||||
|
private BlockPos findNearbyOre(DigConfig config, MinecraftClient client, PlayerEntity player) {
|
||||||
|
Set<String> targets = this.targetOrePaths(config);
|
||||||
|
if (targets.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BlockPos best = null;
|
||||||
|
double bestDist = Double.MAX_VALUE;
|
||||||
|
for (BlockPos pos : BlockPos.iterateOutwards(player.getBlockPos(),
|
||||||
|
ORE_SCAN_RADIUS, ORE_SCAN_RADIUS, ORE_SCAN_RADIUS)) {
|
||||||
|
String path = Registries.BLOCK.getId(client.world.getBlockState(pos).getBlock()).getPath();
|
||||||
|
if (!targets.contains(path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 最近放弃过的矿点在冷却期内不再尝试
|
||||||
|
if (pos.equals(this.abandonedTarget)
|
||||||
|
&& System.currentTimeMillis() - this.abandonedAt < ABANDON_COOLDOWN_MS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double d = pos.getSquaredDistance(player.getBlockPos());
|
||||||
|
if (d < bestDist) {
|
||||||
|
bestDist = d;
|
||||||
|
best = pos.toImmutable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 判断方块状态是否为配置中的目标矿物(按 path 匹配,兼容命名空间前缀)。 */
|
||||||
|
private boolean isTargetOre(DigConfig config, BlockState state) {
|
||||||
|
if (state.isOf(Blocks.AIR)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String path = Registries.BLOCK.getId(state.getBlock()).getPath();
|
||||||
|
return this.targetOrePaths(config).contains(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 目标矿物 path 集合(去命名空间,兼容 "minecraft:" 前缀写法)。 */
|
||||||
|
private Set<String> targetOrePaths(DigConfig config) {
|
||||||
|
Set<String> set = new HashSet<>();
|
||||||
|
for (String ore : config.getOverworldOreList()) {
|
||||||
|
String path = ore.contains(":") ? ore.substring(ore.indexOf(':') + 1) : ore;
|
||||||
|
if (!path.isBlank()) {
|
||||||
|
set.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
private double distanceSq(PlayerEntity player, BlockPos target) {
|
private double distanceSq(PlayerEntity player, BlockPos target) {
|
||||||
double dx = player.getX() - (target.getX() + 0.5);
|
double dx = player.getX() - (target.getX() + 0.5);
|
||||||
double dy = player.getY() - target.getY();
|
double dy = player.getY() - target.getY();
|
||||||
@@ -218,14 +351,4 @@ public final class OverworldMiner {
|
|||||||
this.full = false;
|
this.full = false;
|
||||||
this.fullTicks = 0;
|
this.fullTicks = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 派发矿脉挖掘指令(用于在鱼骨巷道推进过程中遇到目标矿物时挖掉)。
|
|
||||||
* 由控制器在合适时机调用。
|
|
||||||
*/
|
|
||||||
public void mineOres(DigConfig config) {
|
|
||||||
if (!config.getOverworldOreList().isEmpty()) {
|
|
||||||
this.bridge.mineByName(-1, config.getOverworldOreList().toArray(String[]::new));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
package autodig.client.service;
|
package autodig.client.service;
|
||||||
|
|
||||||
import autodig.client.config.DigConfig;
|
import autodig.client.config.DigConfig;
|
||||||
|
import autodig.client.config.MineTask;
|
||||||
|
import java.util.List;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.network.ClientPlayerInteractionManager;
|
import net.minecraft.client.network.ClientPlayerInteractionManager;
|
||||||
import net.minecraft.entity.EquipmentSlot;
|
import net.minecraft.entity.EquipmentSlot;
|
||||||
@@ -14,8 +19,10 @@ import net.minecraft.item.ItemStack;
|
|||||||
import net.minecraft.item.PickaxeItem;
|
import net.minecraft.item.PickaxeItem;
|
||||||
import net.minecraft.item.ShovelItem;
|
import net.minecraft.item.ShovelItem;
|
||||||
import net.minecraft.item.SwordItem;
|
import net.minecraft.item.SwordItem;
|
||||||
|
import net.minecraft.registry.Registries;
|
||||||
import net.minecraft.screen.ScreenHandler;
|
import net.minecraft.screen.ScreenHandler;
|
||||||
import net.minecraft.screen.slot.SlotActionType;
|
import net.minecraft.screen.slot.SlotActionType;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 装备自动更换服务。
|
* 装备自动更换服务。
|
||||||
@@ -26,13 +33,21 @@ import net.minecraft.screen.slot.SlotActionType;
|
|||||||
* 新装备就位。交换走正规网络包(clickSlot),客户端/服务端自动同步,
|
* 新装备就位。交换走正规网络包(clickSlot),客户端/服务端自动同步,
|
||||||
* 单人/多人均可用。找不到备用装备时返回 false,由控制器走"暂停保装备"或
|
* 单人/多人均可用。找不到备用装备时返回 false,由控制器走"暂停保装备"或
|
||||||
* "下界返程"逻辑。</p>
|
* "下界返程"逻辑。</p>
|
||||||
|
*
|
||||||
|
* <p>工具适用性检查({@link #ensureToolForOres}):挖掘前校验手持工具能否
|
||||||
|
* 采掘目标矿物(如远古残骸必须钻石镐以上,铁镐只会白挖不掉落),不适用时
|
||||||
|
* 自动从背包换上能采掘且剩余耐久最高的工具。</p>
|
||||||
*/
|
*/
|
||||||
public final class AutoGear {
|
public final class AutoGear {
|
||||||
|
|
||||||
/** 每次换装后的冷却 tick 数:等待客户端预测/服务端确认生效,避免连续误判 */
|
/** 每次换装后的冷却 tick 数:等待客户端预测/服务端确认生效,避免连续误判 */
|
||||||
private static final int SWAP_COOLDOWN_TICKS = 10;
|
private static final int SWAP_COOLDOWN_TICKS = 10;
|
||||||
|
/** 工具适用性检查间隔(毫秒),避免每帧解析方块注册表 */
|
||||||
|
private static final long TOOL_CHECK_INTERVAL_MS = 1000;
|
||||||
|
|
||||||
private int coolTicks;
|
private int coolTicks;
|
||||||
|
private long lastToolCheck;
|
||||||
|
private boolean lastToolOk = true;
|
||||||
|
|
||||||
/** 每帧推进冷却。 */
|
/** 每帧推进冷却。 */
|
||||||
public void tick() {
|
public void tick() {
|
||||||
@@ -93,6 +108,102 @@ public final class AutoGear {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保手持工具能采掘目标矿物(如远古残骸必须钻石镐以上)。
|
||||||
|
*
|
||||||
|
* <p>每秒检查一次:把目标矿物 id 解析成 Block(兼容 "minecraft:" 前缀),
|
||||||
|
* 用 {@link ItemStack#isSuitableFor(BlockState)} 校验手持工具;不适用时
|
||||||
|
* 从背包里找能采掘该矿物、剩余耐久最高的工具换上。全部换过仍不适用,
|
||||||
|
* 或配置的矿物 id 无法解析,返回 false。</p>
|
||||||
|
*
|
||||||
|
* @param config 挖矿配置
|
||||||
|
* @param player 玩家
|
||||||
|
* @return true 表示手持工具适用(或无需工具),false 表示没有合适工具
|
||||||
|
*/
|
||||||
|
public boolean ensureToolForOres(DigConfig config, PlayerEntity player) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - this.lastToolCheck < TOOL_CHECK_INTERVAL_MS) {
|
||||||
|
return this.lastToolOk;
|
||||||
|
}
|
||||||
|
this.lastToolCheck = now;
|
||||||
|
this.lastToolOk = true;
|
||||||
|
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
if (client.interactionManager == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
List<String> ores = config.task == MineTask.NETHER
|
||||||
|
? config.getNetherOreList() : config.getOverworldOreList();
|
||||||
|
if (ores.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 收集所有目标矿物方块状态(解析失败的 id 直接判定无合适工具,提示用户修正)
|
||||||
|
List<BlockState> oreStates = new java.util.ArrayList<>();
|
||||||
|
for (String ore : ores) {
|
||||||
|
Identifier id = Identifier.tryParse(ore.contains(":") ? ore : "minecraft:" + ore);
|
||||||
|
if (id == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Block block = Registries.BLOCK.get(id);
|
||||||
|
if (block != null && block != Blocks.AIR) {
|
||||||
|
oreStates.add(block.getDefaultState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (oreStates.isEmpty()) {
|
||||||
|
return true; // 无法解析时不拦挖矿(Baritone mineByName 自行处理未知 id)
|
||||||
|
}
|
||||||
|
ItemStack held = player.getMainHandStack();
|
||||||
|
if (isSuitableForAll(held, oreStates)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 手持工具(或空手)不适用:从背包找能采掘全部目标矿物的工具中耐久最高的换上
|
||||||
|
PlayerInventory inv = player.getInventory();
|
||||||
|
ItemStack best = null;
|
||||||
|
for (int i = 0; i < 36; i++) {
|
||||||
|
if (i == inv.selectedSlot) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ItemStack s = inv.main.get(i);
|
||||||
|
if (s.isEmpty() || !isTool(s) || !s.isDamageable()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isSuitableForAll(s, oreStates)
|
||||||
|
&& (best == null || remaining(s) > remaining(best))) {
|
||||||
|
best = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best == null) {
|
||||||
|
this.lastToolOk = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (player.currentScreenHandler != player.playerScreenHandler
|
||||||
|
|| !player.currentScreenHandler.getCursorStack().isEmpty()) {
|
||||||
|
// 界面占用中无法换装,本秒内视为不适用,下一秒重试
|
||||||
|
this.lastToolOk = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.swap(player, held, best)) {
|
||||||
|
this.coolTicks = SWAP_COOLDOWN_TICKS;
|
||||||
|
System.out.println("[AutoDig] 已切换适用工具: " + Registries.ITEM.getId(best.getItem())
|
||||||
|
+ "(原手持 " + Registries.ITEM.getId(held.getItem()) + " 无法采掘目标矿物)");
|
||||||
|
}
|
||||||
|
this.lastToolOk = isSuitableForAll(player.getMainHandStack(), oreStates);
|
||||||
|
return this.lastToolOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 该物品堆是否非空且能采掘全部目标矿物方块。 */
|
||||||
|
private static boolean isSuitableForAll(ItemStack stack, List<BlockState> states) {
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (BlockState state : states) {
|
||||||
|
if (!stack.isSuitableFor(state)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** 手持工具的替换候选:同类别中剩余耐久最高且高于当前手持的。 */
|
/** 手持工具的替换候选:同类别中剩余耐久最高且高于当前手持的。 */
|
||||||
private ItemStack findToolReplacement(PlayerInventory inv, ItemStack held) {
|
private ItemStack findToolReplacement(PlayerInventory inv, ItemStack held) {
|
||||||
String kind = toolKind(held.getItem());
|
String kind = toolKind(held.getItem());
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
package autodig.client.service;
|
|
||||||
|
|
||||||
import autodig.client.config.DigConfig;
|
|
||||||
import autodig.client.config.MineTask;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.block.Blocks;
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.item.ItemStack;
|
|
||||||
import net.minecraft.item.Items;
|
|
||||||
import net.minecraft.util.Hand;
|
|
||||||
import net.minecraft.util.hit.BlockHitResult;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.Direction;
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自动火把服务。
|
|
||||||
*
|
|
||||||
* <p>在挖矿任务进行中,每 3 秒检测一次玩家所在位置的亮度;若光照低于阈值(8),
|
|
||||||
* 自动从背包中取火把并放置在脚下或相邻方块的顶部,防止隧道刷怪。
|
|
||||||
* 光照充足时自动跳过,已放火把的区域光照提升后不会再重复放置。</p>
|
|
||||||
*
|
|
||||||
* <p>放置通过 {@code interactBlock} 走正规网络包,单人/多人均可用。
|
|
||||||
* 火把放置的可行性由 {@link BlockState#canPlaceAt} 委托原版逻辑判定,
|
|
||||||
* 无需自行处理"方块顶部是否可承托"等复杂条件。</p>
|
|
||||||
*/
|
|
||||||
public final class AutoTorch {
|
|
||||||
|
|
||||||
/** 检查间隔(tick),约 3 秒 */
|
|
||||||
private static final int CHECK_INTERVAL = 60;
|
|
||||||
/** 低于此光照值视为需要插火把(原版刷怪上限是 0,取 8 留余量) */
|
|
||||||
private static final int DARK_THRESHOLD = 8;
|
|
||||||
|
|
||||||
private int tickCounter;
|
|
||||||
|
|
||||||
/** 每帧调用。黑暗时自动放置火把。 */
|
|
||||||
public void tick(DigConfig config) {
|
|
||||||
if (!config.autoTorch) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.tickCounter++;
|
|
||||||
if (this.tickCounter < CHECK_INTERVAL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.tickCounter = 0;
|
|
||||||
|
|
||||||
MinecraftClient client = MinecraftClient.getInstance();
|
|
||||||
if (client.player == null || client.world == null || client.interactionManager == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 仅在挖矿任务进行中生效
|
|
||||||
if (config.task == MineTask.NONE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (client.currentScreen != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PlayerEntity player = client.player;
|
|
||||||
BlockPos feet = player.getBlockPos();
|
|
||||||
|
|
||||||
// 光照充足则跳过(火把光 14,6 格内光照 >= 8)
|
|
||||||
if (client.world.getLightLevel(feet) >= DARK_THRESHOLD) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在背包中找火把
|
|
||||||
int torchSlot = -1;
|
|
||||||
for (int i = 0; i < 36; i++) {
|
|
||||||
ItemStack s = player.getInventory().main.get(i);
|
|
||||||
if (!s.isEmpty() && s.getItem() == Items.TORCH) {
|
|
||||||
torchSlot = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (torchSlot < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 找一个可放置火把的位置(先试脚下,再试四相邻)
|
|
||||||
BlockState torchState = Blocks.TORCH.getDefaultState();
|
|
||||||
BlockPos clickPos = null;
|
|
||||||
BlockPos feetDown = feet.down();
|
|
||||||
if (torchState.canPlaceAt(client.world, feet)) {
|
|
||||||
clickPos = feetDown;
|
|
||||||
}
|
|
||||||
if (clickPos == null) {
|
|
||||||
for (Direction d : new Direction[]{Direction.NORTH, Direction.SOUTH,
|
|
||||||
Direction.EAST, Direction.WEST}) {
|
|
||||||
BlockPos place = feet.offset(d);
|
|
||||||
if (torchState.canPlaceAt(client.world, place)) {
|
|
||||||
clickPos = place.down();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (clickPos == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切到火把所在槽位(主背包的先换到快捷栏,下一帧再放)
|
|
||||||
if (torchSlot >= 9) {
|
|
||||||
player.getInventory().swapSlotWithHotbar(torchSlot);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (player.getInventory().selectedSlot != torchSlot) {
|
|
||||||
player.getInventory().selectedSlot = torchSlot;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在支撑方块顶面放置火把
|
|
||||||
Vec3d hitVec = Vec3d.ofCenter(clickPos).add(0, 0.5, 0);
|
|
||||||
BlockHitResult hit = new BlockHitResult(hitVec, Direction.UP, clickPos, false);
|
|
||||||
client.interactionManager.interactBlock(client.player, Hand.MAIN_HAND, hit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"id": "autodig",
|
"id": "autodig",
|
||||||
"version": "${version}",
|
"version": "${version}",
|
||||||
"name": "AutoDig",
|
"name": "AutoDig",
|
||||||
"description": "自动挖矿模组(内置 Baritone 1.13.1,无需另行安装):主世界模式与下界模式,均可自定义目标矿物。主世界鱼骨巷道挖矿,下界自动进出传送门挖矿并支持返程。支持自动切换工具、自动进食(避开特殊食物)、自动插火把防刷怪、杀戮光环(不误伤中立生物)、保命优先、优先绕开岩浆、装备耐久保护(自动换备用装备)、背包近满提前丢垃圾防误判返程、死亡重生回点、实时状态 HUD。",
|
"description": "自动挖矿模组(内置 Baritone 1.13.1,无需另行安装):主世界模式与下界模式,均可自定义目标矿物(带不带 minecraft: 前缀均可)。主世界鱼骨巷道挖矿 + 附近目标矿物自动绕道挖掘;下界自动进出传送门挖矿并支持返程。自动切换最适用工具(远古残骸等自动换钻石镐)、自动进食(避开特殊食物)、杀戮光环(不误伤中立生物)、保命优先、优先绕开岩浆、装备耐久保护(自动换备用装备)、背包近满提前丢垃圾防误判返程、死亡重生回点、实时状态 HUD。",
|
||||||
"authors": [],
|
"authors": [],
|
||||||
"contact": {},
|
"contact": {},
|
||||||
"license": "All-Rights-Reserved",
|
"license": "All-Rights-Reserved",
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户