feat: 重构挖矿模式为主世界/下界,新增装备耐久保护与自定义矿物
- MineTask: DIAMOND->OVERWORLD, ANCIENT_DEBRIS->NETHER, 移除钻石/远古残骸硬编码 - DigConfig: 新增主世界/下界可选矿物列表、护甲/工具耐久保护阈值、下界返程血量阈值、目标数量 - AutoDigCommands: 支持 overworld/nether/none 任务名 - OverworldMiner: 重命名自 FishboneMiner, 支持配置矿物列表 - NetherMiner: 重命名自 DebrisHandler, 支持配置矿物、血量/耐久/背包满自动返程 - DigController: 新增 GEAR_PROTECTION 状态, 装备耐久过低暂停挖矿/触发下界返程 - AutoDigGui: 更新任务按钮、矿物预设循环、装备耐久/返程阈值设置 - ConfigStore: 扩展 sanitize 校验新字段 - 版本升级: 1.0.0 -> 2.0.0
这个提交包含在:
@@ -0,0 +1,208 @@
|
||||
package autodig.client.miner;
|
||||
|
||||
import autodig.client.bridge.BaritoneBridge;
|
||||
import autodig.client.config.DigConfig;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
/**
|
||||
* 主世界鱼骨巷道挖矿器。
|
||||
*
|
||||
* <p>在指定的 Y 层,沿玩家朝向挖一条"主干隧道",并在主干上每隔
|
||||
* {@code branchGap} 格向两侧各开一条侧支巷道,形成鱼骨状分布。
|
||||
* 推进实现为:把鱼骨图案转成一串目标坐标 {@link GoalBlock},按顺序
|
||||
* 派 Baritone 逐个前往;因为允许挖方块,Baritone 会走直线把挡路的方块
|
||||
* 挖掉,从而挖出巷道。自动切换工具由 Baritone 的 {@code autoTool} 负责。</p>
|
||||
*
|
||||
* <p>遇到背包已满时提前结束,把控制权交还控制器(用于返回地表)。</p>
|
||||
*/
|
||||
public final class OverworldMiner {
|
||||
|
||||
private final BaritoneBridge bridge;
|
||||
|
||||
private BlockPos start;
|
||||
private int yLevel;
|
||||
private List<BlockPos> plan = new ArrayList<>();
|
||||
private int step;
|
||||
/** 是否已完成 */
|
||||
private boolean done;
|
||||
/** 完成原因是否为「背包已满」 */
|
||||
private boolean full;
|
||||
/** 上一次朝向目标派发寻路的时间戳,用于失败重试节流 */
|
||||
private long lastIssue;
|
||||
/** 上一次打印诊断日志的时间戳,用于节流 */
|
||||
private long lastLog;
|
||||
|
||||
public OverworldMiner(BaritoneBridge bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以玩家当前位置为起点建立鱼骨巷道计划。
|
||||
*
|
||||
* @param config 挖矿配置
|
||||
*/
|
||||
public void init(DigConfig config) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
PlayerEntity player = client.player;
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
this.yLevel = config.overworldLevel;
|
||||
BlockPos stop = player.getBlockPos();
|
||||
this.start = new BlockPos(stop.getX(), this.yLevel, stop.getZ());
|
||||
int branchLen = 6;
|
||||
int branchGap = 8;
|
||||
// 用玩家水平朝向(而非移动方向)确定挖矿方向,站着不动时也稳定
|
||||
net.minecraft.util.math.Direction facing = player.getHorizontalFacing();
|
||||
int axis = facing.getAxis() == net.minecraft.util.math.Direction.Axis.X ? 0 : 1;
|
||||
int sign = axis == 0 ? facing.getOffsetX() : facing.getOffsetZ();
|
||||
this.plan = this.buildPlan(this.start, this.yLevel, config.tunnelLength, branchLen, branchGap, axis, sign);
|
||||
this.step = 0;
|
||||
this.done = false;
|
||||
this.full = false;
|
||||
}
|
||||
|
||||
private List<BlockPos> buildPlan(BlockPos base, int y, int trunkLen, int branchLen, int branchGap, int axis, int sign) {
|
||||
List<BlockPos> plan = new ArrayList<>();
|
||||
int dx = axis == 0 ? sign : 0;
|
||||
int dz = axis == 0 ? 0 : sign;
|
||||
// 侧向单位向量
|
||||
int sx = axis == 0 ? 0 : 1;
|
||||
int sz = axis == 0 ? 1 : 0;
|
||||
for (int t = 0; t <= trunkLen; t++) {
|
||||
BlockPos trunk = base.add(dx * t, 0, dz * t);
|
||||
plan.add(trunk);
|
||||
// 每 branchGap 格向两侧各开一条侧支(去程+回程)
|
||||
if (t > 0 && t % branchGap == 0) {
|
||||
for (int side = -1; side <= 1; side += 2) {
|
||||
for (int b = 1; b <= branchLen; b++) {
|
||||
plan.add(trunk.add(sx * b * side, 0, sz * b * side));
|
||||
}
|
||||
for (int b = branchLen - 1; b >= 1; b--) {
|
||||
plan.add(trunk.add(sx * b * side, 0, sz * b * side));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return plan;
|
||||
}
|
||||
|
||||
/**
|
||||
* 每帧推进一次。背包已满或走完全程则返回已完成。
|
||||
*
|
||||
* @param config 挖矿配置
|
||||
*/
|
||||
public boolean tick(DigConfig config) {
|
||||
if (this.done) {
|
||||
return true;
|
||||
}
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
PlayerEntity player = client.player;
|
||||
if (player == null) {
|
||||
return true;
|
||||
}
|
||||
if (this.inventoryFull(player)) {
|
||||
this.done = true;
|
||||
this.full = true;
|
||||
System.out.println("[AutoDig] overworld 结束:背包已满");
|
||||
return true;
|
||||
}
|
||||
if (this.step >= this.plan.size()) {
|
||||
this.done = true;
|
||||
System.out.println("[AutoDig] overworld 结束:计划完成 step=" + this.step + " size=" + this.plan.size());
|
||||
return true;
|
||||
}
|
||||
BlockPos target = this.plan.get(this.step);
|
||||
double dist = this.distanceSq(player, target);
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - this.lastLog > 5000) {
|
||||
this.lastLog = now;
|
||||
System.out.println("[AutoDig] overworld: step=" + this.step + "/" + this.plan.size()
|
||||
+ " player=" + player.getBlockPos() + " target=" + target
|
||||
+ " dist=" + String.format("%.1f", dist) + " active=" + this.bridge.isActive());
|
||||
}
|
||||
if (dist <= 2.0) {
|
||||
// 已到达当前目标,推进到下一个
|
||||
this.step++;
|
||||
return this.step >= this.plan.size();
|
||||
}
|
||||
// 距离太长或 Baritone 没在干活时重新派发
|
||||
if (dist > 4.0 || !this.bridge.isActive()) {
|
||||
if (now - this.lastIssue > 250) {
|
||||
this.bridge.gotoBlock(target);
|
||||
this.lastIssue = now;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private double distanceSq(PlayerEntity player, BlockPos target) {
|
||||
double dx = player.getX() - (target.getX() + 0.5);
|
||||
double dy = player.getY() - target.getY();
|
||||
double dz = player.getZ() - (target.getZ() + 0.5);
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
private boolean inventoryFull(PlayerEntity player) {
|
||||
for (int i = 0; i < 36; i++) {
|
||||
if (player.getInventory().main.get(i).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 主背包全满(含快捷栏),无空位可存放
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return 是否已挖完 */
|
||||
public boolean isDone() {
|
||||
return this.done;
|
||||
}
|
||||
|
||||
/** @return 完成原因是否为「背包已满」 */
|
||||
public boolean isFull() {
|
||||
return this.full;
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复挖掘(死亡重生/保命恢复后复用):已有未完成计划时复用原计划,
|
||||
* 把指针回退到离玩家最近的目标点;否则按当前位置重新建立计划。
|
||||
*
|
||||
* @param config 挖矿配置
|
||||
*/
|
||||
public void continueOrInit(DigConfig config) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
PlayerEntity player = client.player;
|
||||
if (this.plan.isEmpty() || this.done || player == null) {
|
||||
this.init(config);
|
||||
return;
|
||||
}
|
||||
int from = Math.max(0, this.step - 8);
|
||||
int to = Math.min(this.plan.size() - 1, this.step + 8);
|
||||
int best = this.step;
|
||||
double bestDist = Double.MAX_VALUE;
|
||||
for (int i = from; i <= to; i++) {
|
||||
double d = this.distanceSq(player, this.plan.get(i));
|
||||
if (d < bestDist) {
|
||||
bestDist = d;
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
this.step = best;
|
||||
this.done = false;
|
||||
this.full = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 派发矿脉挖掘指令(用于在鱼骨巷道推进过程中遇到目标矿物时挖掉)。
|
||||
* 由控制器在合适时机调用。
|
||||
*/
|
||||
public void mineOres(DigConfig config) {
|
||||
if (!config.getOverworldOreList().isEmpty()) {
|
||||
this.bridge.mineByName(-1, config.getOverworldOreList().toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户