feat(camping): three-tier ore detector with private-chest faraday cage
New basic/improved/advanced detectors (8/16/32 block range). Aim and right-click to ping the nearest ore on the view ray; pitch-coded sound and action-bar text show distance and ore type. Any ore within 4 blocks of a silverlabs:private_chest is hidden — chests act as faraday cages so claimed bases stay private from neighbours' detectors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"format_version": "1.21.0",
|
||||
"minecraft:item": {
|
||||
"description": {
|
||||
"identifier": "silverlabs:ore_detector_advanced",
|
||||
"menu_category": {
|
||||
"category": "equipment",
|
||||
"group": "itemGroup.name.miscellaneous"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:max_stack_size": 1,
|
||||
"minecraft:icon": "ore_detector_advanced",
|
||||
"minecraft:hand_equipped": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"format_version": "1.21.0",
|
||||
"minecraft:item": {
|
||||
"description": {
|
||||
"identifier": "silverlabs:ore_detector_basic",
|
||||
"menu_category": {
|
||||
"category": "equipment",
|
||||
"group": "itemGroup.name.miscellaneous"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:max_stack_size": 1,
|
||||
"minecraft:icon": "ore_detector_basic",
|
||||
"minecraft:hand_equipped": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"format_version": "1.21.0",
|
||||
"minecraft:item": {
|
||||
"description": {
|
||||
"identifier": "silverlabs:ore_detector_improved",
|
||||
"menu_category": {
|
||||
"category": "equipment",
|
||||
"group": "itemGroup.name.miscellaneous"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:max_stack_size": 1,
|
||||
"minecraft:icon": "ore_detector_improved",
|
||||
"minecraft:hand_equipped": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"format_version": "1.21.0",
|
||||
"minecraft:recipe_shapeless": {
|
||||
"description": {
|
||||
"identifier": "silverlabs:ore_detector_advanced_recipe"
|
||||
},
|
||||
"tags": [
|
||||
"crafting_table"
|
||||
],
|
||||
"unlock": [
|
||||
{
|
||||
"item": "silverlabs:ore_detector_improved"
|
||||
}
|
||||
],
|
||||
"ingredients": [
|
||||
{ "item": "silverlabs:ore_detector_improved" },
|
||||
{ "item": "minecraft:gold_ingot" },
|
||||
{ "item": "minecraft:gold_ingot" },
|
||||
{ "item": "minecraft:amethyst_shard" },
|
||||
{ "item": "minecraft:amethyst_shard" }
|
||||
],
|
||||
"result": {
|
||||
"item": "silverlabs:ore_detector_advanced",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"format_version": "1.21.0",
|
||||
"minecraft:recipe_shaped": {
|
||||
"description": {
|
||||
"identifier": "silverlabs:ore_detector_basic_recipe"
|
||||
},
|
||||
"tags": [
|
||||
"crafting_table"
|
||||
],
|
||||
"unlock": [
|
||||
{
|
||||
"item": "minecraft:redstone"
|
||||
}
|
||||
],
|
||||
"pattern": [
|
||||
" R ",
|
||||
"CSC",
|
||||
" C "
|
||||
],
|
||||
"key": {
|
||||
"R": {
|
||||
"item": "minecraft:redstone"
|
||||
},
|
||||
"C": {
|
||||
"item": "minecraft:cobblestone"
|
||||
},
|
||||
"S": {
|
||||
"item": "minecraft:stick"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"item": "silverlabs:ore_detector_basic",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"format_version": "1.21.0",
|
||||
"minecraft:recipe_shapeless": {
|
||||
"description": {
|
||||
"identifier": "silverlabs:ore_detector_improved_recipe"
|
||||
},
|
||||
"tags": [
|
||||
"crafting_table"
|
||||
],
|
||||
"unlock": [
|
||||
{
|
||||
"item": "silverlabs:ore_detector_basic"
|
||||
}
|
||||
],
|
||||
"ingredients": [
|
||||
{ "item": "silverlabs:ore_detector_basic" },
|
||||
{ "item": "minecraft:iron_ingot" },
|
||||
{ "item": "minecraft:iron_ingot" },
|
||||
{ "item": "minecraft:iron_ingot" },
|
||||
{ "item": "minecraft:iron_ingot" },
|
||||
{ "item": "minecraft:redstone" }
|
||||
],
|
||||
"result": {
|
||||
"item": "silverlabs:ore_detector_improved",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -703,6 +703,116 @@ function dismantleHammockAt(loc, dimId, player) {
|
||||
if (player) player.sendMessage("§7[Camping] Hammock taken down.");
|
||||
}
|
||||
|
||||
// ─── Ore Detector ───────────────────────────────────────────
|
||||
// Three tiered items share one scan handler. Tier → max scan range.
|
||||
// Faraday: any ore within 4 blocks of a silverlabs:private_chest is hidden
|
||||
// (mirrors the source DetectOre faraday-cage mechanic, but built into private chests).
|
||||
const DETECTOR_RANGES = {
|
||||
"silverlabs:ore_detector_basic": 8,
|
||||
"silverlabs:ore_detector_improved": 16,
|
||||
"silverlabs:ore_detector_advanced": 32,
|
||||
};
|
||||
const PRIVATE_CHEST_ID = "silverlabs:private_chest";
|
||||
const FARADAY_RADIUS = 4; // 9³ cube around each ore candidate
|
||||
|
||||
const ORE_IDS = new Set([
|
||||
"minecraft:coal_ore", "minecraft:deepslate_coal_ore",
|
||||
"minecraft:iron_ore", "minecraft:deepslate_iron_ore",
|
||||
"minecraft:copper_ore", "minecraft:deepslate_copper_ore",
|
||||
"minecraft:gold_ore", "minecraft:deepslate_gold_ore", "minecraft:nether_gold_ore",
|
||||
"minecraft:redstone_ore", "minecraft:deepslate_redstone_ore",
|
||||
"minecraft:lit_redstone_ore", "minecraft:lit_deepslate_redstone_ore",
|
||||
"minecraft:lapis_ore", "minecraft:deepslate_lapis_ore",
|
||||
"minecraft:emerald_ore", "minecraft:deepslate_emerald_ore",
|
||||
"minecraft:diamond_ore", "minecraft:deepslate_diamond_ore",
|
||||
"minecraft:nether_quartz_ore",
|
||||
"minecraft:ancient_debris",
|
||||
]);
|
||||
|
||||
function prettyOreName(typeId) {
|
||||
const bare = typeId.replace(/^minecraft:/, "")
|
||||
.replace(/^lit_/, "")
|
||||
.replace(/^deepslate_/, "Deepslate ")
|
||||
.replace(/^nether_/, "Nether ");
|
||||
return bare.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
||||
}
|
||||
|
||||
function isPrivateChestNearby(dim, cx, cy, cz) {
|
||||
for (let dx = -FARADAY_RADIUS; dx <= FARADAY_RADIUS; dx++) {
|
||||
for (let dy = -FARADAY_RADIUS; dy <= FARADAY_RADIUS; dy++) {
|
||||
for (let dz = -FARADAY_RADIUS; dz <= FARADAY_RADIUS; dz++) {
|
||||
const b = dim.getBlock({ x: cx + dx, y: cy + dy, z: cz + dz });
|
||||
if (b && b.typeId === PRIVATE_CHEST_ID) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function runOreScan(player, range) {
|
||||
const dim = player.dimension;
|
||||
const head = player.getHeadLocation();
|
||||
const dir = player.getViewDirection();
|
||||
|
||||
// Walk integer-block steps along the view ray. Resolution 0.5 blocks
|
||||
// catches ores the cardinal-aligned steps would skip on diagonals.
|
||||
const stepCount = Math.floor(range * 2);
|
||||
const seen = new Set();
|
||||
let foundOre = null;
|
||||
let foundDist = 0;
|
||||
|
||||
for (let i = 1; i <= stepCount; i++) {
|
||||
const t = i * 0.5;
|
||||
const px = head.x + dir.x * t;
|
||||
const py = head.y + dir.y * t;
|
||||
const pz = head.z + dir.z * t;
|
||||
const bx = Math.floor(px);
|
||||
const by = Math.floor(py);
|
||||
const bz = Math.floor(pz);
|
||||
const key = `${bx},${by},${bz}`;
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
|
||||
let block;
|
||||
try { block = dim.getBlock({ x: bx, y: by, z: bz }); } catch (_) { continue; }
|
||||
if (!block) continue;
|
||||
|
||||
if (ORE_IDS.has(block.typeId)) {
|
||||
if (isPrivateChestNearby(dim, bx, by, bz)) continue; // faraday
|
||||
foundOre = block.typeId;
|
||||
foundDist = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundOre) {
|
||||
const distRounded = Math.round(foundDist * 10) / 10;
|
||||
const pitch = Math.max(0.8, Math.min(2.0, 2.0 - (foundDist / range)));
|
||||
try {
|
||||
player.playSound("random.orb", { pitch, volume: 0.7 });
|
||||
} catch (_) {}
|
||||
try {
|
||||
player.onScreenDisplay.setActionBar(`§a● §f${prettyOreName(foundOre)} §7at §b${distRounded}m`);
|
||||
} catch (_) {}
|
||||
} else {
|
||||
try {
|
||||
player.playSound("note.bass", { pitch: 0.7, volume: 0.5 });
|
||||
} catch (_) {}
|
||||
try {
|
||||
player.onScreenDisplay.setActionBar(`§7○ No ores within §f${range}m`);
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
world.afterEvents.itemUse.subscribe((event) => {
|
||||
const player = event.source;
|
||||
const stack = event.itemStack;
|
||||
if (!stack || !player) return;
|
||||
const range = DETECTOR_RANGES[stack.typeId];
|
||||
if (!range) return;
|
||||
system.run(() => runOreScan(player, range));
|
||||
});
|
||||
|
||||
// ─── Boot ───────────────────────────────────────────────────
|
||||
system.run(() => {
|
||||
loadState();
|
||||
|
||||
Reference in New Issue
Block a user