Cristiano Magro
2020-10-13 3d38d757384579ee62dd2f58594e376a34585bfb
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
143
0fb44d 144   if timber_nodenames[node_name]
CM 145     or (xnotreetap_leaves and minetest.get_item_group(node_name, "leaves") ~= 0)
146     or minetest.get_item_group(node_name, "tree") ~= 0 then
4c9935 147     minetest.log("action", "[Xno Tree Tap] "..node_name.." good node to collect.") --print to log
0fb44d 148     return true
CM 149   end
08b09a 150
0fb44d 151   return false
08b09a 152 end
CM 153
154 -- Table for saving what was sawed down
155 local produced = {}
3d38d7 156
CM 157 local function myString (v)
158   return v.x.." "..v.y.." "..v.z.." "
159 end
08b09a 160
CM 161 -- Save the items sawed down so that we can drop them in a nice single stack
162 local function handle_drops(drops)
0fb44d 163   for _, item in ipairs(drops) do
CM 164     local stack = ItemStack(item)
165     local name = stack:get_name()
166     local p = produced[name]
167     if not p then
168       produced[name] = stack
169     else
170       p:set_count(p:get_count() + stack:get_count())
171     end
172   end
08b09a 173 end
CM 174
175 --- Iterator over positions to try to saw around a sawed node.
176 -- This returns positions in a 3x1x3 area around the position, plus the
177 -- position above it.  This does not return the bottom position to prevent
178 -- the chainsaw from cutting down nodes below the cutting position.
179 -- @param pos Sawing position.
180 local function iterSawTries(pos)
0fb44d 181   -- Copy position to prevent mangling it
CM 182   local pos = vector.new(pos)
183   local i = 0
08b09a 184
0fb44d 185   return function()
CM 186     i = i + 1
187     -- Given a (top view) area like so (where 5 is the starting position):
188     -- X -->
189     -- Z 123
190     -- | 456
191     -- V 789
192     -- This will return positions 1, 4, 7, 2, 8 (skip 5), 3, 6, 9,
193     -- and the position above 5.
194     if i == 1 then
195       -- Move to starting position
196       pos.x = pos.x - 1
197       pos.z = pos.z - 1
198     elseif i == 4 or i == 7 then
199       -- Move to next X and back to start of Z when we reach
200       -- the end of a Z line.
201       pos.x = pos.x + 1
202       pos.z = pos.z - 2
203     elseif i == 5 then
204       -- Skip the middle position (we've already run on it)
205       -- and double-increment the counter.
206       pos.z = pos.z + 2
207       i = i + 1
208     elseif i <= 9 then
209       -- Go to next Z.
210       pos.z = pos.z + 1
211     elseif i == 10 then
212       -- Move back to center and up.
213       -- The Y+ position must be last so that we don't dig
214       -- straight upward and not come down (since the Y-
215       -- position isn't checked).
216       pos.x = pos.x - 1
217       pos.z = pos.z - 1
218       pos.y = pos.y + 1
219     else
220       return nil
221     end
222     return pos
223   end
08b09a 224 end
CM 225
4c9935 226 -- inserisco un valore in tabella
CM 227 local function add_table(table, toadd)
3d38d7 228   table:set(toadd.x, toadd.y, toadd.z, true)
4c9935 229 end
08b09a 230
4c9935 231 -- bool controllo se un valore รจ presente in tabella
3d38d7 232 local function in_table(table, pos)
CM 233   local val = table:get(pos.x, pos.y, pos.z)
234   local visited = 'NO'
235   
236   if val ~= nil then
237     if val then 
238       visited = 'SI'
4c9935 239     end
3d38d7 240     minetest.log("action", "[Xno Tree Tap] "..myString(pos).." visited? " .. visited) --print to log
CM 241     return val
4c9935 242   end
CM 243   return false
244 end
08b09a 245
4c9935 246 -- verifico se un nodo รจ nella tabella visitato
CM 247 local function check_if_node_visited (pos)
3d38d7 248   return in_table(nodeVisited, pos)
4c9935 249 end
CM 250
251 local function set_node_visited (pos)
252   minetest.log("action", "[Xno Tree Tap] "..myString(pos).." set node visited.") --print to log
3d38d7 253   add_table(nodeVisited, pos)
4c9935 254 end
08b09a 255
CM 256 -- This function does all the hard work. Recursively we dig the node at hand
257 -- if it is in the table and then search the surroundings for more stuff to dig.
258 local function recursive_dig(pos, remaining_charge)
0fb44d 259   if remaining_charge < xnotreetap_charge_per_node then
CM 260     return remaining_charge
261   end
4c9935 262
CM 263   if check_if_node_visited(pos) then
264     return remaining_charge
265   end
08b09a 266
0fb44d 267   if not check_if_node_sawed(pos) then
CM 268     return remaining_charge
269   end
08b09a 270
4c9935 271   local node = minetest.get_node(pos)
08b09a 272
0fb44d 273   if node.name == "moretrees:rubber_tree_trunk" then
CM 274     --raccolta gomma
275     node.name = "moretrees:rubber_tree_trunk_empty"
276     minetest.swap_node(pos, node)
4c9935 277     handle_drops(minetest.get_node_drops("technic:raw_latex", ""))
0fb44d 278     remaining_charge = remaining_charge - xnotreetap_charge_per_node
CM 279
280     -- Wood found - cut it
281     handle_drops(minetest.get_node_drops(node.name, ""))
282     minetest.remove_node(pos)
283     remaining_charge = remaining_charge - xnotreetap_charge_per_node
284
4c9935 285     --    if not technic.creative_mode then
CM 286     --      local item_wear = tonumber(itemstack:get_wear())
287     --      item_wear = item_wear + 819
288     --      if item_wear > 65535 then
289     --        itemstack:clear()
290     --        return itemstack
291     --      end
292     --      itemstack:set_wear(item_wear)
293     --    end
0fb44d 294   end
4c9935 295
CM 296   set_node_visited(pos)
0fb44d 297
CM 298   -- Check surroundings and run recursively if any charge left
299   for npos in iterSawTries(pos) do
300     if remaining_charge < xnotreetap_charge_per_node then
301       break
302     end
303     if check_if_node_sawed(npos) then
304       remaining_charge = recursive_dig(npos, remaining_charge)
305     else
306       minetest.check_for_falling(npos)
307     end
308   end
309   return remaining_charge
08b09a 310 end
CM 311
312 -- Function to randomize positions for new node drops
313 local function get_drop_pos(pos)
0fb44d 314   local drop_pos = {}
08b09a 315
0fb44d 316   for i = 0, 8 do
CM 317     -- Randomize position for a new drop
318     drop_pos.x = pos.x + math.random(-3, 3)
319     drop_pos.y = pos.y - 1
320     drop_pos.z = pos.z + math.random(-3, 3)
08b09a 321
0fb44d 322     -- Move the randomized position upwards until
CM 323     -- the node is air or unloaded.
324     for y = drop_pos.y, drop_pos.y + 5 do
325       drop_pos.y = y
326       local node = minetest.get_node_or_nil(drop_pos)
08b09a 327
0fb44d 328       if not node then
CM 329         -- If the node is not loaded yet simply drop
330         -- the item at the original digging position.
331         return pos
332       elseif node.name == "air" then
333         -- Add variation to the entity drop position,
334         -- but don't let drops get too close to the edge
335         drop_pos.x = drop_pos.x + (math.random() * 0.8) - 0.5
336         drop_pos.z = drop_pos.z + (math.random() * 0.8) - 0.5
337         return drop_pos
338       end
339     end
340   end
08b09a 341
0fb44d 342   -- Return the original position if this takes too long
CM 343   return pos
08b09a 344 end
CM 345
346 -- Chainsaw entry point
347 local function xnotreetap_dig(pos, current_charge)
0fb44d 348   -- Start sawing things down
CM 349   local remaining_charge = recursive_dig(pos, current_charge)
350   minetest.sound_play("chainsaw", {pos = pos, gain = 1.0,
351     max_hear_distance = 10})
08b09a 352
0fb44d 353   -- Now drop items for the player
CM 354   for name, stack in pairs(produced) do
355     -- Drop stacks of stack max or less
356     local count, max = stack:get_count(), stack:get_stack_max()
357     stack:set_count(max)
358     while count > max do
359       minetest.add_item(get_drop_pos(pos), stack)
360       count = count - max
361     end
362     stack:set_count(count)
363     minetest.add_item(get_drop_pos(pos), stack)
364   end
08b09a 365
0fb44d 366   -- Clean up
CM 367   produced = {}
08b09a 368
0fb44d 369   return remaining_charge
08b09a 370 end
CM 371
482cb1 372 minetest.register_tool("technic:xnotreetap", {
0fb44d 373   description = S("Xno Tree Tap"),
CM 374   inventory_image = "technic_tree_tap.png",
3190d2 375
0fb44d 376   stack_max = 1,
4c9935 377
0fb44d 378   wear_represents = "technic_RE_charge",
CM 379   on_refill = technic.refill_RE_charge,
3190d2 380
0fb44d 381   on_use = function(itemstack, user, pointed_thing)
CM 382     if pointed_thing.type ~= "node" then
383       return itemstack
384     end
08b09a 385
0fb44d 386     --check tool charge
CM 387     local meta = minetest.deserialize(itemstack:get_metadata())
388     if not meta or not meta.charge or
389       meta.charge < xnotreetap_charge_per_node then
390       return
391     end
08b09a 392
0fb44d 393     --check node protection
CM 394     local pos = pointed_thing.under
395     if minetest.is_protected(pos, user:get_player_name()) then
396       minetest.record_protection_violation(pos, user:get_player_name())
397       return
398     end
08b09a 399
CM 400
0fb44d 401     --can collect only from rubber
CM 402     local node = minetest.get_node(pos)
403     local node_name = node.name
404     if node_name ~= "moretrees:rubber_tree_trunk" then
405       return
406     end
4c9935 407
0fb44d 408     -- Send current charge to digging function so that the
CM 409     -- chainsaw will stop after digging a number of nodes
410     meta.charge = xnotreetap_dig(pointed_thing.under, meta.charge)
411     if not technic.creative_mode then
412       technic.set_RE_wear(itemstack, meta.charge, xnotreetap_max_charge)
413       itemstack:set_metadata(minetest.serialize(meta))
414     end
415     return itemstack
08b09a 416
0fb44d 417   end,
482cb1 418 })
CM 419
420 minetest.register_craft({
0fb44d 421   output = "technic:xnotreetap",
CM 422   recipe = {
423     {"pipeworks:tube_1", "group:wood",    "default:stick"},
424     {"technic:battery",  "default:stick", "default:stick"},
425     {"technic:battery",  "default:stick", "default:stick"},
426   },
482cb1 427 })
CM 428
3190d2 429 --minetest.register_abm({
CM 430 --  label = "Tools: xno tree tap",
431 --  nodenames = {"moretrees:rubber_tree_trunk_empty"},
432 --  interval = 60,
433 --  chance = 15,
434 --  action = function(pos, node)
435 --    if minetest.find_node_near(pos, (moretrees and moretrees.leafdecay_radius) or 5, {"moretrees:rubber_tree_leaves"}) then
436 --      node.name = "moretrees:rubber_tree_trunk"
437 --      minetest.swap_node(pos, node)
438 --    end
439 --  end
440 --})
08b09a 441
CM 442
443