--[[ TechAge ======= Copyright (C) 2019 Joachim Stolberg AGPL v3 See LICENSE.txt for more information Helper functions ]]-- -- for lazy programmers local P = minetest.string_to_pos local M = minetest.get_meta -- Input data to generate the Param2ToDir table local Input = { 8,9,10,11, -- 1 16,17,18,19, -- 2 4,5,6,7, -- 3 12,13,14,15, -- 4 0,1,2,3, -- 5 20,21,22,23, -- 6 } -- allowed for digging local RegisteredNodesToBeDug = {} function techage.register_node_to_be_dug(name) RegisteredNodesToBeDug[name] = true end -- translation from param2 to dir (out of the node upwards) local Param2Dir = {} for idx,val in ipairs(Input) do Param2Dir[val] = math.floor((idx - 1) / 4) + 1 end -- used by lamps and power switches function techage.determine_node_bottom_as_dir(node) return tubelib2.Turn180Deg[Param2Dir[node.param2] or 1] end function techage.determine_node_top_as_dir(node) return Param2Dir[node.param2] or 1 end -- rotation rules (screwdriver) for wallmounted "facedir" nodes function techage.rotate_wallmounted(param2) local offs = math.floor(param2 / 4) * 4 local rot = ((param2 % 4) + 1) % 4 return offs + rot end function techage.in_range(val, min, max) val = tonumber(val) if val < min then return min end if val > max then return max end return val end function techage.one_of(val, selection) for _,v in ipairs(selection) do if val == v then return val end end return selection[1] end function techage.index(list, x) for idx, v in pairs(list) do if v == x then return idx end end return nil end function techage.in_list(list, x) for idx, v in pairs(list) do if v == x then return true end end return false end function techage.add_to_set(set, x) if not techage.index(set, x) then table.insert(set, x) end end function techage.get_node_lvm(pos) local node = minetest.get_node_or_nil(pos) if node then return node end local vm = minetest.get_voxel_manip() local MinEdge, MaxEdge = vm:read_from_map(pos, pos) local data = vm:get_data() local param2_data = vm:get_param2_data() local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge}) local idx = area:indexp(pos) if data[idx] and param2_data[idx] then return { name = minetest.get_name_from_content_id(data[idx]), param2 = param2_data[idx] } end return {name="ignore", param2=0} end -- -- Functions used to hide electric cable and biogas pipes -- -- Overridden method of tubelib2! function techage.get_primary_node_param2(pos, dir) local npos = vector.add(pos, tubelib2.Dir6dToVector[dir or 0]) local param2 = M(npos):get_int("tl2_param2") if param2 ~= 0 then return param2, npos end end -- Overridden method of tubelib2! function techage.is_primary_node(pos, dir) local npos = vector.add(pos, tubelib2.Dir6dToVector[dir or 0]) local param2 = M(npos):get_int("tl2_param2") return param2 ~= 0 end function techage.is_air_like(name) local ndef = minetest.registered_nodes[name] if ndef and ndef.buildable_to then return true end return false end -- returns true, if node can be dug, otherwise false function techage.can_node_dig(node, ndef) if RegisteredNodesToBeDug[node.name] then return true end if not ndef then return false end if node.name == "ignore" then return false end if node.name == "air" then return true end if ndef.buildable_to == true then return true end if ndef.diggable == false then return false end if ndef.after_dig_node then return false end -- add it to the white list RegisteredNodesToBeDug[node.name] = true return true end techage.dig_states = { NOT_DIGGABLE = 1, INV_FULL = 2, DUG = 3 } -- Digs a node like a player would by utilizing a fake player object. -- add_to_inv(itemstacks) is a method that should try to add the dropped stacks to an appropriate inventory. -- The node will only be dug, if add_to_inv(itemstacks) returns true. function techage.dig_like_player(pos, fake_player, add_to_inv) local node = techage.get_node_lvm(pos) local ndef = minetest.registered_nodes[node.name] if not ndef or ndef.diggable == false or (ndef.can_dig and not ndef.can_dig(pos, fake_player)) then return techage.dig_states.NOT_DIGGABLE end local drop_as_strings = minetest.get_node_drops(node) local drop_as_stacks = {} for _,itemstring in ipairs(drop_as_strings) do drop_as_stacks[#drop_as_stacks+1] = ItemStack(itemstring) end local meta = M(pos) if ndef.preserve_metadata then ndef.preserve_metadata(pos, node, meta, drop_as_stacks) end if add_to_inv(drop_as_stacks) then local oldmeta = meta:to_table() minetest.remove_node(pos) if ndef.after_dig_node then ndef.after_dig_node(pos, node, oldmeta, fake_player) end return techage.dig_states.DUG end return techage.dig_states.INV_FULL end local function handle_drop(drop) -- To keep it simple, return only the item with the lowest rarity if drop.items then local rarity = 9999 local name for idx,item in ipairs(drop.items) do if item.rarity and item.rarity < rarity then rarity = item.rarity name = item.items[1] -- take always the first item else return item.items[1] -- take always the first item end end return name end return false end -- returns the node name, if node can be dropped, otherwise nil function techage.dropped_node(node, ndef) if node.name == "air" then return end --if ndef.buildable_to == true then return end if not ndef.diggable then return end if ndef.drop == "" then return end if type(ndef.drop) == "table" then return handle_drop(ndef.drop) end return ndef.drop or node.name end -- needed for windmill plants local function determine_ocean_ids() techage.OceanIdTbl = {} for name, _ in pairs(minetest.registered_biomes) do if string.find(name, "ocean") then local id = minetest.get_biome_id(name) --print(id, name) techage.OceanIdTbl[id] = true end end end determine_ocean_ids() -- check if natural water is on given position (water placed by player has param2 = 1) function techage.is_ocean(pos) if pos.y ~= 1 then return false end local node = techage.get_node_lvm(pos) if node.name ~= "default:water_source" then return false end if node.param2 == 1 then return false end return true end function techage.item_image(x, y, itemname, count) local name, size = unpack(string.split(itemname, " ")) size = count and count or size size = tonumber(size) or 1 local label = "" local text = minetest.formspec_escape(ItemStack(itemname):get_description()) local tooltip = "tooltip["..x..","..y..";1,1;"..text..";#0C3D32;#FFFFFF]" if minetest.registered_tools[name] and size > 1 then local offs = 0 if size < 10 then offs = 0.65 elseif size < 100 then offs = 0.5 elseif size < 1000 then offs = 0.35 else offs = 0.2 end label = "label["..(x + offs)..","..(y + 0.45)..";"..tostring(size).."]" end return "box["..x..","..y..";0.85,0.9;#808080]".. "item_image["..x..","..y..";1,1;"..itemname.."]".. tooltip.. label end function techage.item_image_small(x, y, itemname, tooltip_prefix) local name = unpack(string.split(itemname, " ")) local tooltip = "" local ndef = minetest.registered_nodes[name] or minetest.registered_items[name] or minetest.registered_craftitems[name] if ndef and ndef.description then local text = minetest.formspec_escape(ndef.description) tooltip = "tooltip["..x..","..y..";0.8,0.8;"..tooltip_prefix..": "..text..";#0C3D32;#FFFFFF]" end return "box["..x..","..y..";0.65,0.7;#808080]".. "item_image["..x..","..y..";0.8,0.8;"..name.."]".. tooltip end function techage.mydump(o, indent, nested, level) local t = type(o) if not level and t == "userdata" then -- when userdata (e.g. player) is passed directly, print its metatable: return "userdata metatable: " .. techage.mydump(getmetatable(o)) end if t ~= "table" then return basic_dump(o) end -- Contains table -> true/nil of currently nested tables nested = nested or {} if nested[o] then return "" end nested[o] = true indent = " " level = level or 1 local t = {} local dumped_indexes = {} for i, v in ipairs(o) do t[#t + 1] = techage.mydump(v, indent, nested, level + 1) dumped_indexes[i] = true end for k, v in pairs(o) do if not dumped_indexes[k] then if type(k) ~= "string" or not is_valid_identifier(k) then k = "["..techage.mydump(k, indent, nested, level + 1).."]" end v = techage.mydump(v, indent, nested, level + 1) t[#t + 1] = k.." = "..v end end nested[o] = nil if indent ~= "" then local indent_str = string.rep(indent, level) local end_indent_str = string.rep(indent, level - 1) return string.format("{%s%s%s}", indent_str, table.concat(t, ","..indent_str), end_indent_str) end return "{"..table.concat(t, ", ").."}" end -- title bar help (width is the fornmspec width) function techage.question_mark_help(width, tooltip) local x = width- 0.6 return "label["..x..",-0.1;"..minetest.colorize("#000000", minetest.formspec_escape("[?]")).."]".. "tooltip["..x..",-0.1;0.5,0.5;"..tooltip..";#0C3D32;#FFFFFF]" end