diff --git a/.luacheckrc b/.luacheckrc index 45f3046..089b20a 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -15,5 +15,8 @@ read_globals = { -- MTG "default", "sfinv", "creative", + + -- MCL + "mcl_sounds" } diff --git a/README.md b/README.md index 40e8710..7b52b03 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,27 @@ -# Ore Tracker +# Oretracker -A useful mod for tracking what ores are around the player. +Combines various tech to allow advanced mining operations. -## About this mod +## What's in the box -This mod provides a graphical HUD interface which displays a series of ores. +* Orehud: The original Oretracker mod with a single command to toggle the mod on or off per individual player. +* Xray: Based on Orehud except trackes non-ores and makes them appear invisible. -## The ores +## Orehud -| Ore name | Text Shown | Color code (HEX) | -|----------|------------|------------------| -| Coal | Coa | 0xc8c8c8 | -| Iron | Iro | 0xaf644b | -| Gold | Gol | 0xc8c84b | -| Mese\* | Mes | 0xffff4b | -| Diamond | Dia | 0x4bfafa | -| Quartz\*\*| Qua | 0xc8c8c8 | -| Copper\*\*\*| Cop | 0xc86400 | -| Tin\* | Tin | 0xc8c8c8 | -| Ancient Debris\*\*| Deb | 0xaa644b | -| Lapis | Lap | 0x4b4bc8 | -| Redstone\*\* | Red | 0xc81919 | -| Glowstone\*\* | Glo | 0xffff4b | +Displays ore positions via the player's HUD. -\* Only found in MTG. +> `/orehud` toggles the rendering of the ore positions on or off. -\*\* Found in MCL5 or MCL2 with the proper mods. +## Xray -\*\*\* Found in MTG and MCL5 or MCL2 with the proper mods. +Hides unwanted stone (and other varients) from view, only showing a empty node in it's wake. -## Settings +> `/xray` toggles the unwanted stone from visible to invisible (on or off). -### Detect Range +Yes, because xray interacts server side, all clients can make use of a single players client with xray. (This means you can see other players and ores they can see too by their xray) -The number of blocks away from the player the mod will scan/display for ores. - -> Recommended 8 blocks with no more than 16 (Bigger numbers equals much higher loads on server and possibly client) - -### Scan Frequency - -The time in seconds till the mod updates ores it is indicating and if any new ores enter the scaning area (Dictated by Detect Range) - -> Recommended no faster than 1 (for 1 second) (Lower numbers equals faster scans but can/will increase loads on server and possibly client) - -> Use 0 or negative number to indicate instantanious (no delay) scans. (Not recommended) - -# Future Plans - -* Make a hand held item that dictates if the mod runs (displays ores) for that player. -* Possibly make a priviledge to limit what player can see ores. -* Make a GUI for changing colors, what gets shown for what ore, what ore gets displayed. (Per player settings) -* Make a longer range or faster scan priviledge for players (like admins or "ranked" players) +## Common Issues +* It was found that if the server crashes while a player is using xray, xray's nodes are kept. Created a special mode (fix_mode) to attempt to repair nodes which should return back to their original uppon detection. (If fix_mode is left on in production other players xrays can be overriden) +* It is possible to get xray nodes to say simply by logging off, xray attempts to cleanup nodes by players who are attempting to log out. (Please make an issue report with any logs and screenshots/video showing this still occurs) diff --git a/api.lua b/api.lua deleted file mode 100644 index bba690b..0000000 --- a/api.lua +++ /dev/null @@ -1,38 +0,0 @@ - -oretracker.add_ore = function(orename) - if minetest.registered_nodes[orename] then - table.insert(oretracker.ores, orename) - else - minetest.log("action", "[oretracker] Failed to add '"..orename.."' as it is a unregistered node.") - end -end - -oretracker.add_pos = function(pname, pos, title, color) - if not title then - title = minetest.pos_to_string(pos) - end - local player = minetest.get_player_by_name(pname) - local wps = minetest.deserialize(oretracker.store:get_string(pname)) or {} - if not color then - color = 0xffffff - end - table.insert(wps, - player:hud_add({ - hud_elem_type = "waypoint", - name = title, - text = "m", - number = color, - world_pos = pos - }) - ) - oretracker.store:set_string(pname, minetest.serialize(wps)) -end - -oretracker.clear_pos = function(pname) - local player = minetest.get_player_by_name(pname) - local wps = minetest.deserialize(oretracker.store:get_string(pname)) or {} - for i, v in ipairs(wps) do - player:hud_remove(v) - end - oretracker.store:set_string(pname, minetest.serialize({})) -end \ No newline at end of file diff --git a/init.lua b/init.lua deleted file mode 100644 index 41c989e..0000000 --- a/init.lua +++ /dev/null @@ -1,163 +0,0 @@ - --- https://rubenwardy.com/minetest_modding_book/en/map/environment.html#finding-nodes - --- A public API -oretracker = {} - -oretracker.S = minetest.get_translator("oretracker") -oretracker.modpath = minetest.get_modpath("oretracker") -oretracker.store = minetest.get_mod_storage() - --- Settings --- Do not set detect_range to a very high number it may cause extreme loads when there are multiple players with this range --- Recommended range is 8 blocks -oretracker.detect_range = 8 -- Range in blocks --- The prefered fastest is 1 second, 0 or negative is instantanious updates (Which greatly impacts the server/client) --- Recommended default is 3 seconds. -oretracker.scan_frequency = 3 -- Frequency in seconds - --- This attempts to detect the gamemode -if not minetest.registered_nodes["default:stone"] then - if not minetest.registered_nodes["mcl_core:stone"] then - oretracker.gamemode = "N/A" - else - oretracker.gamemode = "MCL" - -- Attempt to determine if it's MCL5 or MCL2 - if not minetest.registered_nodes["mcl_nether:ancient_debris"] then - oretracker.gamemode = "MCL2" - else - oretracker.gamemode = "MCL5" - end - end -else - oretracker.gamemode = "MTG" -end - -minetest.log("action", "[oretracker] Detected game "..oretracker.gamemode..".") - --- Form a container to track what ores we want to follow -oretracker.ores = {} - -dofile(oretracker.modpath .. "/api.lua") - --- Use api to assign ores we know/should be caring about -if oretracker.gamemode == "MCL2" or oretracker.gamemode == "MCL5" then - oretracker.add_ore("mcl_core:stone_with_coal") - oretracker.add_ore("mcl_core:stone_with_iron") - oretracker.add_ore("mcl_core:stone_with_gold") - oretracker.add_ore("mcl_core:stone_with_redstone") - oretracker.add_ore("mcl_core:stone_with_redstone_lit") - oretracker.add_ore("mcl_core:stone_with_lapis") - oretracker.add_ore("mcl_core:stone_with_emerald") - oretracker.add_ore("mcl_core:stone_with_diamond") - oretracker.add_ore("mcl_nether:quartz_ore") -- This fails on MCL2 :S --- oretracker.add_ore("mcl_nether:glowstone") -- Same here, though by default this is disabled as glowstone isn't a "ore" -end - -if oretracker.gamemode == "MCL5" then - oretracker.add_ore("mcl_copper:stone_with_copper") - oretracker.add_ore("mcl_nether:ancient_debris") - oretracker.add_ore("mcl_nether_gold:nether_gold_ore") -end - -if oretracker.gamemode == "MTG" then - oretracker.add_ore("default:stone_with_coal") - oretracker.add_ore("default:stone_with_iron") - oretracker.add_ore("default:stone_with_gold") - oretracker.add_ore("default:stone_with_copper") - oretracker.add_ore("default:stone_with_tin") - oretracker.add_ore("default:stone_with_mese") - oretracker.add_ore("default:stone_with_diamond") -end - -local size = 0 -local result = "Ores: " -for i, v in ipairs(oretracker.ores) do - result = result..v.." " - size = size + 1 -end -minetest.log("action", "[oretracker] Found "..size.." ores configured.") -minetest.log("action", "[oretracker] "..result) - --- Now to add the tracker notification system -oretracker.check_player = function(player) - local p = player - if not minetest.is_player(p) then - p = minetest.get_player_by_name(p) - end - local pos = p:get_pos() - local pname = p:get_player_name() - -- I need to clean up the player's ore waypoints added by the latter code - oretracker.clear_pos(pname) - local p1 = vector.subtract(pos, {x = oretracker.detect_range, y = oretracker.detect_range, z = oretracker.detect_range}) - local p2 = vector.add(pos, {x = oretracker.detect_range, y = oretracker.detect_range, z = oretracker.detect_range}) - local area = minetest.find_nodes_in_area(p1, p2, oretracker.ores) - for i=1, #area do - local node = minetest.get_node_or_nil(area[i]) - if node == nil then - minetest.log("action", "[oretracker] Failed to obtain node at "..minetest.pos_to_string(area[1], 1)..".") - else - local delta = vector.subtract(area[i], pos) - local distance = (delta.x*delta.x) + (delta.y*delta.y) + (delta.z*delta.z) - if distance <= oretracker.detect_range*oretracker.detect_range then - distance = string.format("%.0f", math.sqrt(distance)) - local block = "?" - local color = 0xffffff - if string.find(node.name, "coal") then - block = "Coa" - color = 0xc8c8c8 - elseif string.find(node.name, "iron") then - block = "Iro" - color = 0xaf644b - elseif string.find(node.name, "gold") then - block = "Gol" - color = 0xc8c84b - elseif string.find(node.name, "mese") then - block = "Mes" - color = 0xffff4b - elseif string.find(node.name, "diamond") then - block = "Dia" - color = 0x4bfafa - elseif string.find(node.name, "quartz") then - block = "Qua" - color = 0xc8c8c8 - elseif string.find(node.name, "copper") then - block = "Cop" - color = 0xc86400 - elseif string.find(node.name, "tin") then - block = "Tin" - color = 0xc8c8c8 - elseif string.find(node.name, "debris") then - block = "Deb" - color = 0xaa644b - elseif string.find(node.name, "lapis") then - block = "Lap" - color = 0x4b4bc8 - elseif string.find(node.name, "redstone") then - block = "Red" - color = 0xc81919 - elseif string.find(node.name, "glowstone") then - block = "Glo" - color = 0xffff4b - end - if block == "?" then - minetest.log("action", "[oretracker] Found '"..node.name.."' at "..minetest.pos_to_string(area[i], 1).." which is "..distance.." away from '"..pname..".") - block = node.name - end - -- Make a waypoint with the nodes name - oretracker.add_pos(pname, area[i], block, color) - end - end - end -end - -local interval = 0 -minetest.register_globalstep(function(dtime) - interval = interval - dtime - if interval <= 0 then - for _, player in ipairs(minetest.get_connected_players()) do - oretracker.check_player(player) - end - interval = oretracker.scan_frequency - end -end) diff --git a/mod.conf b/mod.conf deleted file mode 100644 index 3ff9df7..0000000 --- a/mod.conf +++ /dev/null @@ -1,4 +0,0 @@ -name = oretracker -description = "A useful mod for tracking what ores are around the player." -author = ApolloX -optional_depends = default, mcl_core diff --git a/modpack.txt b/modpack.txt new file mode 100644 index 0000000..e69de29 diff --git a/orehud/LICENSE b/orehud/LICENSE new file mode 100644 index 0000000..a2d2240 --- /dev/null +++ b/orehud/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Beanzilla + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/orehud/README.md b/orehud/README.md new file mode 100644 index 0000000..08069ad --- /dev/null +++ b/orehud/README.md @@ -0,0 +1,48 @@ +# Ore Tracker > Ore hud + +Displays ore position by using the HUD. + +## About this mod + +This mod provides a graphical HUD interface which displays a series of ores. + +Simply use `/orehud` to enable or disable displaying the ores on your HUD. + +## The ores + +| Ore name | Text Shown | Color code (HEX) | +|----------|------------|------------------| +| Coal | Coa | 0xc8c8c8 | +| Iron | Iro | 0xaf644b | +| Gold | Gol | 0xc8c84b | +| Mese\* | Mes | 0xffff4b | +| Diamond | Dia | 0x4bfafa | +| Quartz\*\*| Qua | 0xc8c8c8 | +| Copper\*\*\*| Cop | 0xc86400 | +| Tin\* | Tin | 0xc8c8c8 | +| Ancient Debris\*\*| Deb | 0xaa644b | +| Lapis | Lap | 0x4b4bc8 | +| Redstone\*\* | Red | 0xc81919 | +| Glowstone\*\* | Glo | 0xffff4b | + +\* Only found in MTG. + +\*\* Found in MCL5 or MCL2 with the proper mods. + +\*\*\* Found in MTG and MCL5 or MCL2 with the proper mods. + +## Settings + +### Detect Range + +The number of blocks away from the player the mod will scan/display for ores. + +> Recommended 8 blocks with no more than 16 (Bigger numbers equals much higher loads on server and possibly client) + +### Scan Frequency + +The time in seconds till the mod updates ores it is indicating and if any new ores enter the scaning area (Dictated by Detect Range) + +> Recommended no faster than 1 (for 1 second) (Lower numbers equals faster scans but can/will increase loads on server and possibly client) + +> Use 0 or negative number to indicate instantanious (no delay) scans. (Not recommended) diff --git a/orehud/api.lua b/orehud/api.lua new file mode 100644 index 0000000..f340b13 --- /dev/null +++ b/orehud/api.lua @@ -0,0 +1,41 @@ + +-- Adds an "ore" node to track, only if that node actually is a valid node +orehud.add_ore = function(orename) + if minetest.registered_nodes[orename] then + table.insert(orehud.ores, orename) + else + minetest.log("action", "[oretracker-orehud] Failed to add '"..orename.."' as it is a unregistered node.") + end +end + +-- Adds a waypoint to the given player's HUD, given title and color +orehud.add_pos = function(pname, pos, title, color) + if not title then + title = minetest.pos_to_string(pos) + end + local player = minetest.get_player_by_name(pname) + local wps = orehud.store[pname] or {} + if not color then + color = 0xffffff + end + table.insert(wps, + player:hud_add({ + hud_elem_type = "waypoint", + name = title, + text = "m", + number = color, + world_pos = pos + }) + ) + orehud.store[pname] = wps +end + +-- Clears all waypoints from the given player's HUD +orehud.clear_pos = function(pname) + local player = minetest.get_player_by_name(pname) + local wps = orehud.store[pname] or {} + for i, v in ipairs(wps) do + player:hud_remove(v) + end + orehud.store[pname] = {} +end \ No newline at end of file diff --git a/orehud/init.lua b/orehud/init.lua new file mode 100644 index 0000000..9bdfb8d --- /dev/null +++ b/orehud/init.lua @@ -0,0 +1,214 @@ + +-- https://rubenwardy.com/minetest_modding_book/en/map/environment.html#finding-nodes + +-- A public API +orehud = {} + +orehud.S = minetest.get_translator("orehud") +orehud.modpath = minetest.get_modpath("orehud") +orehud.store = {} +orehud.p_stats = {} + +-- Settings +-- Do not set detect_range to a very high number it may cause extreme loads when there are multiple players with this range +-- Recommended range is 8 blocks +orehud.detect_range = 8 -- Range in blocks +-- The prefered fastest is 1 second, 0 or negative is instantanious updates (Which greatly impacts the server/client) +-- Recommended default is 3 seconds. +orehud.scan_frequency = 3 -- Frequency in seconds + +-- This attempts to detect the gamemode +if not minetest.registered_nodes["default:stone"] then + if not minetest.registered_nodes["mcl_core:stone"] then + orehud.gamemode = "N/A" + else + orehud.gamemode = "MCL" + -- Attempt to determine if it's MCL5 or MCL2 + if not minetest.registered_nodes["mcl_nether:ancient_debris"] then + orehud.gamemode = "MCL2" + else + orehud.gamemode = "MCL5" + end + end +else + orehud.gamemode = "MTG" +end + +minetest.log("action", "[oretracker-orehud] Detected game "..orehud.gamemode..".") + +-- Form a container to track what ores we want to follow +orehud.ores = {} + +dofile(orehud.modpath .. "/api.lua") + +-- Use api to assign ores we know/should be caring about +if orehud.gamemode == "MCL2" or orehud.gamemode == "MCL5" then + orehud.add_ore("mcl_core:stone_with_coal") + orehud.add_ore("mcl_core:stone_with_iron") + orehud.add_ore("mcl_core:stone_with_gold") + orehud.add_ore("mcl_core:stone_with_redstone") + orehud.add_ore("mcl_core:stone_with_redstone_lit") + orehud.add_ore("mcl_core:stone_with_lapis") + orehud.add_ore("mcl_core:stone_with_emerald") + orehud.add_ore("mcl_core:stone_with_diamond") + orehud.add_ore("mcl_nether:quartz_ore") -- This fails on MCL2 :S (LOL, I didn't realize my test suite was MCL2, I though it was MCL5) +-- orehud.add_ore("mcl_nether:glowstone") -- By default this is disabled as glowstone isn't a "ore", but just uncomment this line to get it too +end + +if orehud.gamemode == "MCL5" then + orehud.add_ore("mcl_copper:stone_with_copper") + orehud.add_ore("mcl_nether:ancient_debris") + orehud.add_ore("mcl_nether_gold:nether_gold_ore") +end + +if orehud.gamemode == "MTG" then + orehud.add_ore("default:stone_with_coal") + orehud.add_ore("default:stone_with_iron") + orehud.add_ore("default:stone_with_gold") + orehud.add_ore("default:stone_with_copper") + orehud.add_ore("default:stone_with_tin") + orehud.add_ore("default:stone_with_mese") + orehud.add_ore("default:stone_with_diamond") +end + +local size = 0 +local result = "Ores: " +for i, v in ipairs(orehud.ores) do + result = result..v.." " + size = size + 1 +end +minetest.log("action", "[oretracker-orehud] Found "..size.." ores configured.") +minetest.log("action", "[oretracker-orehud] "..result) + +-- Itterates an area of nodes for "ores", then adds a waypoint at that nodes position for that "ore". +orehud.check_player = function(player) + local p = player + if not minetest.is_player(p) then + p = minetest.get_player_by_name(p) + end + local pos = p:get_pos() + local pname = p:get_player_name() + local p1 = vector.subtract(pos, {x = orehud.detect_range, y = orehud.detect_range, z = orehud.detect_range}) + local p2 = vector.add(pos, {x = orehud.detect_range, y = orehud.detect_range, z = orehud.detect_range}) + local area = minetest.find_nodes_in_area(p1, p2, orehud.ores) + for i=1, #area do + local node = minetest.get_node_or_nil(area[i]) + if node == nil then + minetest.log("action", "[oretracker-orehud] Failed to obtain node at "..minetest.pos_to_string(area[1], 1)..".") + else + local delta = vector.subtract(area[i], pos) + local distance = (delta.x*delta.x) + (delta.y*delta.y) + (delta.z*delta.z) + if distance <= orehud.detect_range*orehud.detect_range then + distance = string.format("%.0f", math.sqrt(distance)) + local block = "?" + local color = 0xffffff + if string.find(node.name, "coal") then + block = "Coa" + color = 0xc8c8c8 + elseif string.find(node.name, "iron") then + block = "Iro" + color = 0xaf644b + elseif string.find(node.name, "gold") then + block = "Gol" + color = 0xc8c84b + elseif string.find(node.name, "mese") then + block = "Mes" + color = 0xffff4b + elseif string.find(node.name, "diamond") then + block = "Dia" + color = 0x4bfafa + elseif string.find(node.name, "quartz") then + block = "Qua" + color = 0xc8c8c8 + elseif string.find(node.name, "copper") then + block = "Cop" + color = 0xc86400 + elseif string.find(node.name, "tin") then + block = "Tin" + color = 0xc8c8c8 + elseif string.find(node.name, "debris") then + block = "Deb" + color = 0xaa644b + elseif string.find(node.name, "lapis") then + block = "Lap" + color = 0x4b4bc8 + elseif string.find(node.name, "redstone") then + block = "Red" + color = 0xc81919 + elseif string.find(node.name, "glowstone") then + block = "Glo" + color = 0xffff4b + end + if block == "?" then + minetest.log("action", "[oretracker-orehud] Found '"..node.name.."' at "..minetest.pos_to_string(area[i], 1).." which is "..distance.." away from '"..pname..".") + block = node.name + end + -- Make a waypoint with the nodes name + orehud.add_pos(pname, area[i], block, color) + end + end + end +end + +-- Now register with minetest to actually do something + +local interval = 0 +minetest.register_globalstep(function(dtime) + interval = interval - dtime + if interval <= 0 then + for _, player in ipairs(minetest.get_connected_players()) do + local p = player + if not minetest.is_player(p) then + p = minetest.get_player_by_name(p) + end + -- I need to clean up the player's ore waypoints added by the latter code + orehud.clear_pos(p:get_player_name()) + if orehud.p_stats[p:get_player_name()] or false then + -- Only run if that player wants to run + orehud.check_player(p) + end + end + interval = orehud.scan_frequency + end +end) + +minetest.register_on_joinplayer(function(player, laston) + orehud.p_stats[player:get_player_name()] = false +end) + +minetest.register_on_leaveplayer(function(player, timeout) + local indx = 0 + local found = false + for pname, val in ipairs(orehud.p_stats) do + if pname == player:get_player_name() then + found = true + break + end + indx = indx + 1 + end + if found then + table.remove(orehud.p_stats, indx) + end +end) + +-- A priv for those to use this power +minetest.register_privilege("orehud", { + description = "Oretracker Orehud Priv", + give_to_singleplayer = true -- Also given to those with server priv +}) + +minetest.register_chatcommand("orehud", { + privs = { + shout = true, + orehud = true -- Require our own priv + }, + func = function(name, param) + if orehud.p_stats[name] then + orehud.p_stats[name] = false + minetest.chat_send_player(name, "Orehud: OFF") + else + orehud.p_stats[name] = true + minetest.chat_send_player(name, "Orehud: ON") + end + end, +}) diff --git a/orehud/mod.conf b/orehud/mod.conf new file mode 100644 index 0000000..7ff6ff1 --- /dev/null +++ b/orehud/mod.conf @@ -0,0 +1,4 @@ +name = orehud +description = Displays ore positions via the HUD. +author = ApolloX +optional_depends = default, mcl_core diff --git a/orehud/screenshot.png b/orehud/screenshot.png new file mode 100644 index 0000000..065c18e Binary files /dev/null and b/orehud/screenshot.png differ diff --git a/screenshot.png b/screenshot.png index 065c18e..98df189 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/xray/LICENSE b/xray/LICENSE new file mode 100644 index 0000000..a2d2240 --- /dev/null +++ b/xray/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Beanzilla + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/xray/README.md b/xray/README.md new file mode 100644 index 0000000..bd2857c --- /dev/null +++ b/xray/README.md @@ -0,0 +1,38 @@ +# Ore Tracker > Xray + +Hides stone and stone varients from view. + +## About this mod + +This mod provides graphical counterparts for stone and stone varients to appear invisible. + +Simply use `/xray` to enable or disable displaying the stone or stone varients around you. + +> If your server crashes while any of your players are using xray it may cause residual invisible nodes... simply enable the fix_mode and then wander around the area they were at. (make sure you disable fix_mode in production, as it can overwrite other player's xray zones) + +## How it works + +When you issue `/xray` and it becomes active, it checks a cube like area around you, if within this scan area it detects stone for example it then swaps it with it's own stone node (Which simply is a transparent node, but the counterpart was made so I could replace particular stone varients without causing the player to only get stone and nothing else). Reverse the process for reverting the invisible counterpart back to the visible node. + +> Yes you can see other player's xray's since there isn't a way to hide nodes just for one client but show them for other clients. + +## Settings + +### Detect Range + +The distance at which nodes will be swapped. (Higher numbers means more blocks swapped but might increase server/client loads) + +> Recommended detect_range of 6. (No more than 16) + +### Scan Frequency + +The rate at which nodes will be checked and swapped. (Lower numbers means more "updates" making it smoother to move around and see the environment change, but might increase server/client loads) + +> Recommended scan_frequency of 1. (Use 0 or negative numbers for instantanious "updates", This number is in seconds) + +### Light Level + +The level that xray nodes emit light. (0 is minimum and 14 is maximum, dictated by minetest) + +> Recommended level 4. (This makes xray a dark dull but if you don't mind players never using torches you could make this higher) + diff --git a/xray/api.lua b/xray/api.lua new file mode 100644 index 0000000..393472a --- /dev/null +++ b/xray/api.lua @@ -0,0 +1,136 @@ + +-- Adds a node to track, only if that node actually is a valid node +xray.add_node = function(nodename) + if minetest.registered_nodes[nodename] then + table.insert(xray.nodes, nodename) + else + minetest.log("action", "[oretracker-xray] Failed to add '"..nodename.."' as it is a unregistered node.") + end +end + +-- Swaps with one of the registered nodes that has the invisiblity factor +xray.add_pos = function(pname, pos) + local current = minetest.get_node_or_nil(pos) + if current == nil then + minetest.log("action", "[oretracker-xray] Failed to obtain node at "..minetest.pos_to_string(pos).." for swap.") + end + current = current.name + --minetest.log("action", "[oretracker-xray] Swapping "..current) + local nps = xray.store[pname] or {} + table.insert(nps, pos) + -- Place a switch here to identify which kind of node would be best used here + if current == "default:stone" then + minetest.swap_node(pos, {name="xray:mtg_stone"}) + elseif current == "default:desert_stone" then + minetest.swap_node(pos, {name="xray:mtg_dstone"}) + elseif current == "default:sandstone" then + minetest.swap_node(pos, {name="xray:mtg_sstone"}) + elseif current == "default:desert_sandstone" then + minetest.swap_node(pos, {name="xray:mtg_dsstone"}) + elseif current == "default:silver_sandstone" then + minetest.swap_node(pos, {name="xray:mtg_ssstone"}) + elseif current == "mcl_core:stone" then + minetest.swap_node(pos, {name="xray:mcl_stone"}) + elseif current == "mcl_core:granite" then + minetest.swap_node(pos, {name="xray:mcl_granite"}) + elseif current == "mcl_core:andesite" then + minetest.swap_node(pos, {name="xray:mcl_andesite"}) + elseif current == "mcl_core:diorite" then + minetest.swap_node(pos, {name="xray:mcl_diorite"}) + elseif current == "mcl_core:sandstone" then + minetest.swap_node(pos, {name="xray:mcl_sstone"}) + elseif current == "mcl_core:redsandstone" then + minetest.swap_node(pos, {name="xray:mcl_rsstone"}) + elseif current == "mcl_blackstone:blackstone" then + minetest.swap_node(pos, {name="xray:mcl_bstone"}) + elseif current == "mcl_blackstone:basalt" then + minetest.swap_node(pos, {name="xray:mcl_basalt"}) + elseif current == "mcl_nether:netherrack" then + minetest.swap_node(pos, {name="xray:mcl_netherrack"}) + end + -- Stone, Diorite, Anasite, Granite, etc. + xray.store[pname] = nps +end + +-- Clears all invisible nodes back to their originals (per player) +xray.clear_pos = function(pname) + --local player = minetest.get_player_by_name(pname) + local wps = xray.store[pname] or {} + for i, v in ipairs(wps) do + local node = minetest.get_node_or_nil(v) + if node == nil then + minetest.log("action", "[oretracker-xray] Failed to obtain node at "..minetest.pos_to_string(v).." for revert ("..i..")") + end + node = node.name + --minetest.log("action", "[oretracker-xray] Reverting "..current) + -- Place a switch here to identify what node should be put back here + if node == "xray:mtg_stone" then + minetest.swap_node(v, {name="default:stone"}) + elseif node == "xray:mtg_dstone" then + minetest.swap_node(v, {name="default:desert_stone"}) + elseif node == "xray:mtg_sstone" then + minetest.swap_node(v, {name="default:sandstone"}) + elseif node == "xray:mtg_dsstone" then + minetest.swap_node(v, {name="default:desert_sandstone"}) + elseif node == "xray:mtg_ssstone" then + minetest.swap_node(v, {name="default:silver_sandstone"}) + elseif node == "xray:mcl_stone" then + minetest.swap_node(v, {name="mcl_core:stone"}) + elseif node == "xray:mcl_granite" then + minetest.swap_node(v, {name="mcl_core:granite"}) + elseif node == "xray:mcl_andesite" then + minetest.swap_node(v, {name="mcl_core:andesite"}) + elseif node == "xray:mcl_diorite" then + minetest.swap_node(v, {name="mcl_core:diorite"}) + elseif node == "xray:mcl_sstone" then + minetest.swap_node(v, {name="mcl_core:sandstone"}) + elseif node == "xray:mcl_rsstone" then + minetest.swap_node(v, {name="mcl_core:redsandstone"}) + elseif node == "xray:mcl_bstone" then + minetest.swap_node(v, {name="mcl_blackstone:blackstone"}) + elseif node == "xray:mcl_basalt" then + minetest.swap_node(v, {name="mcl_blackstone:basalt"}) + elseif node == "xray:mcl_netherrack" then + minetest.swap_node(v, {name="mcl_nether:netherrack"}) + end + end + xray.store[pname] = {} +end + +-- Attempt to repair the damage to this node (In the process of development I found my system made a ball of unrepairable goo, invisible blocks) +xray.fix_pos = function (pos) + local node = minetest.get_node_or_nil(pos) + if node == nil then + minetest.log("action", "[oretracker-xray] Failed to obtain node at "..minetest.pos_to_string(pos).." for revert (fix_pos)") + end + node = node.name + if node == "xray:mtg_stone" then + minetest.swap_node(pos, {name="default:stone"}) + elseif node == "xray:mtg_dstone" then + minetest.swap_node(pos, {name="default:desert_stone"}) + elseif node == "xray:mtg_sstone" then + minetest.swap_node(pos, {name="default:sandstone"}) + elseif node == "xray:mtg_dsstone" then + minetest.swap_node(pos, {name="default:desert_sandstone"}) + elseif node == "xray:mtg_ssstone" then + minetest.swap_node(pos, {name="default:silver_sandstone"}) + elseif node == "xray:mcl_stone" then + minetest.swap_node(pos, {name="mcl_core:stone"}) + elseif node == "xray:mcl_granite" then + minetest.swap_node(pos, {name="mcl_core:granite"}) + elseif node == "xray:mcl_andesite" then + minetest.swap_node(pos, {name="mcl_core:andesite"}) + elseif node == "xray:mcl_diorite" then + minetest.swap_node(pos, {name="mcl_core:diorite"}) + elseif node == "xray:mcl_sstone" then + minetest.swap_node(pos, {name="mcl_core:sandstone"}) + elseif node == "xray:mcl_rsstone" then + minetest.swap_node(pos, {name="mcl_core:redsandstone"}) + elseif node == "xray:mcl_bstone" then + minetest.swap_node(pos, {name="mcl_blackstone:blackstone"}) + elseif node == "xray:mcl_basalt" then + minetest.swap_node(pos, {name="mcl_blackstone:basalt"}) + elseif node == "xray:mcl_netherrack" then + minetest.swap_node(pos, {name="mcl_nether:netherrack"}) + end +end \ No newline at end of file diff --git a/xray/init.lua b/xray/init.lua new file mode 100644 index 0000000..b9b8b14 --- /dev/null +++ b/xray/init.lua @@ -0,0 +1,198 @@ +-- https://rubenwardy.com/minetest_modding_book/en/map/environment.html#finding-nodes + +-- A public API +xray = {} + +xray.S = minetest.get_translator("xray") +xray.modpath = minetest.get_modpath("xray") +xray.store = {} +xray.p_stats = {} + +-- Settings +-- Do not set detect_range to a very high number it may cause extreme loads when there are multiple players with this range +-- Recommended range is 6 blocks +xray.detect_range = 6 -- Range in blocks +-- 0 or negative is instantanious updates (Which greatly impacts the server/client) +-- Recommended frequency is 1 second. +xray.scan_frequency = 1 -- Frequency in seconds +-- Light level that xray nodes emit (Max is 14 min is 0) +-- Recommended light_level is 4. (Provides enough light to use the mod, might need to use torches if you want it lighter, or adjust here) +xray.light_level = 4 -- From 0-14 + +-- Only turn this on if you have strange blobs of invisible blocks (due to a server crash, etc.) +local fix_mode = false + +-- This attempts to detect the gamemode +if not minetest.registered_nodes["default:stone"] then + if not minetest.registered_nodes["mcl_core:stone"] then + xray.gamemode = "N/A" + else + xray.gamemode = "MCL" + -- Attempt to determine if it's MCL5 or MCL2 + if not minetest.registered_nodes["mcl_nether:ancient_debris"] then + xray.gamemode = "MCL2" + else + xray.gamemode = "MCL5" + end + end +else + xray.gamemode = "MTG" +end + +minetest.log("action", "[oretracker-xray] Detected game "..xray.gamemode..".") + +-- Form a container to track what ores we want to follow +xray.nodes = {} + +-- Make our counterparts +dofile(xray.modpath .. "/register.lua") + +-- Make our api +dofile(xray.modpath .. "/api.lua") + +-- Use api to assign ores we know/should be caring about +if xray.gamemode == "MCL2" or xray.gamemode == "MCL5" then + xray.add_node("mcl_core:stone") -- xray:mcl_stone + xray.add_node("mcl_core:granite") -- xray:mcl_granite + xray.add_node("mcl_core:andesite") -- xray:mcl_andesite + xray.add_node("mcl_core:diorite") -- xray:mcl_diorite + xray.add_node("mcl_core:sandstone") -- xray:mcl_sstone + xray.add_node("mcl_core:redsandstone") -- xray:mcl_rsstone +end + +if xray.gamemode == "MCL5" then + xray.add_node("mcl_blackstone:blackstone") -- xray:mcl_bstone + xray.add_node("mcl_blackstone:basalt") -- xray:mcl_basalt + xray.add_node("mcl_nether:netherrack") -- xray:mcl_netherrack +end + +if xray.gamemode == "MTG" then + xray.add_node("default:stone") -- xray:mtg_stone + xray.add_node("default:desert_stone") -- xray:mtg_dstone + xray.add_node("default:sandstone") -- xray:mtg_sstone + xray.add_node("default:desert_sandstone") -- xray:mtg_dsstone + xray.add_node("default:silver_sandstone") -- xray:mtg_ssstone +end + +-- Include our nodes so we can cleanup after ourselves +xray.add_node("xray:mtg_stone") +xray.add_node("xray:mtg_dstone") +xray.add_node("xray:mtg_sstone") +xray.add_node("xray:mtg_dsstone") +xray.add_node("xray:mtg_ssstone") +xray.add_node("xray:mcl_stone") +xray.add_node("xray:mcl_granite") +xray.add_node("xray:mcl_andesite") +xray.add_node("xray:mcl_diorite") +xray.add_node("xray:mcl_sstone") +xray.add_node("xray:mcl_rsstone") +xray.add_node("xray:mcl_bstone") +xray.add_node("xray:mcl_basalt") +xray.add_node("xray:mcl_netherrack") + +local size = 0 +local result = "Nodes: " +for i, v in ipairs(xray.nodes) do + result = result..v.." " + size = size + 1 +end +minetest.log("action", "[oretracker-xray] Found "..size.." nodes configured.") +minetest.log("action", "[oretracker-xray] "..result) + +-- Itterates an area of nodes, then swaps nodes if stone or stone varient +xray.check_player = function(p) + local pos = p:get_pos() + local pname = p:get_player_name() + local p1 = vector.subtract(pos, {x = xray.detect_range, y = xray.detect_range, z = xray.detect_range}) + local p2 = vector.add(pos, {x = xray.detect_range, y = xray.detect_range, z = xray.detect_range}) + local area = minetest.find_nodes_in_area(p1, p2, xray.nodes) + for i=1, #area do + local node = minetest.get_node_or_nil(area[i]) + if node == nil then + minetest.log("action", "[oretracker-xray] Failed to obtain node at "..minetest.pos_to_string(area[1], 1)..".") + else + local delta = vector.subtract(area[i], pos) + local distance = (delta.x*delta.x) + (delta.y*delta.y) + (delta.z*delta.z) + if distance <= xray.detect_range*xray.detect_range then + --distance = string.format("%.0f", math.sqrt(distance)) + -- Place the counterpart + --minetest.log("action", "xray "..pname.." "..minetest.pos_to_string(area[1], 1)) + if xray.p_stats[pname] then + -- Adds the counter since we are enabled + xray.add_pos(pname, area[i]) + elseif string.find(node.name, "xray") and fix_mode then + -- Attempt to cleanup that node if it's one of ours + -- Warning don't leave fix_mode on all the time, can cause someone elses xray to be blocked because of ordering + xray.fix_pos(area[i]) + end + end + end + end +end + +-- Now register with minetest to actually do something + +local interval = 0 +minetest.register_globalstep(function(dtime) + interval = interval - dtime + if interval <= 0 then + for _, player in ipairs(minetest.get_connected_players()) do + local p = player + if not minetest.is_player(p) then + p = minetest.get_player_by_name(p) + end + if p ~= nil then + -- I need to clean up the player's nodes added by the latter code + xray.clear_pos(p:get_player_name()) + -- Now make new nodes + xray.check_player(p) + end + end + interval = xray.scan_frequency + end +end) + +minetest.register_on_joinplayer(function(player, laston) + xray.p_stats[player:get_player_name()] = false +end) + +minetest.register_on_leaveplayer(function(player, timeout) + local indx = 0 + local found = false + for pname, val in ipairs(xray.p_stats) do + if pname == player:get_player_name() then + found = true + break + end + indx = indx + 1 + end + if found then + -- Attempt to cleanup that player's invisible nodes before they log off + xray.p_stats[player:get_player_name()] = false + minetest.log("action", "Cleaning up "..player:get_player_name().." as they wish to leave.") + xray.clear_pos(player:get_player_name()) + table.remove(xray.p_stats, indx) + end +end) + +-- A priv for players so they can't abuse this power +minetest.register_privilege("xray", { + description = "Oretracker Xray Priv", + give_to_singleplayer = true -- Also given to those with server priv +}) + +minetest.register_chatcommand("xray", { + privs = { + shout = true, + xray = true -- Require our xray + }, + func = function(name, param) + if xray.p_stats[name] then + xray.p_stats[name] = false + minetest.chat_send_player(name, "Xray: OFF") + else + xray.p_stats[name] = true + minetest.chat_send_player(name, "Xray: ON") + end + end, +}) diff --git a/xray/mcl.png b/xray/mcl.png new file mode 100644 index 0000000..61081c4 Binary files /dev/null and b/xray/mcl.png differ diff --git a/xray/mod.conf b/xray/mod.conf new file mode 100644 index 0000000..792a977 --- /dev/null +++ b/xray/mod.conf @@ -0,0 +1,4 @@ +name = xray +description = Hides stone and stone varients from view. +author = ApolloX +optional_depends = default, mcl_core, mcl_sounds diff --git a/xray/mtg.png b/xray/mtg.png new file mode 100644 index 0000000..56170d2 Binary files /dev/null and b/xray/mtg.png differ diff --git a/xray/register.lua b/xray/register.lua new file mode 100644 index 0000000..5697616 --- /dev/null +++ b/xray/register.lua @@ -0,0 +1,218 @@ + +-- MTG +if xray.gamemode == "MTG" then + minetest.register_node("xray:mtg_stone", { + description = xray.S("Xray Stone"), + tiles = {"xray_stone.png"}, + groups = {cracky = 3, stone = 1}, + drop = "default:cobble", + drawtype = "glasslike", + sunlight_propagates = true, + legacy_mineral = true, + light_source = xray.light_level, + sounds = default.node_sound_stone_defaults(), + }) + minetest.register_node("xray:mtg_dstone", { + description = xray.S("Xray Stone"), + tiles = {"xray_stone.png"}, + groups = {cracky = 3, stone = 1}, + drop = "default:desert_cobble", + drawtype = "glasslike", + sunlight_propagates = true, + legacy_mineral = true, + light_source = xray.light_level, + sounds = default.node_sound_stone_defaults(), + }) + minetest.register_node("xray:mtg_sstone", { + description = xray.S("Xray Stone"), + tiles = {"xray_stone.png"}, + groups = {cracky = 3, stone = 1}, + drop = "default:sandstone", + drawtype = "glasslike", + sunlight_propagates = true, + legacy_mineral = true, + light_source = xray.light_level, + sounds = default.node_sound_stone_defaults(), + }) + minetest.register_node("xray:mtg_dsstone", { + description = xray.S("Xray Stone"), + tiles = {"xray_stone.png"}, + groups = {cracky = 3, stone = 1}, + drop = "default:desert_sandstone", + drawtype = "glasslike", + sunlight_propagates = true, + legacy_mineral = true, + light_source = xray.light_level, + sounds = default.node_sound_stone_defaults(), + }) + minetest.register_node("xray:mtg_ssstone", { + description = xray.S("Xray Stone"), + tiles = {"xray_stone.png"}, + groups = {cracky = 3, stone = 1}, + drop = "default:silver_sandstone", + drawtype = "glasslike", + sunlight_propagates = true, + legacy_mineral = true, + light_source = xray.light_level, + sounds = default.node_sound_stone_defaults(), + }) +end + +-- MCL (2 and 5) +if xray.gamemode == "MCL2" or xray.gamemode == "MCL5" then + minetest.register_node("xray:mcl_stone", { + description = xray.S("Xray Stone"), + _doc_items_longdesc = xray.S("An Invisible block"), + _doc_items_hidden = true, + tiles = {"xray_stone.png"}, + is_ground_content = false, + stack_max = 1, + groups = {pickaxey=1, stone=1, building_block=1, material_stone=1}, + drawtype = "glasslike", + sunlight_propagates = true, + light_source = xray.light_level, + drop = 'mcl_core:cobble', + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 6, + _mcl_hardness = 1.5, + _mcl_silk_touch_drop = false, + }) + minetest.register_node("xray:mcl_granite", { + description = xray.S("Xray Stone"), + _doc_items_longdesc = xray.S("An Invisible block"), + _doc_items_hidden = true, + tiles = {"xray_stone.png"}, + is_ground_content = false, + stack_max = 1, + groups = {pickaxey=1, stone=1, building_block=1, material_stone=1}, + drawtype = "glasslike", + sunlight_propagates = true, + light_source = xray.light_level, + drop = 'mcl_core:granite', + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 6, + _mcl_hardness = 1.5, + _mcl_silk_touch_drop = false, + }) + minetest.register_node("xray:mcl_andesite", { + description = xray.S("Xray Stone"), + _doc_items_longdesc = xray.S("An Invisible block"), + _doc_items_hidden = true, + tiles = {"xray_stone.png"}, + is_ground_content = false, + stack_max = 1, + groups = {pickaxey=1, stone=1, building_block=1, material_stone=1}, + drawtype = "glasslike", + sunlight_propagates = true, + light_source = xray.light_level, + drop = 'mcl_core:andesite', + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 6, + _mcl_hardness = 1.5, + _mcl_silk_touch_drop = false, + }) + minetest.register_node("xray:mcl_diorite", { + description = xray.S("Xray Stone"), + _doc_items_longdesc = xray.S("An Invisible block"), + _doc_items_hidden = true, + tiles = {"xray_stone.png"}, + is_ground_content = false, + light_source = xray.light_level, + stack_max = 1, + groups = {pickaxey=1, stone=1, building_block=1, material_stone=1}, + drawtype = "glasslike", + sunlight_propagates = true, + drop = 'mcl_core:diorite', + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 6, + _mcl_hardness = 1.5, + _mcl_silk_touch_drop = false, + }) + minetest.register_node("xray:mcl_sstone", { + description = xray.S("Xray Stone"), + _doc_items_hidden = true, + _doc_items_longdesc = xray.S("An Invisible block"), + tiles = {"xray_stone.png"}, + is_ground_content = false, + light_source = xray.light_level, + stack_max = 1, + groups = {pickaxey=1, sandstone=1, normal_sandstone=1, building_block=1, material_stone=1}, + drawtype = "glasslike", + sunlight_propagates = true, + drop = 'mcl_core:sandstone', + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 0.8, + _mcl_hardness = 0.8, + _mcl_silk_touch_drop = false, + }) + minetest.register_node("xray:mcl_rsstone", { + description = xray.S("Xray Stone"), + _doc_items_hidden = true, + _doc_items_longdesc = xray.S("An Invisible block"), + tiles = {"xray_stone.png"}, + is_ground_content = false, + light_source = xray.light_level, + stack_max = 1, + groups = {pickaxey=1, sandstone=1, normal_sandstone=1, building_block=1, material_stone=1}, + drawtype = "glasslike", + sunlight_propagates = true, + drop = 'mcl_core:redsandstone', + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 0.8, + _mcl_hardness = 0.8, + _mcl_silk_touch_drop = false, + }) +end + +-- MCL (5 only) +if xray.gamemode == "MCL5" then + minetest.register_node("xray:mcl_bstone", { + description = xray.S("Xray Stone"), + _doc_items_hidden = true, + tiles = {"xray_dark.png"}, + light_source = xray.light_level, + sounds = mcl_sounds.node_sound_stone_defaults(), + is_ground_content = false, + stack_max = 1, + groups = {cracky = 3, pickaxey=2, material_stone=1}, + drawtype = "glasslike", + sunlight_propagates = true, + drop = 'mcl_blackstone:blackstone', + _mcl_blast_resistance = 2, + _mcl_hardness = 2, + _mcl_silk_touch_drop = false, + }) + minetest.register_node("xray:mcl_basalt", { + description = xray.S("Xray Stone"), + _doc_items_hidden = true, + tiles = {"xray_dark.png"}, + light_source = xray.light_level, + sounds = mcl_sounds.node_sound_stone_defaults(), + drawtype = "glasslike", + sunlight_propagates = true, + is_ground_content = false, + stack_max = 1, + groups = {cracky = 3, pickaxey=2, material_stone=1}, + drop = 'mcl_blackstone:basalt', + _mcl_blast_resistance = 2, + _mcl_hardness = 2, + _mcl_silk_touch_drop = false, + }) + minetest.register_node("xray:mcl_netherrack", { + description = xray.S("Xray Stone"), + _doc_items_hidden = true, + light_source = xray.light_level, + _doc_items_longdesc = xray.S("An Invisible block"), + stack_max = 1, + tiles = {"xray_nether.png"}, + is_ground_content = false, + groups = {pickaxey=1, building_block=1, material_stone=1}, + drawtype = "glasslike", + sunlight_propagates = true, + drop = 'mcl_nether:netherrack', + sounds = mcl_sounds.node_sound_stone_defaults(), + _mcl_blast_resistance = 0.4, + _mcl_hardness = 0.4, + _mcl_silk_touch_drop = false, + }) +end \ No newline at end of file diff --git a/xray/textures/xray_dark.png b/xray/textures/xray_dark.png new file mode 100644 index 0000000..2f4cd90 Binary files /dev/null and b/xray/textures/xray_dark.png differ diff --git a/xray/textures/xray_nether.png b/xray/textures/xray_nether.png new file mode 100644 index 0000000..2768db2 Binary files /dev/null and b/xray/textures/xray_nether.png differ diff --git a/xray/textures/xray_stone.png b/xray/textures/xray_stone.png new file mode 100644 index 0000000..f3e62a6 Binary files /dev/null and b/xray/textures/xray_stone.png differ