Cristiano Magro
2020-10-14 d781261523f49191a87f8eda4b8c5ea01272f60d
commit | author | age
d78126 1 local technic_modpath = minetest.get_modpath( "technic" )
CM 2 dofile( technic_modpath .. "/tools/class_pointset.lua" ) 
3d38d7 3
08b09a 4 -- Configuration
CM 5
3190d2 6 local xnotreetap_max_charge      = 30000 -- Maximum charge of the saw
08b09a 7 -- Gives 2500 nodes on a single charge (about 50 complete normal trees)
3190d2 8 local xnotreetap_charge_per_node = 12
08b09a 9 -- Cut down tree leaves.  Leaf decay may cause slowness on large trees
CM 10 -- if this is disabled.
3190d2 11 local xnotreetap_leaves = true
08b09a 12
767b74 13 -- First value is node name; second is whether the node is considered even if xnotreetap_leaves is false.
08b09a 14 local nodes = {
0fb44d 15   -- Rubber trees from moretrees or technic_worldgen if moretrees isn't installed
CM 16   {"moretrees:rubber_tree_trunk_empty", true},
17   {"moretrees:rubber_tree_trunk", true},
18   {"moretrees:rubber_tree_leaves", false},
08b09a 19 }
0fb44d 20
4c9935 21 -- Remember node visited recursive and not dig twice
3d38d7 22 local nodeVisited = Pointset.create()
08b09a 23
CM 24 local timber_nodenames = {}
25 for _, node in pairs(nodes) do
767b74 26   if xnotreetap_leaves or node[2] then
0fb44d 27     timber_nodenames[node[1]] = true
CM 28   end
08b09a 29 end
CM 30
482cb1 31
CM 32 local S = technic.getter
33
08b09a 34 technic.register_power_tool("technic:xnotreetap", xnotreetap_max_charge)
3190d2 35
CM 36 local mesecons_materials = minetest.get_modpath("mesecons_materials")
37
08b09a 38
CM 39 -- This function checks if the specified node should be sawed
40 local function check_if_node_sawed(pos)
4c9935 41   local node = minetest.get_node(pos)
CM 42   local node_name = node.name
43
0fb44d 44   if timber_nodenames[node_name]
CM 45     or (xnotreetap_leaves and minetest.get_item_group(node_name, "leaves") ~= 0)
46     or minetest.get_item_group(node_name, "tree") ~= 0 then
4c9935 47     minetest.log("action", "[Xno Tree Tap] "..node_name.." good node to collect.") --print to log
0fb44d 48     return true
CM 49   end
08b09a 50
0fb44d 51   return false
08b09a 52 end
CM 53
54 -- Table for saving what was sawed down
55 local produced = {}
3d38d7 56
CM 57 local function myString (v)
58   return v.x.." "..v.y.." "..v.z.." "
59 end
08b09a 60
CM 61 -- Save the items sawed down so that we can drop them in a nice single stack
62 local function handle_drops(drops)
0fb44d 63   for _, item in ipairs(drops) do
CM 64     local stack = ItemStack(item)
65     local name = stack:get_name()
66     local p = produced[name]
67     if not p then
68       produced[name] = stack
69     else
70       p:set_count(p:get_count() + stack:get_count())
71     end
72   end
08b09a 73 end
CM 74
75 --- Iterator over positions to try to saw around a sawed node.
76 -- This returns positions in a 3x1x3 area around the position, plus the
77 -- position above it.  This does not return the bottom position to prevent
78 -- the chainsaw from cutting down nodes below the cutting position.
79 -- @param pos Sawing position.
80 local function iterSawTries(pos)
0fb44d 81   -- Copy position to prevent mangling it
CM 82   local pos = vector.new(pos)
83   local i = 0
08b09a 84
0fb44d 85   return function()
CM 86     i = i + 1
87     -- Given a (top view) area like so (where 5 is the starting position):
88     -- X -->
89     -- Z 123
90     -- | 456
91     -- V 789
92     -- This will return positions 1, 4, 7, 2, 8 (skip 5), 3, 6, 9,
93     -- and the position above 5.
94     if i == 1 then
95       -- Move to starting position
96       pos.x = pos.x - 1
97       pos.z = pos.z - 1
98     elseif i == 4 or i == 7 then
99       -- Move to next X and back to start of Z when we reach
100       -- the end of a Z line.
101       pos.x = pos.x + 1
102       pos.z = pos.z - 2
103     elseif i == 5 then
104       -- Skip the middle position (we've already run on it)
105       -- and double-increment the counter.
106       pos.z = pos.z + 2
107       i = i + 1
108     elseif i <= 9 then
109       -- Go to next Z.
110       pos.z = pos.z + 1
111     elseif i == 10 then
112       -- Move back to center and up.
113       -- The Y+ position must be last so that we don't dig
114       -- straight upward and not come down (since the Y-
115       -- position isn't checked).
116       pos.x = pos.x - 1
117       pos.z = pos.z - 1
118       pos.y = pos.y + 1
119     else
120       return nil
121     end
122     return pos
123   end
08b09a 124 end
CM 125
4c9935 126 -- inserisco un valore in tabella
CM 127 local function add_table(table, toadd)
3d38d7 128   table:set(toadd.x, toadd.y, toadd.z, true)
4c9935 129 end
08b09a 130
4c9935 131 -- bool controllo se un valore è presente in tabella
3d38d7 132 local function in_table(table, pos)
CM 133   local val = table:get(pos.x, pos.y, pos.z)
134   local visited = 'NO'
135   
136   if val ~= nil then
137     if val then 
138       visited = 'SI'
4c9935 139     end
3d38d7 140     minetest.log("action", "[Xno Tree Tap] "..myString(pos).." visited? " .. visited) --print to log
CM 141     return val
4c9935 142   end
CM 143   return false
144 end
08b09a 145
4c9935 146 -- verifico se un nodo è nella tabella visitato
CM 147 local function check_if_node_visited (pos)
3d38d7 148   return in_table(nodeVisited, pos)
4c9935 149 end
CM 150
151 local function set_node_visited (pos)
152   minetest.log("action", "[Xno Tree Tap] "..myString(pos).." set node visited.") --print to log
3d38d7 153   add_table(nodeVisited, pos)
4c9935 154 end
08b09a 155
CM 156 -- This function does all the hard work. Recursively we dig the node at hand
157 -- if it is in the table and then search the surroundings for more stuff to dig.
158 local function recursive_dig(pos, remaining_charge)
0fb44d 159   if remaining_charge < xnotreetap_charge_per_node then
CM 160     return remaining_charge
161   end
4c9935 162
CM 163   if check_if_node_visited(pos) then
164     return remaining_charge
165   end
08b09a 166
0fb44d 167   if not check_if_node_sawed(pos) then
CM 168     return remaining_charge
169   end
08b09a 170
4c9935 171   local node = minetest.get_node(pos)
08b09a 172
0fb44d 173   if node.name == "moretrees:rubber_tree_trunk" then
CM 174     --raccolta gomma
175     node.name = "moretrees:rubber_tree_trunk_empty"
176     minetest.swap_node(pos, node)
4c9935 177     handle_drops(minetest.get_node_drops("technic:raw_latex", ""))
0fb44d 178     remaining_charge = remaining_charge - xnotreetap_charge_per_node
CM 179
180     -- Wood found - cut it
e62876 181 --    handle_drops(minetest.get_node_drops(node.name, ""))
CM 182 --    minetest.remove_node(pos)
183 --    remaining_charge = remaining_charge - xnotreetap_charge_per_node
184   end
185
186   if node.name == "moretrees:rubber_tree_leaves" then
187     -- Leaves found - cut it
0fb44d 188     handle_drops(minetest.get_node_drops(node.name, ""))
CM 189     minetest.remove_node(pos)
190     remaining_charge = remaining_charge - xnotreetap_charge_per_node
191   end
4c9935 192
CM 193   set_node_visited(pos)
0fb44d 194
CM 195   -- Check surroundings and run recursively if any charge left
196   for npos in iterSawTries(pos) do
197     if remaining_charge < xnotreetap_charge_per_node then
198       break
199     end
200     if check_if_node_sawed(npos) then
201       remaining_charge = recursive_dig(npos, remaining_charge)
202     else
203       minetest.check_for_falling(npos)
204     end
205   end
206   return remaining_charge
08b09a 207 end
CM 208
209 -- Function to randomize positions for new node drops
210 local function get_drop_pos(pos)
0fb44d 211   local drop_pos = {}
08b09a 212
0fb44d 213   for i = 0, 8 do
CM 214     -- Randomize position for a new drop
215     drop_pos.x = pos.x + math.random(-3, 3)
216     drop_pos.y = pos.y - 1
217     drop_pos.z = pos.z + math.random(-3, 3)
08b09a 218
0fb44d 219     -- Move the randomized position upwards until
CM 220     -- the node is air or unloaded.
221     for y = drop_pos.y, drop_pos.y + 5 do
222       drop_pos.y = y
223       local node = minetest.get_node_or_nil(drop_pos)
08b09a 224
0fb44d 225       if not node then
CM 226         -- If the node is not loaded yet simply drop
227         -- the item at the original digging position.
228         return pos
229       elseif node.name == "air" then
230         -- Add variation to the entity drop position,
231         -- but don't let drops get too close to the edge
232         drop_pos.x = drop_pos.x + (math.random() * 0.8) - 0.5
233         drop_pos.z = drop_pos.z + (math.random() * 0.8) - 0.5
234         return drop_pos
235       end
236     end
237   end
08b09a 238
0fb44d 239   -- Return the original position if this takes too long
CM 240   return pos
08b09a 241 end
CM 242
243 -- Chainsaw entry point
244 local function xnotreetap_dig(pos, current_charge)
0fb44d 245   -- Start sawing things down
CM 246   local remaining_charge = recursive_dig(pos, current_charge)
247   minetest.sound_play("chainsaw", {pos = pos, gain = 1.0,
248     max_hear_distance = 10})
08b09a 249
0fb44d 250   -- Now drop items for the player
CM 251   for name, stack in pairs(produced) do
252     -- Drop stacks of stack max or less
253     local count, max = stack:get_count(), stack:get_stack_max()
254     stack:set_count(max)
255     while count > max do
256       minetest.add_item(get_drop_pos(pos), stack)
257       count = count - max
258     end
259     stack:set_count(count)
260     minetest.add_item(get_drop_pos(pos), stack)
261   end
08b09a 262
0fb44d 263   -- Clean up
CM 264   produced = {}
08b09a 265
0fb44d 266   return remaining_charge
08b09a 267 end
CM 268
482cb1 269 minetest.register_tool("technic:xnotreetap", {
0fb44d 270   description = S("Xno Tree Tap"),
CM 271   inventory_image = "technic_tree_tap.png",
3190d2 272
0fb44d 273   stack_max = 1,
4c9935 274
0fb44d 275   wear_represents = "technic_RE_charge",
CM 276   on_refill = technic.refill_RE_charge,
3190d2 277
0fb44d 278   on_use = function(itemstack, user, pointed_thing)
CM 279     if pointed_thing.type ~= "node" then
280       return itemstack
281     end
08b09a 282
0fb44d 283     --check tool charge
CM 284     local meta = minetest.deserialize(itemstack:get_metadata())
285     if not meta or not meta.charge or
286       meta.charge < xnotreetap_charge_per_node then
287       return
288     end
08b09a 289
0fb44d 290     --check node protection
CM 291     local pos = pointed_thing.under
292     if minetest.is_protected(pos, user:get_player_name()) then
293       minetest.record_protection_violation(pos, user:get_player_name())
294       return
295     end
08b09a 296
d78126 297     --reinizializze visited struct 
CM 298     nodeVisited = Pointset.create()
08b09a 299
d78126 300     --can collect only from rubber tree trunk
0fb44d 301     local node = minetest.get_node(pos)
CM 302     local node_name = node.name
303     if node_name ~= "moretrees:rubber_tree_trunk" then
304       return
305     end
4c9935 306
0fb44d 307     -- Send current charge to digging function so that the
CM 308     -- chainsaw will stop after digging a number of nodes
309     meta.charge = xnotreetap_dig(pointed_thing.under, meta.charge)
310     if not technic.creative_mode then
311       technic.set_RE_wear(itemstack, meta.charge, xnotreetap_max_charge)
312       itemstack:set_metadata(minetest.serialize(meta))
313     end
314     return itemstack
08b09a 315
0fb44d 316   end,
482cb1 317 })
CM 318
319 minetest.register_craft({
0fb44d 320   output = "technic:xnotreetap",
CM 321   recipe = {
322     {"pipeworks:tube_1", "group:wood",    "default:stick"},
323     {"technic:battery",  "default:stick", "default:stick"},
324     {"technic:battery",  "default:stick", "default:stick"},
325   },
482cb1 326 })
08b09a 327
CM 328