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.");
|
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 ───────────────────────────────────────────────────
|
// ─── Boot ───────────────────────────────────────────────────
|
||||||
system.run(() => {
|
system.run(() => {
|
||||||
loadState();
|
loadState();
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
item.silverlabs:tent.name=Tent
|
item.silverlabs:tent.name=Tent
|
||||||
item.silverlabs:hammock.name=Hammock
|
item.silverlabs:hammock.name=Hammock
|
||||||
|
item.silverlabs:ore_detector_basic.name=Ore Detector
|
||||||
|
item.silverlabs:ore_detector_improved.name=Ore Detector (Improved)
|
||||||
|
item.silverlabs:ore_detector_advanced.name=Ore Detector (Advanced)
|
||||||
tile.silverlabs:tent_canvas.name=Tent Canvas
|
tile.silverlabs:tent_canvas.name=Tent Canvas
|
||||||
tile.silverlabs:hammock_cloth.name=Hammock Cloth
|
tile.silverlabs:hammock_cloth.name=Hammock Cloth
|
||||||
|
|||||||
@@ -7,6 +7,15 @@
|
|||||||
},
|
},
|
||||||
"hammock_item": {
|
"hammock_item": {
|
||||||
"textures": "textures/items/hammock"
|
"textures": "textures/items/hammock"
|
||||||
|
},
|
||||||
|
"ore_detector_basic": {
|
||||||
|
"textures": "textures/items/ore_detector_basic"
|
||||||
|
},
|
||||||
|
"ore_detector_improved": {
|
||||||
|
"textures": "textures/items/ore_detector_improved"
|
||||||
|
},
|
||||||
|
"ore_detector_advanced": {
|
||||||
|
"textures": "textures/items/ore_detector_advanced"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 228 B |
Binary file not shown.
|
After Width: | Height: | Size: 227 B |
Binary file not shown.
|
After Width: | Height: | Size: 227 B |
Reference in New Issue
Block a user