矿石勘探效果

基本信息

属性 值
来源物品 寻矿魔杖
触发方式 魔杖在玩家主物品栏内时的 onUpdate,每秒一次(服务端)
结果 1 魔杖 damage = 命中方块数(0 ~ 10),驱动贴图与耐久条
结果 2 命中矿石时,把匹配材质的矿脉推给 VisualProspecting
实现类 com.encraft.dz.items.OreFinderSearch(搜索与匹配)、ItemOreFinderTool.onUpdate(调度)、OreFinderPlayerData(去重缓存)

扫描节流与去重

机制 判定 来源
每秒 1 次 world.getTotalWorldTime() % 20 == entity.hashCode() % 20,否则 return ItemOreFinderTool.java:105
按玩家错开 tick 比较 entity.hashCode() % 20,使不同玩家的扫描不落在同一 tick 同上(源码注释)
仅主魔杖扫描 itemstack != findFirstInInventory(player.inventory) 时只抄 damage 并 return ItemOreFinderTool.java:126-130
同位置同筛选物跳过 props.isSameScan(cur_x, cur_y, cur_z, searchItem):比较 hasLastScan + 三个坐标 + ItemStack.areItemStacksEqual(lastScanFilter, filter) OreFinderPlayerData.java:39-44
记录扫描 props.rememberScan(x, y, z, filter)(lastScanFilter 存 filter.copy()) OreFinderPlayerData.java:46-52
筛选物失效 !target.canSearch() → damage 归 0 并 clearLastScan() ItemOreFinderTool.java:136-140

扫描范围

中心是玩家所在方块 (floor(posX), floor(posY), floor(posZ))。

轴 边界计算 边长
X minX = cx - xzAreaRadius,maxX = cx + xzAreaRadius + 1(闭区间) 默认 5 → 11 格
Z minZ / maxZ 同 X 默认 5 → 11 格
Y minY = cy - yAreaRadius,maxY = cy + yAreaRadius + 1(闭区间) 默认 60 → 121 格

遍历顺序 z → x → y;每个 (x, z) 先 world.getChunkFromBlockCoords(x, z) 取区块,再用 x & 15 / z & 15 转局部坐标。

  • Y 越界视为空气:(y >= 0 && y < 256) ? chunk.getBlock(lx, y, lz) : Blocks.air,取到的方块是 Blocks.air 时直接 continue。
  • 提前结束:found >= MAX_FOUND(10)立刻 return new AreaScan(found, oreMaterial),不再继续扫。
  • AreaScan 是 record AreaScan(int found, IOreMaterial oreMaterial)(源码用 @Desugar 降级到 Java 8)。

匹配规则(4 种 MatchTarget,穷举)

OreFinderSearch.MatchTarget 内部 Kind 枚举共 4 个值:

Kind canSearch() isBlocklisted() 界面表现 触发条件
NONE false false 名称行显示"空",不画搜索行 筛选格为 null;或 resolveMatch 什么都没解析出来
BLOCKLISTED false true 顶部"黑名单方块!"+ 搜索行用 0xAA0000 ItemBlock 命中 ConfigHandler.blocklist;或解析出材质但全被 materialBlocklist 拦掉
BLOCK true false 搜索行显示该方块显示名 ItemBlock 命中 ConfigHandler.allowlist
MATERIALS true false 搜索行显示材质本地化名(多个用 , 连接) GT 解析出 1 个以上 IOreMaterial 且未被 materialBlocklist 拦掉

getInternalName() 对应:BLOCK → modid:block(metadata 非通配时追加 :meta);MATERIALS → 材质内部名 , 连接;BLOCKLISTED → 字面量 "block-listed";NONE → 字面量 "no match"。

resolveMatch 判定顺序

  1. 栈为 null → NONE
  2. 若 searchItem.getItem() instanceof ItemBlock:
    1. 先查 ConfigHandler.blocklist → 命中即 BLOCKLISTED(黑名单优先于白名单)
    2. 再查 ConfigHandler.allowlist → 命中即 MatchTarget.block(block, rule)
    3. 两者都没命中 → 继续往下走材质解析(所以一个允许表 / 阻止表都没写的方块物品,仍可能因 ore 前缀被当成矿脉目标)
  3. 材质解析:先 OreManager.getMaterial(searchItem);为 null 则遍历 OrePrefixes.detectPrefix(searchItem),逐个用 IOreMaterial.findMaterial(oredict.material) 尝试。每个解析出的材质都要过 isMaterialBlocked(对 materialBlocklist 做 trim() + equalsIgnoreCase(material.getInternalName()) 比较)
  4. 材质集非空 → MatchTarget.materials(...);否则 resolved ? BLOCKLISTED : NONE

第 4 步的 resolved 标志表示"认得出这是什么、只是被材质黑名单全拦了",此时归为 BLOCKLISTED(界面给警告);连是什么都认不出才是 NONE(放不进筛选格)。

矿石命中判定

白名单之外的分支走 matchedOre(block, meta, materialNames):

info != null && info.isNatural && !info.isSmall
        && materialNames.contains(info.material.getInternalName())

即只认 isNatural 为真且 isSmall 为假的天然矿脉方块(GT 判定的"天然矿"、且非小矿脉),并按材质内部名比对。allowBlock != null 时则改用"精确方块 + metadata(通配则忽略 metadata)"的判定,两条分支互斥。

矿脉揭示(VisualProspecting 推送)

仅当 scan.oreMaterial() != null(即走的是 MATERIALS 分支且至少命中 1 块)时执行 prospectForVeins:

步骤 调用
1. 取半径内矿脉 VisualProspecting_API.LogicalServer.prospectOreVeinsWithinRadius(world.provider.dimensionId, x1, z1, ConfigHandler.oreVeinDiscoveryRange)
2. 过滤 .filter(it -> it.veinType.containsOre(ore)) —— 只保留含本次实际命中的那个材质的矿脉
3. 推送 VisualProspecting_API.LogicalServer.sendProspectingResultsToClient(player, veins, Collections.emptyList())(第二个参数的玩家位置矿脉列表传空)

注意:面积扫描以 (cur_x, cur_z) 为中心,矿脉揭示同样只用 X/Z 平面,不看 Y;揭示半径取自配置 oreVeinDiscoveryRange(默认 24,方形)。

白名单方块(Kind.BLOCK)分支不会触发矿脉揭示 —— 因为此时 oreMaterial 始终为 null。

数值

数值 值 来源
MAX_FOUND 10 OreFinderSearch.java:29,既是伤害计数上限,也是耐久条满格刻度
ANY_META −1(通配) OreFinderSearch.java:31
NO_MATCH −2 OreFinderSearch.java:32
X/Z 半径 默认 5,可配 1 ~ 16 配置 X Z Area radius
Y 半径 默认 60,可配 1 ~ 60 配置 Y Area radius
矿脉揭示半径 默认 24,可配 xzAreaRadius ~ 64 配置 Ore vein discovery range
扫描节流 20 tick getTotalWorldTime() % 20
音效触发阈值 命中数 ≥ 5 foundOres >= MAX_FOUND / 2
音效 ic2:tools.Treetap,音量 0.6F,音调 0.8F,broadcastToNearby = true ItemOreFinderTool.java:113
耐久条曲线 GTUtility.linearCurve(damage, 0, 1d, MAX_FOUND, 0d) → 1.0 降到 0.0 getDurabilityForDisplay
扫描体积(默认) 11 × 121 × 11 = 14 641 格 由三个半径推出

相关条目