Optimization of table allocation, more sophisticated entity damage (#37)

This pass cleans up a lot of unnecessary table allocation and disposal, which should help with memory usage and garbage collection.

It also adds some sophistication to the entity damage process from diggers, allowing items to be picked up into Digtron inventory.
This commit is contained in:
FaceDeer 2019-01-04 22:06:12 -07:00 committed by GitHub
parent fe753a9bed
commit 5c0700456c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 436 additions and 308 deletions

@ -76,10 +76,10 @@ digtron.award_layout = function(layout, player)
awards.unlock(name, "digtron_size100") awards.unlock(name, "digtron_size100")
end end
end end
if table.getn(layout.diggers) > 24 then if layout.diggers ~= nil and table.getn(layout.diggers) > 24 then
awards.unlock(name, "digtron_digger25") awards.unlock(name, "digtron_digger25")
end end
if table.getn(layout.builders) > 24 then if layout.builders ~= nil and table.getn(layout.builders) > 24 then
awards.unlock(name, "digtron_builder25") awards.unlock(name, "digtron_builder25")
end end

@ -39,13 +39,6 @@ function DigtronLayout.create(pos, player)
--initialize. We're assuming that the start position is a controller digtron, should be a safe assumption since only the controller node should call this --initialize. We're assuming that the start position is a controller digtron, should be a safe assumption since only the controller node should call this
self.traction = 0 self.traction = 0
self.all = {} self.all = {}
self.inventories = {}
self.fuelstores = {}
self.battery_holders = {} -- technic batteries
self.power_connectors = {} -- technic power cable
self.diggers = {}
self.builders = {}
self.auto_ejectors = {}
self.extents = {} self.extents = {}
self.water_touching = false self.water_touching = false
self.lava_touching = false self.lava_touching = false
@ -118,21 +111,30 @@ function DigtronLayout.create(pos, player)
-- add a reference to this node's position to special node lists -- add a reference to this node's position to special node lists
if group_number == 2 then if group_number == 2 then
self.inventories = self.inventories or {}
table.insert(self.inventories, node_image) table.insert(self.inventories, node_image)
elseif group_number == 3 then elseif group_number == 3 then
self.diggers = self.diggers or {}
table.insert(self.diggers, node_image) table.insert(self.diggers, node_image)
elseif group_number == 4 then elseif group_number == 4 then
self.builders = self.builders or {}
table.insert(self.builders, node_image) table.insert(self.builders, node_image)
elseif group_number == 5 then elseif group_number == 5 then
self.fuelstores = self.fuelstores or {}
table.insert(self.fuelstores, node_image) table.insert(self.fuelstores, node_image)
elseif group_number == 6 then elseif group_number == 6 then
self.inventories = self.inventories or {}
self.fuelstores = self.fuelstores or {}
table.insert(self.inventories, node_image) table.insert(self.inventories, node_image)
table.insert(self.fuelstores, node_image) table.insert(self.fuelstores, node_image)
elseif group_number == 7 then elseif group_number == 7 then -- technic batteries
self.battery_holders = self.battery_holders or {}
table.insert(self.battery_holders, node_image) table.insert(self.battery_holders, node_image)
elseif group_number == 8 then elseif group_number == 8 then
table.insert(self.power_connectors, node_image) self.power_connectors = self.power_connectors or {}
table.insert(self.power_connectors, node_image) -- technic power connectors
elseif group_number == 9 and node_image.meta.fields["autoeject"] == "true" then elseif group_number == 9 and node_image.meta.fields["autoeject"] == "true" then
self.auto_ejectors = self.auto_ejectors or {}
table.insert(self.auto_ejectors, node_image) table.insert(self.auto_ejectors, node_image)
end end
@ -202,32 +204,60 @@ local wallmounted_rotate = {
} }
} }
--90 degrees CW about x-axis: (x, y, z) -> (x, -z, y) --90 degrees CW about x-axis: (x, y, z) -> (x, -z, y)
--90 degrees CCW about x-axis: (x, y, z) -> (x, z, -y) --90 degrees CCW about x-axis: (x, y, z) -> (x, z, -y)
--90 degrees CW about y-axis: (x, y, z) -> (-z, y, x) --90 degrees CW about y-axis: (x, y, z) -> (-z, y, x)
--90 degrees CCW about y-axis: (x, y, z) -> (z, y, -x) --90 degrees CCW about y-axis: (x, y, z) -> (z, y, -x)
--90 degrees CW about z-axis: (x, y, z) -> (y, -x, z) --90 degrees CW about z-axis: (x, y, z) -> (y, -x, z)
--90 degrees CCW about z-axis: (x, y, z) -> (-y, x, z) --90 degrees CCW about z-axis: (x, y, z) -> (-y, x, z)
-- operates directly on the pos vector
local rotate_pos = function(axis, direction, pos) local rotate_pos = function(axis, direction, pos)
if axis == "x" then if axis == "x" then
if direction < 0 then if direction < 0 then
return {x= pos.x, y= -pos.z, z= pos.y} local temp_z = pos.z
pos.z = pos.y
pos.y = -temp_z
else else
return {x= pos.x, y= pos.z, z= -pos.y} local temp_z = pos.z
pos.z = -pos.y
pos.y = temp_z
end end
elseif axis == "y" then elseif axis == "y" then
if direction < 0 then if direction < 0 then
return {x= -pos.z, y= pos.y, z= pos.x} local temp_x = pos.x
pos.x = -pos.z
pos.z = temp_x
else else
return {x= pos.z, y= pos.y, z= -pos.x} local temp_x = pos.x
pos.x = pos.z
pos.z = -temp_x
end end
else else
if direction < 0 then if direction < 0 then
return {x= -pos.y, y= pos.x, z= pos.z} local temp_x = pos.x
pos.x = -pos.y
pos.y = temp_x
else else
return {x= pos.y, y= -pos.x, z= pos.z} local temp_x = pos.x
pos.x = pos.y
pos.y = -temp_x
end end
end end
return pos
end
-- operates directly on the pos vector
local subtract_in_place = function(pos, subtract)
pos.x = pos.x - subtract.x
pos.y = pos.y - subtract.y
pos.z = pos.z - subtract.z
return pos
end
local add_in_place = function(pos, add)
pos.x = pos.x + add.x
pos.y = pos.y + add.y
pos.z = pos.z + add.z
return pos
end end
local rotate_node_image = function(node_image, origin, axis, direction, old_pos_pointset) local rotate_node_image = function(node_image, origin, axis, direction, old_pos_pointset)
@ -250,14 +280,23 @@ local rotate_node_image = function(node_image, origin, axis, direction, old_pos_
old_pos_pointset:set(node_image.pos.x, node_image.pos.y, node_image.pos.z, true) old_pos_pointset:set(node_image.pos.x, node_image.pos.y, node_image.pos.z, true)
-- position in space relative to origin -- position in space relative to origin
local pos = vector.subtract(node_image.pos, origin) local pos = subtract_in_place(node_image.pos, origin)
pos = rotate_pos(axis, direction, pos) pos = rotate_pos(axis, direction, pos)
-- Move back to original reference frame -- Move back to original reference frame
node_image.pos = vector.add(pos, origin) node_image.pos = add_in_place(pos, origin)
return node_image return node_image
end end
local top = {
[0]={axis="y", dir=-1},
{axis="z", dir=1},
{axis="z", dir=-1},
{axis="x", dir=1},
{axis="x", dir=-1},
{axis="y", dir=1},
}
-- Rotates 90 degrees widdershins around the axis defined by facedir (which in this case is pointing out the front of the node, so it needs to be converted into an upward-pointing axis internally) -- Rotates 90 degrees widdershins around the axis defined by facedir (which in this case is pointing out the front of the node, so it needs to be converted into an upward-pointing axis internally)
function DigtronLayout.rotate_layout_image(self, facedir) function DigtronLayout.rotate_layout_image(self, facedir)
-- To convert this into the direction the "top" of the axle node is pointing in: -- To convert this into the direction the "top" of the axle node is pointing in:
@ -268,14 +307,6 @@ function DigtronLayout.rotate_layout_image(self, facedir)
-- 16, 17, 18, 19 == (-1,0,0) -- 16, 17, 18, 19 == (-1,0,0)
-- 20, 21, 22, 23== (0,-1,0) -- 20, 21, 22, 23== (0,-1,0)
local top = {
[0]={axis="y", dir=-1},
{axis="z", dir=1},
{axis="z", dir=-1},
{axis="x", dir=1},
{axis="x", dir=-1},
{axis="y", dir=1},
}
local params = top[math.floor(facedir/4)] local params = top[math.floor(facedir/4)]
for k, node_image in pairs(self.all) do for k, node_image in pairs(self.all) do
@ -299,7 +330,7 @@ function DigtronLayout.move_layout_image(self, dir)
for k, node_image in pairs(self.all) do for k, node_image in pairs(self.all) do
self.old_pos_pointset:set(node_image.pos.x, node_image.pos.y, node_image.pos.z, true) self.old_pos_pointset:set(node_image.pos.x, node_image.pos.y, node_image.pos.z, true)
node_image.pos = vector.add(node_image.pos, dir) node_image.pos = add_in_place(node_image.pos, dir)
self.nodes_dug:set(node_image.pos.x, node_image.pos.y, node_image.pos.z, false) -- we've moved a digtron node into this space, mark it so that we don't dig it. self.nodes_dug:set(node_image.pos.x, node_image.pos.y, node_image.pos.z, false) -- we've moved a digtron node into this space, mark it so that we don't dig it.
end end
end end
@ -329,11 +360,23 @@ end
-- if done during mid-write. So we need to defer the calls until after the -- if done during mid-write. So we need to defer the calls until after the
-- Digtron has been fully written. -- Digtron has been fully written.
local node_callbacks = function(dug_nodes, placed_nodes, player) -- using local counters and shared tables like this allows us to avoid some needless allocating and garbage-collecting of tables
for _, dug_node in pairs(dug_nodes) do local dug_nodes_count = 0
local old_pos = dug_node[1] local dug_node_pos = {}
local old_node = dug_node[2] local dug_node = {}
local old_meta = dug_node[3] local dug_node_meta = {}
local placed_nodes_count = 0
local placed_node_pos = {}
local placed_new_node = {}
local placed_old_node = {}
local node_callbacks = function(player)
if dug_nodes_count > 0 then
for i = 1, dug_nodes_count do
local old_pos = dug_node_pos[i]
local old_node = dug_node[i]
local old_meta = dug_node_meta[i]
for _, callback in ipairs(minetest.registered_on_dignodes) do for _, callback in ipairs(minetest.registered_on_dignodes) do
-- Copy pos and node because callback can modify them -- Copy pos and node because callback can modify them
@ -347,11 +390,13 @@ local node_callbacks = function(dug_nodes, placed_nodes, player)
old_def.after_dig_node(old_pos, old_node, old_meta, player) old_def.after_dig_node(old_pos, old_node, old_meta, player)
end end
end end
end
for _, placed_node in pairs(placed_nodes) do if placed_nodes_count > 0 then
local new_pos = placed_node[1] for i = 1, placed_nodes_count do
local new_node = placed_node[2] local new_pos = placed_node_pos[i]
local old_node = placed_node[3] local new_node = placed_new_node[i]
local old_node = placed_old_node[i]
for _, callback in ipairs(minetest.registered_on_placenodes) do for _, callback in ipairs(minetest.registered_on_placenodes) do
-- Copy pos and node because callback can modify them -- Copy pos and node because callback can modify them
@ -366,6 +411,7 @@ local node_callbacks = function(dug_nodes, placed_nodes, player)
new_def.after_place_node(new_pos, player) new_def.after_place_node(new_pos, player)
end end
end end
end
end end
local set_node_with_retry = function(pos, node) local set_node_with_retry = function(pos, node)
@ -388,22 +434,23 @@ local set_meta_with_retry = function(meta, meta_table)
return true return true
end end
local air_node = {name="air"}
function DigtronLayout.write_layout_image(self, player) function DigtronLayout.write_layout_image(self, player)
local dug_nodes = {}
local placed_nodes = {}
-- destroy the old digtron -- destroy the old digtron
local oldpos, _ = self.old_pos_pointset:pop() local oldpos, _ = self.old_pos_pointset:pop()
while oldpos ~= nil do while oldpos ~= nil do
local old_node = minetest.get_node(oldpos) local old_node = minetest.get_node(oldpos)
local old_meta = minetest.get_meta(oldpos) local old_meta = minetest.get_meta(oldpos)
if not set_node_with_retry(oldpos, {name="air"}) then if not set_node_with_retry(oldpos, air_node) then
minetest.log("error", "DigtronLayout.write_layout_image failed to destroy old Digtron node, aborting write.") minetest.log("error", "DigtronLayout.write_layout_image failed to destroy old Digtron node, aborting write.")
return false return false
end end
table.insert(dug_nodes, {oldpos, old_node, old_meta}) dug_nodes_count = dug_nodes_count + 1
dug_node_pos[dug_nodes_count] = oldpos
dug_node[dug_nodes_count] = old_node
dug_node_meta[dug_nodes_count] = old_meta
oldpos, _ = self.old_pos_pointset:pop() oldpos, _ = self.old_pos_pointset:pop()
end end
@ -418,15 +465,19 @@ function DigtronLayout.write_layout_image(self, player)
return false return false
end end
table.insert(placed_nodes, {new_pos, new_node, old_node}) placed_nodes_count = placed_nodes_count + 1
placed_node_pos[placed_nodes_count] = new_pos
placed_new_node[placed_nodes_count] = new_node
placed_old_node[placed_nodes_count] = old_node
end end
-- fake_player will be passed to callbacks to prevent actual player from "taking the blame" for this action. -- fake_player will be passed to callbacks to prevent actual player from "taking the blame" for this action.
-- For example, the hunger mod shouldn't be making the player hungry when he moves Digtron. -- For example, the hunger mod shouldn't be making the player hungry when he moves Digtron.
digtron.fake_player:update(self.controller, player:get_player_name()) digtron.fake_player:update(self.controller, player:get_player_name())
-- note that the actual player is still passed to the per-node after_place_node and after_dig_node, should they exist. -- note that the actual player is still passed to the per-node after_place_node and after_dig_node, should they exist.
node_callbacks(dug_nodes, placed_nodes, player) node_callbacks(player)
dug_nodes_count = 0
placed_nodes_count = 0
return true return true
end end

@ -3,6 +3,13 @@
Pointset = {} Pointset = {}
Pointset.__index = Pointset Pointset.__index = Pointset
-- from builtin\game\misc.lua, modified to take values directly to avoid creating an intermediate vector
local hash_node_position_values = function(x, y, z)
return (z + 32768) * 65536 * 65536
+ (y + 32768) * 65536
+ x + 32768
end
function Pointset.create() function Pointset.create()
local set = {} local set = {}
setmetatable(set,Pointset) setmetatable(set,Pointset)
@ -12,13 +19,7 @@ end
function Pointset:set(x, y, z, value) function Pointset:set(x, y, z, value)
-- sets a value in the 3D array "points". -- sets a value in the 3D array "points".
if self.points[x] == nil then self.points[hash_node_position_values(x,y,z)] = value
self.points[x] = {}
end
if self.points[x][y] == nil then
self.points[x][y] = {}
end
self.points[x][y][z] = value
end end
function Pointset:set_if_not_in(excluded, x, y, z, value) function Pointset:set_if_not_in(excluded, x, y, z, value)
@ -31,10 +32,7 @@ end
function Pointset:get(x, y, z) function Pointset:get(x, y, z)
-- return a value from the 3D array "points" -- return a value from the 3D array "points"
if self.points[x] == nil or self.points[x][y] == nil then return self.points[hash_node_position_values(x,y,z)]
return nil
end
return self.points[x][y][z]
end end
function Pointset:set_pos(pos, value) function Pointset:set_pos(pos, value)
@ -51,48 +49,19 @@ end
function Pointset:pop() function Pointset:pop()
-- returns a point that's in the 3D array, and then removes it. -- returns a point that's in the 3D array, and then removes it.
local pos = {} local hash, value = next(self.points)
local ytable if hash == nil then return nil end
local ztable local pos = minetest.get_position_from_hash(hash)
local val self.points[hash] = nil
return pos, value
local count = 0
for _ in pairs(self.points) do count = count + 1 end
if count == 0 then
return nil
end
pos.x, ytable = next(self.points)
pos.y, ztable = next(ytable)
pos.z, val = next(ztable)
self.points[pos.x][pos.y][pos.z] = nil
count = 0
for _ in pairs(self.points[pos.x][pos.y]) do count = count + 1 end
if count == 0 then
self.points[pos.x][pos.y] = nil
end
count = 0
for _ in pairs(self.points[pos.x]) do count = count + 1 end
if count == 0 then
self.points[pos.x] = nil
end
return pos, val
end end
function Pointset:get_pos_list(value) function Pointset:get_pos_list(value)
-- Returns a list of all points with the given value in standard Minetest vector format. If no value is provided, returns all points -- Returns a list of all points with the given value in standard Minetest vector format. If no value is provided, returns all points
local outlist = {} local outlist = {}
for x, ytable in ipairs(self.points) do for hash, pointsval in pairs(self.points) do
for y, ztable in ipairs(ytable) do if value == nil or pointsval == value then
for z, val in ipairs(ztable) do table.insert(outlist, minetest.get_position_from_hash(hash))
if (value == nil and val ~= nil ) or val == value then
table.insert(outlist, {x=x, y=y, z=z})
end
end
end end
end end
return outlist return outlist

@ -25,7 +25,10 @@ end
setting("bool", "uses_resources", true, "Digtron uses resources when active") setting("bool", "uses_resources", true, "Digtron uses resources when active")
setting("bool", "lava_impassible", true, "Lava counts as a protected node") setting("bool", "lava_impassible", true, "Lava counts as a protected node")
setting("bool", "damage_creatures", true, "Diggers damage creatures") setting("bool", "damage_creatures", true, "Diggers damage creatures") -- TODO: legacy setting, remove eventually
setting("int", "damage_hp", 8, "Damage diggers do")
if digtron.config.damage_creatures == false then digtron.config.damage_hp = 0 end -- TODO: remove when damage_creatures is removed
-- Enables the spray of particles out the back of a digger head and puffs of smoke from the controller -- Enables the spray of particles out the back of a digger head and puffs of smoke from the controller
local particle_effects = minetest.settings:get_bool("enable_particles") local particle_effects = minetest.settings:get_bool("enable_particles")

@ -4,6 +4,8 @@ local S, NS = dofile(MP.."/intllib.lua")
-- Note: builders go in group 4 and have both test_build and execute_build methods. -- Note: builders go in group 4 and have both test_build and execute_build methods.
local node_inventory_table = {type="node"} -- a reusable parameter for get_inventory calls, set the pos parameter before using.
local displace_due_to_help_button = 1.0 local displace_due_to_help_button = 1.0
if minetest.get_modpath("doc") then if minetest.get_modpath("doc") then
displace_due_to_help_button = 0.0 displace_due_to_help_button = 0.0
@ -42,7 +44,6 @@ end
local builder_formspec = function(pos, meta) local builder_formspec = function(pos, meta)
local nodemeta = "nodemeta:"..pos.x .. "," .. pos.y .. "," ..pos.z local nodemeta = "nodemeta:"..pos.x .. "," .. pos.y .. "," ..pos.z
minetest.debug(nodemeta)
return builder_formspec_string return builder_formspec_string
:gsub("${extrusion}", meta:get_int("extrusion"), 1) :gsub("${extrusion}", meta:get_int("extrusion"), 1)
:gsub("${period}", meta:get_int("period"), 1) :gsub("${period}", meta:get_int("period"), 1)
@ -200,7 +201,8 @@ minetest.register_node("digtron:builder", {
return 0 -- don't allow craft items unless their on_place is whitelisted. return 0 -- don't allow craft items unless their on_place is whitelisted.
end end
local inv = minetest.get_inventory({type="node", pos=pos}) node_inventory_table.pos = pos
local inv = minetest.get_inventory(node_inventory_table)
inv:set_stack(listname, index, stack:take_item(1)) inv:set_stack(listname, index, stack:take_item(1))
-- If we're adding a wallmounted item and the build facing is greater than 5, reset it to 0 -- If we're adding a wallmounted item and the build facing is greater than 5, reset it to 0
@ -213,7 +215,8 @@ minetest.register_node("digtron:builder", {
end, end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player) allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local inv = minetest.get_inventory({type="node", pos=pos}) node_inventory_table.pos = pos
local inv = minetest.get_inventory(node_inventory_table)
inv:set_stack(listname, index, ItemStack("")) inv:set_stack(listname, index, ItemStack(""))
return 0 return 0
end, end,
@ -246,7 +249,8 @@ minetest.register_node("digtron:builder", {
local return_items = {} local return_items = {}
local inv = minetest.get_inventory({type="node", pos=pos}) node_inventory_table.pos = pos
local inv = minetest.get_inventory(node_inventory_table)
local item_stack = inv:get_stack("main", 1) local item_stack = inv:get_stack("main", 1)
if item_stack:is_empty() then if item_stack:is_empty() then
@ -296,7 +300,8 @@ minetest.register_node("digtron:builder", {
end end
local built_count = 0 local built_count = 0
local inv = minetest.get_inventory({type="node", pos=pos}) node_inventory_table.pos = pos
local inv = minetest.get_inventory(node_inventory_table)
local item_stack = inv:get_stack("main", 1) local item_stack = inv:get_stack("main", 1)
if item_stack:is_empty() then if item_stack:is_empty() then
return built_count return built_count

@ -15,6 +15,8 @@ local controller_nodebox ={
{-0.5, -0.5, -0.5, -0.125, -0.125, -0.3125}, -- back_connector_4 {-0.5, -0.5, -0.5, -0.125, -0.125, -0.3125}, -- back_connector_4
} }
local node_inventory_table = {type="node"} -- a reusable parameter for get_inventory calls, set the pos parameter before using.
-- Master controller. Most complicated part of the whole system. Determines which direction a digtron moves and triggers all of its component parts. -- Master controller. Most complicated part of the whole system. Determines which direction a digtron moves and triggers all of its component parts.
minetest.register_node("digtron:controller", { minetest.register_node("digtron:controller", {
description = S("Digtron Control Module"), description = S("Digtron Control Module"),
@ -128,7 +130,7 @@ local function auto_cycle(pos)
meta:set_string("infotext", status) meta:set_string("infotext", status)
if return_code == 1 then --return code 1 happens when there's unloaded nodes adjacent, just keep trying. if return_code == 1 then --return code 1 happens when there's unloaded nodes adjacent, just keep trying.
if digtron.config.emerge_unloaded_mapblocks then if digtron.config.emerge_unloaded_mapblocks then
minetest.emerge_area(vector.add(pos, -128), vector.add(pos, 128)) minetest.emerge_area(vector.add(pos, -80), vector.add(pos, 80))
end end
minetest.after(meta:get_int("period"), auto_cycle, newpos) minetest.after(meta:get_int("period"), auto_cycle, newpos)
else else
@ -150,7 +152,7 @@ local function auto_cycle(pos)
meta:set_string("infotext", status) meta:set_string("infotext", status)
if return_code == 1 then --return code 1 happens when there's unloaded nodes adjacent, call emerge and keep trying. if return_code == 1 then --return code 1 happens when there's unloaded nodes adjacent, call emerge and keep trying.
if digtron.config.emerge_unloaded_mapblocks then if digtron.config.emerge_unloaded_mapblocks then
minetest.emerge_area(vector.add(pos, -128), vector.add(pos, 128)) minetest.emerge_area(vector.add(pos, -80), vector.add(pos, 80))
end end
minetest.after(meta:get_int("period"), auto_cycle, newpos) minetest.after(meta:get_int("period"), auto_cycle, newpos)
else else
@ -220,13 +222,15 @@ minetest.register_node("digtron:auto_controller", {
if minetest.get_item_group(stack:get_name(), "digtron") ~= 0 then if minetest.get_item_group(stack:get_name(), "digtron") ~= 0 then
return 0 -- pointless setting a Digtron node as a stop block return 0 -- pointless setting a Digtron node as a stop block
end end
local inv = minetest.get_inventory({type="node", pos=pos}) node_inventory_table.pos = pos
local inv = minetest.get_inventory(node_inventory_table)
inv:set_stack(listname, index, stack:take_item(1)) inv:set_stack(listname, index, stack:take_item(1))
return 0 return 0
end, end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player) allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local inv = minetest.get_inventory({type="node", pos=pos}) node_inventory_table.pos = pos
local inv = minetest.get_inventory(node_inventory_table)
inv:set_stack(listname, index, ItemStack("")) inv:set_stack(listname, index, ItemStack(""))
return 0 return 0
end, end,

@ -4,6 +4,9 @@ local S, NS = dofile(MP.."/intllib.lua")
-- Note: diggers go in group 3 and have an execute_dig method. -- Note: diggers go in group 3 and have an execute_dig method.
local damage_hp = digtron.config.damage_hp
local damage_hp_half = damage_hp/2
local digger_nodebox = { local digger_nodebox = {
{-0.5, -0.5, 0, 0.5, 0.5, 0.4375}, -- Block {-0.5, -0.5, 0, 0.5, 0.5, 0.4375}, -- Block
{-0.4375, -0.3125, 0.4375, 0.4375, 0.3125, 0.5}, -- Cutter1 {-0.4375, -0.3125, 0.4375, 0.4375, 0.3125, 0.5}, -- Cutter1
@ -132,15 +135,15 @@ minetest.register_node("digtron:digger", {
local digpos = digtron.find_new_pos(pos, facing) local digpos = digtron.find_new_pos(pos, facing)
if protected_nodes:get(digpos.x, digpos.y, digpos.z) then if protected_nodes:get(digpos.x, digpos.y, digpos.z) then
return 0, {} return 0
end end
return digtron.mark_diggable(digpos, nodes_dug, player) return digtron.mark_diggable(digpos, nodes_dug, player)
end, end,
damage_creatures = function(player, pos, controlling_coordinate) damage_creatures = function(player, pos, controlling_coordinate, items_dropped)
local facing = minetest.get_node(pos).param2 local facing = minetest.get_node(pos).param2
digtron.damage_creatures(player, digtron.find_new_pos(pos, facing), 8) digtron.damage_creatures(player, pos, digtron.find_new_pos(pos, facing), damage_hp, items_dropped)
end, end,
}) })
@ -184,33 +187,33 @@ minetest.register_node("digtron:intermittent_digger", {
on_rightclick = intermittent_on_rightclick, on_rightclick = intermittent_on_rightclick,
-- returns fuel_cost, item_produced -- returns fuel_cost, item_produced (a table or nil)
execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate, lateral_dig, player) execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate, lateral_dig, player)
if lateral_dig == true then if lateral_dig == true then
return 0, {} return 0
end end
local facing = minetest.get_node(pos).param2 local facing = minetest.get_node(pos).param2
local digpos = digtron.find_new_pos(pos, facing) local digpos = digtron.find_new_pos(pos, facing)
if protected_nodes:get(digpos.x, digpos.y, digpos.z) then if protected_nodes:get(digpos.x, digpos.y, digpos.z) then
return 0, {} return 0
end end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
if (digpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") ~= 0 then if (digpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") ~= 0 then
return 0, {} return 0
end end
return digtron.mark_diggable(digpos, nodes_dug, player) return digtron.mark_diggable(digpos, nodes_dug, player)
end, end,
damage_creatures = function(player, pos, controlling_coordinate) damage_creatures = function(player, pos, controlling_coordinate, items_dropped)
local facing = minetest.get_node(pos).param2 local facing = minetest.get_node(pos).param2
local targetpos = digtron.find_new_pos(pos, facing) local targetpos = digtron.find_new_pos(pos, facing)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
if (targetpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") == 0 then if (targetpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") == 0 then
digtron.damage_creatures(player, targetpos, 8) digtron.damage_creatures(player, pos, targetpos, damage_hp, items_dropped)
end end
end end
}) })
@ -255,19 +258,19 @@ minetest.register_node("digtron:soft_digger", {
local digpos = digtron.find_new_pos(pos, facing) local digpos = digtron.find_new_pos(pos, facing)
if protected_nodes:get(digpos.x, digpos.y, digpos.z) then if protected_nodes:get(digpos.x, digpos.y, digpos.z) then
return 0, {} return 0
end end
if digtron.is_soft_material(digpos) then if digtron.is_soft_material(digpos) then
return digtron.mark_diggable(digpos, nodes_dug, player) return digtron.mark_diggable(digpos, nodes_dug, player)
end end
return 0, {} return 0
end, end,
damage_creatures = function(player, pos, controlling_coordinate) damage_creatures = function(player, pos, controlling_coordinate, items_dropped)
local facing = minetest.get_node(pos).param2 local facing = minetest.get_node(pos).param2
digtron.damage_creatures(player, digtron.find_new_pos(pos, facing), 4) digtron.damage_creatures(player, pos, digtron.find_new_pos(pos, facing), damage_hp_half, items_dropped)
end, end,
}) })
@ -312,34 +315,34 @@ minetest.register_node("digtron:intermittent_soft_digger", {
execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate, lateral_dig, player) execute_dig = function(pos, protected_nodes, nodes_dug, controlling_coordinate, lateral_dig, player)
if lateral_dig == true then if lateral_dig == true then
return 0, {} return 0
end end
local facing = minetest.get_node(pos).param2 local facing = minetest.get_node(pos).param2
local digpos = digtron.find_new_pos(pos, facing) local digpos = digtron.find_new_pos(pos, facing)
if protected_nodes:get(digpos.x, digpos.y, digpos.z) then if protected_nodes:get(digpos.x, digpos.y, digpos.z) then
return 0, {} return 0
end end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
if (digpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") ~= 0 then if (digpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") ~= 0 then
return 0, {} return 0
end end
if digtron.is_soft_material(digpos) then if digtron.is_soft_material(digpos) then
return digtron.mark_diggable(digpos, nodes_dug, player) return digtron.mark_diggable(digpos, nodes_dug, player)
end end
return 0, {} return 0
end, end,
damage_creatures = function(player, pos, controlling_coordinate) damage_creatures = function(player, pos, controlling_coordinate, items_dropped)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local facing = minetest.get_node(pos).param2 local facing = minetest.get_node(pos).param2
local targetpos = digtron.find_new_pos(pos, facing) local targetpos = digtron.find_new_pos(pos, facing)
if (targetpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") == 0 then if (targetpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") == 0 then
digtron.damage_creatures(player, targetpos, 4) digtron.damage_creatures(player, pos, targetpos, damage_hp_half, items_dropped)
end end
end, end,
}) })
@ -398,26 +401,30 @@ minetest.register_node("digtron:dual_digger", {
if protected_nodes:get(digpos.x, digpos.y, digpos.z) ~= true then if protected_nodes:get(digpos.x, digpos.y, digpos.z) ~= true then
local forward_cost, forward_items = digtron.mark_diggable(digpos, nodes_dug, player) local forward_cost, forward_items = digtron.mark_diggable(digpos, nodes_dug, player)
if forward_items ~= nil then
for _, item in pairs(forward_items) do for _, item in pairs(forward_items) do
table.insert(items, item) table.insert(items, item)
end end
end
cost = cost + forward_cost cost = cost + forward_cost
end end
if protected_nodes:get(digdown.x, digdown.y, digdown.z) ~= true then if protected_nodes:get(digdown.x, digdown.y, digdown.z) ~= true then
local down_cost, down_items = digtron.mark_diggable(digdown, nodes_dug, player) local down_cost, down_items = digtron.mark_diggable(digdown, nodes_dug, player)
if down_items ~= nil then
for _, item in pairs(down_items) do for _, item in pairs(down_items) do
table.insert(items, item) table.insert(items, item)
end end
end
cost = cost + down_cost cost = cost + down_cost
end end
return cost, items return cost, items
end, end,
damage_creatures = function(player, pos, controlling_coordinate) damage_creatures = function(player, pos, controlling_coordinate, items_dropped)
local facing = minetest.get_node(pos).param2 local facing = minetest.get_node(pos).param2
digtron.damage_creatures(player, digtron.find_new_pos(pos, facing), 8) digtron.damage_creatures(player, pos, digtron.find_new_pos(pos, facing), damage_hp, items_dropped)
digtron.damage_creatures(player, digtron.find_new_pos_downward(pos, facing), 8) digtron.damage_creatures(player, pos, digtron.find_new_pos_downward(pos, facing), damage_hp, items_dropped)
end, end,
}) })
@ -475,25 +482,29 @@ minetest.register_node("digtron:dual_soft_digger", {
if protected_nodes:get(digpos.x, digpos.y, digpos.z) ~= true and digtron.is_soft_material(digpos) then if protected_nodes:get(digpos.x, digpos.y, digpos.z) ~= true and digtron.is_soft_material(digpos) then
local forward_cost, forward_items = digtron.mark_diggable(digpos, nodes_dug, player) local forward_cost, forward_items = digtron.mark_diggable(digpos, nodes_dug, player)
if forward_items ~= nil then
for _, item in pairs(forward_items) do for _, item in pairs(forward_items) do
table.insert(items, item) table.insert(items, item)
end end
end
cost = cost + forward_cost cost = cost + forward_cost
end end
if protected_nodes:get(digdown.x, digdown.y, digdown.z) ~= true and digtron.is_soft_material(digdown) then if protected_nodes:get(digdown.x, digdown.y, digdown.z) ~= true and digtron.is_soft_material(digdown) then
local down_cost, down_items = digtron.mark_diggable(digdown, nodes_dug, player) local down_cost, down_items = digtron.mark_diggable(digdown, nodes_dug, player)
if down_items ~= nil then
for _, item in pairs(down_items) do for _, item in pairs(down_items) do
table.insert(items, item) table.insert(items, item)
end end
end
cost = cost + down_cost cost = cost + down_cost
end end
return cost, items return cost, items
end, end,
damage_creatures = function(player, pos, controlling_coordinate) damage_creatures = function(player, pos, controlling_coordinate, items_dropped)
local facing = minetest.get_node(pos).param2 local facing = minetest.get_node(pos).param2
digtron.damage_creatures(player, digtron.find_new_pos(pos, facing), 4) digtron.damage_creatures(player, pos, digtron.find_new_pos(pos, facing), damage_hp_half, items_dropped)
digtron.damage_creatures(player, digtron.find_new_pos_downward(pos, facing), 4) digtron.damage_creatures(player, pos, digtron.find_new_pos_downward(pos, facing), damage_hp_half, items_dropped)
end, end,
}) })

