v1.2.2: 寻路不再带玩家下地下矿坑——ChunkGoal增加高度约束,目标为该区块内经排除树叶的地表最高处,停在矿坑不算到达,不搭方块到树上
这个提交包含在:
@@ -15,6 +15,9 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.world.Heightmap;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Baritone API 封装(桥接层)。
|
* Baritone API 封装(桥接层)。
|
||||||
@@ -78,11 +81,39 @@ public final class BaritoneBridge {
|
|||||||
if (baritone == null) {
|
if (baritone == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 使用自定义 ChunkGoal:完成条件为“玩家脚下方块落在目标区块内”,
|
// 使用自定义 ChunkGoal:完成条件为“玩家脚下方块落在目标区块内地表”,
|
||||||
// 与控制器 playerChunk.equals(target) 的判定一致。避免 Baritone 自带
|
// 与控制器 playerChunk.equals(target) 的判定一致,并对高度施加约束,
|
||||||
// GoalXZ(容差 0,精确到区块中心单格)在中心不可达时停在相邻区块,
|
// 避免 Baritone 把终点停在地下洞穴/矿坑里。避免 Baritone 自带 GoalXZ
|
||||||
// 造成目标区块错位与反复寻路。
|
//(容差 0,精确到区块中心单格)在中心不可达时停在相邻区块,造成目标区块错位。
|
||||||
baritone.getCustomGoalProcess().setGoalAndPath(new ChunkGoal(chunk));
|
baritone.getCustomGoalProcess().setGoalAndPath(new ChunkGoal(chunk, this.surfaceY(chunk)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算目标区块内地表高度(方块 Y 上限):
|
||||||
|
* 遍历该区块 16×16 各列,取“排除树叶的实体地表”的最高值,作为高度约束目标。
|
||||||
|
* 区块尚未加载时各列取到 0,max 仍取已加载列;若整块均未加载则返回 0(不约束高度)。
|
||||||
|
*
|
||||||
|
* @param chunk 目标区块
|
||||||
|
* @return 地表最高 Y;0 表示暂无法确定(不施加高度约束)
|
||||||
|
*/
|
||||||
|
private int surfaceY(ChunkPos chunk) {
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
if (client == null || client.world == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
World world = client.world;
|
||||||
|
int x0 = chunk.x() * 16;
|
||||||
|
int z0 = chunk.z() * 16;
|
||||||
|
int maxY = 0;
|
||||||
|
for (int bx = x0; bx < x0 + 16; ++bx) {
|
||||||
|
for (int bz = z0; bz < z0 + 16; ++bz) {
|
||||||
|
int top = world.getTopY(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, bx, bz);
|
||||||
|
if (top > maxY) {
|
||||||
|
maxY = top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 强制取消当前寻路任务。 */
|
/** 强制取消当前寻路任务。 */
|
||||||
|
|||||||
@@ -7,16 +7,16 @@ import autostore.client.territory.ChunkPos;
|
|||||||
import baritone.api.pathing.goals.Goal;
|
import baritone.api.pathing.goals.Goal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 以“到达目标区块内”为完成条件的寻路目标。
|
* 以“到达目标区块内地表”为完成条件的寻路目标。
|
||||||
*
|
*
|
||||||
* <p>修复“目标区块错位”问题:Baritone 自带的 {@code GoalXZ} 容差为 0,
|
* <p>在二维“到达区块内”的基础上增加高度约束:目标为该区块内地表高度最高处
|
||||||
* 会要求玩家精确站在区块中心的单个方块上(如 {@code x*16+8, z*16+8})。
|
* (用排除树叶的实体地表计算,避免把目标定到树冠上)。只要玩家脚下方块落在
|
||||||
* 一旦该方块不可达(被树、石头、水、建筑等占据),玩家会被停在区块边缘甚至
|
* 目标区块内、且高度不低于目标地表,即视为到达。</p>
|
||||||
* 相邻区块,导致玩家所在区块与目标区块错位、反复寻路。</p>
|
|
||||||
*
|
*
|
||||||
* <p>本目标把完成条件放宽为“玩家脚下方块落在目标区块内”,与控制器中
|
* <p>修复“寻路带玩家下到地下矿坑”:旧实现只判断 x/z 是否落在区块内、不限制 y,
|
||||||
* {@code playerChunk.equals(target)} 的判定完全一致,从而彻底消除
|
* 于是玩家停留在区块内的地下洞穴也算“到达”,对后续开垦很麻烦。新增高度下限后,
|
||||||
* 中心点不可达带来的错位。</p>
|
* Baritone 会把终点引导到目标区块的地表可达处;若留在地下,y 远低于目标地表,
|
||||||
|
* 不会被判定为到达。</p>
|
||||||
*/
|
*/
|
||||||
public final class ChunkGoal implements Goal {
|
public final class ChunkGoal implements Goal {
|
||||||
|
|
||||||
@@ -28,25 +28,46 @@ public final class ChunkGoal implements Goal {
|
|||||||
private final int centerX;
|
private final int centerX;
|
||||||
/** 目标区块中心的方块 Z 坐标(用于启发式距离估算) */
|
/** 目标区块中心的方块 Z 坐标(用于启发式距离估算) */
|
||||||
private final int centerZ;
|
private final int centerZ;
|
||||||
|
/**
|
||||||
|
* 目标地表高度(方块 Y):目标区块内 16×16 各列 “排除树叶的实体地表” 的最高值。
|
||||||
|
* ≤0 表示不施加高度约束(保留旧的纯二维行为)。
|
||||||
|
*/
|
||||||
|
private final int targetY;
|
||||||
|
/** 高度容差:脚下方块高于目标地表 2 格内都视为到达,容忍从最高点轻微回落 */
|
||||||
|
private static final int Y_TOLERANCE = 2;
|
||||||
|
|
||||||
public ChunkGoal(ChunkPos chunk) {
|
public ChunkGoal(ChunkPos chunk) {
|
||||||
|
this(chunk, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkGoal(ChunkPos chunk, int targetY) {
|
||||||
this.targetX = chunk.x();
|
this.targetX = chunk.x();
|
||||||
this.targetZ = chunk.z();
|
this.targetZ = chunk.z();
|
||||||
this.centerX = chunk.centerBlockX();
|
this.centerX = chunk.centerBlockX();
|
||||||
this.centerZ = chunk.centerBlockZ();
|
this.centerZ = chunk.centerBlockZ();
|
||||||
|
this.targetY = targetY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 判断玩家脚下方块是否落在目标区块内 */
|
/** 判断玩家脚下方块是否落在目标区块内,且(设置高度约束时)不深于地表 - 容差 */
|
||||||
@Override
|
@Override
|
||||||
public boolean isInGoal(int x, int y, int z) {
|
public boolean isInGoal(int x, int y, int z) {
|
||||||
return Math.floorDiv(x, 16) == this.targetX && Math.floorDiv(z, 16) == this.targetZ;
|
if (Math.floorDiv(x, 16) != this.targetX || Math.floorDiv(z, 16) != this.targetZ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 高度约束:地下矿坑的 y 远低于目标地表,不会被误判为“已到达”
|
||||||
|
return this.targetY <= 0 || y >= this.targetY - Y_TOLERANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 启发式:到目标区块中心的水平欧几里得距离(可采纳,保证寻路效率) */
|
/** 启发式:到目标区块中心的水平欧几里得距离;未达地表时叠加向上的加权引导 */
|
||||||
@Override
|
@Override
|
||||||
public double heuristic(int x, int y, int z) {
|
public double heuristic(int x, int y, int z) {
|
||||||
double dx = x - this.centerX;
|
double dx = x - this.centerX;
|
||||||
double dz = z - this.centerZ;
|
double dz = z - this.centerZ;
|
||||||
return Math.sqrt(dx * dx + dz * dz);
|
double h = Math.sqrt(dx * dx + dz * dz);
|
||||||
|
if (this.targetY > 0 && y < this.targetY - Y_TOLERANCE) {
|
||||||
|
// 垂直引导:还在地表以下时,越深加权越高,促使 Baritone 优先走地表而非洞穴
|
||||||
|
h += (this.targetY - y) * 0.25;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
在新工单中引用
屏蔽一个用户