From 21e044478e259efa202dce2c2e82afc342f07b90 Mon Sep 17 00:00:00 2001 From: SmallJoker <mk939@ymail.com> Date: Sat, 26 Nov 2022 23:25:33 +0100 Subject: [PATCH] Fix battery box charging issues caused by out of sync functions --- technic/tools/chainsaw.lua | 489 ++++++++++++++++++++++++++--------------------------- 1 files changed, 240 insertions(+), 249 deletions(-) diff --git a/technic/tools/chainsaw.lua b/technic/tools/chainsaw.lua index 0bb7340..23d1820 100644 --- a/technic/tools/chainsaw.lua +++ b/technic/tools/chainsaw.lua @@ -1,252 +1,217 @@ -- Configuration local chainsaw_max_charge = 30000 -- Maximum charge of the saw --- Gives 2500 nodes on a single charge (about 50 complete normal trees) -local chainsaw_charge_per_node = 12 -- Cut down tree leaves. Leaf decay may cause slowness on large trees -- if this is disabled. local chainsaw_leaves = true --- The default trees -local timber_nodenames = { - ["default:jungletree"] = true, - ["default:papyrus"] = true, - ["default:cactus"] = true, - ["default:tree"] = true, - ["default:apple"] = true, - ["default:pine"] = true, -} +local chainsaw_efficiency = 0.95 -- Drops less items -if chainsaw_leaves then - timber_nodenames["default:leaves"] = true - timber_nodenames["default:jungleleaves"] = true - timber_nodenames["default:pine_needles"] = true -end - --- technic_worldgen defines rubber trees if moretrees isn't installed -if minetest.get_modpath("technic_worldgen") or - minetest.get_modpath("moretrees") then - timber_nodenames["moretrees:rubber_tree_trunk_empty"] = true - timber_nodenames["moretrees:rubber_tree_trunk"] = true - if chainsaw_leaves then - timber_nodenames["moretrees:rubber_tree_leaves"] = true - end -end - --- Support moretrees if it is there -if minetest.get_modpath("moretrees") then - timber_nodenames["moretrees:acacia_trunk"] = true - timber_nodenames["moretrees:apple_tree_trunk"] = true - timber_nodenames["moretrees:beech_trunk"] = true - timber_nodenames["moretrees:birch_trunk"] = true - timber_nodenames["moretrees:fir_trunk"] = true - timber_nodenames["moretrees:oak_trunk"] = true - timber_nodenames["moretrees:palm_trunk"] = true - timber_nodenames["moretrees:pine_trunk"] = true - timber_nodenames["moretrees:sequoia_trunk"] = true - timber_nodenames["moretrees:spruce_trunk"] = true - timber_nodenames["moretrees:willow_trunk"] = true - timber_nodenames["moretrees:jungletree_trunk"] = true - - if chainsaw_leaves then - timber_nodenames["moretrees:acacia_leaves"] = true - timber_nodenames["moretrees:apple_tree_leaves"] = true - timber_nodenames["moretrees:oak_leaves"] = true - timber_nodenames["moretrees:fir_leaves"] = true - timber_nodenames["moretrees:fir_leaves_bright"] = true - timber_nodenames["moretrees:sequoia_leaves"] = true - timber_nodenames["moretrees:birch_leaves"] = true - timber_nodenames["moretrees:birch_leaves"] = true - timber_nodenames["moretrees:palm_leaves"] = true - timber_nodenames["moretrees:spruce_leaves"] = true - timber_nodenames["moretrees:spruce_leaves"] = true - timber_nodenames["moretrees:pine_leaves"] = true - timber_nodenames["moretrees:willow_leaves"] = true - timber_nodenames["moretrees:jungletree_leaves_green"] = true - timber_nodenames["moretrees:jungletree_leaves_yellow"] = true - timber_nodenames["moretrees:jungletree_leaves_red"] = true - timber_nodenames["moretrees:acorn"] = true - timber_nodenames["moretrees:coconut"] = true - timber_nodenames["moretrees:spruce_cone"] = true - timber_nodenames["moretrees:pine_cone"] = true - timber_nodenames["moretrees:fir_cone"] = true - timber_nodenames["moretrees:apple_blossoms"] = true - end -end - --- Support growing_trees -if minetest.get_modpath("growing_trees") then - timber_nodenames["growing_trees:trunk"] = true - timber_nodenames["growing_trees:medium_trunk"] = true - timber_nodenames["growing_trees:big_trunk"] = true - timber_nodenames["growing_trees:trunk_top"] = true - timber_nodenames["growing_trees:trunk_sprout"] = true - timber_nodenames["growing_trees:branch_sprout"] = true - timber_nodenames["growing_trees:branch"] = true - timber_nodenames["growing_trees:branch_xmzm"] = true - timber_nodenames["growing_trees:branch_xpzm"] = true - timber_nodenames["growing_trees:branch_xmzp"] = true - timber_nodenames["growing_trees:branch_xpzp"] = true - timber_nodenames["growing_trees:branch_zz"] = true - timber_nodenames["growing_trees:branch_xx"] = true - - if chainsaw_leaves then - timber_nodenames["growing_trees:leaves"] = true - end -end - --- Support growing_cactus -if minetest.get_modpath("growing_cactus") then - timber_nodenames["growing_cactus:sprout"] = true - timber_nodenames["growing_cactus:branch_sprout_vertical"] = true - timber_nodenames["growing_cactus:branch_sprout_vertical_fixed"] = true - timber_nodenames["growing_cactus:branch_sprout_xp"] = true - timber_nodenames["growing_cactus:branch_sprout_xm"] = true - timber_nodenames["growing_cactus:branch_sprout_zp"] = true - timber_nodenames["growing_cactus:branch_sprout_zm"] = true - timber_nodenames["growing_cactus:trunk"] = true - timber_nodenames["growing_cactus:branch_trunk"] = true - timber_nodenames["growing_cactus:branch"] = true - timber_nodenames["growing_cactus:branch_xp"] = true - timber_nodenames["growing_cactus:branch_xm"] = true - timber_nodenames["growing_cactus:branch_zp"] = true - timber_nodenames["growing_cactus:branch_zm"] = true - timber_nodenames["growing_cactus:branch_zz"] = true - timber_nodenames["growing_cactus:branch_xx"] = true -end - --- Support farming_plus -if minetest.get_modpath("farming_plus") then - if chainsaw_leaves then - timber_nodenames["farming_plus:banana_leaves"] = true - timber_nodenames["farming_plus:banana"] = true - timber_nodenames["farming_plus:cocoa_leaves"] = true - timber_nodenames["farming_plus:cocoa"] = true - end -end - --- Support nature -if minetest.get_modpath("nature") then - if chainsaw_leaves then - timber_nodenames["nature:blossom"] = true - end -end - --- Support snow -if minetest.get_modpath("snow") then - if chainsaw_leaves then - timber_nodenames["snow:needles"] = true - timber_nodenames["snow:needles_decorated"] = true - timber_nodenames["snow:star"] = true - end -end - --- Support vines (also generated by moretrees if available) -if minetest.get_modpath("vines") then - if chainsaw_leaves then - timber_nodenames["vines:vines"] = true - end -end +-- Maximal dimensions of the tree to cut +local tree_max_radius = 10 +local tree_max_height = 70 local S = technic.getter +--[[ +Format: [node_name] = dig_cost + +This table is filled automatically afterwards to support mods such as: + + cool_trees + ethereal + moretrees +]] +local tree_nodes = { + -- For the sake of maintenance, keep this sorted alphabetically! + ["default:acacia_bush_stem"] = -1, + ["default:bush_stem"] = -1, + ["default:pine_bush_stem"] = -1, + + ["default:cactus"] = -1, + ["default:papyrus"] = -1, + + -- dfcaves "fruits" + ["df_trees:blood_thorn_spike"] = -1, + ["df_trees:blood_thorn_spike_dead"] = -1, + ["df_trees:tunnel_tube_fruiting_body"] = -1, + + ["ethereal:bamboo"] = -1, +} + +-- Function to decide whether or not to cut a certain node (and at which energy cost) +local function populate_costs(name, def) + repeat + if tree_nodes[name] == -1 then + tree_nodes[name] = nil + break -- Manually added, but need updating + end + if (def.groups.tree or 0) > 0 then + break -- Tree node + end + if (def.groups.leaves or 0) > 0 and chainsaw_leaves then + break -- Leaves + end + if (def.groups.leafdecay_drop or 0) > 0 then + break -- Food + end + return -- Abort function: do not dig this node + + -- luacheck: push ignore 511 + until 1 + -- luacheck: pop + + -- Function did not return! --> add content ID to the digging table + local content_id = minetest.get_content_id(name) + + -- Get 12 in average + local cost = 0 + if def.groups.choppy then + cost = def.groups.choppy * 5 -- trunks (usually 3 * 5) + elseif def.groups.snappy then + cost = def.groups.snappy * 2 -- leaves + end + tree_nodes[content_id] = math.max(4, cost) +end + +minetest.register_on_mods_loaded(function() + local ndefs = minetest.registered_nodes + -- Populate hardcoded nodes + for name in pairs(tree_nodes) do + local ndef = ndefs[name] + if ndef and ndef.groups then + populate_costs(name, ndef) + end + end + + -- Find all trees and leaves + for name, def in pairs(ndefs) do + if def.groups then + populate_costs(name, def) + end + end +end) + + technic.register_power_tool("technic:chainsaw", chainsaw_max_charge) --- Table for saving what was sawed down -local produced = {} +local pos9dir = { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 1, 0, 1}, + {-1, 0, -1}, + { 1, 0, -1}, + {-1, 0, 1}, + { 0, 1, 0}, -- up +} --- 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()) +local cutter = { + -- See function cut_tree() +} + +local c_air = minetest.get_content_id("air") +local function dig_recursive(x, y, z) + local i = cutter.area:index(x, y, z) + if cutter.seen[i] then + return + end + cutter.seen[i] = 1 -- Mark as visited + + if cutter.param2[i] ~= 0 then + -- Do not dig manually placed nodes + return + end + + local c_id = cutter.data[i] + local cost = tree_nodes[c_id] + if not cost or cost > cutter.charge then + return -- Cannot dig this node + end + + -- Count dug nodes + cutter.drops[c_id] = (cutter.drops[c_id] or 0) + 1 + cutter.seen[i] = 2 -- Mark as dug (for callbacks) + cutter.data[i] = c_air + cutter.charge = cutter.charge - cost + + -- Expand maximal bounds for area protection check + if x < cutter.minp.x then cutter.minp.x = x end + if y < cutter.minp.y then cutter.minp.y = y end + if z < cutter.minp.z then cutter.minp.z = z end + if x > cutter.maxp.x then cutter.maxp.x = x end + if y > cutter.maxp.y then cutter.maxp.y = y end + if z > cutter.maxp.z then cutter.maxp.z = z end + + -- Traverse neighbors + local xn, yn, zn + for _, offset in ipairs(pos9dir) do + xn, yn, zn = x + offset[1], y + offset[2], z + offset[3] + if cutter.area:contains(xn, yn, zn) then + dig_recursive(xn, yn, zn) 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 +local handle_drops - 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 +local function chainsaw_dig(player, pos, remaining_charge) + local minp = { + x = pos.x - (tree_max_radius + 1), + y = pos.y, + z = pos.z - (tree_max_radius + 1) + } + local maxp = { + x = pos.x + (tree_max_radius + 1), + y = pos.y + tree_max_height, + z = pos.z + (tree_max_radius + 1) + } --- 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 < chainsaw_charge_per_node then - return remaining_charge - end - local node = minetest.get_node(pos) + local vm = minetest.get_voxel_manip() + local emin, emax = vm:read_from_map(minp, maxp) - if not timber_nodenames[node.name] then - return remaining_charge + cutter = { + area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}, + data = vm:get_data(), + param2 = vm:get_param2_data(), + seen = {}, + drops = {}, -- [content_id] = count + minp = vector.copy(pos), + maxp = vector.copy(pos), + charge = remaining_charge + } + + dig_recursive(pos.x, pos.y, pos.z) + + -- Check protection + local player_name = player:get_player_name() + if minetest.is_area_protected(cutter.minp, cutter.maxp, player_name, 6) then + minetest.chat_send_player(player_name, "The chainsaw cannot cut this tree. The cuboid " .. + minetest.pos_to_string(cutter.minp) .. ", " .. minetest.pos_to_string(cutter.maxp) .. + " contains protected nodes.") + minetest.record_protection_violation(pos, player_name) + return end - -- Wood found - cut it - handle_drops(minetest.get_node_drops(node.name, "")) - minetest.remove_node(pos) - remaining_charge = remaining_charge - chainsaw_charge_per_node + minetest.sound_play("chainsaw", { + pos = pos, + gain = 1.0, + max_hear_distance = 20 + }) - -- Check surroundings and run recursively if any charge left - for npos in iterSawTries(pos) do - if remaining_charge < chainsaw_charge_per_node then - break - end - if timber_nodenames[minetest.get_node(npos).name] then - remaining_charge = recursive_dig(npos, remaining_charge) + handle_drops(pos) + + vm:set_data(cutter.data) + vm:write_to_map(true) + vm:update_map() + + -- Update falling nodes + for i, status in pairs(cutter.seen) do + if status == 2 then -- actually dug + minetest.check_for_falling(cutter.area:position(i)) end end - return remaining_charge end -- Function to randomize positions for new node drops @@ -283,30 +248,50 @@ return pos end --- Chainsaw entry point -local function chainsaw_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}) +local drop_inv = minetest.create_detached_inventory("technic:chainsaw_drops", {}, ":technic") +handle_drops = function(pos) + local n_slots = 100 + drop_inv:set_size("main", n_slots) + drop_inv:set_list("main", {}) - -- 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 + -- Put all dropped items into the detached inventory + for c_id, count in pairs(cutter.drops) do + local name = minetest.get_name_from_content_id(c_id) + + -- Add drops in bulk -> keep some randomness + while count > 0 do + local drops = minetest.get_node_drops(name, "") + -- higher numbers are faster but return uneven sapling counts + local decrement = math.ceil(count * 0.3) + decrement = math.min(count, math.max(5, decrement)) + + for _, stack in ipairs(drops) do + stack = ItemStack(stack) + local total = math.ceil(stack:get_count() * decrement * chainsaw_efficiency) + local stack_max = stack:get_stack_max() + + -- Split into full stacks + while total > 0 do + local size = math.min(total, stack_max) + stack:set_count(size) + drop_inv:add_item("main", stack) + total = total - size + end + end + count = count - decrement end - stack:set_count(count) + end + + -- Drop in random places + for i = 1, n_slots do + local stack = drop_inv:get_stack("main", i) + if stack:is_empty() then + break + end minetest.add_item(get_drop_pos(pos), stack) end - -- Clean up - produced = {} - - return remaining_charge + drop_inv:set_size("main", 0) -- free RAM end @@ -322,8 +307,7 @@ end local meta = minetest.deserialize(itemstack:get_metadata()) - if not meta or not meta.charge or - meta.charge < chainsaw_charge_per_node then + if not meta or not meta.charge then return end @@ -335,10 +319,15 @@ -- Send current charge to digging function so that the -- chainsaw will stop after digging a number of nodes - meta.charge = chainsaw_dig(pointed_thing.under, meta.charge) + chainsaw_dig(user, pointed_thing.under, meta.charge) + meta.charge = cutter.charge - technic.set_RE_wear(itemstack, meta.charge, chainsaw_max_charge) - itemstack:set_metadata(minetest.serialize(meta)) + cutter = {} -- Free RAM + + if not technic.creative_mode then + technic.set_RE_wear(itemstack, meta.charge, chainsaw_max_charge) + itemstack:set_metadata(minetest.serialize(meta)) + end return itemstack end, }) @@ -350,8 +339,10 @@ output = "technic:chainsaw", recipe = { {"technic:stainless_steel_ingot", trigger, "technic:battery"}, - {"technic:fine_copper_wire", "technic:motor", "technic:battery"}, + {"basic_materials:copper_wire", "basic_materials:motor", "technic:battery"}, {"", "", "technic:stainless_steel_ingot"}, - } + }, + replacements = { {"basic_materials:copper_wire", "basic_materials:empty_spool"}, }, + }) -- Gitblit v1.8.0