David Leal
2020-06-12 a8daa417c485ee20716ec050d4c676b5c91af773
commit | author | age
8e03d7 1 -- Configuration
R 2
9019eb 3 local chainsaw_max_charge      = 30000 -- Maximum charge of the saw
S 4 -- Gives 2500 nodes on a single charge (about 50 complete normal trees)
5 local chainsaw_charge_per_node = 12
6 -- Cut down tree leaves.  Leaf decay may cause slowness on large trees
7 -- if this is disabled.
8 local chainsaw_leaves = true
9
e66bb2 10 -- First value is node name; second is whether the node is considered even if chainsaw_leaves is false.
C 11 local nodes = {
12     -- The default trees
13     {"default:acacia_tree", true},
14     {"default:aspen_tree", true},
15     {"default:jungletree", true},
16     {"default:papyrus", true},
17     {"default:cactus", true},
18     {"default:tree", true},
19     {"default:apple", true},
20     {"default:pine_tree", true},
21     {"default:acacia_leaves", false},
22     {"default:aspen_leaves", false},
23     {"default:leaves", false},
24     {"default:jungleleaves", false},
25     {"default:pine_needles", false},
26
31a052 27     -- The default bushes
D 28     {"default:acacia_bush_stem", true},
29     {"default:bush_stem", true},
30     {"default:pine_bush_stem", true},
31     {"default:acacia_bush_leaves", false},
32     {"default:blueberry_bush_leaves", false},
33     {"default:blueberry_bush_leaves_with_berries", false},
34     {"default:bush_leaves", false},
35     {"default:pine_bush_needles", false},
36
e66bb2 37     -- Rubber trees from moretrees or technic_worldgen if moretrees isn't installed
C 38     {"moretrees:rubber_tree_trunk_empty", true},
39     {"moretrees:rubber_tree_trunk", true},
40     {"moretrees:rubber_tree_leaves", false},
41
31a052 42     -- Support moretrees (trunk)
e66bb2 43     {"moretrees:acacia_trunk", true},
C 44     {"moretrees:apple_tree_trunk", true},
45     {"moretrees:beech_trunk", true},
46     {"moretrees:birch_trunk", true},
31a052 47     {"moretrees:cedar_trunk", true},
D 48     {"moretrees:date_palm_ffruit_trunk", true},
49     {"moretrees:date_palm_fruit_trunk", true},
50     {"moretrees:date_palm_mfruit_trunk", true},
51     {"moretrees:date_palm_trunk", true},
e66bb2 52     {"moretrees:fir_trunk", true},
31a052 53     {"moretrees:jungletree_trunk", true},
e66bb2 54     {"moretrees:oak_trunk", true},
C 55     {"moretrees:palm_trunk", true},
31a052 56     {"moretrees:palm_fruit_trunk", true},
D 57     {"moretrees:palm_fruit_trunk_gen", true},
e66bb2 58     {"moretrees:pine_trunk", true},
31a052 59     {"moretrees:poplar_trunk", true},
e66bb2 60     {"moretrees:sequoia_trunk", true},
C 61     {"moretrees:spruce_trunk", true},
62     {"moretrees:willow_trunk", true},
31a052 63     -- Support moretrees (leaves)
e66bb2 64     {"moretrees:acacia_leaves", false},
C 65     {"moretrees:apple_tree_leaves", false},
31a052 66     {"moretrees:beech_leaves", false},
D 67     {"moretrees:birch_leaves", false},
68     {"moretrees:cedar_leaves", false},
69     {"moretrees:date_palm_leaves", false},
e66bb2 70     {"moretrees:fir_leaves", false},
C 71     {"moretrees:fir_leaves_bright", false},
72     {"moretrees:jungletree_leaves_green", false},
73     {"moretrees:jungletree_leaves_yellow", false},
74     {"moretrees:jungletree_leaves_red", false},
31a052 75     {"moretrees:oak_leaves", false},
D 76     {"moretrees:palm_leaves", false},
e66bb2 77     {"moretrees:poplar_leaves", false},
31a052 78     {"moretrees:pine_leaves", false},
D 79     {"moretrees:sequoia_leaves", false},
80     {"moretrees:spruce_leaves", false},
81     {"moretrees:willow_leaves", false},
82     -- Support moretrees (fruit)
83     {"moretrees:acorn", false},
84     {"moretrees:apple_blossoms", false},
85     {"moretrees:cedar_cone", false},
86     {"moretrees:coconut", false},
87     {"moretrees:coconut_0", false},
88     {"moretrees:coconut_1", false},
89     {"moretrees:coconut_2", false},
90     {"moretrees:coconut_3", false},
91     {"moretrees:dates_f0", false},
92     {"moretrees:dates_f1", false},
93     {"moretrees:dates_f2", false},
94     {"moretrees:dates_f3", false},
95     {"moretrees:dates_f4", false},
96     {"moretrees:dates_fn", false},
97     {"moretrees:dates_m0", false},
98     {"moretrees:dates_n", false},
99     {"moretrees:fir_cone", false},
100     {"moretrees:pine_cone", false},
101     {"moretrees:spruce_cone", false},
e66bb2 102
C 103     -- Support growing_trees
104     {"growing_trees:trunk", true},
105     {"growing_trees:medium_trunk", true},
106     {"growing_trees:big_trunk", true},
107     {"growing_trees:trunk_top", true},
108     {"growing_trees:trunk_sprout", true},
109     {"growing_trees:branch_sprout", true},
110     {"growing_trees:branch", true},
111     {"growing_trees:branch_xmzm", true},
112     {"growing_trees:branch_xpzm", true},
113     {"growing_trees:branch_xmzp", true},
114     {"growing_trees:branch_xpzp", true},
115     {"growing_trees:branch_zz", true},
116     {"growing_trees:branch_xx", true},
117     {"growing_trees:leaves", false},
118
119     -- Support cool_trees
120     {"bamboo:trunk", true},
121     {"bamboo:leaves", false},
122     {"birch:trunk", true},
123     {"birch:leaves", false},
124     {"cherrytree:trunk", true},
125     {"cherrytree:blossom_leaves", false},
126     {"cherrytree:leaves", false},
127     {"chestnuttree:trunk", true},
128     {"chestnuttree:leaves", false},
129     {"clementinetree:trunk", true},
130     {"clementinetree:leaves", false},
131     {"ebony:trunk", true},
132     {"ebony:creeper", false},
133     {"ebony:creeper_leaves", false},
134     {"ebony:leaves", false},
135     {"jacaranda:trunk", true},
136     {"jacaranda:blossom_leaves", false},
137     {"larch:trunk", true},
138     {"larch:leaves", false},
139     {"lemontree:trunk", true},
140     {"lemontree:leaves", false},
141     {"mahogany:trunk", true},
142     {"mahogany:leaves", false},
143     {"palm:trunk", true},
144     {"palm:leaves", false},
145
146     -- Support growing_cactus
147     {"growing_cactus:sprout", true},
148     {"growing_cactus:branch_sprout_vertical", true},
149     {"growing_cactus:branch_sprout_vertical_fixed", true},
150     {"growing_cactus:branch_sprout_xp", true},
151     {"growing_cactus:branch_sprout_xm", true},
152     {"growing_cactus:branch_sprout_zp", true},
153     {"growing_cactus:branch_sprout_zm", true},
154     {"growing_cactus:trunk", true},
155     {"growing_cactus:branch_trunk", true},
156     {"growing_cactus:branch", true},
157     {"growing_cactus:branch_xp", true},
158     {"growing_cactus:branch_xm", true},
159     {"growing_cactus:branch_zp", true},
160     {"growing_cactus:branch_zm", true},
161     {"growing_cactus:branch_zz", true},
162     {"growing_cactus:branch_xx", true},
163
164     -- Support farming_plus
165     {"farming_plus:banana_leaves", false},
166     {"farming_plus:banana", false},
167     {"farming_plus:cocoa_leaves", false},
168     {"farming_plus:cocoa", false},
169
170     -- Support nature
171     {"nature:blossom", false},
172
173     -- Support snow
174     {"snow:needles", false},
175     {"snow:needles_decorated", false},
176     {"snow:star", false},
177
178     -- Support vines (also generated by moretrees if available)
179     {"vines:vines", false},
180
181     {"trunks:moss", false},
182     {"trunks:moss_fungus", false},
183     {"trunks:treeroot", false},
8e03d7 184 }
82cba9 185
e66bb2 186 local timber_nodenames = {}
C 187 for _, node in pairs(nodes) do
188     if chainsaw_leaves or node[2] then
189         timber_nodenames[node[1]] = true
72d541 190     end
T 191 end
192
5cf765 193 local S = technic.getter
S 194
195 technic.register_power_tool("technic:chainsaw", chainsaw_max_charge)
196
31a052 197 -- This function checks if the specified node should be sawed
D 198 local function check_if_node_sawed(pos)
199     local node_name = minetest.get_node(pos).name
200     if timber_nodenames[node_name]
201             or (chainsaw_leaves and minetest.get_item_group(node_name, "leaves") ~= 0)
202             or minetest.get_item_group(node_name, "tree") ~= 0 then
203         return true
204     end
205
206     return false
207 end
208
8e03d7 209 -- Table for saving what was sawed down
9019eb 210 local produced = {}
8e03d7 211
9019eb 212 -- Save the items sawed down so that we can drop them in a nice single stack
S 213 local function handle_drops(drops)
214     for _, item in ipairs(drops) do
215         local stack = ItemStack(item)
216         local name = stack:get_name()
217         local p = produced[name]
218         if not p then
219             produced[name] = stack
c63658 220         else
9019eb 221             p:set_count(p:get_count() + stack:get_count())
c63658 222         end
9019eb 223     end
S 224 end
225
226 --- Iterator over positions to try to saw around a sawed node.
ec069a 227 -- This returns positions in a 3x1x3 area around the position, plus the
S 228 -- position above it.  This does not return the bottom position to prevent
229 -- the chainsaw from cutting down nodes below the cutting position.
16302c 230 -- @param pos Sawing position.
9019eb 231 local function iterSawTries(pos)
16302c 232     -- Copy position to prevent mangling it
S 233     local pos = vector.new(pos)
ec069a 234     local i = 0
9019eb 235
S 236     return function()
ec069a 237         i = i + 1
S 238         -- Given a (top view) area like so (where 5 is the starting position):
239         -- X -->
240         -- Z 123
241         -- | 456
242         -- V 789
243         -- This will return positions 1, 4, 7, 2, 8 (skip 5), 3, 6, 9,
244         -- and the position above 5.
245         if i == 1 then
246             -- Move to starting position
247             pos.x = pos.x - 1
248             pos.z = pos.z - 1
249         elseif i == 4 or i == 7 then
250             -- Move to next X and back to start of Z when we reach
251             -- the end of a Z line.
9019eb 252             pos.x = pos.x + 1
ec069a 253             pos.z = pos.z - 2
S 254         elseif i == 5 then
255             -- Skip the middle position (we've already run on it)
256             -- and double-increment the counter.
257             pos.z = pos.z + 2
258             i = i + 1
259         elseif i <= 9 then
260             -- Go to next Z.
261             pos.z = pos.z + 1
262         elseif i == 10 then
263             -- Move back to center and up.
264             -- The Y+ position must be last so that we don't dig
265             -- straight upward and not come down (since the Y-
266             -- position isn't checked).
267             pos.x = pos.x - 1
268             pos.z = pos.z - 1
269             pos.y = pos.y + 1
9019eb 270         else
ec069a 271             return nil
9019eb 272         end
S 273         return pos
c63658 274     end
8e03d7 275 end
R 276
277 -- This function does all the hard work. Recursively we dig the node at hand
278 -- if it is in the table and then search the surroundings for more stuff to dig.
9019eb 279 local function recursive_dig(pos, remaining_charge)
S 280     if remaining_charge < chainsaw_charge_per_node then
c63658 281         return remaining_charge
P 282     end
9019eb 283     local node = minetest.get_node(pos)
S 284
31a052 285     if not check_if_node_sawed(pos) then
9019eb 286         return remaining_charge
S 287     end
288
ec069a 289     -- Wood found - cut it
9019eb 290     handle_drops(minetest.get_node_drops(node.name, ""))
S 291     minetest.remove_node(pos)
292     remaining_charge = remaining_charge - chainsaw_charge_per_node
293
294     -- Check surroundings and run recursively if any charge left
16302c 295     for npos in iterSawTries(pos) do
9019eb 296         if remaining_charge < chainsaw_charge_per_node then
S 297             break
298         end
31a052 299         if check_if_node_sawed(npos) then
16302c 300             remaining_charge = recursive_dig(npos, remaining_charge)
31a052 301         else
D 302             minetest.check_for_falling(npos)
9019eb 303         end
S 304     end
c63658 305     return remaining_charge
P 306 end
307
308 -- Function to randomize positions for new node drops
309 local function get_drop_pos(pos)
310     local drop_pos = {}
311
312     for i = 0, 8 do
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)
317
318         -- Move the randomized position upwards until
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)
323
324             if not node then
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
9019eb 329                 -- Add variation to the entity drop position,
S 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
c63658 333                 return drop_pos
P 334             end
335         end
336     end
337
338     -- Return the original position if this takes too long
339     return pos
e23f87 340 end
19c9a0 341
9019eb 342 -- Chainsaw entry point
S 343 local function chainsaw_dig(pos, current_charge)
344     -- Start sawing things down
345     local remaining_charge = recursive_dig(pos, current_charge)
346     minetest.sound_play("chainsaw", {pos = pos, gain = 1.0,
347             max_hear_distance = 10})
5cf765 348
9019eb 349     -- Now drop items for the player
S 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
c63658 357         end
9019eb 358         stack:set_count(count)
S 359         minetest.add_item(get_drop_pos(pos), stack)
360     end
361
362     -- Clean up
363     produced = {}
364
365     return remaining_charge
5cf765 366 end
S 367
368
369 minetest.register_tool("technic:chainsaw", {
370     description = S("Chainsaw"),
371     inventory_image = "technic_chainsaw.png",
372     stack_max = 1,
99fd5d 373     wear_represents = "technic_RE_charge",
00d7c9 374     on_refill = technic.refill_RE_charge,
5cf765 375     on_use = function(itemstack, user, pointed_thing)
S 376         if pointed_thing.type ~= "node" then
377             return itemstack
378         end
9019eb 379
5cf765 380         local meta = minetest.deserialize(itemstack:get_metadata())
9019eb 381         if not meta or not meta.charge or
S 382                 meta.charge < chainsaw_charge_per_node then
5cf765 383             return
S 384         end
385
9019eb 386         local name = user:get_player_name()
51c02e 387         if minetest.is_protected(pointed_thing.under, name) then
S 388             minetest.record_protection_violation(pointed_thing.under, name)
389             return
9019eb 390         end
S 391
392         -- Send current charge to digging function so that the
393         -- chainsaw will stop after digging a number of nodes
394         meta.charge = chainsaw_dig(pointed_thing.under, meta.charge)
b8c902 395         if not technic.creative_mode then
M 396             technic.set_RE_wear(itemstack, meta.charge, chainsaw_max_charge)
397             itemstack:set_metadata(minetest.serialize(meta))
398         end
5cf765 399         return itemstack
S 400     end,
401 })
402
e4c34c 403 local mesecons_button = minetest.get_modpath("mesecons_button")
T 404 local trigger = mesecons_button and "mesecons_button:button_off" or "default:mese_crystal_fragment"
405
5cf765 406 minetest.register_craft({
9019eb 407     output = "technic:chainsaw",
c63658 408     recipe = {
e4c34c 409         {"technic:stainless_steel_ingot", trigger,                      "technic:battery"},
44cb8d 410         {"basic_materials:copper_wire",      "basic_materials:motor",              "technic:battery"},
5e4a87 411         {"",                              "",                           "technic:stainless_steel_ingot"},
44cb8d 412     },
VD 413     replacements = { {"basic_materials:copper_wire", "basic_materials:empty_spool"}, },
414
5cf765 415 })
9019eb 416