Ore scanner Raw

This renders a minimap showing nearby ores using the overlay glasses and block scanner.

We start the program by specifying a series of configuration options. Feel free to ignore these, and use the values inline. Whilst you don’t strictly speaking need a delay between each iteration, it does reduce the impact on the server.

local scanInterval = 0.2
local renderInterval = 0.05
local scannerRange = 8
local scannerWidth = scannerRange * 2 + 1

These values aren’t very exciting, they just control what the minimap looks like

local size = 0.5
local cellSize = 16
local offsetX = 75
local offsetY = 75

We end our configuration section by defining the ores we’re interested in and what colour we’ll draw them as. We define some ores as having a higher priority, so large ore veins don’t mask smaller veins of more precious ores.

local ores = {
	["minecraft:diamond_ore"] = 10,
	["minecraft:emerald_ore"] = 10,
	["minecraft:gold_ore"] = 8,
	["minecraft:redstone_ore"] = 5,
	["minecraft:lapis_ore"] = 5,
	["minecraft:iron_ore"] = 2,
	["minecraft:coal_ore"] = 1
}

local colours = {
	["minecraft:coal_ore"] = { 150, 150, 150 },
	["minecraft:iron_ore"] = { 255, 150, 50 },
	["minecraft:lava"] = { 150, 75, 0 },
	["minecraft:gold_ore"] = { 255, 255, 0 },
	["minecraft:diamond_ore"] = { 0, 255, 255 },
	["minecraft:redstone_ore"] = { 255, 0, 0 },
	["minecraft:lapis_ore"] = { 0, 50, 255 },
	["minecraft:emerald_ore"] = { 0, 255, 0 }
}

Now let’s get into the interesting stuff! Let’s look for a neural interface and check we’ve got all the required modules.

local modules = peripheral.find("neuralInterface")
if not modules then error("Must have a neural interface", 0) end
if not modules.hasModule("plethora:scanner") then error("The block scanner is missing", 0) end
if not modules.hasModule("plethora:glasses") then error("The overlay glasses are missing", 0) end

Now we’ve got our neural interface, let’s extract the canvas and ensure nothing else is on it.

local canvas = modules.canvas()
canvas.clear()

We now need to set up our minimap. We create a 2D array of text objects around the player, each starting off displaying an empty string. If we find an ore, we’ll update their colour and text.

local block_text = {}
local blocks = {}
for x = -scannerRange, scannerRange, 1 do
	block_text[x] = {}
	blocks[x] = {}

	for z = -scannerRange, scannerRange, 1 do
		block_text[x][z] = canvas.addText({ 0, 0 }, " ", 0xFFFFFFFF, size)
		blocks[x][z] = { y = nil, block = nil }
	end
end

We also create a marker showing the current player’s location.

canvas.addText({ offsetX, offsetY }, "^", 0xFFFFFFFF, size * 2)

Our first big function is the scanner: this searches for ores near the player, finds the most important ones, and updates the block table.

local function scan()
	while true do
		local scanned_blocks = modules.scan()

For each nearby position, we search the y axis for interesting ores. We look for the one which has the highest priority and update the block information

		for x = -scannerRange, scannerRange do
			for z = -scannerRange, scannerRange do
				local best_score, best_block, best_y = -1
				for y = -scannerRange, scannerRange do

The block scanner returns blocks in a flat array, so we index into it with this rather scary formulae.

					local scanned = scanned_blocks[scannerWidth ^ 2 * (x + scannerRange) + scannerWidth * (y + scannerRange) + (z + scannerRange) + 1]

If there is a block here, and it’s more interesting than our previous ores, then let’s use that!

					if scanned then
						local new_score = ores[scanned.name]
						if new_score and new_score > best_score then
							best_block = scanned.name
							best_score = new_score
							best_y = y
						end
					end
				end

				-- Update our block table with this information.
				blocks[x][z].block = best_block
				blocks[x][z].y = best_y
			end
		end

We wait for some delay before starting again. This isn’t strictly needed, but helps reduce server load

		sleep(scanInterval)
	end
end

The render function takes our block information generated in the previous function and updates the text elements.

local function render()
	while true do

If possible, we rotate the map using the current player’s look direction. If it’s not available, we’ll just use north as up.

		local meta = modules.getMetaOwner and modules.getMetaOwner()
		local angle = meta and math.rad(-meta.yaw % 360) or math.rad(180)

Like before, loop over every nearby block and update something. Though this time we’re updating objects on the overlay canvas.

		for x = -scannerRange, scannerRange do
			for z = -scannerRange, scannerRange do
				local text = block_text[x][z]
				local block = blocks[x][z]

				if block.block then

If we’ve got a block here, we update the position of our text element to account for rotation,

					local px = math.cos(angle) * -x - math.sin(angle) * -z
					local py = math.sin(angle) * -x + math.cos(angle) * -z

					local sx = math.floor(px * size * cellSize)
					local sy = math.floor(py * size * cellSize)
					text.setPosition(offsetX + sx, offsetY + sy)

Then change the text and colour to match the location of the ore

					text.setText(tostring(block.y))
					text.setColor(table.unpack(colours[block.block]))
				else

Otherwise we just make sure the text is empty. We don’t need to faff about with clearing the colour or position, as we’ll change it next iteration anyway.

					text.setText(" ")
				end
			end
		end

		sleep(renderInterval)
	end
end

We now run our render and scan loops in parallel, continually updating our block list and redisplaying it to the wearer.

parallel.waitForAll(render, scan)