矿石勘探效果
基本信息
| 属性 | 值 |
|---|---|
| 来源物品 | 寻矿魔杖 |
| 触发方式 | 魔杖在玩家主物品栏内时的 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 判定顺序
- 栈为
null→NONE - 若
searchItem.getItem() instanceof ItemBlock:- 先查
ConfigHandler.blocklist→ 命中即BLOCKLISTED(黑名单优先于白名单) - 再查
ConfigHandler.allowlist→ 命中即MatchTarget.block(block, rule) - 两者都没命中 → 继续往下走材质解析(所以一个允许表 / 阻止表都没写的方块物品,仍可能因 ore 前缀被当成矿脉目标)
- 先查
- 材质解析:先
OreManager.getMaterial(searchItem);为null则遍历OrePrefixes.detectPrefix(searchItem),逐个用IOreMaterial.findMaterial(oredict.material)尝试。每个解析出的材质都要过isMaterialBlocked(对materialBlocklist做trim()+equalsIgnoreCase(material.getInternalName())比较) - 材质集非空 →
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 格 | 由三个半径推出 |