-- Configuration local xnotreetap_max_charge = 30000 -- Maximum charge of the saw -- Gives 2500 nodes on a single charge (about 50 complete normal trees) local xnotreetap_charge_per_node = 12 -- Cut down tree leaves. Leaf decay may cause slowness on large trees -- if this is disabled. local xnotreetap_leaves = true -- First value is node name; second is whether the node is considered even if xnotreetap_leaves is false. local nodes = { -- Rubber trees from moretrees or technic_worldgen if moretrees isn't installed {"moretrees:rubber_tree_trunk_empty", true}, {"moretrees:rubber_tree_trunk", true}, {"moretrees:rubber_tree_leaves", false}, } -- Remember node visited recursive and not dig twice local node_visited = {} local timber_nodenames = {} for _, node in pairs(nodes) do if xnotreetap_leaves or node[2] then timber_nodenames[node[1]] = true end end local S = technic.getter technic.register_power_tool("technic:xnotreetap", xnotreetap_max_charge) local mesecons_materials = minetest.get_modpath("mesecons_materials") -- This function checks if the specified node should be sawed local function check_if_node_sawed(pos) local node = minetest.get_node(pos) local node_name = node.name if timber_nodenames[node_name] or (xnotreetap_leaves and minetest.get_item_group(node_name, "leaves") ~= 0) or minetest.get_item_group(node_name, "tree") ~= 0 then minetest.log("action", "[Xno Tree Tap] "..node_name.." good node to collect.") --print to log return true end return false end -- Table for saving what was sawed down local produced = {} -- Save the items sawed down so that we can drop them in a nice single stack local function handle_drops(drops) for _, item in ipairs(drops) do local stack = ItemStack(item) local name = stack:get_name() local p = produced[name] if not p then produced[name] = stack else p:set_count(p:get_count() + stack:get_count()) end end end --- Iterator over positions to try to saw around a sawed node. -- This returns positions in a 3x1x3 area around the position, plus the -- position above it. This does not return the bottom position to prevent -- the chainsaw from cutting down nodes below the cutting position. -- @param pos Sawing position. local function iterSawTries(pos) -- Copy position to prevent mangling it local pos = vector.new(pos) local i = 0 return function() i = i + 1 -- Given a (top view) area like so (where 5 is the starting position): -- X --> -- Z 123 -- | 456 -- V 789 -- This will return positions 1, 4, 7, 2, 8 (skip 5), 3, 6, 9, -- and the position above 5. if i == 1 then -- Move to starting position pos.x = pos.x - 1 pos.z = pos.z - 1 elseif i == 4 or i == 7 then -- Move to next X and back to start of Z when we reach -- the end of a Z line. pos.x = pos.x + 1 pos.z = pos.z - 2 elseif i == 5 then -- Skip the middle position (we've already run on it) -- and double-increment the counter. pos.z = pos.z + 2 i = i + 1 elseif i <= 9 then -- Go to next Z. pos.z = pos.z + 1 elseif i == 10 then -- Move back to center and up. -- The Y+ position must be last so that we don't dig -- straight upward and not come down (since the Y- -- position isn't checked). pos.x = pos.x - 1 pos.z = pos.z - 1 pos.y = pos.y + 1 else return nil end return pos end end -- inserisco un valore in tabella local function add_table(table, toadd) local i = 1 while true do local o = table[i] if o == toadd then return end if o == nil then break end i = i + 1 end table[i] = toadd end -- bool controllo se un valore è presente in tabella local function in_table(table, value) local i = 1 while true do local o = table[i] if o == value then minetest.log("action", "[Xno Tree Tap] "..value.." visited.") --print to log return true end if o == nil then break end i = i + 1 end return false end -- verifico se un nodo è nella tabella visitato local function check_if_node_visited (pos) return in_table(node_visited, pos) end local function myString (v) return v.x.." "..v.y.." "..v.z.." " end local function set_node_visited (pos) minetest.log("action", "[Xno Tree Tap] "..myString(pos).." set node visited.") --print to log add_table(node_visited, pos) end -- This function does all the hard work. Recursively we dig the node at hand -- if it is in the table and then search the surroundings for more stuff to dig. local function recursive_dig(pos, remaining_charge) if remaining_charge < xnotreetap_charge_per_node then return remaining_charge end if check_if_node_visited(pos) then return remaining_charge end if not check_if_node_sawed(pos) then return remaining_charge end local node = minetest.get_node(pos) if node.name == "moretrees:rubber_tree_trunk" then --raccolta gomma node.name = "moretrees:rubber_tree_trunk_empty" minetest.swap_node(pos, node) handle_drops(minetest.get_node_drops("technic:raw_latex", "")) remaining_charge = remaining_charge - xnotreetap_charge_per_node -- Wood found - cut it handle_drops(minetest.get_node_drops(node.name, "")) minetest.remove_node(pos) remaining_charge = remaining_charge - xnotreetap_charge_per_node -- if not technic.creative_mode then -- local item_wear = tonumber(itemstack:get_wear()) -- item_wear = item_wear + 819 -- if item_wear > 65535 then -- itemstack:clear() -- return itemstack -- end -- itemstack:set_wear(item_wear) -- end end set_node_visited(pos) -- Check surroundings and run recursively if any charge left for npos in iterSawTries(pos) do if remaining_charge < xnotreetap_charge_per_node then break end if check_if_node_sawed(npos) then remaining_charge = recursive_dig(npos, remaining_charge) else minetest.check_for_falling(npos) end end return remaining_charge end -- Function to randomize positions for new node drops local function get_drop_pos(pos) local drop_pos = {} for i = 0, 8 do -- Randomize position for a new drop drop_pos.x = pos.x + math.random(-3, 3) drop_pos.y = pos.y - 1 drop_pos.z = pos.z + math.random(-3, 3) -- Move the randomized position upwards until -- the node is air or unloaded. for y = drop_pos.y, drop_pos.y + 5 do drop_pos.y = y local node = minetest.get_node_or_nil(drop_pos) if not node then -- If the node is not loaded yet simply drop -- the item at the original digging position. return pos elseif node.name == "air" then -- Add variation to the entity drop position, -- but don't let drops get too close to the edge drop_pos.x = drop_pos.x + (math.random() * 0.8) - 0.5 drop_pos.z = drop_pos.z + (math.random() * 0.8) - 0.5 return drop_pos end end end -- Return the original position if this takes too long return pos end -- Chainsaw entry point local function xnotreetap_dig(pos, current_charge) -- Start sawing things down local remaining_charge = recursive_dig(pos, current_charge) minetest.sound_play("chainsaw", {pos = pos, gain = 1.0, max_hear_distance = 10}) -- Now drop items for the player for name, stack in pairs(produced) do -- Drop stacks of stack max or less local count, max = stack:get_count(), stack:get_stack_max() stack:set_count(max) while count > max do minetest.add_item(get_drop_pos(pos), stack) count = count - max end stack:set_count(count) minetest.add_item(get_drop_pos(pos), stack) end -- Clean up produced = {} return remaining_charge end minetest.register_tool("technic:xnotreetap", { description = S("Xno Tree Tap"), inventory_image = "technic_tree_tap.png", stack_max = 1, wear_represents = "technic_RE_charge", on_refill = technic.refill_RE_charge, on_use = function(itemstack, user, pointed_thing) if pointed_thing.type ~= "node" then return itemstack end --check tool charge local meta = minetest.deserialize(itemstack:get_metadata()) if not meta or not meta.charge or meta.charge < xnotreetap_charge_per_node then return end --check node protection local pos = pointed_thing.under if minetest.is_protected(pos, user:get_player_name()) then minetest.record_protection_violation(pos, user:get_player_name()) return end --can collect only from rubber local node = minetest.get_node(pos) local node_name = node.name if node_name ~= "moretrees:rubber_tree_trunk" then return end -- Send current charge to digging function so that the -- chainsaw will stop after digging a number of nodes meta.charge = xnotreetap_dig(pointed_thing.under, meta.charge) if not technic.creative_mode then technic.set_RE_wear(itemstack, meta.charge, xnotreetap_max_charge) itemstack:set_metadata(minetest.serialize(meta)) end return itemstack end, }) minetest.register_craft({ output = "technic:xnotreetap", recipe = { {"pipeworks:tube_1", "group:wood", "default:stick"}, {"technic:battery", "default:stick", "default:stick"}, {"technic:battery", "default:stick", "default:stick"}, }, }) --minetest.register_abm({ -- label = "Tools: xno tree tap", -- nodenames = {"moretrees:rubber_tree_trunk_empty"}, -- interval = 60, -- chance = 15, -- action = function(pos, node) -- if minetest.find_node_near(pos, (moretrees and moretrees.leafdecay_radius) or 5, {"moretrees:rubber_tree_leaves"}) then -- node.name = "moretrees:rubber_tree_trunk" -- minetest.swap_node(pos, node) -- end -- end --})