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,247 @@
|
||||
package autodig.client.miner;
|
||||
|
||||
import autodig.client.bridge.BaritoneBridge;
|
||||
import autodig.client.config.DigConfig;
|
||||
import java.util.List;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* 下界自动挖矿流转。
|
||||
*
|
||||
* <p>流程:主世界 -> 走进最近传送门 -> 进入下界 -> 挖可配置矿物 ->
|
||||
* (挖够数量/背包满/血量低/装备耐久低)-> 找下界传送门走回主世界。
|
||||
* 死亡重生的返回逻辑由控制器统一处理,本类每次 tick 依据当前所在维度
|
||||
* 自适应地执行对应阶段。</p>
|
||||
*
|
||||
* <p>下界无死亡不掉落,比主世界更"惜命":血量低或装备耐久低时主动触发返程。</p>
|
||||
*/
|
||||
public final class NetherMiner {
|
||||
|
||||
private final BaritoneBridge bridge;
|
||||
|
||||
private enum Stage {
|
||||
/** 在下界从传送门前往主世界(或已在主世界即完成) */
|
||||
GO_COMMIT,
|
||||
/** 在前一个维度前往传送门 */
|
||||
GO_PORTAL,
|
||||
/** 挖矿 */
|
||||
MINE,
|
||||
/** 返回主世界 */
|
||||
RETURN_PORTAL
|
||||
}
|
||||
|
||||
private Stage stage = Stage.GO_COMMIT;
|
||||
private boolean done;
|
||||
/** 结束原因是否为「背包已满」 */
|
||||
private boolean full;
|
||||
/** 结束原因是否为「血量过低触发返程」 */
|
||||
private boolean fled;
|
||||
/** 结束原因是否为「装备耐久过低触发返程」 */
|
||||
private boolean gearBroken;
|
||||
/** 是否已派发过挖矿指令(用于区分「尚未开始」和「已经挖完」) */
|
||||
private boolean miningStarted;
|
||||
/** 派发节流 */
|
||||
private long lastIssue;
|
||||
/** 已挖到的目标矿物数量(通过扫描背包近似) */
|
||||
private int minedCount;
|
||||
|
||||
public NetherMiner(BaritoneBridge bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
|
||||
/** 任务结束或需要切换任务时归还控制权。 */
|
||||
public void reset() {
|
||||
this.stage = Stage.GO_COMMIT;
|
||||
this.done = false;
|
||||
this.full = false;
|
||||
this.fled = false;
|
||||
this.gearBroken = false;
|
||||
this.miningStarted = false;
|
||||
this.minedCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 每帧推进下界挖矿流转。
|
||||
*
|
||||
* @param config 挖矿配置
|
||||
* @return 是否已全部完成(含回到主世界)
|
||||
*/
|
||||
public boolean tick(DigConfig config) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client.player == null || client.world == null) {
|
||||
return this.done;
|
||||
}
|
||||
PlayerEntity player = client.player;
|
||||
RegistryKey<World> dim = client.world.getRegistryKey();
|
||||
boolean inOverworld = dim == World.OVERWORLD;
|
||||
boolean inNether = dim == World.NETHER;
|
||||
|
||||
// 下界惜命:血量过低时触发返程
|
||||
if (inNether && config.netherFleeHealthThreshold > 0
|
||||
&& this.healthPercent(player) < config.netherFleeHealthThreshold) {
|
||||
this.fled = true;
|
||||
this.miningStarted = false;
|
||||
this.bridge.cancelAll();
|
||||
if (config.netherAutoReturn) {
|
||||
this.stage = Stage.RETURN_PORTAL;
|
||||
} else {
|
||||
this.done = true;
|
||||
}
|
||||
return this.done;
|
||||
}
|
||||
|
||||
switch (this.stage) {
|
||||
case GO_COMMIT -> {
|
||||
// 选择去哪个维度
|
||||
if (inNether) {
|
||||
this.stage = Stage.MINE;
|
||||
} else {
|
||||
this.stage = Stage.GO_PORTAL;
|
||||
}
|
||||
}
|
||||
case GO_PORTAL -> {
|
||||
if (inNether) {
|
||||
this.stage = Stage.MINE; // 已通过传送门进入下界
|
||||
} else {
|
||||
this.issue(() -> this.bridge.toNetherPortal());
|
||||
}
|
||||
}
|
||||
case MINE -> {
|
||||
if (!inNether) {
|
||||
// 意外回到主世界,重走流程
|
||||
this.stage = Stage.GO_COMMIT;
|
||||
this.miningStarted = false;
|
||||
} else if (this.inventoryFull(player)) {
|
||||
// 背包已满:期望回程则去传送门;不回程时无处可放,直接结束
|
||||
this.full = true;
|
||||
this.miningStarted = false;
|
||||
if (config.netherAutoReturn) {
|
||||
this.stage = Stage.RETURN_PORTAL;
|
||||
} else {
|
||||
this.done = true;
|
||||
}
|
||||
} else if (this.minedEnough(config)) {
|
||||
// 已挖够目标数量
|
||||
this.miningStarted = false;
|
||||
if (config.netherAutoReturn) {
|
||||
this.stage = Stage.RETURN_PORTAL;
|
||||
} else {
|
||||
this.done = true;
|
||||
}
|
||||
} else if (this.miningStarted && !this.bridge.isActive()) {
|
||||
// 挖矿进程已跑完(附近无矿可挖)
|
||||
this.miningStarted = false;
|
||||
if (config.netherAutoReturn) {
|
||||
this.stage = Stage.RETURN_PORTAL;
|
||||
} else {
|
||||
this.stage = Stage.GO_COMMIT; // 停在下界继续挖(不返回)
|
||||
}
|
||||
} else {
|
||||
// 先派发挖矿指令;派发成功后 miningStarted 置位,
|
||||
// 之后才能用 isActive()==false 判定「挖完了」
|
||||
this.issue(() -> {
|
||||
List<String> ores = config.getNetherOreList();
|
||||
if (!ores.isEmpty()) {
|
||||
int count = config.netherTargetCount > 0 ? config.netherTargetCount : -1;
|
||||
this.bridge.mineByName(count, ores.toArray(String[]::new));
|
||||
this.miningStarted = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
case RETURN_PORTAL -> {
|
||||
if (inOverworld) {
|
||||
this.done = true; // 已回到主世界,全流程完成
|
||||
} else {
|
||||
this.issue(() -> this.bridge.toNetherPortal());
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.done;
|
||||
}
|
||||
|
||||
/** 判断是否已挖够目标矿物数量 */
|
||||
private boolean minedEnough(DigConfig config) {
|
||||
if (config.netherTargetCount <= 0) {
|
||||
return false; // -1 或 0 表示无限挖
|
||||
}
|
||||
// 通过扫描背包中目标矿物数量近似判断
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client.player == null) {
|
||||
return false;
|
||||
}
|
||||
int count = 0;
|
||||
var ores = config.getNetherOreList();
|
||||
for (int i = 0; i < 36; i++) {
|
||||
var stack = client.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.minedCount = count;
|
||||
return count >= config.netherTargetCount;
|
||||
}
|
||||
|
||||
private void issue(Runnable action) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - this.lastIssue > 1000) {
|
||||
action.run();
|
||||
this.lastIssue = now;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean inventoryFull(PlayerEntity player) {
|
||||
for (int i = 0; i < 36; i++) {
|
||||
if (player.getInventory().main.get(i).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private double healthPercent(PlayerEntity player) {
|
||||
return player.getHealth() / Math.max(player.getMaxHealth(), 1.0) * 100.0;
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
return this.done;
|
||||
}
|
||||
|
||||
/** @return 结束原因是否为「背包已满」 */
|
||||
public boolean isFull() {
|
||||
return this.full;
|
||||
}
|
||||
|
||||
/** @return 结束原因是否为「血量过低触发返程」 */
|
||||
public boolean isFled() {
|
||||
return this.fled;
|
||||
}
|
||||
|
||||
/** @return 结束原因是否为「装备耐久过低触发返程」 */
|
||||
public boolean isGearBroken() {
|
||||
return this.gearBroken;
|
||||
}
|
||||
|
||||
/** 标记装备耐久过低,触发返程 */
|
||||
public void markGearBroken() {
|
||||
this.gearBroken = true;
|
||||
this.miningStarted = false;
|
||||
}
|
||||
|
||||
/** @return 已挖到的目标矿物数量 */
|
||||
public int getMinedCount() {
|
||||
return this.minedCount;
|
||||
}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户