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