@ -144,6 +144,7 @@ minetest.register_node("digtron:duplicator", {
end end
-- clear inventories of image's nodes -- clear inventories of image's nodes
if layout.inventories ~= nil then
for _, node_image in pairs(layout.inventories) do for _, node_image in pairs(layout.inventories) do
local main_inventory = node_image.meta.inventory.main local main_inventory = node_image.meta.inventory.main
if type(main_inventory) ~= "table" then if type(main_inventory) ~= "table" then
@ -153,18 +154,23 @@ minetest.register_node("digtron:duplicator", {
main_inventory[index] = ItemStack(nil) main_inventory[index] = ItemStack(nil)
end end
end end
end
if layout.fuelstores ~= nil then
for _, node_image in pairs(layout.fuelstores) do for _, node_image in pairs(layout.fuelstores) do
local fuel_inventory = node_image.meta.inventory.fuel local fuel_inventory = node_image.meta.inventory.fuel
for index, _ in pairs(fuel_inventory) do for index, _ in pairs(fuel_inventory) do
fuel_inventory[index] = ItemStack(nil) fuel_inventory[index] = ItemStack(nil)
end end
end end
end
if layout.battery_holders ~= nil then
for _, node_image in pairs(layout.battery_holders) do for _, node_image in pairs(layout.battery_holders) do
local battery_inventory = node_image.meta.inventory.batteries local battery_inventory = node_image.meta.inventory.batteries
for index, _ in pairs(battery_inventory) do for index, _ in pairs(battery_inventory) do
battery_inventory[index] = ItemStack(nil) battery_inventory[index] = ItemStack(nil)
end end
end end
end
-- replace empty crate with loaded crate and write image to its metadata -- replace empty crate with loaded crate and write image to its metadata
local layout_string = layout:serialize() local layout_string = layout:serialize()

@ -58,15 +58,17 @@ local function eject_items(pos, node, player, eject_even_without_pipeworks)
-- Build a list of all the items that builder nodes want to use. -- Build a list of all the items that builder nodes want to use.
local filter_items = {} local filter_items = {}
if layout.builders ~= nil then
for _, node_image in pairs(layout.builders) do for _, node_image in pairs(layout.builders) do
filter_items[node_image.meta.inventory.main[1]:get_name()] = true filter_items[node_image.meta.inventory.main[1]:get_name()] = true
end end
end
-- Look through the inventories and find an item that's not on that list. -- Look through the inventories and find an item that's not on that list.
local source_node = nil local source_node = nil
local source_index = nil local source_index = nil
local source_stack = nil local source_stack = nil
for _, node_image in pairs(layout.inventories) do for _, node_image in pairs(layout.inventories or {}) do
if type(node_image.meta.inventory.main) ~= "table" then if type(node_image.meta.inventory.main) ~= "table" then
node_image.meta.inventory.main = {} node_image.meta.inventory.main = {}
end end

@ -69,12 +69,16 @@ minetest.register_node("digtron:power_connector", {
on_receive_fields = function(pos, formname, fields, sender) on_receive_fields = function(pos, formname, fields, sender)
local layout = DigtronLayout.create(pos, sender) local layout = DigtronLayout.create(pos, sender)
local max_cost = 0 local max_cost = 0
if layout.builders ~= nil then
for _, node_image in pairs(layout.builders) do for _, node_image in pairs(layout.builders) do
max_cost = max_cost + (digtron.config.build_cost * (node_image.meta.fields.extrusion or 1)) max_cost = max_cost + (digtron.config.build_cost * (node_image.meta.fields.extrusion or 1))
end end
end
if layout.diggers ~= nil then
for _, node_image in pairs(layout.diggers) do for _, node_image in pairs(layout.diggers) do
max_cost = max_cost + max_dig_cost max_cost = max_cost + max_dig_cost
end end
end
local current_max = max_cost * digtron.config.power_ratio local current_max = max_cost * digtron.config.power_ratio
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)

@ -6,8 +6,9 @@ digtron_uses_resources (Digtron uses resources) bool true
#When true, lava counts as protected blocks. #When true, lava counts as protected blocks.
digtron_lava_impassible (Lava is impassible to Digtrons) bool true digtron_lava_impassible (Lava is impassible to Digtrons) bool true
#When true, diggers deal damage to creatures when they trigger. #Sets how much HP damage a digger does. Soft material diggers do half this.
digtron_damage_creatures (Digtrons cause damage) bool true #Set to 0 to disable damage entirely.
digtron_damage_hp (Diggers damage this many hp) int 8
#How many seconds a digtron waits between cycles. #How many seconds a digtron waits between cycles.
#Auto-controllers can make this wait longer, but cannot make it shorter. #Auto-controllers can make this wait longer, but cannot make it shorter.

119
util.lua

@ -3,6 +3,8 @@
dofile( minetest.get_modpath( "digtron" ) .. "/util_item_place_node.lua" ) -- separated out to avoid potential for license complexity dofile( minetest.get_modpath( "digtron" ) .. "/util_item_place_node.lua" ) -- separated out to avoid potential for license complexity
dofile( minetest.get_modpath( "digtron" ) .. "/util_execute_cycle.lua" ) -- separated out simply for tidiness, there's some big code in there dofile( minetest.get_modpath( "digtron" ) .. "/util_execute_cycle.lua" ) -- separated out simply for tidiness, there's some big code in there
local node_inventory_table = {type="node"} -- a reusable parameter for get_inventory calls, set the pos parameter before using.
-- Apparently node_sound_metal_defaults is a newer thing, I ran into games using an older version of the default mod without it. -- Apparently node_sound_metal_defaults is a newer thing, I ran into games using an older version of the default mod without it.
if default.node_sound_metal_defaults ~= nil then if default.node_sound_metal_defaults ~= nil then
digtron.metal_sounds = default.node_sound_metal_defaults() digtron.metal_sounds = default.node_sound_metal_defaults()
@ -17,14 +19,16 @@ digtron.find_new_pos = function(pos, facing)
return vector.add(pos, dir) return vector.add(pos, dir)
end end
digtron.facedir_to_down_dir = function(facing) local facedir_to_down_dir_table = {
return ( [0]={x=0, y=-1, z=0},
{[0]={x=0, y=-1, z=0},
{x=0, y=0, z=-1}, {x=0, y=0, z=-1},
{x=0, y=0, z=1}, {x=0, y=0, z=1},
{x=-1, y=0, z=0}, {x=-1, y=0, z=0},
{x=1, y=0, z=0}, {x=1, y=0, z=0},
{x=0, y=1, z=0}})[math.floor(facing/4)] {x=0, y=1, z=0}
}
digtron.facedir_to_down_dir = function(facing)
return facedir_to_down_dir_table[math.floor(facing/4)]
end end
digtron.find_new_pos_downward = function(pos, facing) digtron.find_new_pos_downward = function(pos, facing)
@ -42,7 +46,7 @@ digtron.mark_diggable = function(pos, nodes_dug, player)
-- prevent digtrons from being marked for digging. -- prevent digtrons from being marked for digging.
if minetest.get_item_group(target.name, "digtron") ~= 0 or minetest.get_item_group(target.name, "digtron_protected") ~= 0 then if minetest.get_item_group(target.name, "digtron") ~= 0 or minetest.get_item_group(target.name, "digtron_protected") ~= 0 then
return 0, {} return 0
end end
local targetdef = minetest.registered_nodes[target.name] local targetdef = minetest.registered_nodes[target.name]
@ -73,7 +77,7 @@ digtron.mark_diggable = function(pos, nodes_dug, player)
return material_cost, minetest.get_node_drops(target.name, "") return material_cost, minetest.get_node_drops(target.name, "")
end end
end end
return 0, {} return 0
end end
digtron.can_build_to = function(pos, protected_nodes, dug_nodes) digtron.can_build_to = function(pos, protected_nodes, dug_nodes)
@ -108,13 +112,16 @@ end
digtron.place_in_inventory = function(itemname, inventory_positions, fallback_pos) digtron.place_in_inventory = function(itemname, inventory_positions, fallback_pos)
--tries placing the item in each inventory node in turn. If there's no room, drop it at fallback_pos --tries placing the item in each inventory node in turn. If there's no room, drop it at fallback_pos
local itemstack = ItemStack(itemname) local itemstack = ItemStack(itemname)
if inventory_positions ~= nil then
for k, location in pairs(inventory_positions) do for k, location in pairs(inventory_positions) do
local inv = minetest.get_inventory({type="node", pos=location.pos}) node_inventory_table.pos = location.pos
local inv = minetest.get_inventory(node_inventory_table)
itemstack = inv:add_item("main", itemstack) itemstack = inv:add_item("main", itemstack)
if itemstack:is_empty() then if itemstack:is_empty() then
return nil return nil
end end
end end
end
minetest.add_item(fallback_pos, itemstack) minetest.add_item(fallback_pos, itemstack)
end end
@ -124,7 +131,8 @@ digtron.place_in_specific_inventory = function(itemname, pos, inventory_position
--is trying to keep various inventories organized manually stuff will go back where it came from, --is trying to keep various inventories organized manually stuff will go back where it came from,
--probably. --probably.
local itemstack = ItemStack(itemname) local itemstack = ItemStack(itemname)
local inv = minetest.get_inventory({type="node", pos=pos}) node_inventory_table.pos = pos
local inv = minetest.get_inventory(node_inventory_table)
local returned_stack = inv:add_item("main", itemstack) local returned_stack = inv:add_item("main", itemstack)
if not returned_stack:is_empty() then if not returned_stack:is_empty() then
-- we weren't able to put the item back into that particular inventory for some reason. -- we weren't able to put the item back into that particular inventory for some reason.
@ -134,10 +142,12 @@ digtron.place_in_specific_inventory = function(itemname, pos, inventory_position
end end
digtron.take_from_inventory = function(itemname, inventory_positions) digtron.take_from_inventory = function(itemname, inventory_positions)
if inventory_positions == nil then return nil end
--tries to take an item from each inventory node in turn. Returns location of inventory item was taken from on success, nil on failure --tries to take an item from each inventory node in turn. Returns location of inventory item was taken from on success, nil on failure
local itemstack = ItemStack(itemname) local itemstack = ItemStack(itemname)
for k, location in pairs(inventory_positions) do for k, location in pairs(inventory_positions) do
local inv = minetest.get_inventory({type="node", pos=location.pos}) node_inventory_table.pos = location.pos
local inv = minetest.get_inventory(node_inventory_table)
local output = inv:remove_item("main", itemstack) local output = inv:remove_item("main", itemstack)
if not output:is_empty() then if not output:is_empty() then
return location.pos return location.pos
@ -159,24 +169,31 @@ digtron.get_controlling_coordinate = function(pos, facedir)
end end
end end
local fuel_craft = {method="fuel", width=1, items={}} -- reusable crafting recipe table for get_craft_result calls below
-- Searches fuel store inventories for burnable items and burns them until target is reached or surpassed -- Searches fuel store inventories for burnable items and burns them until target is reached or surpassed
-- (or there's nothing left to burn). Returns the total fuel value burned -- (or there's nothing left to burn). Returns the total fuel value burned
-- if the "test" parameter is set to true, doesn't actually take anything out of inventories. -- if the "test" parameter is set to true, doesn't actually take anything out of inventories.
-- We can get away with this sort of thing for fuel but not for builder inventory because there's just one -- We can get away with this sort of thing for fuel but not for builder inventory because there's just one
-- controller node burning stuff, not multiple build heads drawing from inventories in turn. Much simpler. -- controller node burning stuff, not multiple build heads drawing from inventories in turn. Much simpler.
digtron.burn = function(fuelstore_positions, target, test) digtron.burn = function(fuelstore_positions, target, test)
if fuelstore_positions == nil then
return 0
end
local current_burned = 0 local current_burned = 0
for k, location in pairs(fuelstore_positions) do for k, location in pairs(fuelstore_positions) do
if current_burned > target then if current_burned > target then
break break
end end
local inv = minetest.get_inventory({type="node", pos=location.pos}) node_inventory_table.pos = location.pos
local inv = minetest.get_inventory(node_inventory_table)
local invlist = inv:get_list("fuel") local invlist = inv:get_list("fuel")
for i, itemstack in pairs(invlist) do for i, itemstack in pairs(invlist) do
local fuel_per_item = minetest.get_craft_result({method="fuel", width=1, items={itemstack:peek_item(1)}}).time fuel_craft.items[1] = itemstack:peek_item(1)
local fuel_per_item = minetest.get_craft_result(fuel_craft).time
if fuel_per_item ~= 0 then if fuel_per_item ~= 0 then
local actual_burned = math.min( local actual_burned = math.min(
math.ceil((target - current_burned)/fuel_per_item ), -- burn this many, if we can. math.ceil((target - current_burned)/fuel_per_item), -- burn this many, if we can.
itemstack:get_count() -- how many we have at most. itemstack:get_count() -- how many we have at most.
) )
if test ~= true then if test ~= true then
@ -203,6 +220,10 @@ end
-- factor, since if taken at face value (10000 EU), the batteries would be the ultimate power source barely -- factor, since if taken at face value (10000 EU), the batteries would be the ultimate power source barely
-- ever needing replacement. -- ever needing replacement.
digtron.tap_batteries = function(battery_positions, target, test) digtron.tap_batteries = function(battery_positions, target, test)
if (battery_positions == nil) then
return 0
end
local current_burned = 0 local current_burned = 0
-- 1 coal block is 370 PU -- 1 coal block is 370 PU
-- 1 coal lump is 40 PU -- 1 coal lump is 40 PU
@ -210,15 +231,12 @@ digtron.tap_batteries = function(battery_positions, target, test)
-- local power_ratio = 100 -- How much charge equals 1 unit of PU from coal -- local power_ratio = 100 -- How much charge equals 1 unit of PU from coal
-- setting Moved to digtron.config.power_ratio -- setting Moved to digtron.config.power_ratio
if (battery_positions == nil) then
return 0
end
for k, location in pairs(battery_positions) do for k, location in pairs(battery_positions) do
if current_burned > target then if current_burned > target then
break break
end end
local inv = minetest.get_inventory({type="node", pos=location.pos}) node_inventory_table.pos = location.pos
local inv = minetest.get_inventory(node_inventory_table)
local invlist = inv:get_list("batteries") local invlist = inv:get_list("batteries")
if (invlist == nil) then if (invlist == nil) then
@ -272,10 +290,6 @@ digtron.tap_batteries = function(battery_positions, target, test)
return current_burned return current_burned
end end
digtron.remove_builder_item = function(pos) digtron.remove_builder_item = function(pos)
local objects = minetest.env:get_objects_inside_radius(pos, 0.5) local objects = minetest.env:get_objects_inside_radius(pos, 0.5)
if objects ~= nil then if objects ~= nil then
@ -289,7 +303,8 @@ end
digtron.update_builder_item = function(pos) digtron.update_builder_item = function(pos)
digtron.remove_builder_item(pos) digtron.remove_builder_item(pos)
local inv = minetest.get_inventory({type="node", pos=pos}) node_inventory_table.pos = pos
local inv = minetest.get_inventory(node_inventory_table)
local item_stack = inv:get_stack("main", 1) local item_stack = inv:get_stack("main", 1)
if not item_stack:is_empty() then if not item_stack:is_empty() then
digtron.create_builder_item = item_stack:get_name() digtron.create_builder_item = item_stack:get_name()
@ -297,15 +312,61 @@ digtron.update_builder_item = function(pos)
end end
end end
digtron.damage_creatures = function(player, pos, amount) local damage_def = {
local objects = minetest.env:get_objects_inside_radius(pos, 1.0) full_punch_interval = 1.0,
damage_groups = {},
}
digtron.damage_creatures = function(player, source_pos, target_pos, amount, items_dropped)
local objects = minetest.env:get_objects_inside_radius(target_pos, 1.0)
if objects ~= nil then
damage_def.damage_groups.fleshy = amount
local velocity = {
x = target_pos.x-source_pos.x,
y = target_pos.y-source_pos.y + 0.2,
z = target_pos.z-source_pos.z,
}
for _, obj in ipairs(objects) do
if obj:is_player() then
-- See issue #2960 for status of a "set player velocity" method
-- instead, knock the player back
newpos = {
x = target_pos.x + velocity.x,
y = target_pos.y + velocity.y,
z = target_pos.z + velocity.z,
}
obj:set_pos(newpos)
obj:punch(player, 1.0, damage_def, nil)
else
local lua_entity = obj:get_luaentity()
if lua_entity ~= nil then
if lua_entity.name == "__builtin:item" then
table.insert(items_dropped, lua_entity.itemstring)
lua_entity.itemstring = ""
obj:remove()
else
if obj.add_velocity ~= nil then
obj:add_velocity(velocity)
else
local vel = obj:get_velocity()
obj:set_velocity(vector.add(vel, velocity))
end
obj:punch(player, 1.0, damage_def, nil)
end
end
end
end
end
-- If we killed any mobs they might have dropped some stuff, vacuum that up now too.
objects = minetest.env:get_objects_inside_radius(target_pos, 1.0)
if objects ~= nil then if objects ~= nil then
for _, obj in ipairs(objects) do for _, obj in ipairs(objects) do
if obj then if not obj:is_player() then
obj:punch(player, 1.0, { local lua_entity = obj:get_luaentity()
full_punch_interval = 1.0, if lua_entity ~= nil and lua_entity.name == "__builtin:item" then
damage_groups = {fleshy = amount}, table.insert(items_dropped, lua_entity.itemstring)
}, nil ) lua_entity.itemstring = ""
obj:remove()
end
end end
end end
end end

@ -80,8 +80,10 @@ local function move_player_test(layout, player)
return false return false
end end
local node_inventory_table = {type="node"} -- a reusable parameter for get_inventory calls, set the pos parameter before using.
local function test_stop_block(pos, items) local function test_stop_block(pos, items)
local inv = minetest.get_inventory({type="node", pos=pos}) node_inventory_table.pos = pos
local inv = minetest.get_inventory(node_inventory_table)
local item_stack = inv:get_stack("stop", 1) local item_stack = inv:get_stack("stop", 1)
if not item_stack:is_empty() then if not item_stack:is_empty() then
for _, item in pairs(items) do for _, item in pairs(items) do
@ -129,12 +131,13 @@ digtron.execute_dig_cycle = function(pos, clicker)
-- This builds a set of nodes that will be dug and returns a list of products that will be generated -- This builds a set of nodes that will be dug and returns a list of products that will be generated
-- but doesn't actually dig the nodes yet. That comes later. -- but doesn't actually dig the nodes yet. That comes later.
-- If we dug them now, sand would fall and some digtron nodes would die. -- If we dug them now, sand would fall and some digtron nodes would die.
if layout.diggers ~= nil then
for k, location in pairs(layout.diggers) do for k, location in pairs(layout.diggers) do
local target = minetest.get_node(location.pos) local target = minetest.get_node(location.pos)
local targetdef = minetest.registered_nodes[target.name] local targetdef = minetest.registered_nodes[target.name]
if targetdef.execute_dig ~= nil then if targetdef.execute_dig ~= nil then
local fuel_cost, dropped = targetdef.execute_dig(location.pos, layout.protected, layout.nodes_dug, controlling_coordinate, false, clicker) local fuel_cost, dropped = targetdef.execute_dig(location.pos, layout.protected, layout.nodes_dug, controlling_coordinate, false, clicker)
if table.getn(dropped) > 0 then if dropped ~= nil then
for _, itemname in pairs(dropped) do for _, itemname in pairs(dropped) do
table.insert(items_dropped, itemname) table.insert(items_dropped, itemname)
end end
@ -147,6 +150,7 @@ digtron.execute_dig_cycle = function(pos, clicker)
minetest.log(string.format("%s has digger group but is missing execute_dig method! This is an error in mod programming, file a bug.", targetdef.name)) minetest.log(string.format("%s has digger group but is missing execute_dig method! This is an error in mod programming, file a bug.", targetdef.name))
end end
end end
end
---------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------
@ -186,6 +190,7 @@ digtron.execute_dig_cycle = function(pos, clicker)
local test_items = {} local test_items = {}
local test_fuel_items = {} local test_fuel_items = {}
local test_build_fuel_cost = 0 local test_build_fuel_cost = 0
if layout.builders ~= nil then
for k, location in pairs(layout.builders) do for k, location in pairs(layout.builders) do
local target = minetest.get_node(location.pos) local target = minetest.get_node(location.pos)
local targetdef = minetest.registered_nodes[target.name] local targetdef = minetest.registered_nodes[target.name]
@ -204,12 +209,14 @@ digtron.execute_dig_cycle = function(pos, clicker)
minetest.log(string.format("%s has builder group but is missing test_build method! This is an error in mod programming, file a bug.", targetdef.name)) minetest.log(string.format("%s has builder group but is missing test_build method! This is an error in mod programming, file a bug.", targetdef.name))
end end
end end
end
local test_fuel_needed = test_build_fuel_cost + digging_fuel_cost - fuel_burning local test_fuel_needed = test_build_fuel_cost + digging_fuel_cost - fuel_burning
local test_fuel_burned = 0 local test_fuel_burned = 0
local power_from_cables = 0 local power_from_cables = 0
if minetest.get_modpath("technic") then if minetest.get_modpath("technic") then
if layout.power_connectors ~= nil then
local power_inputs = {} local power_inputs = {}
for _, power_connector in pairs(layout.power_connectors) do for _, power_connector in pairs(layout.power_connectors) do
if power_connector.meta.fields.HV_network and power_connector.meta.fields.HV_EU_input then if power_connector.meta.fields.HV_network and power_connector.meta.fields.HV_EU_input then
@ -221,6 +228,7 @@ digtron.execute_dig_cycle = function(pos, clicker)
end end
power_from_cables = power_from_cables / digtron.config.power_ratio power_from_cables = power_from_cables / digtron.config.power_ratio
test_fuel_burned = power_from_cables test_fuel_burned = power_from_cables
end
if test_fuel_needed - test_fuel_burned > 0 then if test_fuel_needed - test_fuel_burned > 0 then
-- check for the available electrical power -- check for the available electrical power
@ -271,12 +279,12 @@ digtron.execute_dig_cycle = function(pos, clicker)
local move_player = move_player_test(layout, clicker) local move_player = move_player_test(layout, clicker)
-- damage the weak flesh -- damage the weak flesh
if digtron.config.damage_creatures then if digtron.config.damage_hp > 0 and layout.diggers ~= nil then
for k, location in pairs(layout.diggers) do for k, location in pairs(layout.diggers) do
local target = minetest.get_node(location.pos) local target = minetest.get_node(location.pos)
local targetdef = minetest.registered_nodes[target.name] local targetdef = minetest.registered_nodes[target.name]
if targetdef.damage_creatures ~= nil then if targetdef.damage_creatures ~= nil then
targetdef.damage_creatures(clicker, location.pos, controlling_coordinate) targetdef.damage_creatures(clicker, location.pos, controlling_coordinate, items_dropped)
end end
end end
end end
@ -302,6 +310,7 @@ digtron.execute_dig_cycle = function(pos, clicker)
local building_fuel_cost = 0 local building_fuel_cost = 0
local strange_failure = false local strange_failure = false
-- execute_build on all digtron components that have one -- execute_build on all digtron components that have one
if layout.builders ~= nil then
for k, location in pairs(layout.builders) do for k, location in pairs(layout.builders) do
local target = minetest.get_node(location.pos) local target = minetest.get_node(location.pos)
local targetdef = minetest.registered_nodes[target.name] local targetdef = minetest.registered_nodes[target.name]
@ -321,7 +330,9 @@ digtron.execute_dig_cycle = function(pos, clicker)
minetest.log(string.format("%s has builder group but is missing execute_build method! This is an error in mod programming, file a bug.", targetdef.name)) minetest.log(string.format("%s has builder group but is missing execute_build method! This is an error in mod programming, file a bug.", targetdef.name))
end end
end end
end
if layout.auto_ejectors ~= nil then
for k, location in pairs(layout.auto_ejectors) do for k, location in pairs(layout.auto_ejectors) do
local target = minetest.get_node(location.pos) local target = minetest.get_node(location.pos)
local targetdef = minetest.registered_nodes[target.name] local targetdef = minetest.registered_nodes[target.name]
@ -331,6 +342,7 @@ digtron.execute_dig_cycle = function(pos, clicker)
minetest.log(string.format("%s has an ejector group but is missing execute_eject method! This is an error in mod programming, file a bug.", targetdef.name)) minetest.log(string.format("%s has an ejector group but is missing execute_eject method! This is an error in mod programming, file a bug.", targetdef.name))
end end
end end
end
local status_text = "" local status_text = ""
if strange_failure then if strange_failure then
@ -460,12 +472,13 @@ digtron.execute_downward_dig_cycle = function(pos, clicker)
-- This builds a set of nodes that will be dug and returns a list of products that will be generated -- This builds a set of nodes that will be dug and returns a list of products that will be generated
-- but doesn't actually dig the nodes yet. That comes later. -- but doesn't actually dig the nodes yet. That comes later.
-- If we dug them now, sand would fall and some digtron nodes would die. -- If we dug them now, sand would fall and some digtron nodes would die.
if layout.diggers ~= nil then
for k, location in pairs(layout.diggers) do for k, location in pairs(layout.diggers) do
local target = minetest.get_node(location.pos) local target = minetest.get_node(location.pos)
local targetdef = minetest.registered_nodes[target.name] local targetdef = minetest.registered_nodes[target.name]
if targetdef.execute_dig ~= nil then if targetdef.execute_dig ~= nil then
local fuel_cost, dropped = targetdef.execute_dig(location.pos, layout.protected, layout.nodes_dug, controlling_coordinate, true, clicker) local fuel_cost, dropped = targetdef.execute_dig(location.pos, layout.protected, layout.nodes_dug, controlling_coordinate, true, clicker)
if table.getn(dropped) > 0 then if dropped ~= nil then
for _, itemname in pairs(dropped) do for _, itemname in pairs(dropped) do
table.insert(items_dropped, itemname) table.insert(items_dropped, itemname)
end end
@ -478,6 +491,7 @@ digtron.execute_downward_dig_cycle = function(pos, clicker)
minetest.log(string.format("%s has digger group but is missing execute_dig method! This is an error in mod programming, file a bug.", targetdef.name)) minetest.log(string.format("%s has digger group but is missing execute_dig method! This is an error in mod programming, file a bug.", targetdef.name))
end end
end end
end
---------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------
@ -513,7 +527,7 @@ digtron.execute_downward_dig_cycle = function(pos, clicker)
local move_player = move_player_test(layout, clicker) local move_player = move_player_test(layout, clicker)
-- damage the weak flesh -- damage the weak flesh
if digtron.config.damage_creatures then if digtron.config.damage_hp > 0 and layout.diggers ~= nil then
for k, location in pairs(layout.diggers) do for k, location in pairs(layout.diggers) do
local target = minetest.get_node(location.pos) local target = minetest.get_node(location.pos)
local targetdef = minetest.registered_nodes[target.name] local targetdef = minetest.registered_nodes[target.name]
@ -574,9 +588,6 @@ digtron.execute_downward_dig_cycle = function(pos, clicker)
minetest.log("action", string.format("%s uses Digtron to dig %s at (%d, %d, %d)", clicker:get_player_name(), minetest.get_node(node_to_dig).name, node_to_dig.x, node_to_dig.y, node_to_dig.z)) minetest.log("action", string.format("%s uses Digtron to dig %s at (%d, %d, %d)", clicker:get_player_name(), minetest.get_node(node_to_dig).name, node_to_dig.x, node_to_dig.y, node_to_dig.z))
minetest.remove_node(node_to_dig) minetest.remove_node(node_to_dig)
end end
-- all of the digtron's nodes wind up in nodes_dug, so this is an ideal place to stick
-- a check to make sand fall after the digtron has passed.
--minetest.check_for_falling({x=node_to_dig.x, y=node_to_dig.y+1, z=node_to_dig.z})
node_to_dig, whether_to_dig = layout.nodes_dug:pop() node_to_dig, whether_to_dig = layout.nodes_dug:pop()
end end
return pos, status_text, 0 return pos, status_text, 0